From 7b3b51c8022c03c7f543e9da91e8c58eea0f6d39 Mon Sep 17 00:00:00 2001 From: tomkl123 Date: Wed, 27 Aug 2025 10:50:22 +0800 Subject: [PATCH 1/2] scrollable components support contentStartOffset and contentEndOffset Signed-off-by: tomkl123 Change-Id: I3fccddff1527d7796ce69c32b155a736c050e89b --- .../jsview/js_scrollable_base.cpp | 14 ++ .../jsview/js_scrollable_base.h | 2 + .../core/components_ng/pattern/BUILD.gn | 1 + .../grid/grid_layout_base_algorithm.cpp | 13 ++ .../pattern/grid/grid_layout_base_algorithm.h | 2 + .../pattern/grid/grid_layout_info.cpp | 8 +- .../pattern/grid/grid_layout_info.h | 2 + .../pattern/grid/grid_layout_property.cpp | 2 +- .../pattern/grid/grid_layout_property.h | 5 +- .../pattern/grid/grid_pattern.cpp | 77 ++++++--- .../components_ng/pattern/grid/grid_pattern.h | 8 +- .../grid_scroll_layout_algorithm.cpp | 47 ++++-- .../grid_irregular_layout_algorithm.cpp | 46 ++++-- .../overlay/sheet_presentation_pattern.cpp | 2 +- .../scroll/scroll_layout_algorithm.cpp | 32 +++- .../pattern/scroll/scroll_layout_algorithm.h | 14 ++ .../pattern/scroll/scroll_layout_property.h | 10 +- .../pattern/scroll/scroll_pattern.cpp | 31 ++-- .../pattern/scroll/scroll_pattern.h | 2 + .../scrollable/scrollable_layout_property.cpp | 30 ++++ .../scrollable/scrollable_layout_property.h | 59 +++++++ .../scrollable/scrollable_model_ng.cpp | 11 ++ .../pattern/scrollable/scrollable_model_ng.h | 2 + .../pattern/scrollable/scrollable_pattern.h | 5 + .../water_flow_layout_info_sw.cpp | 25 +-- .../water_flow_layout_info_sw.h | 2 +- .../sliding_window/water_flow_layout_sw.cpp | 18 +- .../top_down/water_flow_layout_algorithm.cpp | 28 ++-- .../top_down/water_flow_layout_info.cpp | 17 +- .../layout/top_down/water_flow_layout_info.h | 2 +- .../top_down/water_flow_segmented_layout.cpp | 9 +- .../water_flow_layout_algorithm_base.cpp | 14 ++ .../layout/water_flow_layout_algorithm_base.h | 3 + .../layout/water_flow_layout_info_base.h | 2 + .../waterflow/water_flow_layout_property.h | 5 +- test/unittest/BUILD.gn | 1 + .../grid/grid_cache_layout_test_ng.cpp | 4 +- .../grid/grid_option_layout_test_ng.cpp | 73 +++++++++ .../pattern/grid/grid_pattern_test_ng.cpp | 155 ++++++++++++++++++ .../grid/grid_scroll_layout_testtwo_ng.cpp | 46 +++++- .../pattern/grid/grid_scroller_test_ng.cpp | 72 ++++++++ .../unittest/core/pattern/grid/grid_test_ng.h | 1 + .../overlay/overlay_manager_test_ng.cpp | 4 +- .../pattern/scroll/scroll_layout_test_ng.cpp | 97 +++++++++++ .../scroll/scroll_pattern_test_ng_three.cpp | 128 +++++++++++++++ .../core/pattern/scroll/scroll_test_ng.h | 1 + .../waterflow/water_flow_scroller_test_ng.cpp | 2 +- .../water_flow_segment_layout_test.cpp | 50 +++++- .../pattern/waterflow/water_flow_sw_test.cpp | 64 ++++++++ .../pattern/waterflow/water_flow_test_ng.h | 1 + .../waterflow/water_flow_top_down_test.cpp | 61 +++++++ 51 files changed, 1171 insertions(+), 139 deletions(-) create mode 100644 frameworks/core/components_ng/pattern/scrollable/scrollable_layout_property.cpp create mode 100644 frameworks/core/components_ng/pattern/scrollable/scrollable_layout_property.h diff --git a/frameworks/bridge/declarative_frontend/jsview/js_scrollable_base.cpp b/frameworks/bridge/declarative_frontend/jsview/js_scrollable_base.cpp index e5f886e62ee..de0d68c49e6 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_scrollable_base.cpp +++ b/frameworks/bridge/declarative_frontend/jsview/js_scrollable_base.cpp @@ -198,4 +198,18 @@ void JSScrollableBase::JSBackToTop(const JSCallbackInfo& info) NG::ScrollableModelNG::ResetBackToTop(); } } + +void JSScrollableBase::SetContentStartOffset(const JSCallbackInfo& info) +{ + double value = 0.0; + ParseJsDouble(info[0], value); + NG::ScrollableModelNG::SetContentStartOffset(value); +} + +void JSScrollableBase::SetContentEndOffset(const JSCallbackInfo& info) +{ + double value = 0.0; + ParseJsDouble(info[0], value); + NG::ScrollableModelNG::SetContentEndOffset(value); +} } // namespace OHOS::Ace::Framework diff --git a/frameworks/bridge/declarative_frontend/jsview/js_scrollable_base.h b/frameworks/bridge/declarative_frontend/jsview/js_scrollable_base.h index 42d3c326058..e1629689dd2 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_scrollable_base.h +++ b/frameworks/bridge/declarative_frontend/jsview/js_scrollable_base.h @@ -31,6 +31,8 @@ public: static void SetScrollBarMargin(const JSCallbackInfo& info); static void JSBackToTop(const JSCallbackInfo& info); static void JSOnWillStopDragging(const JSCallbackInfo& args); + static void SetContentStartOffset(const JSCallbackInfo& info); + static void SetContentEndOffset(const JSCallbackInfo& info); }; } // namespace OHOS::Ace::Framework #endif // FRAMEWORKS_BRIDGE_DECLARATIVE_FRONTEND_JS_VIEW_JS_SCROLLABLE_BASE_H diff --git a/frameworks/core/components_ng/pattern/BUILD.gn b/frameworks/core/components_ng/pattern/BUILD.gn index 2e8bef33107..822f8f16017 100644 --- a/frameworks/core/components_ng/pattern/BUILD.gn +++ b/frameworks/core/components_ng/pattern/BUILD.gn @@ -466,6 +466,7 @@ build_component_ng("pattern_ng") { "scrollable/scrollable_controller_multi_thread.cpp", "scrollable/scrollable_item.cpp", "scrollable/scrollable_item_pool.cpp", + "scrollable/scrollable_layout_property.cpp", "scrollable/scrollable_model_ng.cpp", "scrollable/scrollable_model_static.cpp", "scrollable/scrollable_model_ng_multi_thread.cpp", diff --git a/frameworks/core/components_ng/pattern/grid/grid_layout_base_algorithm.cpp b/frameworks/core/components_ng/pattern/grid/grid_layout_base_algorithm.cpp index db6eecca2f5..21ae927dcdf 100644 --- a/frameworks/core/components_ng/pattern/grid/grid_layout_base_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/grid/grid_layout_base_algorithm.cpp @@ -108,4 +108,17 @@ void GridLayoutBaseAlgorithm::LostChildFocusToSelf(LayoutWrapper* layoutWrapper, focusHub->LostChildFocusToSelf(); } } + +void GridLayoutBaseAlgorithm::CalcContentOffset(const RefPtr& property, float mainSize) +{ + CHECK_NULL_VOID(property); + auto startOffset = property->GetContentStartOffset().value_or(0.0f); + info_.contentStartOffset_ = std::max(PipelineBase::Vp2PxWithCurrentDensity(startOffset), 0.0); + auto endOffset = property->GetContentEndOffset().value_or(0.0f); + info_.contentEndOffset_ = std::max(PipelineBase::Vp2PxWithCurrentDensity(endOffset), 0.0); + if (GreatOrEqual(info_.contentStartOffset_ + info_.contentEndOffset_, mainSize)) { + info_.contentStartOffset_ = 0.0f; + info_.contentEndOffset_ = 0.0f; + } +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/grid/grid_layout_base_algorithm.h b/frameworks/core/components_ng/pattern/grid/grid_layout_base_algorithm.h index 9a417d80a54..94e7e2b4228 100644 --- a/frameworks/core/components_ng/pattern/grid/grid_layout_base_algorithm.h +++ b/frameworks/core/components_ng/pattern/grid/grid_layout_base_algorithm.h @@ -92,6 +92,8 @@ protected: void UpdateOverlay(LayoutWrapper* layoutWrapper); + void CalcContentOffset(const RefPtr& property, float mainSize); + GridLayoutInfo info_; bool measureInNextFrame_ = false; bool syncLoad_ = false; diff --git a/frameworks/core/components_ng/pattern/grid/grid_layout_info.cpp b/frameworks/core/components_ng/pattern/grid/grid_layout_info.cpp index 235344b0871..eb57f9809c8 100644 --- a/frameworks/core/components_ng/pattern/grid/grid_layout_info.cpp +++ b/frameworks/core/components_ng/pattern/grid/grid_layout_info.cpp @@ -152,15 +152,15 @@ void GridLayoutInfo::UpdateEndIndex(float overScrollOffset, float mainSize, floa bool GridLayoutInfo::IsOutOfStart() const { - return reachStart_ && Positive(currentOffset_); + return reachStart_ && GreatNotEqual(currentOffset_, contentStartOffset_); } bool GridLayoutInfo::IsOutOfEnd(float mainGap, bool irregular) const { - const bool atOrOutOfStart = reachStart_ && NonNegative(currentOffset_); + const bool atOrOutOfStart = reachStart_ && GreatOrEqual(currentOffset_, contentStartOffset_); if (irregular) { return !atOrOutOfStart && - Negative(GetDistanceToBottom(lastMainSize_ - contentEndPadding_, totalHeightOfItemsInView_, mainGap)); + Negative(GetDistanceToBottom(lastMainSize_, totalHeightOfItemsInView_, mainGap)); } const float endPos = currentOffset_ + totalHeightOfItemsInView_; return !atOrOutOfStart && (endIndex_ == childrenCount_ - 1) && @@ -905,7 +905,7 @@ float GridLayoutInfo::GetDistanceToBottom(float mainSize, float heightInView, fl offset += it->second + mainGap; ++it; } - const float bottomPos = offset + heightInView; + const float bottomPos = offset + heightInView + contentEndOffset_; return bottomPos - mainSize; } diff --git a/frameworks/core/components_ng/pattern/grid/grid_layout_info.h b/frameworks/core/components_ng/pattern/grid/grid_layout_info.h index 7bc1c8242ea..ea528993ae6 100644 --- a/frameworks/core/components_ng/pattern/grid/grid_layout_info.h +++ b/frameworks/core/components_ng/pattern/grid/grid_layout_info.h @@ -382,6 +382,8 @@ struct GridLayoutInfo { // additional padding to accommodate navigation bar when SafeArea is expanded float contentEndPadding_ = 0.0f; + float contentStartOffset_ = 0.0f; + float contentEndOffset_ = 0.0f; std::optional lastCrossCount_; // index of first and last GridItem in viewport diff --git a/frameworks/core/components_ng/pattern/grid/grid_layout_property.cpp b/frameworks/core/components_ng/pattern/grid/grid_layout_property.cpp index 17234392615..81df6040224 100644 --- a/frameworks/core/components_ng/pattern/grid/grid_layout_property.cpp +++ b/frameworks/core/components_ng/pattern/grid/grid_layout_property.cpp @@ -42,7 +42,7 @@ void GridLayoutProperty::ResetPositionFlags() const void GridLayoutProperty::ToJsonValue(std::unique_ptr& json, const InspectorFilter& filter) const { - LayoutProperty::ToJsonValue(json, filter); + ScrollableLayoutProperty::ToJsonValue(json, filter); /* no fixed attr below, just return */ if (filter.IsFastFilter()) { return; diff --git a/frameworks/core/components_ng/pattern/grid/grid_layout_property.h b/frameworks/core/components_ng/pattern/grid/grid_layout_property.h index 5c492a68bbd..42d455e5e56 100644 --- a/frameworks/core/components_ng/pattern/grid/grid_layout_property.h +++ b/frameworks/core/components_ng/pattern/grid/grid_layout_property.h @@ -19,11 +19,12 @@ #include "core/components_ng/layout/layout_property.h" #include "core/components_ng/pattern/grid/grid_constants.h" #include "core/components_ng/pattern/grid/grid_layout_options.h" +#include "core/components_ng/pattern/scrollable/scrollable_layout_property.h" namespace OHOS::Ace::NG { class InspectorFilter; -class ACE_EXPORT GridLayoutProperty : public LayoutProperty { +class ACE_EXPORT GridLayoutProperty : public ScrollableLayoutProperty { DECLARE_ACE_TYPE(GridLayoutProperty, LayoutProperty); public: @@ -53,7 +54,7 @@ public: void Reset() override { - LayoutProperty::Reset(); + ScrollableLayoutProperty::Reset(); ResetColumnsTemplate(); ResetRowsTemplate(); ResetColumnsGap(); diff --git a/frameworks/core/components_ng/pattern/grid/grid_pattern.cpp b/frameworks/core/components_ng/pattern/grid/grid_pattern.cpp index cd8e568efea..f479356d653 100644 --- a/frameworks/core/components_ng/pattern/grid/grid_pattern.cpp +++ b/frameworks/core/components_ng/pattern/grid/grid_pattern.cpp @@ -476,7 +476,7 @@ bool GridPattern::UpdateCurrentOffset(float offset, int32_t source) info_.currentOffset_ -= userOffset; host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); - if (GreatNotEqual(info_.currentOffset_, mainContentSize - itemsHeight)) { + if (GreatNotEqual(info_.currentOffset_, mainContentSize - itemsHeight - info_.contentEndOffset_)) { info_.offsetEnd_ = false; info_.reachEnd_ = false; } @@ -494,8 +494,7 @@ bool GridPattern::UpdateCurrentOffset(float offset, int32_t source) userOffset = FireObserverOnWillScroll(userOffset); info_.currentOffset_ -= userOffset; host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); - - if (LessNotEqual(info_.currentOffset_, 0.0)) { + if (LessNotEqual(info_.currentOffset_, info_.contentStartOffset_)) { info_.reachStart_ = false; } return true; @@ -537,10 +536,10 @@ bool GridPattern::OnDirtyLayoutWrapperSwap(const RefPtr& dirty, c info_.synced_ = true; AnimateToTarget(scrollAlign_, layoutAlgorithmWrapper); - info_.reachStart_ = info_.startIndex_ == 0 && GreatOrEqual(info_.currentOffset_, 0.0f); + info_.reachStart_ = info_.startIndex_ == 0 && GreatOrEqual(info_.currentOffset_, info_.contentStartOffset_); auto curDelta = info_.currentOffset_ - info_.prevOffset_; - info_.currentHeight_ = EstimateHeight(); + info_.currentHeight_ = EstimateHeight() + info_.contentStartOffset_; bool sizeDiminished = IsOutOfBoundary(true) && !NearZero(curDelta) && (info_.prevHeight_ - info_.currentHeight_ - curDelta > 0.1f); @@ -596,7 +595,9 @@ void GridPattern::CheckScrollable() CHECK_NULL_VOID(gridLayoutProperty); auto lastScrollable = scrollable_; if (((info_.endIndex_ - info_.startIndex_ + 1) < info_.childrenCount_) || - (GreatNotEqual(info_.GetTotalHeightOfItemsInView(GetMainGap()), GetMainContentSize()))) { + (GreatNotEqual( + info_.GetTotalHeightOfItemsInView(GetMainGap()) + info_.contentStartOffset_ + info_.contentEndOffset_, + GetMainContentSize()))) { scrollable_ = true; } else { scrollable_ = info_.startMainLineIndex_ != 0 || GetAlwaysEnabled(); @@ -923,15 +924,16 @@ float GridPattern::GetTotalHeight() const auto props = host->GetLayoutProperty(); auto mainGap = GridUtils::GetMainGap(props, viewScopeSize, info_.axis_); if (UseIrregularLayout()) { - return info_.GetIrregularHeight(mainGap); + return info_.GetIrregularHeight(mainGap) + info_.contentStartOffset_ + info_.contentEndOffset_; } if (props->HasLayoutOptions()) { if (info_.IsAllItemsMeasured()) { - return info_.GetTotalLineHeight(mainGap); + return info_.GetTotalLineHeight(mainGap) + info_.contentStartOffset_ + info_.contentEndOffset_; } - return info_.GetContentHeight(*props->GetLayoutOptions(), info_.childrenCount_, mainGap); + return info_.GetContentHeight(*props->GetLayoutOptions(), info_.childrenCount_, mainGap) + + info_.contentStartOffset_ + info_.contentEndOffset_; } - return info_.GetContentHeight(mainGap); + return info_.GetContentHeight(mainGap) + info_.contentStartOffset_ + info_.contentEndOffset_; } void GridPattern::UpdateScrollBarOffset() @@ -971,12 +973,14 @@ void GridPattern::UpdateScrollBarOffset() } auto viewSize = geometryNode->GetFrameSize(); auto overScroll = 0.0f; - if (info_.reachStart_ && Positive(info_.currentOffset_)) { - overScroll = info_.currentOffset_; + if (info_.reachStart_ && GreatNotEqual(info_.currentOffset_, info_.contentStartOffset_)) { + overScroll = info_.currentOffset_ - info_.contentStartOffset_; } else { - overScroll = info_.lastMainSize_ - estimatedHeight + offset; + overScroll = info_.lastMainSize_ - estimatedHeight - info_.contentEndOffset_ + offset; overScroll = Positive(overScroll) ? overScroll : 0.0f; } + offset += info_.contentStartOffset_; + estimatedHeight += info_.contentStartOffset_ + info_.contentEndOffset_; if (info_.offsetEnd_ && NearZero(overScroll) && info_.repeatDifference_ == 0) { offset = estimatedHeight - info_.lastMainSize_; } @@ -1067,23 +1071,23 @@ bool GridPattern::IsOutOfBoundary(bool /*useCurrentDelta*/) float GridPattern::GetEndOffset() { - auto& info = info_; - float contentHeight = info.lastMainSize_ - info.contentEndPadding_; + float contentHeight = info_.lastMainSize_ - info_.contentEndPadding_; const float mainGap = GetMainGap(); const bool irregular = UseIrregularLayout(); - float heightInView = info.GetTotalHeightOfItemsInView(mainGap, irregular); + float heightInView = info_.GetTotalHeightOfItemsInView(mainGap, irregular); const float totalHeight = GetTotalHeight(); if (GetAlwaysEnabled() && LessNotEqual(totalHeight, contentHeight)) { // overScroll with contentHeight < viewport if (irregular) { - return info.GetHeightInRange(0, info.startMainLineIndex_, mainGap); + return info_.GetHeightInRange(0, info_.startMainLineIndex_, mainGap) + info_.contentStartOffset_ + + info_.contentEndOffset_; } - return totalHeight - heightInView; + return totalHeight - heightInView - info_.contentStartOffset_ - info_.contentEndOffset_; } if (!irregular) { - return contentHeight - heightInView; + return contentHeight - heightInView - info_.contentEndOffset_; } float disToBot = info_.GetDistanceToBottom(contentHeight, heightInView, mainGap); return info_.currentOffset_ - disToBot; @@ -1104,13 +1108,21 @@ void GridPattern::SetEdgeEffectCallback(const RefPtr& scrollEf CHECK_NULL_RETURN(grid, 0.0); return grid->GetEndOffset(); }); - scrollEffect->SetTrailingCallback([]() -> double { return 0.0; }); + scrollEffect->SetTrailingCallback([weak = AceType::WeakClaim(this)]() -> double { + auto grid = weak.Upgrade(); + CHECK_NULL_RETURN(grid, 0.0); + return grid->info_.contentStartOffset_; + }); scrollEffect->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() -> double { auto grid = weak.Upgrade(); CHECK_NULL_RETURN(grid, 0.0); return grid->GetEndOffset(); }); - scrollEffect->SetInitTrailingCallback([]() -> double { return 0.0; }); + scrollEffect->SetInitTrailingCallback([weak = AceType::WeakClaim(this)]() -> double { + auto grid = weak.Upgrade(); + CHECK_NULL_RETURN(grid, 0.0); + return grid->info_.contentStartOffset_; + }); } void GridPattern::SyncLayoutBeforeSpring() @@ -1165,7 +1177,7 @@ OverScrollOffset GridPattern::GetOverScrollOffset(double delta) const { OverScrollOffset offset = { 0, 0 }; if (info_.startIndex_ == 0 && info_.startMainLineIndex_ == 0) { - auto startPos = info_.currentOffset_; + auto startPos = info_.currentOffset_ - info_.contentStartOffset_; auto newStartPos = startPos + delta; if (GreatNotEqual(startPos, 0) && GreatNotEqual(newStartPos, 0)) { offset.start = delta; @@ -1186,8 +1198,9 @@ OverScrollOffset GridPattern::GetOverScrollOffset(double delta) const if (info_.endIndex_ == (info_.childrenCount_ + info_.repeatDifference_ - 1)) { float endPos = info_.currentOffset_ + info_.totalHeightOfItemsInView_; float mainSize = info_.lastMainSize_ - info_.contentEndPadding_; - if (GreatNotEqual(GetMainContentSize(), info_.currentOffset_ + info_.totalHeightOfItemsInView_)) { - endPos = info_.currentOffset_ + GetMainContentSize(); + if (GreatNotEqual(GetMainContentSize(), info_.currentOffset_ + info_.totalHeightOfItemsInView_ + + info_.contentEndOffset_ + info_.contentStartOffset_)) { + endPos = info_.currentOffset_ + GetMainContentSize() + info_.contentEndOffset_ + info_.contentStartOffset_; } float newEndPos = endPos + delta; if (LessNotEqual(endPos, mainSize) && LessNotEqual(newEndPos, mainSize)) { @@ -1771,4 +1784,20 @@ void GridPattern::OnColorModeChange(uint32_t colorMode) CHECK_NULL_VOID(host); host->MarkDirtyNode(PROPERTY_UPDATE_RENDER); } + +// avoid start position move when offset is bigger then item height +float GridPattern::GetOffsetWithLimit(float offset) const +{ + float totalOffset = GetTotalOffset() + info_.contentStartOffset_; + if (Positive(offset)) { + return std::min(totalOffset, offset); + } else if (Negative(offset)) { + auto frameNode = GetHost(); + CHECK_NULL_RETURN(frameNode, offset); + auto hostSize = frameNode->GetGeometryNode()->GetFrameSize(); + float remainHeight = GetTotalHeight() - totalOffset - hostSize.MainSize(info_.axis_); + return std::max(offset, -remainHeight); + } + return 0; +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/grid/grid_pattern.h b/frameworks/core/components_ng/pattern/grid/grid_pattern.h index 26816f1ef8f..e27bb5060eb 100644 --- a/frameworks/core/components_ng/pattern/grid/grid_pattern.h +++ b/frameworks/core/components_ng/pattern/grid/grid_pattern.h @@ -154,12 +154,14 @@ public: bool IsAtTopWithDelta() const override { - return info_.reachStart_ || LessNotEqual(EstimateHeight(), 0); + return (info_.reachStart_ && GreatOrEqual(info_.currentOffset_, info_.contentStartOffset_)) || + LessNotEqual(EstimateHeight(), -info_.contentStartOffset_); } bool IsAtBottomWithDelta() const override { - return info_.offsetEnd_ || GreatNotEqual(EstimateHeight() + info_.lastMainSize_, GetTotalHeight()); + return info_.offsetEnd_ || + GreatNotEqual(EstimateHeight() + info_.lastMainSize_ + info_.contentEndOffset_, GetTotalHeight()); } bool IsFadingBottom() const override; @@ -269,6 +271,7 @@ public: void HandleOnItemFocus(int32_t index); void OnColorModeChange(uint32_t colorMode) override; + private: /** * @brief calculate where startMainLine_ should be after spring animation. @@ -312,6 +315,7 @@ private: inline bool UseIrregularLayout() const; std::string GetIrregularIndexesString() const; + float GetOffsetWithLimit(float offset) const override; bool supportAnimation_ = false; bool isConfigScrollable_ = false; diff --git a/frameworks/core/components_ng/pattern/grid/grid_scroll/grid_scroll_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/grid/grid_scroll/grid_scroll_layout_algorithm.cpp index 2996cc233e5..7096abf5ea9 100644 --- a/frameworks/core/components_ng/pattern/grid/grid_scroll/grid_scroll_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/grid/grid_scroll/grid_scroll_layout_algorithm.cpp @@ -109,6 +109,7 @@ void GridScrollLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) // Step2: Measure children that can be displayed in viewport of Grid float mainSize = GetMainAxisSize(frameSize_, axis); float crossSize = GetCrossAxisSize(frameSize_, axis); + CalcContentOffset(gridLayoutProperty, mainSize); if (!NearEqual(mainSize, info_.lastMainSize_)) { UpdateOffsetOnVirtualKeyboardHeightChange(layoutWrapper, mainSize); UpdateOffsetOnHeightChangeDuringAnimation(layoutWrapper, mainSize); @@ -240,8 +241,9 @@ void GridScrollLayoutAlgorithm::AdaptToChildMainSize(LayoutWrapper* layoutWrappe } } - auto lengthOfItemsInViewport = info_.GetTotalHeightOfItemsInView(mainGap_); - auto gridMainSize = std::min(lengthOfItemsInViewport, mainSize); + float lengthOfItemsInViewport = info_.GetTotalHeightOfItemsInView(mainGap_); + float totalHeight = lengthOfItemsInViewport + info_.contentStartOffset_ + info_.contentEndOffset_; + float gridMainSize = std::min(totalHeight, mainSize); gridMainSize = std::max(gridMainSize, GetMainAxisSize(gridLayoutProperty->GetLayoutConstraint()->minSize, axis_)); idealSize.SetMainSize(gridMainSize, axis_); AddPaddingToSize(gridLayoutProperty->CreatePaddingAndBorder(), idealSize); @@ -481,6 +483,9 @@ void GridScrollLayoutAlgorithm::FillGridViewportAndMeasureChildren( CHECK_NULL_VOID(host); auto gridPattern = host->GetPattern(); CHECK_NULL_VOID(gridPattern); + if (!gridPattern->IsInitialized()) { + info_.currentOffset_ = info_.contentStartOffset_; + } itemsCrossPosition_.clear(); UpdateGridLayoutInfo(layoutWrapper, mainSize); if (info_.targetIndex_.has_value()) { @@ -515,10 +520,10 @@ void GridScrollLayoutAlgorithm::FillGridViewportAndMeasureChildren( // Step3: Check if need to fill blank at start (in situation of grid items moving down) auto haveNewLineAtStart = FillBlankAtStart(mainSize, crossSize, layoutWrapper); if (info_.reachStart_) { - auto offset = info_.currentOffset_; + auto offset = info_.currentOffset_ - info_.contentStartOffset_; if ((NonNegative(offset) && !canOverScrollStart_) || (NonPositive(offset) && !canOverScrollEnd_)) { - info_.currentOffset_ = 0.0; - info_.prevOffset_ = 0.0; + info_.currentOffset_ = info_.contentStartOffset_; + info_.prevOffset_ = info_.contentStartOffset_; } if (!haveNewLineAtStart) { if (canOverScrollStart_) { @@ -691,32 +696,36 @@ void GridScrollLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize, float lengthOfItemsInViewport = info_.GetTotalHeightOfItemsInView(mainGap_); // scroll forward if (LessNotEqual(info_.prevOffset_, info_.currentOffset_)) { - if ((NonNegative(info_.currentOffset_) && !canOverScrollStart_) || + if ((GreatOrEqual(info_.currentOffset_, info_.contentStartOffset_) && !canOverScrollStart_) || (NonPositive(info_.currentOffset_) && !canOverScrollEnd_)) { info_.reachEnd_ = false; + info_.offsetEnd_ = false; return; } else if (!isChildrenUpdated_) { - if (LessNotEqual(lengthOfItemsInViewport, mainSize) && NearEqual(mainSize, info_.lastMainSize_)) { + if (LessNotEqual(lengthOfItemsInViewport + info_.contentEndOffset_, mainSize) && + NearEqual(mainSize, info_.lastMainSize_)) { return; } } } // Step2. Calculate real offset that items can only be moved up by. // Hint: [prevOffset_] is a non-positive value - if (LessNotEqual(lengthOfItemsInViewport, mainSize) && info_.startIndex_ == 0) { + if (LessNotEqual(lengthOfItemsInViewport + info_.contentStartOffset_ + info_.contentEndOffset_, mainSize) && + info_.startIndex_ == 0) { if (((NonNegative(info_.currentOffset_) && !canOverScrollStart_) || (NonPositive(info_.currentOffset_) && !canOverScrollEnd_)) || isChildrenUpdated_) { - info_.currentOffset_ = 0; - info_.prevOffset_ = 0; + info_.currentOffset_ = info_.contentStartOffset_; + info_.prevOffset_ = info_.contentStartOffset_; } info_.reachStart_ = true; - info_.offsetEnd_ = LessOrEqual(info_.currentOffset_ + lengthOfItemsInViewport, mainSize); + info_.offsetEnd_ = + LessOrEqual(info_.currentOffset_ + lengthOfItemsInViewport + info_.contentEndOffset_, mainSize); return; } // last grid item is not fully showed - if (GreatNotEqual(info_.currentOffset_ + lengthOfItemsInViewport, mainSize)) { + if (GreatNotEqual(info_.currentOffset_ + lengthOfItemsInViewport + info_.contentEndOffset_, mainSize)) { info_.offsetEnd_ = false; return; } @@ -730,7 +739,7 @@ void GridScrollLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize, // Step3. modify [currentOffset_] if (!canOverScrollEnd_) { - float realOffsetToMoveUp = lengthOfItemsInViewport - mainSize + info_.prevOffset_; + float realOffsetToMoveUp = lengthOfItemsInViewport + info_.contentEndOffset_ - mainSize + info_.prevOffset_; info_.currentOffset_ = info_.prevOffset_ - realOffsetToMoveUp; info_.prevOffset_ = info_.currentOffset_; } @@ -1108,7 +1117,7 @@ void GridScrollLayoutAlgorithm::ScrollToIndexStart(LayoutWrapper* layoutWrapper, info_.startMainLineIndex_ = startLine; info_.UpdateStartIndexByStartLine(); info_.prevOffset_ = 0; - info_.currentOffset_ = 0; + info_.currentOffset_ = info_.startIndex_ == 0 ? info_.contentStartOffset_:0; info_.ResetPositionFlags(); return; } @@ -1148,6 +1157,12 @@ void GridScrollLayoutAlgorithm::UpdateCurrentOffsetForJumpTo(float mainSize) TAG_LOGW(AceLogTag::ACE_GRID, "can not find jumpIndex in Grid Matrix :%{public}d", info_.jumpIndex_); } } + if (info_.jumpIndex_ == info_.childrenCount_ - 1) { + info_.currentOffset_ -= info_.contentEndOffset_; + } + if (info_.jumpIndex_ == 0) { + info_.currentOffset_ += info_.contentStartOffset_; + } if (info_.extraOffset_.has_value() && !info_.targetIndex_.has_value()) { info_.currentOffset_ += info_.extraOffset_.value(); info_.prevOffset_ = info_.currentOffset_; @@ -2187,7 +2202,7 @@ float GridScrollLayoutAlgorithm::FillNewCacheLineBackward( } return -1.0f; } - // // Step2. Measure child + // Step2. Measure child auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize); auto crossSpan = MeasureCachedChild(frameSize, currentIndex, layoutWrapper, itemWrapper); if (crossSpan < 0) { @@ -2197,7 +2212,7 @@ float GridScrollLayoutAlgorithm::FillNewCacheLineBackward( break; } i = static_cast(lastCross_ - 1); - // // Step3. Measure [GridItem] + // Step3. Measure [GridItem] LargeItemLineHeight(itemWrapper); info_.endIndex_ = currentIndex; diff --git a/frameworks/core/components_ng/pattern/grid/irregular/grid_irregular_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/grid/irregular/grid_irregular_layout_algorithm.cpp index 55f306c2a6d..0e79a634164 100644 --- a/frameworks/core/components_ng/pattern/grid/irregular/grid_irregular_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/grid/irregular/grid_irregular_layout_algorithm.cpp @@ -52,9 +52,8 @@ void GridIrregularLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) auto props = DynamicCast(wrapper_->GetLayoutProperty()); float mainSize = MeasureSelf(props); - auto gridLayoutProperty = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); - CHECK_NULL_VOID(gridLayoutProperty); - auto layoutPolicy = gridLayoutProperty->GetLayoutPolicyProperty(); + CalcContentOffset(props, mainSize); + auto layoutPolicy = props->GetLayoutPolicyProperty(); auto isMainWrap = false; if (layoutPolicy.has_value()) { auto isVertical = info_.axis_ == Axis::VERTICAL; @@ -88,6 +87,7 @@ void GridIrregularLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) if (matchChildren) { AdaptToChildMainSize(props, mainSize, frameSize_); } + std::cout<<"after measure offset:"<GetCachedCountValue(info_.defCachedCount_) * info_.crossCount_; if (props->GetShowCachedItemsValue(false)) { SyncPreloadItems(cacheCnt); @@ -168,6 +168,15 @@ void GridIrregularLayoutAlgorithm::Init(const RefPtr& props) info_.crossCount_ = static_cast(crossLens_.size()); CheckForReset(); + + auto host = wrapper_->GetHostNode(); + CHECK_NULL_VOID(host); + auto pattern = host->GetPattern(); + CHECK_NULL_VOID(pattern); + if (!pattern->IsInitialized()) { + info_.currentOffset_ += info_.contentStartOffset_; + std::cout<<"currentOffset after init :"<& gridLayoutProperty, float mainSize, SizeF idealSize) { - auto lengthOfItemsInViewport = info_.GetTotalHeightOfItemsInView(mainGap_); - auto gridMainSize = std::min(lengthOfItemsInViewport, mainSize); + float lengthOfItemsInViewport = info_.GetTotalHeightOfItemsInView(mainGap_); + float totalHeight = lengthOfItemsInViewport + info_.contentStartOffset_ + info_.contentEndOffset_; + float gridMainSize = std::min(totalHeight, mainSize); gridMainSize = std::max(gridMainSize, GetMainAxisSize(gridLayoutProperty->GetLayoutConstraint()->minSize, info_.axis_)); idealSize.SetMainSize(gridMainSize, info_.axis_); diff --git a/frameworks/core/components_ng/pattern/overlay/sheet_presentation_pattern.cpp b/frameworks/core/components_ng/pattern/overlay/sheet_presentation_pattern.cpp index 38b483ecf27..81ea4bcabeb 100644 --- a/frameworks/core/components_ng/pattern/overlay/sheet_presentation_pattern.cpp +++ b/frameworks/core/components_ng/pattern/overlay/sheet_presentation_pattern.cpp @@ -298,7 +298,7 @@ void SheetPresentationPattern::AvoidAiBar() CHECK_NULL_VOID(pipeline); auto inset = pipeline->GetSafeArea(); auto layoutProperty = scrollNode->GetLayoutProperty(); - layoutProperty->UpdateScrollContentEndOffset(inset.bottom_.Length()); + layoutProperty->UpdateContentEndOffset(inset.bottom_.Length()); TAG_LOGD(AceLogTag::ACE_SHEET, "AvoidAiBar function execution completed"); host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT); } diff --git a/frameworks/core/components_ng/pattern/scroll/scroll_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/scroll/scroll_layout_algorithm.cpp index 80bb6403dfa..3e3f782984f 100644 --- a/frameworks/core/components_ng/pattern/scroll/scroll_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/scroll/scroll_layout_algorithm.cpp @@ -46,6 +46,7 @@ void ScrollLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) auto axis = layoutProperty->GetAxis().value_or(Axis::VERTICAL); auto constraint = layoutProperty->GetLayoutConstraint(); auto idealSize = CreateIdealSize(constraint.value_or(LayoutConstraintF()), axis, MeasureType::MATCH_CONTENT); + CalcContentOffset(layoutProperty); auto layoutPolicy = layoutProperty->GetLayoutPolicyProperty(); auto isMainFix = false; if (layoutPolicy.has_value()) { @@ -78,6 +79,10 @@ void ScrollLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) } AddPaddingToSize(padding, idealSize); auto selfSize = idealSize.ConvertToSizeT(); + if (GreatOrEqual(contentStartOffset_ + contentEndOffset_, GetMainAxisSize(selfSize, axis))) { + contentStartOffset_ = 0; + contentEndOffset_ = 0; + } if (!isMainFix) { selfSize.Constrain(constraint->minSize, constraint->maxSize); } else { @@ -110,6 +115,15 @@ void ScrollLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) UseInitialOffset(axis, selfSize, layoutWrapper); } +void ScrollLayoutAlgorithm::CalcContentOffset(const RefPtr& property) +{ + CHECK_NULL_VOID(property); + auto startOffset = property->GetContentStartOffset().value_or(0.0f); + contentStartOffset_ = std::max(PipelineBase::Vp2PxWithCurrentDensity(startOffset), 0.0); + auto endOffset = property->GetContentEndOffset().value_or(0.0f); + contentEndOffset_ = std::max(PipelineBase::Vp2PxWithCurrentDensity(endOffset), 0.0); +} + namespace { float DimensionToFloat(const CalcDimension& value, float selfLength) { @@ -187,12 +201,12 @@ void ScrollLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper) CHECK_NULL_VOID(scroll); float zoomScale = scroll->GetZoomScale(); auto childSize = childGeometryNode->GetMarginFrameSize() * zoomScale; - auto contentEndOffset = layoutProperty->GetScrollContentEndOffsetValue(.0f); float lastScrollableDistance = scrollableDistance_; if (axis == Axis::FREE) { // horizontal is the main axis in Free mode - scrollableDistance_ = childSize.Width() - viewPort_.Width() + contentEndOffset; + scrollableDistance_ = childSize.Width() - viewPort_.Width() + contentStartOffset_ + contentEndOffset_; } else { - scrollableDistance_ = GetMainAxisSize(childSize, axis) - GetMainAxisSize(viewPort_, axis) + contentEndOffset; + scrollableDistance_ = GetMainAxisSize(childSize, axis) - GetMainAxisSize(viewPort_, axis) + + contentStartOffset_ + contentEndOffset_; } if (axis == Axis::FREE) { const auto effect = scroll->GetEdgeEffect(); @@ -204,9 +218,11 @@ void ScrollLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper) AdjustOffsetInFreeMode(currentOffset_, scrollableDistance_, effect, scroll->GetEffectEdge(), alwaysEnabled); } else if (UnableOverScroll(layoutWrapper)) { if (scrollableDistance_ > 0.0f) { - currentOffset_ = std::clamp(currentOffset_, -scrollableDistance_, 0.0f); + currentOffset_ = std::clamp(currentOffset_, -scrollableDistance_, contentStartOffset_); } else { - currentOffset_ = Positive(currentOffset_) ? 0.0f : std::clamp(currentOffset_, 0.0f, -scrollableDistance_); + currentOffset_ = Positive(currentOffset_) + ? contentStartOffset_ + : std::clamp(currentOffset_, contentStartOffset_, -scrollableDistance_); } } else if (LessNotEqual(scrollableDistance_, lastScrollableDistance)) { if (GreatOrEqual(scrollableDistance_, 0.f) && LessOrEqual(-currentOffset_, lastScrollableDistance) && @@ -270,8 +286,10 @@ bool ScrollLayoutAlgorithm::UnableOverScroll(LayoutWrapper* layoutWrapper) const auto scrollNode = layoutWrapper->GetHostNode(); CHECK_NULL_RETURN(scrollNode, false); auto scrollPattern = AceType::DynamicCast(scrollNode->GetPattern()); - return (Positive(currentOffset_) && !scrollPattern->CanOverScrollStart(scrollPattern->GetScrollSource())) || - (Negative(currentOffset_) && GreatNotEqual(-currentOffset_, scrollableDistance_) && + return (Positive(currentOffset_ - contentStartOffset_) && + !scrollPattern->CanOverScrollStart(scrollPattern->GetScrollSource())) || + (Negative(currentOffset_ - contentStartOffset_) && + GreatNotEqual(-currentOffset_ - contentStartOffset_, scrollableDistance_) && !scrollPattern->CanOverScrollEnd(scrollPattern->GetScrollSource())); } diff --git a/frameworks/core/components_ng/pattern/scroll/scroll_layout_algorithm.h b/frameworks/core/components_ng/pattern/scroll/scroll_layout_algorithm.h index e0d67675545..72deb47c5b8 100644 --- a/frameworks/core/components_ng/pattern/scroll/scroll_layout_algorithm.h +++ b/frameworks/core/components_ng/pattern/scroll/scroll_layout_algorithm.h @@ -24,6 +24,7 @@ #include "core/components/common/properties/alignment.h" #include "core/components_ng/layout/layout_algorithm.h" #include "core/components_ng/layout/layout_wrapper.h" +#include "core/components_ng/pattern/scroll/scroll_layout_property.h" namespace OHOS::Ace::NG { @@ -88,6 +89,16 @@ public: return viewPortExtent_; } + float GetContentStartOffset() const + { + return contentStartOffset_; + } + + float GetContentEndOffset() const + { + return contentEndOffset_; + } + void Measure(LayoutWrapper* layoutWrapper) override; void Layout(LayoutWrapper* layoutWrapper) override; @@ -98,11 +109,14 @@ private: void UseInitialOffset(Axis axis, SizeF selfSize, LayoutWrapper* layoutWrapper); bool UnableOverScroll(LayoutWrapper* layoutWrapper) const; void OnSurfaceChanged(LayoutWrapper* layoutWrapper, float contentMainSize); + void CalcContentOffset(const RefPtr& property); float crossOffset_; float currentOffset_ = 0.0f; float scrollableDistance_ = 0.0f; float viewPortLength_ = 0.0f; + float contentStartOffset_ = 0.0f; + float contentEndOffset_ = 0.0f; SizeF viewPort_; // content area size (viewSize_ minus padding) SizeF viewPortExtent_; // size of child (scrollable area) SizeF viewSize_; // size of the Scroll component diff --git a/frameworks/core/components_ng/pattern/scroll/scroll_layout_property.h b/frameworks/core/components_ng/pattern/scroll/scroll_layout_property.h index 30d91ec44ac..52197dae88a 100644 --- a/frameworks/core/components_ng/pattern/scroll/scroll_layout_property.h +++ b/frameworks/core/components_ng/pattern/scroll/scroll_layout_property.h @@ -25,10 +25,11 @@ #include "core/components_ng/base/inspector_filter.h" #include "core/components_ng/layout/layout_property.h" #include "core/components_ng/pattern/scroll/scroll_edge_effect.h" +#include "core/components_ng/pattern/scrollable/scrollable_layout_property.h" #include "core/components_ng/property/property.h" namespace OHOS::Ace::NG { -class ACE_EXPORT ScrollLayoutProperty : public LayoutProperty { +class ACE_EXPORT ScrollLayoutProperty : public ScrollableLayoutProperty { DECLARE_ACE_TYPE(ScrollLayoutProperty, LayoutProperty); public: @@ -41,16 +42,16 @@ public: value->propAxis_ = CloneAxis(); value->propScrollEnabled_ = CloneScrollEnabled(); value->propScrollSnapAlign_ = CloneScrollSnapAlign(); - value->propScrollContentEndOffset_ = CloneScrollContentEndOffset(); + value->propContentEndOffset_ = CloneContentEndOffset(); return value; } void Reset() override { - LayoutProperty::Reset(); + ScrollableLayoutProperty::Reset(); ResetAxis(); ResetScrollEnabled(); - ResetScrollContentEndOffset(); + ResetContentEndOffset(); } void ToJsonValue(std::unique_ptr& json, const InspectorFilter& filter) const override @@ -78,7 +79,6 @@ public: ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(ScrollEnabled, bool, PROPERTY_UPDATE_MEASURE); ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(ScrollSnapAlign, ScrollSnapAlign, PROPERTY_UPDATE_MEASURE); ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(ScrollWidth, float, PROPERTY_UPDATE_MEASURE); - ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(ScrollContentEndOffset, float, PROPERTY_UPDATE_MEASURE); }; } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/scroll/scroll_pattern.cpp b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.cpp index 92a6417b124..db8169eb7af 100644 --- a/frameworks/core/components_ng/pattern/scroll/scroll_pattern.cpp +++ b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.cpp @@ -183,6 +183,10 @@ bool ScrollPattern::SetScrollProperties(const RefPtr& dirty, cons auto layoutAlgorithm = DynamicCast(layoutAlgorithmWrapper->GetLayoutAlgorithm()); CHECK_NULL_RETURN(layoutAlgorithm, false); currentOffset_ = layoutAlgorithm->GetCurrentOffset(); + contentStartOffset_ = layoutAlgorithm->GetContentStartOffset(); + if (!isInitialized_ && !initialOffset_.has_value()) { + currentOffset_ -= contentStartOffset_; + } if (freeScroll_ && scrollBar2d_) { freeScroll_->OnLayoutFinished(layoutAlgorithm->GetFreeOffset(), layoutAlgorithm->GetScrollableArea()); scrollBar2d_->SyncLayout( @@ -207,6 +211,8 @@ bool ScrollPattern::SetScrollProperties(const RefPtr& dirty, cons viewPort_ = layoutAlgorithm->GetViewPort(); viewSize_ = layoutAlgorithm->GetViewSize(); viewPortExtent_ = layoutAlgorithm->GetViewPortExtent(); + + contentEndOffset_ = layoutAlgorithm->GetContentEndOffset(); if (IsEnablePagingValid()) { SetIntervalSize(Dimension(static_cast(viewPortLength_))); } @@ -296,7 +302,7 @@ void ScrollPattern::ResetPosition() bool ScrollPattern::IsAtTop() const { - return GreatOrEqual(currentOffset_, 0.0); + return GreatOrEqual(currentOffset_ + contentStartOffset_, 0.0); } bool ScrollPattern::IsAtBottom(bool considerRepeat) const @@ -304,13 +310,13 @@ bool ScrollPattern::IsAtBottom(bool considerRepeat) const if (LessNotEqual(scrollableDistance_, 0.0f)) { return LessOrEqual(currentOffset_, 0.0f); } - return LessOrEqual(currentOffset_, -scrollableDistance_); + return LessOrEqual(currentOffset_ + contentStartOffset_, -scrollableDistance_); } OverScrollOffset ScrollPattern::GetOverScrollOffset(double delta) const { OverScrollOffset offset = { 0, 0 }; - auto startPos = currentOffset_; + auto startPos = currentOffset_ + contentStartOffset_; auto newStartPos = startPos + delta; if (startPos > 0 && newStartPos > 0) { offset.start = delta; @@ -379,7 +385,7 @@ void ScrollPattern::AdjustOffset(float& delta, int32_t source) double ScrollPattern::ValidateOffset(int32_t source, double willScrollOffset) { - if (LessOrEqual(scrollableDistance_, 0.0f) || source == SCROLL_FROM_JUMP) { + if (LessOrEqual(scrollableDistance_, 0.0) || source == SCROLL_FROM_JUMP) { return willScrollOffset; } @@ -388,7 +394,7 @@ double ScrollPattern::ValidateOffset(int32_t source, double willScrollOffset) source == SCROLL_FROM_ROTATE || source == SCROLL_FROM_AXIS) { if (GetAxis() == Axis::HORIZONTAL) { if (IsRowReverse()) { - willScrollOffset = std::clamp(willScrollOffset, 0.0, scrollableDistance_); + willScrollOffset = std::clamp(willScrollOffset, -0.0, scrollableDistance_); } else { willScrollOffset = std::clamp(willScrollOffset, -scrollableDistance_, 0.0); } @@ -401,7 +407,7 @@ double ScrollPattern::ValidateOffset(int32_t source, double willScrollOffset) void ScrollPattern::ValidateOffset(int32_t source) { - if (LessOrEqual(scrollableDistance_, 0.0f) || source == SCROLL_FROM_JUMP) { + if (LessOrEqual(scrollableDistance_, -contentStartOffset_) || source == SCROLL_FROM_JUMP) { return; } @@ -809,10 +815,11 @@ void ScrollPattern::SetEdgeEffectCallback(const RefPtr& scroll }); scrollEffect->SetTrailingCallback([weakScroll = AceType::WeakClaim(this)]() -> double { auto scroll = weakScroll.Upgrade(); - if (scroll && (scroll->IsRowReverse() || scroll->IsColReverse())) { + CHECK_NULL_RETURN(scroll, 0.0); + if (scroll->IsRowReverse() || scroll->IsColReverse()) { return scroll->GetScrollableDistance(); } - return 0.0; + return scroll->contentStartOffset_; }); scrollEffect->SetInitLeadingCallback([weakScroll = AceType::WeakClaim(this)]() -> double { auto scroll = weakScroll.Upgrade(); @@ -823,10 +830,11 @@ void ScrollPattern::SetEdgeEffectCallback(const RefPtr& scroll }); scrollEffect->SetInitTrailingCallback([weakScroll = AceType::WeakClaim(this)]() -> double { auto scroll = weakScroll.Upgrade(); - if (scroll && (scroll->IsRowReverse() || scroll->IsColReverse())) { + CHECK_NULL_RETURN(scroll, 0.0); + if (scroll->IsRowReverse() || scroll->IsColReverse()) { return scroll->GetScrollableDistance(); } - return 0.0; + return scroll->contentStartOffset_; }); } @@ -853,8 +861,7 @@ void ScrollPattern::UpdateScrollBarOffset() auto layoutProperty = host->GetLayoutProperty(); CHECK_NULL_VOID(layoutProperty); auto padding = layoutProperty->CreatePaddingAndBorder(); - auto contentEndOffset = layoutProperty->GetScrollContentEndOffsetValue(.0f); - Size size(viewSize_.Width(), viewSize_.Height() - contentEndOffset); + Size size(viewSize_.Width(), viewSize_.Height() - contentEndOffset_); auto viewPortExtent = viewPortExtent_; AddPaddingToSize(padding, viewPortExtent); auto estimatedHeight = (GetAxis() == Axis::HORIZONTAL) ? viewPortExtent.Width() : viewPortExtent.Height(); diff --git a/frameworks/core/components_ng/pattern/scroll/scroll_pattern.h b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.h index da07c483fc2..665f94838d3 100644 --- a/frameworks/core/components_ng/pattern/scroll/scroll_pattern.h +++ b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.h @@ -441,6 +441,8 @@ private: double prevOffset_ = 0.0; double scrollableDistance_ = 0.0; float viewPortLength_ = 0.0f; + float contentStartOffset_ = 0.0f; + float contentEndOffset_ = 0.0f; SizeF viewPort_; SizeF viewSize_; SizeF viewPortExtent_; diff --git a/frameworks/core/components_ng/pattern/scrollable/scrollable_layout_property.cpp b/frameworks/core/components_ng/pattern/scrollable/scrollable_layout_property.cpp new file mode 100644 index 00000000000..932d6b8e9d7 --- /dev/null +++ b/frameworks/core/components_ng/pattern/scrollable/scrollable_layout_property.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 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/scrollable/scrollable_layout_property.h" + +namespace OHOS::Ace::NG { + +void ScrollableLayoutProperty::ToJsonValue(std::unique_ptr& json, const InspectorFilter& filter) const +{ + LayoutProperty::ToJsonValue(json, filter); + if (filter.IsFastFilter()) { + return; + } + json->PutExtAttr("contentStartOffset", std::to_string(propContentStartOffset_.value_or(0)).c_str(), filter); + json->PutExtAttr("contentEndOffset", std::to_string(propContentEndOffset_.value_or(0)).c_str(), filter); +} + +} // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/scrollable/scrollable_layout_property.h b/frameworks/core/components_ng/pattern/scrollable/scrollable_layout_property.h new file mode 100644 index 00000000000..22e9d17b13d --- /dev/null +++ b/frameworks/core/components_ng/pattern/scrollable/scrollable_layout_property.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 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_SCROLLABLE_SCROLLABLE_LAYOUT_PROPERTY_H +#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_SCROLLABLE_SCROLLABLE_LAYOUT_PROPERTY_H + +#include "core/components_ng/layout/layout_property.h" + +namespace OHOS::Ace::NG { +class InspectorFilter; + +class ACE_EXPORT ScrollableLayoutProperty : public LayoutProperty { + DECLARE_ACE_TYPE(ScrollableLayoutProperty, LayoutProperty); + +public: + ScrollableLayoutProperty() = default; + ~ScrollableLayoutProperty() override = default; + + RefPtr Clone() const override + { + auto value = MakeRefPtr(); + value->LayoutProperty::UpdateLayoutProperty(DynamicCast(this)); + value->propContentStartOffset_ = CloneContentStartOffset(); + value->propContentEndOffset_ = CloneContentEndOffset(); + return value; + } + + void Reset() override + { + LayoutProperty::Reset(); + ResetContentStartOffset(); + ResetContentEndOffset(); + } + + void ToJsonValue(std::unique_ptr& json, const InspectorFilter& filter) const override; + + ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(ContentStartOffset, float, PROPERTY_UPDATE_MEASURE); + ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(ContentEndOffset, float, PROPERTY_UPDATE_MEASURE); + +protected: + void UpdateLayoutProperty(const ScrollableLayoutProperty* layoutProperty); + +private: + ACE_DISALLOW_COPY_AND_MOVE(ScrollableLayoutProperty); +}; +} // namespace OHOS::Ace::NG +#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_SCROLLABLE_SCROLLABLE_LAYOUT_PROPERTY_H diff --git a/frameworks/core/components_ng/pattern/scrollable/scrollable_model_ng.cpp b/frameworks/core/components_ng/pattern/scrollable/scrollable_model_ng.cpp index bdc442f8336..49f0f3bb672 100644 --- a/frameworks/core/components_ng/pattern/scrollable/scrollable_model_ng.cpp +++ b/frameworks/core/components_ng/pattern/scrollable/scrollable_model_ng.cpp @@ -18,6 +18,7 @@ #include "base/utils/multi_thread.h" #include "base/utils/utils.h" #include "core/components_ng/pattern/scrollable/scrollable_event_hub.h" +#include "core/components_ng/pattern/scrollable/scrollable_layout_property.h" #include "core/components_ng/pattern/scrollable/scrollable_pattern.h" #include "core/common/resource/resource_parse_utils.h" @@ -513,4 +514,14 @@ void ScrollableModelNG::CreateWithResourceObjScrollBarColor(FrameNode* frameNode }; pattern->AddResObj(key, resObj, std::move(updateFunc)); } + +void ScrollableModelNG::SetContentStartOffset(float offset) +{ + ACE_UPDATE_LAYOUT_PROPERTY(ScrollableLayoutProperty, ContentStartOffset, offset); +} + +void ScrollableModelNG::SetContentEndOffset(float offset) +{ + ACE_UPDATE_LAYOUT_PROPERTY(ScrollableLayoutProperty, ContentEndOffset, offset); +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/scrollable/scrollable_model_ng.h b/frameworks/core/components_ng/pattern/scrollable/scrollable_model_ng.h index 5c81e2cd3c1..a3dab81b145 100644 --- a/frameworks/core/components_ng/pattern/scrollable/scrollable_model_ng.h +++ b/frameworks/core/components_ng/pattern/scrollable/scrollable_model_ng.h @@ -104,6 +104,8 @@ public: static void SetScrollBarMargin(FrameNode* frameNode, const ScrollBarMargin& scrollBarMargin); static void ResetScrollBarMargin(FrameNode* frameNode); static void GetScrollBarMargin(FrameNode* frameNode, ScrollBarMargin& scrollBarMargin); + static void SetContentStartOffset(float offset); + static void SetContentEndOffset(float offset); static void CreateWithResourceObjScrollBarColor(FrameNode* frameNode, const RefPtr& resObj); }; diff --git a/frameworks/core/components_ng/pattern/scrollable/scrollable_pattern.h b/frameworks/core/components_ng/pattern/scrollable/scrollable_pattern.h index 951c0a27f88..83588ebbe68 100644 --- a/frameworks/core/components_ng/pattern/scrollable/scrollable_pattern.h +++ b/frameworks/core/components_ng/pattern/scrollable/scrollable_pattern.h @@ -925,6 +925,11 @@ public: { return scrollBar_; } + + bool IsInitialized() const + { + return isInitialized_; + } protected: void SuggestOpIncGroup(bool flag); void OnAttachToFrameNodeMultiThread(); 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 76cc6342cbe..80d0f3f4161 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 @@ -40,7 +40,7 @@ void WaterFlowLayoutInfoSW::Sync(int32_t itemCnt, float mainSize, const std::vec endPos_ = EndPos(); prevItemStart_ = itemStart_; - itemStart_ = startIndex_ == 0 && NonNegative(startPos_ - TopMargin()); + itemStart_ = startIndex_ == 0 && NonNegative(startPos_ - TopMargin() - contentStartOffset_); itemEnd_ = endIndex_ == itemCnt - 1; if (footerIndex_ == 0) { itemEnd_ &= LessOrEqualCustomPrecision(endPos_, mainSize + expandHeight_, 0.1f); @@ -52,7 +52,7 @@ void WaterFlowLayoutInfoSW::Sync(int32_t itemCnt, float mainSize, const std::vec footerHeight_ = 0.0f; } - const float contentEnd = endPos_ + footerHeight_ + BotMargin(); + const float contentEnd = endPos_ + footerHeight_ + BotMargin() + contentEndOffset_; offsetEnd_ = itemEnd_ && LessOrEqualCustomPrecision(contentEnd, mainSize, 0.1f); maxHeight_ = std::max(-totalOffset_ + contentEnd, maxHeight_); @@ -127,12 +127,13 @@ bool WaterFlowLayoutInfoSW::OutOfBounds() const return false; } // checking first lane is enough because re-align automatically happens when reaching start - if (itemStart_ && !lanes_[0].empty() && Positive(lanes_[0][0].startPos - TopMargin() + delta_)) { + if (itemStart_ && !lanes_[0].empty() && + Positive(lanes_[0][0].startPos - TopMargin() - contentStartOffset_ + delta_)) { return true; } if (!itemStart_ && offsetEnd_) { return std::all_of(lanes_.back().begin(), lanes_.back().end(), [this](const Lane& lane) { - return LessNotEqual(lane.endPos + footerHeight_ + BotMargin() + delta_, lastMainSize_); + return LessNotEqual(lane.endPos + footerHeight_ + BotMargin() + contentEndOffset_ + delta_, lastMainSize_); }); } return false; @@ -159,7 +160,7 @@ OverScrollOffset WaterFlowLayoutInfoSW::GetOverScrolledDelta(float delta) const if (!itemEnd_) { return res; } - float disToBot = EndPosWithMargin() + footerHeight_ - std::min(lastMainSize_, maxHeight_); + float disToBot = EndPosWithMargin() + footerHeight_ + contentEndOffset_ - std::min(lastMainSize_, maxHeight_); if (Positive(disToBot) && LessNotEqual(maxHeight_, lastMainSize_)) { res.end = std::min(0.0f, disToBot + delta); return res; @@ -184,7 +185,7 @@ float WaterFlowLayoutInfoSW::CalcOverScroll(float mainSize, float delta) const res = StartPosWithMargin() + delta; } if (offsetEnd_) { - res = mainSize - (EndPosWithMargin() + footerHeight_ + delta); + res = mainSize - (EndPosWithMargin() + footerHeight_ + contentEndOffset_ + delta); } return res; } @@ -258,7 +259,7 @@ bool WaterFlowLayoutInfoSW::ReachStart(float prevPos, bool firstLayout) const if (!itemStart_ || lanes_.empty()) { return false; } - const bool backFromOverScroll = Positive(prevPos) && NonPositive(totalOffset_); + const bool backFromOverScroll = Positive(prevPos) && LessOrEqual(totalOffset_, contentStartOffset_); return firstLayout || prevItemStart_ != itemStart_ || backFromOverScroll; } @@ -267,10 +268,10 @@ bool WaterFlowLayoutInfoSW::ReachEnd(float prevPos, bool firstLayout) const if (!offsetEnd_ || lanes_.empty()) { return false; } - const float prevEndPos = EndPosWithMargin() - (totalOffset_ - prevPos) + footerHeight_; + const float prevEndPos = EndPosWithMargin() - (totalOffset_ - prevPos) + footerHeight_ + contentEndOffset_; const bool backFromOverScroll = LessNotEqualCustomPrecision(prevEndPos, lastMainSize_, -0.1f) && - GreatOrEqualCustomPrecision(EndPosWithMargin() + footerHeight_, lastMainSize_, -0.1f); + GreatOrEqualCustomPrecision(EndPosWithMargin() + footerHeight_ + contentEndOffset_, lastMainSize_, -0.1f); return firstLayout || GreatNotEqualCustomPrecision(prevEndPos, lastMainSize_, 0.1f) || backFromOverScroll; } @@ -525,7 +526,7 @@ float WaterFlowLayoutInfoSW::TopFinalPos() const float WaterFlowLayoutInfoSW::BottomFinalPos(float viewHeight) const { - return -(EndPosWithMargin() + delta_ + footerHeight_) + std::min(maxHeight_, viewHeight); + return -(EndPosWithMargin() + delta_ + footerHeight_ + contentEndOffset_) + std::min(maxHeight_, viewHeight); }; bool WaterFlowLayoutInfoSW::IsMisaligned() const @@ -954,9 +955,9 @@ void WaterFlowLayoutInfoSW::SyncOnEmptyLanes(float mainSize) { startPos_ = StartPos(); endPos_ = EndPos(); - itemStart_ = NonNegative(startPos_ - TopMargin()); + itemStart_ = NonNegative(startPos_ - TopMargin() - contentStartOffset_); itemEnd_ = true; - offsetEnd_ = LessOrEqualCustomPrecision(endPos_ + footerHeight_ + BotMargin(), mainSize, 0.1f); + offsetEnd_ = LessOrEqualCustomPrecision(endPos_ + footerHeight_ + BotMargin() + contentEndOffset_, mainSize, 0.1f); maxHeight_ = footerHeight_; knowTotalHeight_ = true; newStartIndex_ = EMPTY_NEW_START_INDEX; 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 99016308c96..5125de5c98d 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 @@ -176,7 +176,7 @@ public: float StartPos() const; inline float StartPosWithMargin() const { - return StartPos() - TopMargin(); + return StartPos() - TopMargin() - contentStartOffset_; } void ClearDataFrom(int32_t idx, const std::vector& mainGap); 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 85d31ebffeb..538053f1128 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 @@ -45,6 +45,7 @@ void WaterFlowLayoutSW::Measure(LayoutWrapper* wrapper) syncLoad_ = props_->GetSyncLoad().value_or(!FeatureParam::IsSyncLoadEnabled()) || matchChildren || !NearZero(info_->delta_) || info_->targetIndex_.has_value() ; Init(size); + if (!IsSectionValid(info_, itemCnt_) || !CheckData()) { info_->isDataValid_ = false; return; @@ -138,13 +139,16 @@ void WaterFlowLayoutSW::Layout(LayoutWrapper* wrapper) void WaterFlowLayoutSW::Init(const SizeF& frameSize) { mainLen_ = frameSize.MainSize(axis_); - + CalcContentOffset(props_, info_, mainLen_); // omit footer from children count CHECK_NULL_VOID(wrapper_); auto host = wrapper_->GetHostNode(); CHECK_NULL_VOID(host); auto pattern = host->GetPattern(); CHECK_NULL_VOID(pattern); + if (!pattern->IsInitialized()) { + info_->totalOffset_ += info_->contentStartOffset_; + } info_->repeatDifference_ = 0; info_->firstRepeatCount_ = 0; info_->childrenCount_ = 0; @@ -281,7 +285,7 @@ void WaterFlowLayoutSW::MeasureOnOffset(float delta) { // handle initial layout if (NearZero(delta) && info_->startIndex_ > info_->endIndex_ && itemCnt_ != 0) { - info_->ResetWithLaneOffset(info_->TopMargin()); + info_->ResetWithLaneOffset(info_->TopMargin() + info_->contentStartOffset_); } const bool forward = NonPositive(delta); @@ -306,7 +310,7 @@ void WaterFlowLayoutSW::ApplyDelta(float delta) if (Positive(delta)) { // positive offset is scrolling upwards int32_t oldStartIdx = info_->StartIndex(); - FillFront(0.0f, info_->StartIndex() - 1, 0); + FillFront(info_->contentStartOffset_, info_->StartIndex() - 1, 0); MeasureRemainingLazyChild(oldStartIdx, info_->EndIndex(), false); } else { int32_t oldEndIdx = info_->EndIndex(); @@ -705,7 +709,7 @@ void WaterFlowLayoutSW::Jump(int32_t jumpIdx, ScrollAlign align, bool noSkip) lane.endPos = (mainLen_ + itemH) / 2.0f; lane.items_.push_back({ jumpIdx, itemH }); - FillFront(0.0f, jumpIdx - 1, 0); + FillFront(info_->contentStartOffset_, jumpIdx - 1, 0); FillBack(mainLen_, jumpIdx + 1, itemCnt_ - 1); } break; @@ -715,7 +719,7 @@ void WaterFlowLayoutSW::Jump(int32_t jumpIdx, ScrollAlign align, bool noSkip) ApplyDelta(info_->DistanceToBottom(jumpIdx, mainLen_, mainGaps_[info_->GetSegment(jumpIdx)])); } else { info_->ResetWithLaneOffset(mainLen_); - FillFront(0.0f, jumpIdx, 0); + FillFront(info_->contentStartOffset_, jumpIdx, 0); } break; } @@ -737,8 +741,8 @@ void WaterFlowLayoutSW::AdjustOverScroll() maxEnd += info_->footerHeight_; } - maxEnd += info_->BotMargin(); - minStart -= info_->TopMargin(); + maxEnd += info_->BotMargin() + info_->contentEndOffset_; + minStart -= info_->TopMargin() + info_->contentStartOffset_; int32_t startIdx = info_->StartIndex(); if (info_->AtStartPos(startIdx) && Positive(minStart)) { diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_algorithm.cpp index bd761fbf3bd..dc8248dac9d 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_algorithm.cpp @@ -142,6 +142,7 @@ void WaterFlowLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) InitialItemsCrossSize(layoutProperty, idealSize, layoutInfo_->GetChildrenCount()); mainSize_ = GetMainAxisSize(idealSize, axis); + CalcContentOffset(layoutProperty, layoutInfo_, mainSize_); if (layoutInfo_->jumpIndex_ >= 0 && layoutInfo_->jumpIndex_ < layoutInfo_->GetChildrenCount()) { auto crossIndex = layoutInfo_->GetCrossIndex(layoutInfo_->jumpIndex_); if (crossIndex == -1) { @@ -149,12 +150,18 @@ void WaterFlowLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) } else { layoutInfo_->JumpTo(layoutInfo_->items_[0][crossIndex][layoutInfo_->jumpIndex_]); } + if (layoutInfo_->jumpIndex_ == 0) { + layoutInfo_->currentOffset_ += layoutInfo_->contentStartOffset_; + } } else if (layoutInfo_->jumpIndex_ == LAST_ITEM) { + layoutInfo_->currentOffset_ -= layoutInfo_->contentEndOffset_; // jump to bottom. } else { layoutInfo_->jumpIndex_ = WaterFlowLayoutInfoBase::EMPTY_JUMP_INDEX; } - + if (!pattern->IsInitialized()) { + layoutInfo_->currentOffset_ = layoutInfo_->contentStartOffset_; + } FillViewport(mainSize_, layoutWrapper); if (layoutInfo_->targetIndex_.has_value()) { MeasureToTarget(layoutWrapper, layoutInfo_->endIndex_, std::nullopt); @@ -467,7 +474,7 @@ void WaterFlowLayoutAlgorithm::FillViewport(float mainSize, LayoutWrapper* layou void WaterFlowLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize, LayoutWrapper* layoutWrapper) { - auto maxItemHeight = layoutInfo_->GetMaxMainHeight(); + auto maxItemHeight = layoutInfo_->GetMaxMainHeight() + layoutInfo_->contentEndOffset_; if (layoutInfo_->footerIndex_ >= 0) { footerMainStartPos_ = maxItemHeight; footerMainSize_ = WaterFlowLayoutUtils::MeasureFooter(layoutWrapper, axis_); @@ -482,20 +489,21 @@ void WaterFlowLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize, L } layoutInfo_->maxHeight_ = maxItemHeight; - if (mainSize >= maxItemHeight) { - if ((NonNegative(layoutInfo_->currentOffset_) && !canOverScrollStart_) || - (NonPositive(layoutInfo_->currentOffset_) && !canOverScrollEnd_)) { - layoutInfo_->currentOffset_ = 0; + if (mainSize - layoutInfo_->contentStartOffset_ - layoutInfo_->contentEndOffset_ >= maxItemHeight) { + if ((GreatOrEqual(layoutInfo_->currentOffset_, layoutInfo_->contentStartOffset_) && !canOverScrollStart_) || + (LessOrEqual(layoutInfo_->currentOffset_, layoutInfo_->contentEndOffset_) && !canOverScrollEnd_)) { + layoutInfo_->currentOffset_ = layoutInfo_->contentStartOffset_; } - layoutInfo_->itemStart_ = GreatOrEqual(layoutInfo_->currentOffset_, 0.0f); - layoutInfo_->offsetEnd_ = LessOrEqual(layoutInfo_->currentOffset_, 0.0f); + layoutInfo_->itemStart_ = GreatOrEqual(layoutInfo_->currentOffset_, layoutInfo_->contentStartOffset_); + layoutInfo_->offsetEnd_ = LessOrEqual(layoutInfo_->currentOffset_, layoutInfo_->contentStartOffset_); return; } - if (LessOrEqualCustomPrecision(layoutInfo_->currentOffset_ + maxItemHeight, mainSize, 0.1f)) { + if (LessOrEqualCustomPrecision( + layoutInfo_->currentOffset_ + maxItemHeight + layoutInfo_->contentStartOffset_, mainSize, 0.1f)) { layoutInfo_->offsetEnd_ = true; if (!canOverScrollEnd_) { - layoutInfo_->currentOffset_ = mainSize - maxItemHeight; + layoutInfo_->currentOffset_ = mainSize - maxItemHeight + layoutInfo_->contentStartOffset_; } auto oldStart = layoutInfo_->startIndex_; diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.cpp index 24a9993386a..946d1da4be4 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.cpp @@ -434,19 +434,20 @@ void WaterFlowLayoutInfo::SetNextSegmentStartPos(int32_t itemIdx) void WaterFlowLayoutInfo::Sync(float mainSize, bool canOverScrollStart, bool canOverScrollEnd) { // adjust offset when it can't overScroll at top - if (!canOverScrollStart) { - currentOffset_ = std::min(currentOffset_, 0.0); + if (!canOverScrollStart && GreatNotEqual(currentOffset_, contentStartOffset_)) { + currentOffset_ = contentStartOffset_; } + endIndex_ = FastSolveEndIndex(mainSize + expandHeight_); - maxHeight_ = GetMaxMainHeight(); + maxHeight_ = GetMaxMainHeight() + contentEndOffset_; - itemStart_ = GreatOrEqual(currentOffset_, 0.0f); + itemStart_ = GreatOrEqual(currentOffset_, contentStartOffset_); itemEnd_ = endIndex_ >= 0 && endIndex_ == GetChildrenCount() - 1; offsetEnd_ = itemEnd_ && GreatOrEqual(mainSize - currentOffset_, maxHeight_); // adjust offset when it can't overScroll at bottom - if (offsetEnd_ && Negative(currentOffset_) && !canOverScrollEnd) { - currentOffset_ = std::min(-maxHeight_ + mainSize, 0.0f); + if (offsetEnd_ && LessNotEqual(currentOffset_, contentStartOffset_) && !canOverScrollEnd) { + currentOffset_ = std::min(-maxHeight_ + mainSize, contentStartOffset_); } startIndex_ = FastSolveStartIndex(); @@ -657,9 +658,9 @@ int32_t WaterFlowLayoutInfo::GetLastItem() const void WaterFlowLayoutInfo::UpdateItemStart(bool canOverScrollStart) { - if (currentOffset_ >= 0) { + if (GreatOrEqual(currentOffset_, contentStartOffset_)) { if (!canOverScrollStart) { - currentOffset_ = 0; + currentOffset_ = contentStartOffset_; } itemStart_ = true; } else { 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 f2ddc1cd5bb..b249a452558 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 @@ -112,7 +112,7 @@ public: } float TopFinalPos() const override { - return 0.0f; + return contentStartOffset_; }; float BottomFinalPos(float viewHeight) const override { diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_segmented_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_segmented_layout.cpp index 9bdd633dfe8..5fe9fcc103e 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_segmented_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_segmented_layout.cpp @@ -74,6 +74,10 @@ void WaterFlowSegmentedLayout::Measure(LayoutWrapper* wrapper) Init(idealSize); mainSize_ = GetMainAxisSize(idealSize, axis_); + CalcContentOffset(props_, info_, mainSize_); + if (!pattern->IsInitialized()) { + info_->currentOffset_ += info_->contentStartOffset_; + } PerformMeasurement(); @@ -401,7 +405,10 @@ void WaterFlowSegmentedLayout::MeasureOnJump(int32_t jumpIdx) } if (info_->jumpIndex_ == LAST_ITEM) { auto maxHeight = info_->GetMaxMainHeight() - info_->margins_.back().bottom.value_or(0.0f); - info_->currentOffset_ = SolveJumpOffset({ 0, maxHeight, 0 }) + postJumpOffset_.value_or(0.0f); + info_->currentOffset_ = + SolveJumpOffset({ 0, maxHeight, 0 }) + postJumpOffset_.value_or(0.0f) - info_->contentEndOffset_; + } else if (info_->jumpIndex_ == 0) { + info_->currentOffset_ = SolveJumpOffset(item) + postJumpOffset_.value_or(0.0f) + info_->contentStartOffset_; } else { info_->currentOffset_ = SolveJumpOffset(item) + postJumpOffset_.value_or(0.0f); } diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.cpp index fea56e5590c..478141bbb57 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.cpp @@ -180,4 +180,18 @@ void WaterFlowLayoutBase::ClearUnlayoutedItems(LayoutWrapper* layoutWrapper) frameNode->ClearSubtreeLayoutAlgorithm(); } } + +void WaterFlowLayoutBase::CalcContentOffset( + const RefPtr& property, const RefPtr& info, float mainSize) +{ + CHECK_NULL_VOID(property); + auto startOffset = property->GetContentStartOffset().value_or(0.0f); + info->contentStartOffset_ = std::max(PipelineBase::Vp2PxWithCurrentDensity(startOffset), 0.0); + auto endOffset = property->GetContentEndOffset().value_or(0.0f); + info->contentEndOffset_ = std::max(PipelineBase::Vp2PxWithCurrentDensity(endOffset), 0.0); + if (GreatOrEqual(info->contentStartOffset_ + info->contentEndOffset_, mainSize)) { + info->contentStartOffset_ = 0.0f; + info->contentEndOffset_ = 0.0f; + } +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.h b/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.h index 1e9e05800cf..f173ea55668 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.h @@ -107,6 +107,9 @@ protected: */ virtual RefPtr LayoutInfo() const = 0; + void CalcContentOffset( + const RefPtr& property, const RefPtr& info, float mainSize); + bool syncLoad_ = false; private: 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 acc5b084a4a..a7c16d3903c 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 @@ -247,6 +247,8 @@ public: float restoreOffset_ = 0.0f; float expandHeight_ = 0.0f; + float contentStartOffset_ = 0.0f; + float contentEndOffset_ = 0.0f; // Stores the tail item index of each segment. std::vector segmentTails_; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_property.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_property.h index aef6079e618..a994b08046c 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_property.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_property.h @@ -18,11 +18,12 @@ #include "core/components/common/layout/constants.h" #include "core/components_ng/layout/layout_property.h" +#include "core/components_ng/pattern/scrollable/scrollable_layout_property.h" namespace OHOS::Ace::NG { class InspectorFilter; -class ACE_EXPORT WaterFlowLayoutProperty : public LayoutProperty { +class ACE_EXPORT WaterFlowLayoutProperty : public ScrollableLayoutProperty { DECLARE_ACE_TYPE(WaterFlowLayoutProperty, LayoutProperty); public: @@ -33,7 +34,7 @@ public: void Reset() override { - LayoutProperty::Reset(); + ScrollableLayoutProperty::Reset(); ResetColumnsTemplate(); ResetRowsTemplate(); ResetColumnsGap(); diff --git a/test/unittest/BUILD.gn b/test/unittest/BUILD.gn index b12535a884f..09e340273e7 100644 --- a/test/unittest/BUILD.gn +++ b/test/unittest/BUILD.gn @@ -1238,6 +1238,7 @@ ohos_source_set("ace_components_pattern") { "$ace_root/frameworks/core/components_ng/pattern/scrollable/scrollable_controller_multi_thread.cpp", "$ace_root/frameworks/core/components_ng/pattern/scrollable/scrollable_item.cpp", "$ace_root/frameworks/core/components_ng/pattern/scrollable/scrollable_item_pool.cpp", + "$ace_root/frameworks/core/components_ng/pattern/scrollable/scrollable_layout_property.cpp", "$ace_root/frameworks/core/components_ng/pattern/scrollable/scrollable_model_ng.cpp", "$ace_root/frameworks/core/components_ng/pattern/scrollable/scrollable_model_static.cpp", "$ace_root/frameworks/core/components_ng/pattern/scrollable/scrollable_model_ng_multi_thread.cpp", diff --git a/test/unittest/core/pattern/grid/grid_cache_layout_test_ng.cpp b/test/unittest/core/pattern/grid/grid_cache_layout_test_ng.cpp index ab54f56ab76..6005d64b1bb 100644 --- a/test/unittest/core/pattern/grid/grid_cache_layout_test_ng.cpp +++ b/test/unittest/core/pattern/grid/grid_cache_layout_test_ng.cpp @@ -632,8 +632,8 @@ HWTEST_F(GridCacheLayoutTestNg, LayoutCachedItem002, TestSize.Level1) MockAnimationManager::GetInstance().Tick(); FlushUITasks(); - EXPECT_FLOAT_EQ(pattern_->info_.currentOffset_, 0); - EXPECT_FLOAT_EQ(GetChildY(frameNode_, 0), 0); + EXPECT_EQ(pattern_->info_.currentOffset_, 0); + EXPECT_EQ(GetChildY(frameNode_, 0), 0); EXPECT_FLOAT_EQ(GetChildY(frameNode_, 2), 100); EXPECT_FLOAT_EQ(GetChildY(frameNode_, 4), 200); EXPECT_EQ(pattern_->info_.startIndex_, 0); diff --git a/test/unittest/core/pattern/grid/grid_option_layout_test_ng.cpp b/test/unittest/core/pattern/grid/grid_option_layout_test_ng.cpp index 5003f3a7b5f..e45930ba3de 100644 --- a/test/unittest/core/pattern/grid/grid_option_layout_test_ng.cpp +++ b/test/unittest/core/pattern/grid/grid_option_layout_test_ng.cpp @@ -1504,4 +1504,77 @@ HWTEST_F(GridOptionLayoutTestNg, DataReload003, TestSize.Level1) rect = GetChildRect(frameNode_, 0); EXPECT_FLOAT_EQ(rect.width_, columnWidth); } + +/** + * @tc.name: ContentOffset001 + * @tc.desc: Test Grid ContentStartOffset and ContentEndOffset. + * @tc.type: FUNC + */ +HWTEST_F(GridOptionLayoutTestNg, ContentOffset003, TestSize.Level1) +{ + std::cout<<"start========================"<info_.currentOffset_, - contentOffset * 1.5); + + // ScrollToEdge(ScrollEdgeType::SCROLL_TOP, false); + // EXPECT_EQ(pattern_->GetTotalOffset(), -20.0f); + // EXPECT_EQ(pattern_->info_.currentOffset_, contentOffset); + + GestureEvent info; + info.SetMainVelocity(-1200.f); + info.SetMainDelta(-300.f); + auto scrollable = pattern_->GetScrollableEvent()->scrollable_; + ASSERT_TRUE(scrollable); + scrollable->HandleTouchDown(); + scrollable->HandleDragStart(info); + scrollable->HandleDragUpdate(info); + scrollable->HandleTouchUp(); + scrollable->HandleDragEnd(info); + MockAnimationManager::GetInstance().Tick(); + FlushUITasks(); + MockAnimationManager::GetInstance().Tick(); + // MockAnimationManager::GetInstance().Tick(); + FlushUITasks(); + EXPECT_TRUE(MockAnimationManager::GetInstance().AllFinished()); + std::cout<<"animation size:"<info_.currentOffset_, - contentOffset * 1.5); + EXPECT_EQ(pattern_->GetTotalOffset(), 330); + EXPECT_EQ(pattern_->info_.GetDistanceToBottom(HEIGHT, pattern_->info_.GetTotalHeightOfItemsInView(0, true), 0), 0); + + info.SetMainVelocity(1200.f); + info.SetMainDelta(300.f); + scrollable->HandleTouchDown(); + scrollable->HandleDragStart(info); + scrollable->HandleDragUpdate(info); + scrollable->HandleTouchUp(); + scrollable->HandleDragEnd(info); + FlushUITasks(); + MockAnimationManager::GetInstance().Tick(); + MockAnimationManager::GetInstance().Tick(); + MockAnimationManager::GetInstance().Tick(); + FlushUITasks(); + EXPECT_TRUE(MockAnimationManager::GetInstance().AllFinished()); + std::cout<<"animation size:"<info_.currentOffset_, - contentOffset * 1.5); + EXPECT_EQ(pattern_->GetTotalOffset(), 330); + EXPECT_EQ(pattern_->info_.GetDistanceToBottom(HEIGHT, pattern_->info_.GetTotalHeightOfItemsInView(0, true), 0), 0); + + // ScrollBy(0, -100); + // FlushUITasks(); + // EXPECT_EQ(pattern_->info_.currentOffset_, - contentOffset * 1.5); + std::cout<<"end============"<GetOffsetWithLimit(ITEM_MAIN_SIZE + contentOffset * 3); + EXPECT_EQ(offset, 0.0f); +} + +/** + * @tc.name: GetOffsetWithLimit002 + * @tc.desc: Test offset is Positive and totalOffset is larger than offset + * @tc.type: FUNC + */ +HWTEST_F(GridPatternTestNg, GetOffsetWithLimit002, TestSize.Level1) +{ + GridModelNG model = CreateGrid(); + model.SetColumnsTemplate("1fr"); + float contentOffset = 20.f; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset); + CreateItemsInLazyForEach(20, [](uint32_t idx) { return ITEM_MAIN_SIZE; }); + CreateDone(); + ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); + // total item height ITEM_MAIN_SIZE * 10 + 20 + 20 + float offset = pattern_->GetOffsetWithLimit(ITEM_MAIN_SIZE); + EXPECT_EQ(offset, ITEM_MAIN_SIZE); +} + +/** + * @tc.name: GetOffsetWithLimit003 + * @tc.desc: Test offset is Negative and remainHeight is enough + * @tc.type: FUNC + */ +HWTEST_F(GridPatternTestNg, GetOffsetWithLimit003, TestSize.Level1) +{ + GridModelNG model = CreateGrid(); + model.SetColumnsTemplate("1fr"); + float contentOffset = 20.f; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset); + CreateItemsInLazyForEach(10, [](uint32_t idx) { return ITEM_MAIN_SIZE; }); + CreateDone(); + + // total item height ITEM_MAIN_SIZE * 10 + 20 + 20 + float offset = pattern_->GetOffsetWithLimit(-ITEM_MAIN_SIZE); + EXPECT_EQ(offset, -ITEM_MAIN_SIZE); +} + +/** + * @tc.name: GetOffsetWithLimit004 + * @tc.desc: Test offset is Negative and remainHeight is not enough + * @tc.type: FUNC + */ +HWTEST_F(GridPatternTestNg, GetOffsetWithLimit004, TestSize.Level1) +{ + GridModelNG model = CreateGrid(); + model.SetColumnsTemplate("1fr"); + float contentOffset = 20.f; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateItemsInLazyForEach(10, [](uint32_t idx) { return ITEM_MAIN_SIZE; }); + CreateDone(); + + ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); + // total item height ITEM_MAIN_SIZE * 10 + 20 + 20 + float offset = pattern_->GetOffsetWithLimit(-ITEM_MAIN_SIZE); + EXPECT_EQ(offset, 0.0f); +} + +/** + * @tc.name: GetOffsetWithLimit005 + * @tc.desc: Test offset is 0 + * @tc.type: FUNC + */ +HWTEST_F(GridPatternTestNg, GetOffsetWithLimit005, TestSize.Level1) +{ + GridModelNG model = CreateGrid(); + model.SetColumnsTemplate("1fr"); + float contentOffset = 20.f; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateItemsInLazyForEach(10, [](uint32_t idx) { return ITEM_MAIN_SIZE; }); + CreateDone(); + + float offset = pattern_->GetOffsetWithLimit(0); + EXPECT_EQ(offset, 0.0f); +} + +/** + * @tc.name: GridPattern::IsAtTopWithDelta + * @tc.desc: test IsAtTopWithDelta with contentOffset + * @tc.type: FUNC + */ +HWTEST_F(GridPatternTestNg, IsAtTopWithDelta, TestSize.Level1) +{ + GridModelNG model = CreateGrid(); + model.SetColumnsTemplate("1fr"); + float contentOffset = 20.f; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateItemsInLazyForEach(50, [](uint32_t idx) { return ITEM_MAIN_SIZE; }); + CreateDone(); + + EXPECT_TRUE(pattern_->IsAtTopWithDelta()); + EXPECT_EQ(pattern_->info_.currentOffset_, 20.0f); + + ScrollBy(0, 20); + EXPECT_EQ(pattern_->info_.currentOffset_, 0.0f); + EXPECT_FALSE(pattern_->IsAtTopWithDelta()); +} + +/** + * @tc.name: GridPattern::IsAtTopOrBottom + * @tc.desc: test IsAtTop and IsAtBottom + * @tc.type: FUNC + */ +HWTEST_F(GridPatternTestNg, IsAtTopOrBottom, TestSize.Level1) +{ + GridModelNG model = CreateGrid(); + model.SetColumnsTemplate("1fr"); + float contentOffset = 20.f; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateItemsInLazyForEach(50, [](uint32_t idx) { return ITEM_MAIN_SIZE; }); + CreateDone(); + + EXPECT_TRUE(pattern_->IsAtTop()); + EXPECT_FALSE(pattern_->IsAtBottom()); + + ScrollBy(0, 20); + EXPECT_FALSE(pattern_->IsAtTop()); + EXPECT_FALSE(pattern_->IsAtBottom()); + + ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); + EXPECT_FALSE(pattern_->IsAtTop()); + EXPECT_TRUE(pattern_->IsAtBottom()); + + ScrollBy(0, -20); + EXPECT_FALSE(pattern_->IsAtTop()); + EXPECT_FALSE(pattern_->IsAtBottom()); +} } // namespace OHOS::Ace::NG diff --git a/test/unittest/core/pattern/grid/grid_scroll_layout_testtwo_ng.cpp b/test/unittest/core/pattern/grid/grid_scroll_layout_testtwo_ng.cpp index 30feca80ff3..b6078e0579c 100644 --- a/test/unittest/core/pattern/grid/grid_scroll_layout_testtwo_ng.cpp +++ b/test/unittest/core/pattern/grid/grid_scroll_layout_testtwo_ng.cpp @@ -17,7 +17,6 @@ #include "core/components_ng/pattern/grid/grid_scroll/grid_scroll_layout_algorithm.h" #include "core/components_ng/pattern/grid/grid_scroll/grid_scroll_with_options_layout_algorithm.h" -#include "core/components_ng/pattern/scrollable/scrollable_model_ng.h" namespace OHOS::Ace::NG { class GridScrollLayoutTestNg : public GridTestNg { @@ -219,5 +218,50 @@ HWTEST_F(GridScrollLayoutTestNg, FadingEdge003, TestSize.Level1) geo = frameNode_->GetOverlayNode()->GetGeometryNode(); EXPECT_EQ(geo->GetFrameSize().Height(), 410.f); } + +/** + * @tc.name: ContentOffset001 + * @tc.desc: Test Grid ContentStartOffset and ContentEndOffset. + * @tc.type: FUNC + */ +HWTEST_F(GridScrollLayoutTestNg, ContentOffset001, TestSize.Level1) +{ + GridModelNG model = CreateGrid(); + model.SetColumnsTemplate("1fr 1fr 1fr"); + model.SetLayoutOptions({}); + float contentOffset = 20; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateFixedItems(20); + CreateDone(); + + EXPECT_EQ(layoutProperty_->GetContentStartOffset(), contentOffset); + EXPECT_EQ(layoutProperty_->GetContentEndOffset(), contentOffset * 1.5); +} + +/** + * @tc.name: ContentOffset001 + * @tc.desc: Test Grid ContentStartOffset and ContentEndOffset. + * @tc.type: FUNC + */ +HWTEST_F(GridScrollLayoutTestNg, ContentOffset002, TestSize.Level1) +{ + GridModelNG model = CreateGrid(); + model.SetColumnsTemplate("1fr"); + model.SetLayoutOptions({}); + float contentOffset = 20; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateFixedItems(20); + CreateDone(); + + ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); + EXPECT_EQ(pattern_->GetTotalOffset(), ITEM_MAIN_SIZE * 16 + contentOffset * 1.5); + EXPECT_EQ(pattern_->GetTotalHeight(), ITEM_MAIN_SIZE * 20 + contentOffset * 2.5); + + ScrollToEdge(ScrollEdgeType::SCROLL_TOP, false); + EXPECT_EQ(pattern_->GetTotalOffset(), -20.0f); + EXPECT_EQ(pattern_->info_.currentOffset_, contentOffset); +} } // namespace OHOS::Ace::NG \ No newline at end of file diff --git a/test/unittest/core/pattern/grid/grid_scroller_test_ng.cpp b/test/unittest/core/pattern/grid/grid_scroller_test_ng.cpp index afb38263d02..e0ea6f24791 100644 --- a/test/unittest/core/pattern/grid/grid_scroller_test_ng.cpp +++ b/test/unittest/core/pattern/grid/grid_scroller_test_ng.cpp @@ -1580,4 +1580,76 @@ HWTEST_F(GridScrollerTestNg, CreateWithResourceObjScrollBarColor002, TestSize.Le ASSERT_NE(pattern_->resourceMgr_, nullptr); EXPECT_NE(pattern_->resourceMgr_->resMap_.size(), 0); } + +/** + * @tc.name: GetOverScrollOffsetWithContentOffset + * @tc.desc: Test GetOverScrollOffsetWithContentOffset + * @tc.type: FUNC + */ +HWTEST_F(GridScrollerTestNg, GetOverScrollOffsetWithContentOffset, TestSize.Level1) +{ + GridModelNG model = CreateGrid(); + model.SetColumnsTemplate("1fr 1fr"); + model.SetLayoutOptions({}); + float contentOffset = 20; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateFixedItems(10); + CreateDone(); + + EXPECT_EQ(pattern_->info_.currentOffset_, contentOffset); + OverScrollOffset offset = pattern_->GetOverScrollOffset(ITEM_MAIN_SIZE); + OverScrollOffset expectOffset = { ITEM_MAIN_SIZE, 0 }; + EXPECT_TRUE(IsEqual(offset, expectOffset)); + offset = pattern_->GetOverScrollOffset(0); + expectOffset = { 0, 0 }; + EXPECT_TRUE(IsEqual(offset, expectOffset)); + offset = pattern_->GetOverScrollOffset(-ITEM_MAIN_SIZE); + expectOffset = { 0, 0 }; + EXPECT_TRUE(IsEqual(offset, expectOffset)); + + pattern_->info_.currentOffset_ = -ITEM_MAIN_SIZE; + offset = pattern_->GetOverScrollOffset(ITEM_MAIN_SIZE * 2); + expectOffset = { ITEM_MAIN_SIZE - contentOffset, 0 }; + EXPECT_TRUE(IsEqual(offset, expectOffset)); + offset = pattern_->GetOverScrollOffset(0); + expectOffset = { 0, 0 }; + EXPECT_TRUE(IsEqual(offset, expectOffset)); + offset = pattern_->GetOverScrollOffset(-ITEM_MAIN_SIZE * 2); + expectOffset = { 0, 0 }; + EXPECT_TRUE(IsEqual(offset, expectOffset)); + + pattern_->info_.currentOffset_ = -ITEM_MAIN_SIZE * 2; + offset = pattern_->GetOverScrollOffset(ITEM_MAIN_SIZE); + expectOffset = { 0, 0 }; + EXPECT_TRUE(IsEqual(offset, expectOffset)); + offset = pattern_->GetOverScrollOffset(0); + expectOffset = { 0, 0 }; + EXPECT_TRUE(IsEqual(offset, expectOffset)); + offset = pattern_->GetOverScrollOffset(-ITEM_MAIN_SIZE); + expectOffset = { 0, 0 }; + EXPECT_TRUE(IsEqual(offset, expectOffset)); + + pattern_->info_.currentOffset_ = ITEM_MAIN_SIZE; + offset = pattern_->GetOverScrollOffset(ITEM_MAIN_SIZE); + expectOffset = { ITEM_MAIN_SIZE, 0 }; + EXPECT_TRUE(IsEqual(offset, expectOffset)); + offset = pattern_->GetOverScrollOffset(0); + expectOffset = { 0, 0 }; + EXPECT_TRUE(IsEqual(offset, expectOffset)); + offset = pattern_->GetOverScrollOffset(-ITEM_MAIN_SIZE * 2); + expectOffset = { -ITEM_MAIN_SIZE + contentOffset, 0 }; + EXPECT_TRUE(IsEqual(offset, expectOffset)); + + pattern_->info_.currentOffset_ = -ITEM_MAIN_SIZE * 3; + offset = pattern_->GetOverScrollOffset(ITEM_MAIN_SIZE * 2); + expectOffset = { 0, 0 }; + EXPECT_TRUE(IsEqual(offset, expectOffset)); + offset = pattern_->GetOverScrollOffset(0); + expectOffset = { 0, 0 }; + EXPECT_TRUE(IsEqual(offset, expectOffset)); + offset = pattern_->GetOverScrollOffset(-ITEM_MAIN_SIZE); + expectOffset = { 0, 0 }; + EXPECT_TRUE(IsEqual(offset, expectOffset)); +} } // namespace OHOS::Ace::NG diff --git a/test/unittest/core/pattern/grid/grid_test_ng.h b/test/unittest/core/pattern/grid/grid_test_ng.h index c9b4e5f4f9c..f1c77bb6809 100644 --- a/test/unittest/core/pattern/grid/grid_test_ng.h +++ b/test/unittest/core/pattern/grid/grid_test_ng.h @@ -23,6 +23,7 @@ #define protected public #define private public #include "core/components_ng/pattern/grid/grid_pattern.h" +#include "core/components_ng/pattern/scrollable/scrollable_model_ng.h" namespace OHOS::Ace::NG { namespace { diff --git a/test/unittest/core/pattern/overlay/overlay_manager_test_ng.cpp b/test/unittest/core/pattern/overlay/overlay_manager_test_ng.cpp index cce8a0b548d..aae18bd3a48 100644 --- a/test/unittest/core/pattern/overlay/overlay_manager_test_ng.cpp +++ b/test/unittest/core/pattern/overlay/overlay_manager_test_ng.cpp @@ -3232,10 +3232,10 @@ HWTEST_F(OverlayManagerTestNg, TestSheetAvoidaiBar, TestSize.Level1) auto scrollLayoutProperty = scrollNode->GetLayoutProperty(); ASSERT_NE(scrollLayoutProperty, nullptr); sheetPattern->AvoidAiBar(); - EXPECT_EQ(scrollLayoutProperty->GetScrollContentEndOffsetValue(.0f), .0f); + EXPECT_EQ(scrollLayoutProperty->GetContentEndOffsetValue(.0f), .0f); scrollPattern->scrollableDistance_ = 10.0f; sheetPattern->AvoidAiBar(); - EXPECT_EQ(scrollLayoutProperty->GetScrollContentEndOffsetValue(.0f), + EXPECT_EQ(scrollLayoutProperty->GetContentEndOffsetValue(.0f), PipelineContext::GetCurrentContext()->GetSafeArea().bottom_.Length()); } diff --git a/test/unittest/core/pattern/scroll/scroll_layout_test_ng.cpp b/test/unittest/core/pattern/scroll/scroll_layout_test_ng.cpp index 7d6795b9053..e109b49f888 100644 --- a/test/unittest/core/pattern/scroll/scroll_layout_test_ng.cpp +++ b/test/unittest/core/pattern/scroll/scroll_layout_test_ng.cpp @@ -1340,5 +1340,102 @@ HWTEST_F(ScrollLayoutTestNg, AdjustCurrentOffset_001, TestSize.Level1) FlushUITasks(); EXPECT_EQ(pattern_->currentOffset_, -50.0f); EXPECT_EQ(pattern_->scrollableDistance_, 50.0f); + +} + +/** + * @tc.name: ContentOffset001 + * @tc.desc: Test Scroll ContentStartOffset and ContentEndOffset. + * @tc.type: FUNC + */ +HWTEST_F(ScrollLayoutTestNg, ContentOffset001, TestSize.Level1) +{ + CreateScroll(); + ScrollableModelNG::SetContentStartOffset(20); + ScrollableModelNG::SetContentEndOffset(20); + CreateContent(); + CreateScrollDone(); + + EXPECT_EQ(layoutProperty_->GetContentStartOffset(), 20); + EXPECT_EQ(layoutProperty_->GetContentEndOffset(), 20); +} + +/** + * @tc.name: ContentOffset002 + * @tc.desc: Test Scroll scrollableDistance with ContentStartOffset and ContentEndOffset. + * @tc.type: FUNC + */ +HWTEST_F(ScrollLayoutTestNg, ContentOffset002, TestSize.Level1) +{ + CreateScroll(); + ScrollableModelNG::SetContentStartOffset(20); + ScrollableModelNG::SetContentEndOffset(20); + CreateContent(); + CreateScrollDone(); + + EXPECT_EQ(layoutProperty_->GetContentStartOffset(), 20); + EXPECT_EQ(layoutProperty_->GetContentEndOffset(), 20); + EXPECT_EQ(pattern_->scrollableDistance_, CONTENT_MAIN_SIZE - HEIGHT + 20 + 20); +} + +/** + * @tc.name: ContentOffset003 + * @tc.desc: Test Scroll ContentStartOffset and ContentEndOffset with illegle value + * @tc.type: FUNC + */ +HWTEST_F(ScrollLayoutTestNg, ContentOffset003, TestSize.Level1) +{ + CreateScroll(); + ScrollableModelNG::SetContentStartOffset(HEIGHT/2); + ScrollableModelNG::SetContentEndOffset(HEIGHT/2); + CreateContent(); + CreateScrollDone(); + + EXPECT_EQ(pattern_->contentEndOffset_, 0); + EXPECT_EQ(pattern_->contentStartOffset_, 0); + EXPECT_EQ(pattern_->scrollableDistance_, CONTENT_MAIN_SIZE - HEIGHT); +} + +/** + * @tc.name: ContentOffset004 + * @tc.desc: Test Scroll ContentStartOffset and ContentEndOffset with ReachStart + * @tc.type: FUNC + */ +HWTEST_F(ScrollLayoutTestNg, ContentOffset004, TestSize.Level1) +{ + CreateScroll(); + float contentOffset = 20; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset); + CreateContent(); + CreateScrollDone(); + + EXPECT_EQ(pattern_->currentOffset_, -20); + EXPECT_TRUE(pattern_->IsAtTop()); + EXPECT_FALSE(pattern_->IsAtBottom()); + + pattern_->ScrollBy(0, -640, false); + FlushUITasks(); + EXPECT_EQ(pattern_->currentOffset_, -660); + EXPECT_FALSE(pattern_->IsAtTop()); + EXPECT_TRUE(pattern_->IsAtBottom()); +} + +/** + * @tc.name: ContentOffset005 + * @tc.desc: Test Scroll ContentStartOffset and ContentEndOffset + * @tc.type: FUNC + */ +HWTEST_F(ScrollLayoutTestNg, ContentOffset005, TestSize.Level1) +{ + CreateScroll(); + ScrollableModelNG::SetContentStartOffset(HEIGHT/2); + ScrollableModelNG::SetContentEndOffset(HEIGHT/2); + CreateContent(); + CreateScrollDone(); + + EXPECT_EQ(pattern_->contentEndOffset_, 0); + EXPECT_EQ(pattern_->contentStartOffset_, 0); + EXPECT_EQ(pattern_->scrollableDistance_, CONTENT_MAIN_SIZE - HEIGHT); } } // namespace OHOS::Ace::NG diff --git a/test/unittest/core/pattern/scroll/scroll_pattern_test_ng_three.cpp b/test/unittest/core/pattern/scroll/scroll_pattern_test_ng_three.cpp index 96e0a2771dd..ce215cb16fb 100644 --- a/test/unittest/core/pattern/scroll/scroll_pattern_test_ng_three.cpp +++ b/test/unittest/core/pattern/scroll/scroll_pattern_test_ng_three.cpp @@ -366,4 +366,132 @@ TEST_F(ScrollPatternThreeTestNg, FireObserverOnDidScroll) ScrollTo(ITEM_MAIN_SIZE); EXPECT_TRUE(isCallback); } + + +/** + * @tc.name: GetOverScrollOffset_PositiveStart + * @tc.desc: Test GetOverScrollOffset with Positive offset at start + * @tc.type: FUNC + */ +HWTEST_F(ScrollPatternThreeTestNg, GetOverScrollOffset_PositiveStart, TestSize.Level1) { + ScrollModelNG model = CreateScroll(); + float contentOffset = 20; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateContent(); + CreateScrollDone(); + + EXPECT_EQ(pattern_->currentOffset_, -contentOffset); + ScrollBy(0, contentOffset * 0.5); + // startPos = -10(currentOffset) + 20(contentStartOffset) = 10 + EXPECT_EQ(pattern_->currentOffset_, -contentOffset * 0.5); + + // startPos > 0 && newStartPos > 0 + auto result = pattern_->GetOverScrollOffset(contentOffset); + EXPECT_DOUBLE_EQ(result.start, contentOffset); + EXPECT_DOUBLE_EQ(result.end, 0.0); + + // startPos > 0 && newStartPos <= 0 + result = pattern_->GetOverScrollOffset(-contentOffset); + EXPECT_DOUBLE_EQ(result.start, -contentOffset * 0.5); + EXPECT_DOUBLE_EQ(result.end, 0.0); +} + +/** + * @tc.name: GetOverScrollOffset_NegativeStart + * @tc.desc: Test GetOverScrollOffset with Negative offset at start + * @tc.type: FUNC + */ +HWTEST_F(ScrollPatternThreeTestNg, GetOverScrollOffset_NegativeStart, TestSize.Level1) { + ScrollModelNG model = CreateScroll(); + float contentOffset = 20; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateContent(); + CreateScrollDone(); + + EXPECT_EQ(pattern_->currentOffset_, -contentOffset); + pattern_->currentOffset_ -= contentOffset * 0.5; + + // startPos <= 0 && newStartPos > 0 + auto result = pattern_->GetOverScrollOffset(contentOffset); + EXPECT_DOUBLE_EQ(result.start, contentOffset * 0.5); + EXPECT_DOUBLE_EQ(result.end, 0.0); + + // startPos <= 0 && newStartPos <= 0 + result = pattern_->GetOverScrollOffset(-contentOffset); + EXPECT_DOUBLE_EQ(result.start, 0.0); + EXPECT_DOUBLE_EQ(result.end, 0.0); +} + +/** + * @tc.name: GetOverScrollOffset_PositiveEnd + * @tc.desc: Test GetOverScrollOffset with Positive offset at end + * @tc.type: FUNC + */ +HWTEST_F(ScrollPatternThreeTestNg, GetOverScrollOffset_PositiveEnd, TestSize.Level1) { + ScrollModelNG model = CreateScroll(); + float contentOffset = 20; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateContent(); + CreateScrollDone(); + + ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); + pattern_->currentOffset_ -= 10; + + // endPos < endRefences && newEndPos < endRefences + auto result = pattern_->GetOverScrollOffset(-10.0); + EXPECT_DOUBLE_EQ(result.start, 0.0); + EXPECT_DOUBLE_EQ(result.end, -10.0); + + // endPos < endRefences && newEndPos >= endRefences + result = pattern_->GetOverScrollOffset(20.0); + EXPECT_DOUBLE_EQ(result.start, 0.0); + EXPECT_DOUBLE_EQ(result.end, 10.0); +} + +/** + * @tc.name: GetOverScrollOffset_NegativeEnd + * @tc.desc: Test GetOverScrollOffset with Negative offset at end + * @tc.type: FUNC + */ +HWTEST_F(ScrollPatternThreeTestNg, GetOverScrollOffset_NegativeEnd, TestSize.Level1) { + ScrollModelNG model = CreateScroll(); + float contentOffset = 20; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateContent(); + CreateScrollDone(); + + ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); + pattern_->currentOffset_ += 10; + + // endPos >= endRefences && newEndPos < endRefences + auto result = pattern_->GetOverScrollOffset(-15.0); + EXPECT_DOUBLE_EQ(result.start, 0.0); + EXPECT_DOUBLE_EQ(result.end, -5.0); + + // endPos >= endRefences && newEndPos >= endRefences + result = pattern_->GetOverScrollOffset(10.0); + EXPECT_DOUBLE_EQ(result.start, 0.0); + EXPECT_DOUBLE_EQ(result.end, 0.0); +} + +/** + * @tc.name: GetOverScrollOffset_ZeroScrollableDistance + * @tc.desc: Test GetOverScrollOffset with 0 scrollableDistance + * @tc.type: FUNC + */ +HWTEST_F(ScrollPatternThreeTestNg, GetOverScrollOffset_ZeroScrollableDistance, TestSize.Level1) { + ScrollModelNG model = CreateScroll(); + CreateContent(); + CreateScrollDone(); + pattern_->scrollableDistance_ = 0.0f; + pattern_->currentOffset_ = -10.0; + + auto result = pattern_->GetOverScrollOffset(-5.0); + EXPECT_DOUBLE_EQ(result.start, 0.0); + EXPECT_DOUBLE_EQ(result.end, -5.0); +} } // namespace OHOS::Ace::NG \ No newline at end of file diff --git a/test/unittest/core/pattern/scroll/scroll_test_ng.h b/test/unittest/core/pattern/scroll/scroll_test_ng.h index 529fba38945..aad0dfcd370 100644 --- a/test/unittest/core/pattern/scroll/scroll_test_ng.h +++ b/test/unittest/core/pattern/scroll/scroll_test_ng.h @@ -24,6 +24,7 @@ #include "core/components_ng/pattern/scroll/scroll_model_ng.h" #include "core/components_ng/pattern/scroll/scroll_pattern.h" +#include "core/components_ng/pattern/scrollable/scrollable_model_ng.h" namespace OHOS::Ace::NG { constexpr float DEFAULT_ACTIVE_WIDTH = 8.0f; 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 700270541ef..7293af4519d 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 @@ -908,7 +908,7 @@ HWTEST_F(WaterFlowScrollerTestNg, ReachStart002, TestSize.Level1) MockAnimationManager::GetInstance().Tick(); FlushUITasks(); EXPECT_EQ(reached, 3); - EXPECT_EQ(GetChildY(frameNode_, 1), 0.0f); + EXPECT_NEAR(GetChildY(frameNode_, 1), 0.0f, 0.00001f); } /** 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 ae65b79b547..df80a6276d8 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 @@ -2197,12 +2197,12 @@ HWTEST_F(WaterFlowSegmentTest, EmptySectionWithDefaultSize, TestSize.Level1) EXPECT_EQ(geometryNode->GetFrameSize().Height(), 600.0f); } - /** - * @tc.name: WaterFlowSegmentReMeasureTest001 - * @tc.desc: Test WaterFlow segmented layout selective clearing mechanism - * @tc.type: FUNC - */ - HWTEST_F(WaterFlowSegmentTest, WaterFlowSegmentReMeasureTest001, TestSize.Level1) +/** + * @tc.name: WaterFlowSegmentReMeasureTest001 + * @tc.desc: Test WaterFlow segmented layout selective clearing mechanism + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSegmentTest, WaterFlowSegmentReMeasureTest001, TestSize.Level1) { /** * @tc.steps: step1. Create WaterFlow with segmented layout @@ -2243,4 +2243,42 @@ HWTEST_F(WaterFlowSegmentTest, EmptySectionWithDefaultSize, TestSize.Level1) EXPECT_TRUE(algo->isLayouted_); } + +/** + * @tc.name: ContentOffsetTest001 + * @tc.desc: Test contentStartOffset_ and contentEndOffset_ + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSegmentTest, ContentOffsetTest001, TestSize.Level1) +{ + WaterFlowModelNG model = CreateWaterFlow(); + model.SetColumnsTemplate("1fr 1fr 1fr 1fr"); + float contentOffset = 20; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateWaterFlowItems(10); + CreateDone(); + + EXPECT_EQ(layoutProperty_->GetContentStartOffset(), contentOffset); + EXPECT_EQ(layoutProperty_->GetContentEndOffset(), contentOffset * 1.5); +} + +/** + * @tc.name: ContentOffsetTest002 + * @tc.desc: Test contentStartOffset_ and contentEndOffset_ with invalid value + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSegmentTest, ContentOffsetTest002, TestSize.Level1) +{ + WaterFlowModelNG model = CreateWaterFlow(); + model.SetColumnsTemplate("1fr 1fr 1fr 1fr"); + float contentOffset = WATER_FLOW_HEIGHT / 2; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateWaterFlowItems(10); + CreateDone(); + + EXPECT_EQ(pattern_->layoutInfo_->contentStartOffset_, 0.0f); + EXPECT_EQ(pattern_->layoutInfo_->contentEndOffset_, 0.0f); +} } // namespace OHOS::Ace::NG \ No newline at end of file diff --git a/test/unittest/core/pattern/waterflow/water_flow_sw_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_sw_test.cpp index cc12530bfa2..c49eff6782f 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_sw_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_sw_test.cpp @@ -583,4 +583,68 @@ HWTEST_F(WaterFlowSWTest, LazyVGridInWaterFlowSW002, TestSize.Level1) EXPECT_EQ(swInfo->endIndex_, 7); EXPECT_EQ(swInfo->totalOffset_, -1200); } + +/** + * @tc.name: ContentOffsetTest001 + * @tc.desc: Test contentStartOffset_ and contentEndOffset_ + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSWTest, ContentOffsetTest001, TestSize.Level1) +{ + WaterFlowModelNG model = CreateWaterFlow(); + model.SetColumnsTemplate("1fr 1fr"); + float contentOffset = 20; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateItemsInLazyForEach(100, [](int32_t) { return 100.0f; }); + CreateDone(); + + EXPECT_EQ(layoutProperty_->GetContentStartOffset(), contentOffset); + EXPECT_EQ(layoutProperty_->GetContentEndOffset(), contentOffset * 1.5); +} + +/** + * @tc.name: ContentOffsetTest002 + * @tc.desc: Test contentStartOffset_ and contentEndOffset_ with invalid value + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSWTest, ContentOffsetTest002, TestSize.Level1) +{ + WaterFlowModelNG model = CreateWaterFlow(); + model.SetColumnsTemplate("1fr 1fr"); + float contentOffset = WATER_FLOW_HEIGHT / 2; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateItemsInLazyForEach(100, [](int32_t) { return 100.0f; }); + CreateDone(); + + EXPECT_EQ(pattern_->layoutInfo_->contentStartOffset_, 0.0f); + EXPECT_EQ(pattern_->layoutInfo_->contentEndOffset_, 0.0f); +} + +/** + * @tc.name: ContentOffsetTest003 + * @tc.desc: Test contentStartOffset_ and contentEndOffset_ with invalid value + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSWTest, ContentOffsetTest003, TestSize.Level1) +{ + WaterFlowModelNG model = CreateWaterFlow(); + model.SetColumnsTemplate("1fr 1fr"); + float contentOffset = 20; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateItemsInLazyForEach(100, [](int32_t) { return 100.0f; }); + CreateDone(); + + EXPECT_TRUE(pattern_->IsAtTop()); + EXPECT_FALSE(pattern_->IsAtBottom()); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), 20.0); + + pattern_->UpdateCurrentOffset(-20, SCROLL_FROM_UPDATE); + FlushUITasks(); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), 0.0); + EXPECT_FALSE(pattern_->IsAtTop()); + EXPECT_FALSE(pattern_->IsAtBottom()); +} } // 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 4251a725e5d..232e40017c1 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.h +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.h @@ -23,6 +23,7 @@ #include "test/unittest/core/syntax/mock_lazy_for_each_builder.h" #include "core/components/button/button_theme.h" +#include "core/components_ng/pattern/scrollable/scrollable_model_ng.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" 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 4b7f2a23c97..f333fc47603 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 @@ -1535,4 +1535,65 @@ HWTEST_F(WaterFlowTestNg, onWillStopDragging002, TestSize.Level1) EXPECT_TRUE(isOnWillStopDraggingCallBack); EXPECT_FLOAT_EQ(willStopDraggingVelocity.Value(), info.GetMainVelocity()); } + +/** + * @tc.name: ContentOffsetTest001 + * @tc.desc: Test contentStartOffset_ and contentEndOffset_ + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTopDownScrollerTestNg, ContentOffsetTest001, TestSize.Level1) +{ + CreateWaterFlow(); + float contentOffset = 20; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateWaterFlowItems(); + CreateDone(); + + EXPECT_EQ(layoutProperty_->GetContentStartOffset(), contentOffset); + EXPECT_EQ(layoutProperty_->GetContentEndOffset(), contentOffset * 1.5); +} + +/** + * @tc.name: ContentOffsetTest002 + * @tc.desc: Test contentStartOffset_ and contentEndOffset_ with invalid value + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTopDownScrollerTestNg, ContentOffsetTest002, TestSize.Level1) +{ + CreateWaterFlow(); + float contentOffset = WATER_FLOW_HEIGHT / 2; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateWaterFlowItems(); + CreateDone(); + + EXPECT_EQ(pattern_->layoutInfo_->contentStartOffset_, 0.0f); + EXPECT_EQ(pattern_->layoutInfo_->contentEndOffset_, 0.0f); +} + +/** + * @tc.name: ContentOffsetTest003 + * @tc.desc: Test IsAtTop and IsAtBottom with contentStartOffset_ and contentEndOffset_ + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTopDownScrollerTestNg, ContentOffsetTest003, TestSize.Level1) +{ + CreateWaterFlow(); + float contentOffset = 20; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateWaterFlowItems(); + CreateDone(); + + EXPECT_TRUE(pattern_->IsAtTop()); + EXPECT_FALSE(pattern_->IsAtBottom()); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), contentOffset); + + pattern_->UpdateCurrentOffset(-20, SCROLL_FROM_UPDATE); + FlushUITasks(); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), 0); + EXPECT_FALSE(pattern_->IsAtTop()); + EXPECT_FALSE(pattern_->IsAtBottom()); +} } // namespace OHOS::Ace::NG -- Gitee From 3852cd405ccf7f56ddf397c9a863cc5c67483b16 Mon Sep 17 00:00:00 2001 From: tomkl123 Date: Wed, 27 Aug 2025 10:50:22 +0800 Subject: [PATCH 2/2] scrollable components support contentStartOffset and contentEndOffset Signed-off-by: tomkl123 Change-Id: I3fccddff1527d7796ce69c32b155a736c050e89b --- .../grid_irregular_layout_algorithm.cpp | 5 -- .../scroll/scroll_layout_algorithm.cpp | 12 ++-- .../pattern/scroll/scroll_pattern.cpp | 17 +++--- .../pattern/scroll/scroll_pattern.h | 4 +- .../top_down/water_flow_layout_algorithm.cpp | 11 ++-- .../top_down/water_flow_layout_info.cpp | 8 +-- .../pattern/waterflow/water_flow_pattern.cpp | 8 +-- test/unittest/core/pattern/grid/BUILD.gn | 4 -- .../grid/grid_option_layout_test_ng.cpp | 60 ++----------------- .../pattern/scroll/scroll_layout_test_ng.cpp | 6 +- .../scroll/scroll_pattern_test_ng_three.cpp | 13 ++-- 11 files changed, 43 insertions(+), 105 deletions(-) diff --git a/frameworks/core/components_ng/pattern/grid/irregular/grid_irregular_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/grid/irregular/grid_irregular_layout_algorithm.cpp index 0e79a634164..73ff74fcf7c 100644 --- a/frameworks/core/components_ng/pattern/grid/irregular/grid_irregular_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/grid/irregular/grid_irregular_layout_algorithm.cpp @@ -87,7 +87,6 @@ void GridIrregularLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) if (matchChildren) { AdaptToChildMainSize(props, mainSize, frameSize_); } - std::cout<<"after measure offset:"<GetCachedCountValue(info_.defCachedCount_) * info_.crossCount_; if (props->GetShowCachedItemsValue(false)) { SyncPreloadItems(cacheCnt); @@ -175,7 +174,6 @@ void GridIrregularLayoutAlgorithm::Init(const RefPtr& props) CHECK_NULL_VOID(pattern); if (!pattern->IsInitialized()) { info_.currentOffset_ += info_.contentStartOffset_; - std::cout<<"currentOffset after init :"<GetEffectEdge(), alwaysEnabled); } else if (UnableOverScroll(layoutWrapper)) { if (scrollableDistance_ > 0.0f) { - currentOffset_ = std::clamp(currentOffset_, -scrollableDistance_, contentStartOffset_); + currentOffset_ = std::clamp(currentOffset_, -scrollableDistance_, 0.0f); } else { currentOffset_ = Positive(currentOffset_) ? contentStartOffset_ - : std::clamp(currentOffset_, contentStartOffset_, -scrollableDistance_); + : std::clamp(currentOffset_, 0.0f, -scrollableDistance_); } } else if (LessNotEqual(scrollableDistance_, lastScrollableDistance)) { if (GreatOrEqual(scrollableDistance_, 0.f) && LessOrEqual(-currentOffset_, lastScrollableDistance) && @@ -232,7 +232,7 @@ void ScrollLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper) } viewPortExtent_ = childSize; viewPortLength_ = axis == Axis::FREE ? viewPort_.Width() : GetMainAxisSize(viewPort_, axis); - auto currentOffset = axis == Axis::VERTICAL ? OffsetF(0.0f, currentOffset_) : OffsetF(currentOffset_, crossOffset_); + auto currentOffset = axis == Axis::VERTICAL ? OffsetF(0.0f, currentOffset_ + contentStartOffset_) : OffsetF(currentOffset_ + contentStartOffset_, crossOffset_); if (layoutDirection == TextDirection::RTL && axis == Axis::HORIZONTAL) { currentOffset = OffsetF(std::min(size.Width() - childSize.Width(), 0.f) - currentOffset_, 0.0f); } @@ -286,10 +286,8 @@ bool ScrollLayoutAlgorithm::UnableOverScroll(LayoutWrapper* layoutWrapper) const auto scrollNode = layoutWrapper->GetHostNode(); CHECK_NULL_RETURN(scrollNode, false); auto scrollPattern = AceType::DynamicCast(scrollNode->GetPattern()); - return (Positive(currentOffset_ - contentStartOffset_) && - !scrollPattern->CanOverScrollStart(scrollPattern->GetScrollSource())) || - (Negative(currentOffset_ - contentStartOffset_) && - GreatNotEqual(-currentOffset_ - contentStartOffset_, scrollableDistance_) && + return (Positive(currentOffset_) && !scrollPattern->CanOverScrollStart(scrollPattern->GetScrollSource())) || + (Negative(currentOffset_) && GreatNotEqual(-currentOffset_, scrollableDistance_) && !scrollPattern->CanOverScrollEnd(scrollPattern->GetScrollSource())); } diff --git a/frameworks/core/components_ng/pattern/scroll/scroll_pattern.cpp b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.cpp index db8169eb7af..3b939160d3c 100644 --- a/frameworks/core/components_ng/pattern/scroll/scroll_pattern.cpp +++ b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.cpp @@ -183,10 +183,6 @@ bool ScrollPattern::SetScrollProperties(const RefPtr& dirty, cons auto layoutAlgorithm = DynamicCast(layoutAlgorithmWrapper->GetLayoutAlgorithm()); CHECK_NULL_RETURN(layoutAlgorithm, false); currentOffset_ = layoutAlgorithm->GetCurrentOffset(); - contentStartOffset_ = layoutAlgorithm->GetContentStartOffset(); - if (!isInitialized_ && !initialOffset_.has_value()) { - currentOffset_ -= contentStartOffset_; - } if (freeScroll_ && scrollBar2d_) { freeScroll_->OnLayoutFinished(layoutAlgorithm->GetFreeOffset(), layoutAlgorithm->GetScrollableArea()); scrollBar2d_->SyncLayout( @@ -213,6 +209,7 @@ bool ScrollPattern::SetScrollProperties(const RefPtr& dirty, cons viewPortExtent_ = layoutAlgorithm->GetViewPortExtent(); contentEndOffset_ = layoutAlgorithm->GetContentEndOffset(); + contentStartOffset_ = layoutAlgorithm->GetContentStartOffset(); if (IsEnablePagingValid()) { SetIntervalSize(Dimension(static_cast(viewPortLength_))); } @@ -302,7 +299,7 @@ void ScrollPattern::ResetPosition() bool ScrollPattern::IsAtTop() const { - return GreatOrEqual(currentOffset_ + contentStartOffset_, 0.0); + return GreatOrEqual(currentOffset_, 0.0); } bool ScrollPattern::IsAtBottom(bool considerRepeat) const @@ -310,13 +307,13 @@ bool ScrollPattern::IsAtBottom(bool considerRepeat) const if (LessNotEqual(scrollableDistance_, 0.0f)) { return LessOrEqual(currentOffset_, 0.0f); } - return LessOrEqual(currentOffset_ + contentStartOffset_, -scrollableDistance_); + return LessOrEqual(currentOffset_, -scrollableDistance_); } OverScrollOffset ScrollPattern::GetOverScrollOffset(double delta) const { OverScrollOffset offset = { 0, 0 }; - auto startPos = currentOffset_ + contentStartOffset_; + auto startPos = currentOffset_; auto newStartPos = startPos + delta; if (startPos > 0 && newStartPos > 0) { offset.start = delta; @@ -407,7 +404,7 @@ double ScrollPattern::ValidateOffset(int32_t source, double willScrollOffset) void ScrollPattern::ValidateOffset(int32_t source) { - if (LessOrEqual(scrollableDistance_, -contentStartOffset_) || source == SCROLL_FROM_JUMP) { + if (LessOrEqual(scrollableDistance_, 0.0f) || source == SCROLL_FROM_JUMP) { return; } @@ -819,7 +816,7 @@ void ScrollPattern::SetEdgeEffectCallback(const RefPtr& scroll if (scroll->IsRowReverse() || scroll->IsColReverse()) { return scroll->GetScrollableDistance(); } - return scroll->contentStartOffset_; + return 0.0; }); scrollEffect->SetInitLeadingCallback([weakScroll = AceType::WeakClaim(this)]() -> double { auto scroll = weakScroll.Upgrade(); @@ -834,7 +831,7 @@ void ScrollPattern::SetEdgeEffectCallback(const RefPtr& scroll if (scroll->IsRowReverse() || scroll->IsColReverse()) { return scroll->GetScrollableDistance(); } - return scroll->contentStartOffset_; + return 0.0; }); } diff --git a/frameworks/core/components_ng/pattern/scroll/scroll_pattern.h b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.h index 665f94838d3..e2f83c1ed1c 100644 --- a/frameworks/core/components_ng/pattern/scroll/scroll_pattern.h +++ b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.h @@ -118,9 +118,9 @@ public: Offset GetCurrentOffset() const { if (GetAxis() == Axis::HORIZONTAL) { - return Offset { currentOffset_, 0 }; + return Offset { currentOffset_ - contentStartOffset_, 0 }; } - return Offset { 0, currentOffset_ }; + return Offset { 0, currentOffset_ - contentStartOffset_ }; } float GetScrollableDistance() const diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_algorithm.cpp index dc8248dac9d..544b6f5ae91 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_algorithm.cpp @@ -474,12 +474,13 @@ void WaterFlowLayoutAlgorithm::FillViewport(float mainSize, LayoutWrapper* layou void WaterFlowLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize, LayoutWrapper* layoutWrapper) { - auto maxItemHeight = layoutInfo_->GetMaxMainHeight() + layoutInfo_->contentEndOffset_; + auto maxItemHeight = layoutInfo_->GetMaxMainHeight(); if (layoutInfo_->footerIndex_ >= 0) { footerMainStartPos_ = maxItemHeight; footerMainSize_ = WaterFlowLayoutUtils::MeasureFooter(layoutWrapper, axis_); maxItemHeight += footerMainSize_; } + maxItemHeight += layoutInfo_->contentEndOffset_; if (layoutInfo_->jumpIndex_ != WaterFlowLayoutInfoBase::EMPTY_JUMP_INDEX) { if (layoutInfo_->extraOffset_.has_value() && Negative(layoutInfo_->extraOffset_.value())) { layoutInfo_->extraOffset_.reset(); @@ -489,9 +490,9 @@ void WaterFlowLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize, L } layoutInfo_->maxHeight_ = maxItemHeight; - if (mainSize - layoutInfo_->contentStartOffset_ - layoutInfo_->contentEndOffset_ >= maxItemHeight) { + if (mainSize - layoutInfo_->contentStartOffset_ >= maxItemHeight) { if ((GreatOrEqual(layoutInfo_->currentOffset_, layoutInfo_->contentStartOffset_) && !canOverScrollStart_) || - (LessOrEqual(layoutInfo_->currentOffset_, layoutInfo_->contentEndOffset_) && !canOverScrollEnd_)) { + (LessOrEqual(layoutInfo_->currentOffset_, layoutInfo_->contentStartOffset_) && !canOverScrollEnd_)) { layoutInfo_->currentOffset_ = layoutInfo_->contentStartOffset_; } layoutInfo_->itemStart_ = GreatOrEqual(layoutInfo_->currentOffset_, layoutInfo_->contentStartOffset_); @@ -500,10 +501,10 @@ void WaterFlowLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize, L } if (LessOrEqualCustomPrecision( - layoutInfo_->currentOffset_ + maxItemHeight + layoutInfo_->contentStartOffset_, mainSize, 0.1f)) { + layoutInfo_->currentOffset_ + maxItemHeight, mainSize, 0.1f)) { layoutInfo_->offsetEnd_ = true; if (!canOverScrollEnd_) { - layoutInfo_->currentOffset_ = mainSize - maxItemHeight + layoutInfo_->contentStartOffset_; + layoutInfo_->currentOffset_ = mainSize - maxItemHeight; } auto oldStart = layoutInfo_->startIndex_; diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.cpp index 946d1da4be4..6e6636d5cd4 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.cpp @@ -455,7 +455,7 @@ void WaterFlowLayoutInfo::Sync(float mainSize, bool canOverScrollStart, bool can bool WaterFlowLayoutInfo::IsAtTopWithDelta() { - return GreatOrEqual(currentOffset_, 0.0f); + return GreatOrEqual(currentOffset_, contentStartOffset_); } bool WaterFlowLayoutInfo::IsAtBottomWithDelta() @@ -597,11 +597,11 @@ float WaterFlowLayoutInfo::CalcTargetPosition(int32_t idx, int32_t crossIdx) con bool WaterFlowLayoutInfo::OutOfBounds() const { - bool outOfStart = itemStart_ && Positive(currentOffset_); + bool outOfStart = itemStart_ && GreatNotEqual(currentOffset_, contentStartOffset_); bool outOfEnd = offsetEnd_ && LessNotEqual(currentOffset_ + maxHeight_, lastMainSize_); // not outOfEnd when content size < mainSize but currentOffset_ == 0 if (LessNotEqual(maxHeight_, lastMainSize_)) { - outOfEnd &= Negative(currentOffset_); + outOfEnd &= Negative(currentOffset_ - contentStartOffset_); } return outOfStart || outOfEnd; } @@ -610,7 +610,7 @@ float WaterFlowLayoutInfo::CalcOverScroll(float mainSize, float delta) const { float res = 0; if (itemStart_) { - res = currentOffset_ + delta; + res = currentOffset_ - contentStartOffset_ + delta; } if (offsetEnd_) { res = mainSize - (GetMaxMainHeight() + currentOffset_ - delta); 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 bcef1d75106..2be9cdda6c9 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -69,7 +69,7 @@ bool WaterFlowPattern::UpdateCurrentOffset(float delta, int32_t source) } 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() + layoutInfo_->contentStartOffset_); } } delta = -FireOnWillScroll(-delta); @@ -132,14 +132,14 @@ void WaterFlowPattern::UpdateScrollBarOffset() auto geometryNode = host->GetGeometryNode(); auto viewSize = geometryNode->GetFrameSize(); float overScroll = 0.0f; - if (Positive(layoutInfo_->Offset())) { - overScroll = layoutInfo_->Offset(); + if (GreatNotEqual(layoutInfo_->Offset(), layoutInfo_->contentStartOffset_)) { + overScroll = layoutInfo_->Offset() - layoutInfo_->contentStartOffset_; } else if (layoutInfo_->offsetEnd_) { overScroll = layoutInfo_->BottomFinalPos(GetMainContentSize()) - layoutInfo_->CurrentPos(); overScroll = Positive(overScroll) ? overScroll : 0.0f; } HandleScrollBarOutBoundary(overScroll); - UpdateScrollBarRegion(-layoutInfo_->Offset(), layoutInfo_->EstimateTotalHeight(), + UpdateScrollBarRegion(-layoutInfo_->Offset() + layoutInfo_->contentStartOffset_, layoutInfo_->EstimateTotalHeight(), Size(viewSize.Width(), viewSize.Height()), Offset(0.0f, 0.0f)); }; diff --git a/test/unittest/core/pattern/grid/BUILD.gn b/test/unittest/core/pattern/grid/BUILD.gn index b6a736bc491..c853d13da77 100644 --- a/test/unittest/core/pattern/grid/BUILD.gn +++ b/test/unittest/core/pattern/grid/BUILD.gn @@ -32,13 +32,9 @@ ace_unittest("grid_test_regular") { sources += [ "grid_attr_test_ng.cpp", "grid_event_test_ng.cpp", - "grid_focus_test_ng.cpp", "grid_layout_test_ng.cpp", - "grid_pattern_testtwo_ng.cpp", "grid_scroll_layout_test_ng.cpp", - "grid_scroll_layout_testtwo_ng.cpp", "grid_scroller_event_test_ng.cpp", - "grid_sync_load_test_ng.cpp", ] } diff --git a/test/unittest/core/pattern/grid/grid_option_layout_test_ng.cpp b/test/unittest/core/pattern/grid/grid_option_layout_test_ng.cpp index e45930ba3de..aba938bd94a 100644 --- a/test/unittest/core/pattern/grid/grid_option_layout_test_ng.cpp +++ b/test/unittest/core/pattern/grid/grid_option_layout_test_ng.cpp @@ -1512,69 +1512,21 @@ HWTEST_F(GridOptionLayoutTestNg, DataReload003, TestSize.Level1) */ HWTEST_F(GridOptionLayoutTestNg, ContentOffset003, TestSize.Level1) { - std::cout<<"start========================"<info_.currentOffset_, - contentOffset * 1.5); - - // ScrollToEdge(ScrollEdgeType::SCROLL_TOP, false); - // EXPECT_EQ(pattern_->GetTotalOffset(), -20.0f); - // EXPECT_EQ(pattern_->info_.currentOffset_, contentOffset); - - GestureEvent info; - info.SetMainVelocity(-1200.f); - info.SetMainDelta(-300.f); - auto scrollable = pattern_->GetScrollableEvent()->scrollable_; - ASSERT_TRUE(scrollable); - scrollable->HandleTouchDown(); - scrollable->HandleDragStart(info); - scrollable->HandleDragUpdate(info); - scrollable->HandleTouchUp(); - scrollable->HandleDragEnd(info); - MockAnimationManager::GetInstance().Tick(); - FlushUITasks(); - MockAnimationManager::GetInstance().Tick(); - // MockAnimationManager::GetInstance().Tick(); - FlushUITasks(); - EXPECT_TRUE(MockAnimationManager::GetInstance().AllFinished()); - std::cout<<"animation size:"<info_.currentOffset_, - contentOffset * 1.5); - EXPECT_EQ(pattern_->GetTotalOffset(), 330); - EXPECT_EQ(pattern_->info_.GetDistanceToBottom(HEIGHT, pattern_->info_.GetTotalHeightOfItemsInView(0, true), 0), 0); - - info.SetMainVelocity(1200.f); - info.SetMainDelta(300.f); - scrollable->HandleTouchDown(); - scrollable->HandleDragStart(info); - scrollable->HandleDragUpdate(info); - scrollable->HandleTouchUp(); - scrollable->HandleDragEnd(info); - FlushUITasks(); - MockAnimationManager::GetInstance().Tick(); - MockAnimationManager::GetInstance().Tick(); - MockAnimationManager::GetInstance().Tick(); - FlushUITasks(); - EXPECT_TRUE(MockAnimationManager::GetInstance().AllFinished()); - std::cout<<"animation size:"<info_.currentOffset_, - contentOffset * 1.5); - EXPECT_EQ(pattern_->GetTotalOffset(), 330); - EXPECT_EQ(pattern_->info_.GetDistanceToBottom(HEIGHT, pattern_->info_.GetTotalHeightOfItemsInView(0, true), 0), 0); - // ScrollBy(0, -100); - // FlushUITasks(); - // EXPECT_EQ(pattern_->info_.currentOffset_, - contentOffset * 1.5); - std::cout<<"end============"<GetTotalOffset(), -20.0f); + EXPECT_EQ(pattern_->info_.currentOffset_, contentOffset); } } // namespace OHOS::Ace::NG diff --git a/test/unittest/core/pattern/scroll/scroll_layout_test_ng.cpp b/test/unittest/core/pattern/scroll/scroll_layout_test_ng.cpp index e109b49f888..ba0f8927d2f 100644 --- a/test/unittest/core/pattern/scroll/scroll_layout_test_ng.cpp +++ b/test/unittest/core/pattern/scroll/scroll_layout_test_ng.cpp @@ -1410,13 +1410,15 @@ HWTEST_F(ScrollLayoutTestNg, ContentOffset004, TestSize.Level1) CreateContent(); CreateScrollDone(); - EXPECT_EQ(pattern_->currentOffset_, -20); + EXPECT_EQ(pattern_->currentOffset_, 0.0f); + EXPECT_EQ(pattern_->GetCurrentOffset(), Offset(0, -20.f)); EXPECT_TRUE(pattern_->IsAtTop()); EXPECT_FALSE(pattern_->IsAtBottom()); pattern_->ScrollBy(0, -640, false); FlushUITasks(); - EXPECT_EQ(pattern_->currentOffset_, -660); + EXPECT_EQ(pattern_->currentOffset_, -640.f); + EXPECT_EQ(pattern_->GetCurrentOffset(), Offset(0, -660.f)); EXPECT_FALSE(pattern_->IsAtTop()); EXPECT_TRUE(pattern_->IsAtBottom()); } diff --git a/test/unittest/core/pattern/scroll/scroll_pattern_test_ng_three.cpp b/test/unittest/core/pattern/scroll/scroll_pattern_test_ng_three.cpp index ce215cb16fb..b71f8fa65aa 100644 --- a/test/unittest/core/pattern/scroll/scroll_pattern_test_ng_three.cpp +++ b/test/unittest/core/pattern/scroll/scroll_pattern_test_ng_three.cpp @@ -381,19 +381,16 @@ HWTEST_F(ScrollPatternThreeTestNg, GetOverScrollOffset_PositiveStart, TestSize.L CreateContent(); CreateScrollDone(); - EXPECT_EQ(pattern_->currentOffset_, -contentOffset); - ScrollBy(0, contentOffset * 0.5); - // startPos = -10(currentOffset) + 20(contentStartOffset) = 10 + EXPECT_EQ(pattern_->currentOffset_, 0.0f); + ScrollBy(0, -contentOffset * 0.5); EXPECT_EQ(pattern_->currentOffset_, -contentOffset * 0.5); - // startPos > 0 && newStartPos > 0 auto result = pattern_->GetOverScrollOffset(contentOffset); - EXPECT_DOUBLE_EQ(result.start, contentOffset); + EXPECT_DOUBLE_EQ(result.start, contentOffset * 0.5); EXPECT_DOUBLE_EQ(result.end, 0.0); - // startPos > 0 && newStartPos <= 0 result = pattern_->GetOverScrollOffset(-contentOffset); - EXPECT_DOUBLE_EQ(result.start, -contentOffset * 0.5); + EXPECT_DOUBLE_EQ(result.start, 0.0); EXPECT_DOUBLE_EQ(result.end, 0.0); } @@ -410,7 +407,7 @@ HWTEST_F(ScrollPatternThreeTestNg, GetOverScrollOffset_NegativeStart, TestSize.L CreateContent(); CreateScrollDone(); - EXPECT_EQ(pattern_->currentOffset_, -contentOffset); + EXPECT_EQ(pattern_->currentOffset_, 0.0f); pattern_->currentOffset_ -= contentOffset * 0.5; // startPos <= 0 && newStartPos > 0 -- Gitee