diff --git a/frameworks/bridge/declarative_frontend/jsview/js_scrollable_base.cpp b/frameworks/bridge/declarative_frontend/jsview/js_scrollable_base.cpp index 5fd8fb61bc6c1d93364445751303943592d14da4..8e03b25faeab1740378b1f8df7aa0229d6622f95 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_scrollable_base.cpp +++ b/frameworks/bridge/declarative_frontend/jsview/js_scrollable_base.cpp @@ -268,4 +268,32 @@ void JSScrollableBase::JSOnDidStopFling(const JSCallbackInfo& args) NG::ScrollableModelNG::SetOnDidStopFling(nullptr); } } + +void JSScrollableBase::SetContentStartOffset(const JSCallbackInfo& info) +{ + double offset = 0.0; + RefPtr resObj; + if (JSViewAbstract::ParseJsDouble(info[0], offset, resObj)) { + NG::ScrollableModelNG::SetContentStartOffset(offset); + } else { + NG::ScrollableModelNG::ResetContentStartOffset(); + } + if (SystemProperties::ConfigChangePerform()) { + NG::ScrollableModelNG::CreateWithResourceObjContentStartOffset(resObj); + } +} + +void JSScrollableBase::SetContentEndOffset(const JSCallbackInfo& info) +{ + double offset = 0.0; + RefPtr resObj; + if (JSViewAbstract::ParseJsDouble(info[0], offset, resObj)) { + NG::ScrollableModelNG::SetContentEndOffset(offset); + } else { + NG::ScrollableModelNG::ResetContentEndOffset(); + } + if (SystemProperties::ConfigChangePerform()) { + NG::ScrollableModelNG::CreateWithResourceObjContentEndOffset(resObj); + } +} } // 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 30e49017c77358fb8331ff9822865d4194e59fa2..ebe5916a3399dea12c0a2cdce5e9b8083d7c2b21 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_scrollable_base.h +++ b/frameworks/bridge/declarative_frontend/jsview/js_scrollable_base.h @@ -35,6 +35,8 @@ public: static void JSOnDidStopDragging(const JSCallbackInfo& info); static void JSOnWillStartFling(const JSCallbackInfo& info); static void JSOnDidStopFling(const JSCallbackInfo& info); + 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 eb15895f16cee1f4b3806b1fd443c6e19f00a6d6..7a9f6ee8b8d9ea7b977e92962eff8d98908fd557 100644 --- a/frameworks/core/components_ng/pattern/BUILD.gn +++ b/frameworks/core/components_ng/pattern/BUILD.gn @@ -465,6 +465,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/arc_list/arc_list_layout_algorithm.h b/frameworks/core/components_ng/pattern/arc_list/arc_list_layout_algorithm.h index eb2c01e588f80bb35458ed7a8584aaa9c218ac87..5c8b0a97063efcf0761bd43f50344fc5bbe96d1e 100755 --- a/frameworks/core/components_ng/pattern/arc_list/arc_list_layout_algorithm.h +++ b/frameworks/core/components_ng/pattern/arc_list/arc_list_layout_algorithm.h @@ -119,7 +119,7 @@ private: float CalculateHeaderOffset(LayoutWrapper* layoutWrapper, const ListItemInfo& info); bool CheckNeedUpdateHeaderOffset(LayoutWrapper* layoutWrapper); void UpdateZIndex(const RefPtr& layoutWrapper); - void CalcContentOffset(const RefPtr& property) override + void CalcContentOffset(LayoutWrapper* layoutWrapper) override { contentStartOffset_ = 0.0; contentEndOffset_ = 0.0; 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 db6eecca2f50112c4b2f03ffe80f57aad09751b7..78671b80770f13b225e30bb0d7ca471efd772f31 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,38 @@ void GridLayoutBaseAlgorithm::LostChildFocusToSelf(LayoutWrapper* layoutWrapper, focusHub->LostChildFocusToSelf(); } } + +void GridLayoutBaseAlgorithm::CalcContentOffset(LayoutWrapper* layoutWrapper, float mainSize) +{ + CHECK_NULL_VOID(layoutWrapper); + auto property = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); + CHECK_NULL_VOID(property); + auto startOffset = property->GetContentStartOffset(); + if (!startOffset.has_value()) { + info_.contentStartOffset_ = 0; + } + auto endOffset = property->GetContentEndOffset(); + if (!endOffset.has_value()) { + info_.contentEndOffset_ = 0; + } + if (!endOffset && !startOffset) { + return; + } + auto host = layoutWrapper->GetHostNode(); + CHECK_NULL_VOID(host); + auto pipeline = host->GetContext(); + CHECK_NULL_VOID(pipeline); + if (startOffset) { + info_.contentStartOffset_ = + std::max(pipeline->NormalizeToPx(Dimension(startOffset.value(), DimensionUnit::VP)), 0.0); + } + if (endOffset) { + info_.contentEndOffset_ = + std::max(pipeline->NormalizeToPx(Dimension(endOffset.value(), DimensionUnit::VP)), 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 9a417d80a5458c295c76bafbc3ecfa66287fd0ed..bf1851cdd1cd3ac83974eca335d5781a5e4d2c5a 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(LayoutWrapper* layoutWrapper, 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 3008b8d750e10345bb43d33f6c09634b9bdeaec8..2d52ec659b9b920cd4aff6bb84a673c9cabb7e33 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 7bc1c8242eaf2fc7a10ec90bb32d5fc8e7bf9026..ea528993ae6da272de1164b276662a70ebb7cd41 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 1723439261531f6f5af84bff65c8f0f12d090321..81df6040224cc3f1ea938f26225ce84b517f99e3 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 9a268bbe5f0f852b847ebb7d777868755e07eb40..aa08ee9a974ef2d07a4023d8f3a963f3596c5818 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: @@ -33,27 +34,13 @@ public: RefPtr Clone() const override { auto value = MakeRefPtr(); - value->LayoutProperty::UpdateLayoutProperty(DynamicCast(this)); - value->propRowsTemplate_ = CloneRowsTemplate(); - value->propColumnsTemplate_ = CloneColumnsTemplate(); - value->propRowsGap_ = CloneRowsGap(); - value->propColumnsGap_ = CloneColumnsGap(); - value->propCachedCount_ = CloneCachedCount(); - value->propShowCachedItems_ = CloneShowCachedItems(); - value->propGridDirection_ = CloneGridDirection(); - value->propFocusWrapMode_ = CloneFocusWrapMode(); - value->propMaxCount_ = CloneMaxCount(); - value->propMinCount_ = CloneMinCount(); - value->propCellLength_ = CloneCellLength(); - value->propScrollEnabled_ = CloneScrollEnabled(); - value->propLayoutOptions_ = CloneLayoutOptions(); - value->propSyncLoad_ = CloneSyncLoad(); + Clone(value); return value; } void Reset() override { - LayoutProperty::Reset(); + ScrollableLayoutProperty::Reset(); ResetColumnsTemplate(); ResetRowsTemplate(); ResetColumnsGap(); @@ -163,6 +150,27 @@ public: return {true, true}; } +protected: + void Clone(RefPtr property) const override + { + auto value = DynamicCast(property); + ScrollableLayoutProperty::Clone(value); + value->propRowsTemplate_ = CloneRowsTemplate(); + value->propColumnsTemplate_ = CloneColumnsTemplate(); + value->propRowsGap_ = CloneRowsGap(); + value->propColumnsGap_ = CloneColumnsGap(); + value->propCachedCount_ = CloneCachedCount(); + value->propShowCachedItems_ = CloneShowCachedItems(); + value->propGridDirection_ = CloneGridDirection(); + value->propFocusWrapMode_ = CloneFocusWrapMode(); + value->propMaxCount_ = CloneMaxCount(); + value->propMinCount_ = CloneMinCount(); + value->propCellLength_ = CloneCellLength(); + value->propScrollEnabled_ = CloneScrollEnabled(); + value->propLayoutOptions_ = CloneLayoutOptions(); + value->propSyncLoad_ = CloneSyncLoad(); + } + private: ACE_DISALLOW_COPY_AND_MOVE(GridLayoutProperty); diff --git a/frameworks/core/components_ng/pattern/grid/grid_pattern.cpp b/frameworks/core/components_ng/pattern/grid/grid_pattern.cpp index cd8e568efea841cd44aa15cf5e4a26292ea77699..f479356d653b76cdd90705d201bb11a648f28e59 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 26816f1ef8f938347568236dde258af0e35e1a51..e27bb5060ebbe53802ce79bd17ade03da4d317a7 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 e0a3928b14b0163c64e99cccbb839a8813abd480..c1d529e91930698c67be6364cc98579e92e8596a 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(layoutWrapper, 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_; @@ -2188,7 +2203,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) { @@ -2198,7 +2213,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 55f306c2a6d747fa21ee7b032202719e44b74e0a..a53a8120ff532c71b8c35543022e6ffc2f80d110 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(wrapper_, mainSize); + auto layoutPolicy = props->GetLayoutPolicyProperty(); auto isMainWrap = false; if (layoutPolicy.has_value()) { auto isVertical = info_.axis_ == Axis::VERTICAL; @@ -168,6 +167,14 @@ 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_; + } } namespace { @@ -187,7 +194,7 @@ inline void ResetLayoutRange(GridLayoutInfo& info) info.endIndex_ = -1; info.startMainLineIndex_ = 0; info.endMainLineIndex_ = -1; - info.currentOffset_ = 0.0f; + info.currentOffset_ = info.contentStartOffset_; info.prevOffset_ = 0.0f; } } // namespace @@ -273,6 +280,9 @@ inline float GetPrevHeight(const GridLayoutInfo& info, float mainGap) void GridIrregularLayoutAlgorithm::MeasureForward(float mainSize) { float heightToFill = mainSize - info_.currentOffset_ - GetPrevHeight(info_, mainGap_); + if (info_.startMainLineIndex_ == 0) { + heightToFill += info_.contentStartOffset_; + } if (Positive(heightToFill)) { GridIrregularFiller filler(&info_, wrapper_); filler.Fill({ crossLens_, crossGap_, mainGap_ }, heightToFill, info_.endMainLineIndex_); @@ -285,7 +295,7 @@ void GridIrregularLayoutAlgorithm::MeasureForward(float mainSize) info_.endMainLineIndex_ = endMainLineIdx; info_.endIndex_ = endIdx; - if (info_.startIndex_ == 0 && NonNegative(info_.currentOffset_)) { + if (info_.startIndex_ == 0 && GreatOrEqual(info_.currentOffset_, info_.contentStartOffset_)) { return; } // adjust offset @@ -296,7 +306,7 @@ void GridIrregularLayoutAlgorithm::MeasureForward(float mainSize) return; } info_.currentOffset_ += overDis; - if (Positive(info_.currentOffset_)) { + if (GreatNotEqual(info_.currentOffset_, info_.contentEndOffset_)) { MeasureBackward(mainSize, true); } } @@ -312,7 +322,7 @@ void GridIrregularLayoutAlgorithm::MeasureBackward(float mainSize, bool toAdjust GridLayoutRangeSolver solver(&info_, wrapper_); auto res = solver.FindStartingRow(mainGap_); if ((toAdjust || !canOverScrollStart_) && res.row == 0) { - res.pos = std::min(res.pos, 0.0f); + res.pos = std::min(res.pos, info_.contentStartOffset_); } UpdateStartInfo(info_, res); @@ -360,14 +370,17 @@ void GridIrregularLayoutAlgorithm::MeasureOnJump(float mainSize) void GridIrregularLayoutAlgorithm::Jump(float mainSize) { + float scrollToEndLine = false; if (info_.jumpIndex_ == JUMP_TO_BOTTOM_EDGE) { GridIrregularFiller filler(&info_, wrapper_); filler.FillMatrixOnly(info_.GetChildrenCount() - 1); info_.PrepareJumpToBottom(); + scrollToEndLine = true; } if (info_.jumpIndex_ == LAST_ITEM) { info_.jumpIndex_ = info_.GetChildrenCount() - 1; + scrollToEndLine = true; } if (info_.scrollAlign_ == ScrollAlign::AUTO) { @@ -385,8 +398,13 @@ void GridIrregularLayoutAlgorithm::Jump(float mainSize) GridLayoutRangeSolver solver(&info_, wrapper_); const auto res = solver.FindRangeOnJump(info_.jumpIndex_, jumpLineIdx, mainGap_); - info_.currentOffset_ = res.pos; + if (res.startRow == 0) { + info_.currentOffset_ += info_.contentStartOffset_; + } + if (scrollToEndLine || info_.jumpIndex_ == info_.GetChildrenCount() - 1) { + info_.currentOffset_ -= info_.contentEndOffset_; + } info_.startMainLineIndex_ = res.startRow; info_.startIndex_ = res.startIdx; info_.endMainLineIndex_ = res.endRow; @@ -396,7 +414,7 @@ void GridIrregularLayoutAlgorithm::Jump(float mainSize) void GridIrregularLayoutAlgorithm::UpdateLayoutInfo() { - info_.reachStart_ = info_.startIndex_ == 0 && NonNegative(info_.currentOffset_); + info_.reachStart_ = info_.startIndex_ == 0 && GreatOrEqual(info_.currentOffset_, info_.contentStartOffset_); // GridLayoutInfo::reachEnd_ has a different meaning info_.reachEnd_ = info_.endIndex_ == info_.GetChildrenCount() - 1; @@ -732,8 +750,9 @@ void GridIrregularLayoutAlgorithm::PreloadItems(int32_t cacheCnt) void GridIrregularLayoutAlgorithm::AdaptToChildMainSize( RefPtr& 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/list/list_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/list/list_layout_algorithm.cpp index 38ddc2a9b0c669e9e66ed3915f5a10043c68c9ef..d00a4c2e17861ccf3722407944a1c04ccdd46a92 100644 --- a/frameworks/core/components_ng/pattern/list/list_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/list/list_layout_algorithm.cpp @@ -97,7 +97,7 @@ void ListLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) const auto& layoutConstraint = layoutConstraintOps.value(); // calculate idealSize and set FrameSize - CalcContentOffset(listLayoutProperty); + CalcContentOffset(layoutWrapper); // calculate main size. const auto& contentConstraintOps = listLayoutProperty->GetContentLayoutConstraint(); @@ -224,13 +224,32 @@ void ListLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) isLayouted_ = false; } -void ListLayoutAlgorithm::CalcContentOffset(const RefPtr& property) +void ListLayoutAlgorithm::CalcContentOffset(LayoutWrapper* layoutWrapper) { + CHECK_NULL_VOID(layoutWrapper); + auto property = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); 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); + auto startOffset = property->GetContentStartOffset(); + if (!startOffset.has_value()) { + contentStartOffset_ = 0.0f; + } + auto endOffset = property->GetContentEndOffset(); + if (!endOffset.has_value()) { + contentEndOffset_ = 0.0f; + } + if (!endOffset && !startOffset) { + return; + } + auto host = layoutWrapper->GetHostNode(); + CHECK_NULL_VOID(host); + auto pipeline = host->GetContext(); + CHECK_NULL_VOID(pipeline); + if (startOffset) { + contentStartOffset_ = std::max(pipeline->NormalizeToPx(Dimension(startOffset.value(), DimensionUnit::VP)), 0.0); + } + if (endOffset) { + contentEndOffset_ = std::max(pipeline->NormalizeToPx(Dimension(endOffset.value(), DimensionUnit::VP)), 0.0); + } } void ListLayoutAlgorithm::SetCacheCount(LayoutWrapper* layoutWrapper, int32_t cacheCount) diff --git a/frameworks/core/components_ng/pattern/list/list_layout_algorithm.h b/frameworks/core/components_ng/pattern/list/list_layout_algorithm.h index 2b80fc90334b2442950486ed22c34f88c5038d04..57122944aaad117f78bfbf52afd6d2cbcbe6e68b 100644 --- a/frameworks/core/components_ng/pattern/list/list_layout_algorithm.h +++ b/frameworks/core/components_ng/pattern/list/list_layout_algorithm.h @@ -620,7 +620,7 @@ protected: virtual void MeasureHeader(LayoutWrapper* layoutWrapper) {} virtual void LayoutHeader(LayoutWrapper* layoutWrapper, const OffsetF& paddingOffset, float crossSize) {} - virtual void CalcContentOffset(const RefPtr& property); + virtual void CalcContentOffset(LayoutWrapper* layoutWrapper); virtual bool IsScrollSnapAlignCenter(LayoutWrapper* layoutWrapper); virtual void FixItemLayoutOffset(LayoutWrapper* layoutWrapper) {} 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 38b483ecf273c604224c0a9ff1ab47b1e30ae3f8..81ea4bcabeb0039df93b89296827430089816c38 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 80bb6403dfa02185ea73f59248989b4bcac135d4..eb1188520e399b176c0b01e97b3e14d9697b09e0 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(layoutWrapper); 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,34 @@ void ScrollLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) UseInitialOffset(axis, selfSize, layoutWrapper); } +void ScrollLayoutAlgorithm::CalcContentOffset(LayoutWrapper* layoutWrapper) +{ + CHECK_NULL_VOID(layoutWrapper); + auto property = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); + CHECK_NULL_VOID(property); + auto startOffset = property->GetContentStartOffset(); + if (!startOffset.has_value()) { + contentStartOffset_ = 0.0f; + } + auto endOffset = property->GetContentEndOffset(); + if (!endOffset.has_value()) { + contentEndOffset_ = 0.0f; + } + if (!endOffset && !startOffset) { + return; + } + auto host = layoutWrapper->GetHostNode(); + CHECK_NULL_VOID(host); + auto pipeline = host->GetContext(); + CHECK_NULL_VOID(pipeline); + if (startOffset) { + contentStartOffset_ = std::max(pipeline->NormalizeToPx(Dimension(startOffset.value(), DimensionUnit::VP)), 0.0); + } + if (endOffset) { + contentEndOffset_ = std::max(pipeline->NormalizeToPx(Dimension(endOffset.value(), DimensionUnit::VP)), 0.0); + } +} + namespace { float DimensionToFloat(const CalcDimension& value, float selfLength) { @@ -187,12 +220,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(); @@ -216,7 +249,8 @@ 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); } 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 e0d676755458b2cf7e1551e26f86e7376d63cb68..e456abcc1b3caedfd3a81c685afee45718960638 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(LayoutWrapper* layoutWrapper); 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 30d91ec44ac4e86bc566584e078dbb111d9d1468..ffebf3e077eba16aa58982d7a754b330f593881c 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: @@ -37,25 +38,20 @@ public: RefPtr Clone() const override { auto value = MakeRefPtr(); - value->LayoutProperty::UpdateLayoutProperty(DynamicCast(this)); - value->propAxis_ = CloneAxis(); - value->propScrollEnabled_ = CloneScrollEnabled(); - value->propScrollSnapAlign_ = CloneScrollSnapAlign(); - value->propScrollContentEndOffset_ = CloneScrollContentEndOffset(); + Clone(value); return value; } void Reset() override { - LayoutProperty::Reset(); + ScrollableLayoutProperty::Reset(); ResetAxis(); ResetScrollEnabled(); - ResetScrollContentEndOffset(); } void ToJsonValue(std::unique_ptr& json, const InspectorFilter& filter) const override { - LayoutProperty::ToJsonValue(json, filter); + ScrollableLayoutProperty::ToJsonValue(json, filter); std::unordered_map scrollableMap { { Axis::VERTICAL, "ScrollDirection.Vertical" }, { Axis::HORIZONTAL, "ScrollDirection.Horizontal" }, { Axis::FREE, "ScrollDirection.Free" }, { Axis::NONE, "ScrollDirection.None" } }; @@ -78,7 +74,17 @@ 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); + +protected: + void Clone(RefPtr property) const override + { + auto value = DynamicCast(property); + ScrollableLayoutProperty::Clone(value); + value->propAxis_ = CloneAxis(); + value->propScrollEnabled_ = CloneScrollEnabled(); + value->propScrollSnapAlign_ = CloneScrollSnapAlign(); + value->propContentEndOffset_ = CloneContentEndOffset(); + } }; } // 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 40b2ed9204ff93083c612b69c1dc0acbfa93f231..4cd30ccdbe9c758df0945e16965fdf20f57978ca 100644 --- a/frameworks/core/components_ng/pattern/scroll/scroll_pattern.cpp +++ b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.cpp @@ -207,6 +207,9 @@ bool ScrollPattern::SetScrollProperties(const RefPtr& dirty, cons viewPort_ = layoutAlgorithm->GetViewPort(); viewSize_ = layoutAlgorithm->GetViewSize(); viewPortExtent_ = layoutAlgorithm->GetViewPortExtent(); + + contentEndOffset_ = layoutAlgorithm->GetContentEndOffset(); + contentStartOffset_ = layoutAlgorithm->GetContentStartOffset(); if (IsEnablePagingValid()) { SetIntervalSize(Dimension(static_cast(viewPortLength_))); } @@ -381,7 +384,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; } @@ -390,7 +393,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); } @@ -811,7 +814,8 @@ 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; @@ -825,7 +829,8 @@ 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; @@ -855,8 +860,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 195821c3f5116ac45cc70adc4824ed100b62b358..bed8f96b58b59d6d47e3a40ccfa19c6ae537b627 100644 --- a/frameworks/core/components_ng/pattern/scroll/scroll_pattern.h +++ b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.h @@ -105,12 +105,12 @@ public: double GetCurrentPosition() const { - return currentOffset_; + return currentOffset_ - contentStartOffset_; } double GetTotalOffset() const override { - return -currentOffset_; + return -currentOffset_ - contentStartOffset_; } void ResetPosition(); @@ -442,6 +442,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 0000000000000000000000000000000000000000..932d6b8e9d7be53f52017c3eaa6f05f5242758f6 --- /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 0000000000000000000000000000000000000000..fdaa524fd1dcf93f509db3133a1529e18d772cff --- /dev/null +++ b/frameworks/core/components_ng/pattern/scrollable/scrollable_layout_property.h @@ -0,0 +1,63 @@ +/* + * 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(); + Clone(value); + 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 Clone(RefPtr property) const override + { + auto value = MakeRefPtr(); + value->LayoutProperty::UpdateLayoutProperty(DynamicCast(this)); + value->propContentStartOffset_ = CloneContentStartOffset(); + value->propContentEndOffset_ = CloneContentEndOffset(); + } + +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 736c1457f2adf899bd1217e505f25a3a7349b781..ffd06b5523cbf20c85d156a6a5a055b75fffa408 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" @@ -579,4 +580,98 @@ 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); +} + +void ScrollableModelNG::SetContentStartOffset(FrameNode* frameNode, float offset) +{ + ACE_UPDATE_NODE_LAYOUT_PROPERTY(ScrollableLayoutProperty, ContentStartOffset, offset, frameNode); +} + +void ScrollableModelNG::SetContentEndOffset(FrameNode* frameNode, float offset) +{ + ACE_UPDATE_NODE_LAYOUT_PROPERTY(ScrollableLayoutProperty, ContentEndOffset, offset, frameNode); +} + +void ScrollableModelNG::ResetContentStartOffset(FrameNode* frameNode) +{ + CHECK_NULL_VOID(frameNode); + SetContentStartOffset(frameNode, 0.0f); +} + +void ScrollableModelNG::ResetContentEndOffset(FrameNode* frameNode) +{ + CHECK_NULL_VOID(frameNode); + SetContentEndOffset(frameNode, 0.0f); +} + +void ScrollableModelNG::ResetContentStartOffset() +{ + SetContentStartOffset(0.0f); +} + +void ScrollableModelNG::ResetContentEndOffset() +{ + SetContentEndOffset(0.0f); +} + +void ScrollableModelNG::CreateWithResourceObjContentStartOffset(const RefPtr& resObj) +{ + auto frameNode = ViewStackProcessor::GetInstance()->GetMainFrameNode(); + CHECK_NULL_VOID(frameNode); + CreateWithResourceObjContentStartOffset(frameNode, resObj); +} + +void ScrollableModelNG::CreateWithResourceObjContentEndOffset(const RefPtr& resObj) +{ + auto frameNode = ViewStackProcessor::GetInstance()->GetMainFrameNode(); + CHECK_NULL_VOID(frameNode); + CreateWithResourceObjContentEndOffset(frameNode, resObj); +} + +void ScrollableModelNG::CreateWithResourceObjContentStartOffset( + FrameNode* frameNode, const RefPtr& resObj) +{ + CHECK_NULL_VOID(frameNode); + auto pattern = frameNode->GetPattern(); + CHECK_NULL_VOID(pattern); + const std::string key = "contentStartOffset"; + pattern->RemoveResObj(key); + CHECK_NULL_VOID(resObj); + auto&& updateFunc = [weak = AceType::WeakClaim(frameNode)](const RefPtr& resObj) { + auto frameNode = weak.Upgrade(); + CHECK_NULL_VOID(frameNode); + double offset = 0.0; + ResourceParseUtils::ParseResDouble(resObj, offset); + ScrollableModelNG::SetContentStartOffset(AceType::RawPtr(frameNode), static_cast(offset)); + }; + pattern->AddResObj(key, resObj, std::move(updateFunc)); +} + +void ScrollableModelNG::CreateWithResourceObjContentEndOffset( + FrameNode* frameNode, const RefPtr& resObj) +{ + CHECK_NULL_VOID(frameNode); + auto pattern = frameNode->GetPattern(); + CHECK_NULL_VOID(pattern); + const std::string key = "contentEndOffset"; + pattern->RemoveResObj(key); + CHECK_NULL_VOID(resObj); + auto&& updateFunc = [weak = AceType::WeakClaim(frameNode)](const RefPtr& resObj) { + auto frameNode = weak.Upgrade(); + CHECK_NULL_VOID(frameNode); + double offset = 0.0; + ResourceParseUtils::ParseResDouble(resObj, offset); + ScrollableModelNG::SetContentEndOffset(AceType::RawPtr(frameNode), static_cast(offset)); + }; + pattern->AddResObj(key, resObj, std::move(updateFunc)); +} } // 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 11896e8f29451606e83bdf2616f7bf0404aee7aa..ee9d242f46d3db0b40df8c52df7e5385a956a671 100644 --- a/frameworks/core/components_ng/pattern/scrollable/scrollable_model_ng.h +++ b/frameworks/core/components_ng/pattern/scrollable/scrollable_model_ng.h @@ -112,6 +112,18 @@ 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 SetContentStartOffset(FrameNode* frameNode, float offset); + static void SetContentEndOffset(FrameNode* frameNode, float offset); + static void ResetContentStartOffset(); + static void ResetContentEndOffset(); + static void ResetContentStartOffset(FrameNode* frameNode); + static void ResetContentEndOffset(FrameNode* frameNode); + static void CreateWithResourceObjContentStartOffset(const RefPtr& resObj); + static void CreateWithResourceObjContentEndOffset(const RefPtr& resObj); + static void CreateWithResourceObjContentStartOffset(FrameNode* frameNode, const RefPtr& resObj); + static void CreateWithResourceObjContentEndOffset(FrameNode* frameNode, const RefPtr& resObj); 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 edae826fa05ce4fdad9d0aeef611954f0a483bc3..2353e59543540482499b7ecf52e9e80bcb0a9831 100644 --- a/frameworks/core/components_ng/pattern/scrollable/scrollable_pattern.h +++ b/frameworks/core/components_ng/pattern/scrollable/scrollable_pattern.h @@ -927,6 +927,12 @@ public: } static double GetDefaultFriction(); + + 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 76cc6342cbe1e9b4b86d285660b94d57c224ea99..e5140226e576bc857cb323e3d05f8f4337a68bb3 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 @@ -846,7 +847,7 @@ float WaterFlowLayoutInfoSW::EstimateTotalHeight() const + (endPos_ - startPos_); if (itemEnd_ && repeatDifference_ == 0) { float bottomOverScroll = std::max(BottomFinalPos(lastMainSize_), 0.0f); - return height - bottomOverScroll + BotMargin() + footerHeight_; + return height - bottomOverScroll + BotMargin() + footerHeight_ + contentEndOffset_; } const float average = GetAverageItemHeight(); @@ -854,6 +855,7 @@ float WaterFlowLayoutInfoSW::EstimateTotalHeight() const height += EstimateSectionHeight(i, average, endIndex_ + 1, INT_MAX); } float virtualTotalHeight = 0.f; + height += contentEndOffset_; if (EstimateVirtualTotalHeight(average, virtualTotalHeight)) { height += virtualTotalHeight; return height; @@ -954,9 +956,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 99016308c9611ff50adcc69e0396c2fcab0ede3d..5125de5c98dfdb7db07fc99bfb72e9ecb44dd14e 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 89a33b071860132cc1c62488f82758263d848524..dacc9bb5106b3a0c4feb4c5a7b82c193fe73ed38 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(wrapper_, 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 1870a8a70df5106602d2bf6d774cf0ed610a681c..b21d81bd527c42ef81d1eb3fe044efac84c2a294 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(layoutWrapper, 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); @@ -469,6 +476,7 @@ void WaterFlowLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize, L 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(); @@ -478,13 +486,13 @@ 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_ >= maxItemHeight) { + if ((GreatOrEqual(layoutInfo_->currentOffset_, layoutInfo_->contentStartOffset_) && !canOverScrollStart_) || + (LessOrEqual(layoutInfo_->currentOffset_, layoutInfo_->contentStartOffset_) && !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; } 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 24a9993386aa8f473effcd48e899c4eb8ba317c4..99366f74863a82f434b149fbc343e592652efaca 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 @@ -112,7 +112,7 @@ float WaterFlowLayoutInfo::GetMaxMainHeight() const float WaterFlowLayoutInfo::GetContentHeight() const { - return NearZero(maxHeight_) ? GetMaxMainHeight() : maxHeight_; + return NearZero(maxHeight_) ? GetMaxMainHeight() + contentEndOffset_ : maxHeight_; } float WaterFlowLayoutInfo::GetMainHeight(int32_t crossIndex, int32_t itemIndex) const @@ -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(); @@ -454,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() @@ -596,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; } @@ -609,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); @@ -637,7 +638,7 @@ float WaterFlowLayoutInfo::EstimateTotalHeight() const return 0; } auto estimateHeight = GetMaxMainHeight() / childCount * totalChildrenCount; - return estimateHeight; + return estimateHeight + contentEndOffset_; } int32_t WaterFlowLayoutInfo::GetLastItem() const @@ -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 f2ddc1cd5bb1adb05d306d66ded659be6ac43cc7..b249a4525586fadcf39f2d24acd2286bb4a2c599 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 9bdd633dfe82a700edfd3df241df5df9b80e3acb..8c827668fb172c62682f3423f42762276ef65d68 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(wrapper, 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 fea56e5590c88da4d2f59937925e3e4f81f6f427..3f3abe8a57f04208a89285edaf1bf7f7cd7b4f39 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,39 @@ void WaterFlowLayoutBase::ClearUnlayoutedItems(LayoutWrapper* layoutWrapper) frameNode->ClearSubtreeLayoutAlgorithm(); } } + +void WaterFlowLayoutBase::CalcContentOffset( + LayoutWrapper* layoutWrapper, const RefPtr& info, float mainSize) +{ + CHECK_NULL_VOID(layoutWrapper); + auto property = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); + CHECK_NULL_VOID(property); + auto startOffset = property->GetContentStartOffset(); + if (!startOffset.has_value()) { + info->contentStartOffset_ = 0.0f; + } + auto endOffset = property->GetContentEndOffset(); + if (!endOffset.has_value()) { + info->contentEndOffset_ = 0.0f; + } + if (!endOffset && !startOffset) { + return; + } + auto host = layoutWrapper->GetHostNode(); + CHECK_NULL_VOID(host); + auto pipeline = host->GetContext(); + CHECK_NULL_VOID(pipeline); + if (startOffset) { + info->contentStartOffset_ = + std::max(pipeline->NormalizeToPx(Dimension(startOffset.value(), DimensionUnit::VP)), 0.0); + } + if (endOffset) { + info->contentEndOffset_ = + std::max(pipeline->NormalizeToPx(Dimension(endOffset.value(), DimensionUnit::VP)), 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 1e9e05800cf47755853fd4ccfd8e709ae80f3539..53064bf8cd587a2368addbd4d04967c6f768bb06 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,8 @@ protected: */ virtual RefPtr LayoutInfo() const = 0; + void CalcContentOffset(LayoutWrapper* layoutWrapper, 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 acc5b084a4a00e11d8fbb8400eec6b04cde06757..a7c16d3903ced73c4a7215ad9d21093e042a4733 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.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_property.cpp index 3433bceef6cf317448899921f526ffed4d1cf590..cac06177aa814a5a5c9ec4785d4a1d7ccfe4142e 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_property.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_property.cpp @@ -37,7 +37,7 @@ void WaterFlowLayoutProperty::ResetWaterflowLayoutInfoAndMeasure() const void WaterFlowLayoutProperty::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; @@ -87,17 +87,7 @@ std::string WaterFlowLayoutProperty::GetWaterflowDirectionStr() const RefPtr WaterFlowLayoutProperty::Clone() const { auto value = MakeRefPtr(); - value->LayoutProperty::UpdateLayoutProperty(DynamicCast(this)); - value->propRowsTemplate_ = CloneRowsTemplate(); - value->propColumnsTemplate_ = CloneColumnsTemplate(); - value->propRowsGap_ = CloneRowsGap(); - value->propColumnsGap_ = CloneColumnsGap(); - value->propWaterflowDirection_ = CloneWaterflowDirection(); - value->propScrollEnabled_ = CloneScrollEnabled(); - value->propSyncLoad_ = CloneSyncLoad(); - if (itemLayoutConstraint_) { - value->itemLayoutConstraint_ = std::make_unique(*itemLayoutConstraint_); - } + Clone(value); return value; } 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 aef6079e618f85c1ee15620ec97947a49074fc7a..1d6e52002392b76890c9143dae5441b7d23ee476 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,12 +18,13 @@ #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 { - DECLARE_ACE_TYPE(WaterFlowLayoutProperty, LayoutProperty); +class ACE_EXPORT WaterFlowLayoutProperty : public ScrollableLayoutProperty { + DECLARE_ACE_TYPE(WaterFlowLayoutProperty, ScrollableLayoutProperty); public: WaterFlowLayoutProperty() = default; @@ -33,7 +34,7 @@ public: void Reset() override { - LayoutProperty::Reset(); + ScrollableLayoutProperty::Reset(); ResetColumnsTemplate(); ResetRowsTemplate(); ResetColumnsGap(); @@ -138,6 +139,24 @@ public: ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(ScrollEnabled, bool, PROPERTY_UPDATE_MEASURE); ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(SyncLoad, bool, PROPERTY_UPDATE_NORMAL); +protected: + void Clone(RefPtr property) const override + { + auto value = DynamicCast(property); + ScrollableLayoutProperty::Clone(value); + value->LayoutProperty::UpdateLayoutProperty(DynamicCast(this)); + value->propRowsTemplate_ = CloneRowsTemplate(); + value->propColumnsTemplate_ = CloneColumnsTemplate(); + value->propRowsGap_ = CloneRowsGap(); + value->propColumnsGap_ = CloneColumnsGap(); + value->propWaterflowDirection_ = CloneWaterflowDirection(); + value->propScrollEnabled_ = CloneScrollEnabled(); + value->propSyncLoad_ = CloneSyncLoad(); + if (itemLayoutConstraint_) { + value->itemLayoutConstraint_ = std::make_unique(*itemLayoutConstraint_); + } + } + private: ACE_DISALLOW_COPY_AND_MOVE(WaterFlowLayoutProperty); 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 bcef1d7510627fd55725afebc98c7e65b4fb7b04..2be9cdda6c9ca2b3ffbd3cc12a0d0a8b30ef795a 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/BUILD.gn b/test/unittest/BUILD.gn index fd1322709ae715172cc906a9ee4a459c7498935c..30e553a2312fe4252f176c93927a3ac18b0f569c 100644 --- a/test/unittest/BUILD.gn +++ b/test/unittest/BUILD.gn @@ -1255,6 +1255,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_ng_multi_thread.cpp", "$ace_root/frameworks/core/components_ng/pattern/scrollable/scrollable_model_static.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 ab54f56ab763b827c29012747006f13e8901d0be..6005d64b1bbbd9f33c95e8d94723a24bc0e64209 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 5003f3a7b5ff6833fbe7984da1928da016606928..aba938bd94a5e1b5a58e5f30e51abb985e9b3c21 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,29 @@ 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) +{ + GridModelNG model = CreateGrid(); + model.SetColumnsTemplate("1fr 1fr"); + model.SetLayoutOptions({}); + model.SetEdgeEffect(EdgeEffect::SPRING, true); + float contentOffset = 20; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateFixedItems(10); + CreateDone(); + + ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); + EXPECT_EQ(pattern_->info_.currentOffset_, - contentOffset * 1.5); + + ScrollToEdge(ScrollEdgeType::SCROLL_TOP, false); + EXPECT_EQ(pattern_->GetTotalOffset(), -20.0f); + EXPECT_EQ(pattern_->info_.currentOffset_, contentOffset); +} } // namespace OHOS::Ace::NG diff --git a/test/unittest/core/pattern/grid/grid_pattern_test_ng.cpp b/test/unittest/core/pattern/grid/grid_pattern_test_ng.cpp index e5c096e176b39c2822e2bc0f081511b6b4003a9e..8dacc3d098b1a7dac43ee18b8f2bd1402dfacbfa 100644 --- a/test/unittest/core/pattern/grid/grid_pattern_test_ng.cpp +++ b/test/unittest/core/pattern/grid/grid_pattern_test_ng.cpp @@ -20,6 +20,7 @@ #include "core/components_ng/base/simplified_inspector.h" #include "core/components_ng/pattern/grid/grid_pattern.h" #include "core/components_ng/pattern/grid/grid_utils.h" +#include "core/components_ng/pattern/scrollable/scrollable_model_ng.h" namespace OHOS::Ace::NG { class GridPatternTestNg : public GridTestNg {}; @@ -180,4 +181,158 @@ HWTEST_F(GridPatternTestNg, ScrollToTagetTest002, TestSize.Level1) SimplifiedInspector::TestScrollToTarget(params, frameNode_); EXPECT_TRUE(TickPosition(-350.0f)); } + +/** + * @tc.name: GetOffsetWithLimit001 + * @tc.desc: Test offset is Positive and totalOffset is Less than offset + * @tc.type: FUNC + */ +HWTEST_F(GridPatternTestNg, GetOffsetWithLimit001, TestSize.Level1) +{ + GridModelNG model = CreateGrid(); + model.SetColumnsTemplate("1fr"); + float contentOffset = 20.f; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset); + CreateItemsInLazyForEach(1, [](uint32_t idx) { return ITEM_MAIN_SIZE; }); + CreateDone(); + + // total item height ITEM_MAIN_SIZE + 20 + 20, totalOffset = 0 + float offset = pattern_->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 30feca80ff355ec95ccc69cc3fe4dc417edb094f..b6078e0579c15e01de4c3d0e6298a836c70da8d2 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 0d92c1dce2d3d3bb3ab34f387b4dc9f33c5796f2..d900f8dc453da93156cfda4c8ea30ea13666e27c 100644 --- a/test/unittest/core/pattern/grid/grid_scroller_test_ng.cpp +++ b/test/unittest/core/pattern/grid/grid_scroller_test_ng.cpp @@ -1575,4 +1575,93 @@ HWTEST_F(GridScrollerTestNg, CreateWithResourceObjScrollBarColor002, TestSize.Le ASSERT_NE(pattern_->resourceMgr_, nullptr); EXPECT_NE(pattern_->resourceMgr_->resMap_.size(), 0); } + +/** + * @tc.name: GetOverScrollOffsetWithContentOffset001 + * @tc.desc: Test GetOverScrollOffsetWithContentOffset + * @tc.type: FUNC + */ +HWTEST_F(GridScrollerTestNg, GetOverScrollOffsetWithContentOffset001, 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)); +} + +/** + * @tc.name: GetOverScrollOffsetWithContentOffset + * @tc.desc: Test GetOverScrollOffsetWithContentOffset + * @tc.type: FUNC + */ +HWTEST_F(GridScrollerTestNg, GetOverScrollOffsetWithContentOffset002, 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(); + + pattern_->info_.currentOffset_ = ITEM_MAIN_SIZE; + 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 * 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 c9b4e5f4f9c4135b9f0f44a88f11b8551ebc5883..f1c77bb680999c01b3b75225a02f4551ed75bece 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 e33d2c56212b2b276bb8acb36cbc67a911edd9c0..6ff2c5cc30b5943cb3ea9beb76d8d0ecfe4d31cb 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 7d6795b905320e8cd74ceac0f91d2896a30e1261..57eada99daef7d6ac14c48b5e708f218d6383096 100644 --- a/test/unittest/core/pattern/scroll/scroll_layout_test_ng.cpp +++ b/test/unittest/core/pattern/scroll/scroll_layout_test_ng.cpp @@ -1341,4 +1341,172 @@ HWTEST_F(ScrollLayoutTestNg, AdjustCurrentOffset_001, TestSize.Level1) 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_, 0.0f); + EXPECT_EQ(pattern_->GetTotalOffset(), -20.f); + EXPECT_TRUE(pattern_->IsAtTop()); + EXPECT_FALSE(pattern_->IsAtBottom()); + + pattern_->ScrollBy(0, -640, false); + FlushUITasks(); + EXPECT_EQ(pattern_->currentOffset_, -640.f); + EXPECT_EQ(pattern_->GetTotalOffset(), 620.f); + 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); +} + +/** + * @tc.name: ContentOffset005 + * @tc.desc: Test Scroll ContentStartOffset and ContentEndOffset + * @tc.type: FUNC + */ +HWTEST_F(ScrollLayoutTestNg, ContentOffset006, TestSize.Level1) +{ + int32_t isToEdge = 0; + int32_t isReachStart = 0; + int32_t isReachEnd = 0; + NG::ScrollEdgeEvent scrollEdgeEvent = [&isToEdge](ScrollEdge) { isToEdge++; }; + auto reachStartEvent = [&isReachStart]() { isReachStart++; }; + auto reachEndEvent = [&isReachEnd]() { isReachEnd++; }; + ScrollModelNG model = CreateScroll(); + model.SetAxis(Axis::VERTICAL); + model.SetOnScrollEdge(std::move(scrollEdgeEvent)); + model.SetOnReachStart(std::move(reachStartEvent)); + model.SetOnReachEnd(std::move(reachEndEvent)); + float contentOffset = 20; + ScrollableModelNG::SetContentStartOffset(contentOffset); + ScrollableModelNG::SetContentEndOffset(contentOffset * 1.5); + CreateContent(); + CreateScrollDone(); + + /** + * @tc.steps: step1. Trigger reachStartEvent init + */ + EXPECT_EQ(isReachStart, 1); + EXPECT_EQ(isReachEnd, 0); + EXPECT_EQ(isToEdge, 0); + EXPECT_EQ(pattern_->GetTotalOffset(), -20.0f); + EXPECT_EQ(pattern_->currentOffset_, 0.0f); + EXPECT_EQ(isReachStart, 1); + + /** + * @tc.steps: step2. ScrollTo 0 + * @tc.expected: totalOffset and currentOffset is correct + */ + ScrollTo(0); + FlushUITasks(); + EXPECT_EQ(pattern_->GetTotalOffset(), -20.0f); + EXPECT_EQ(pattern_->currentOffset_, 0.0f); + + /** + * @tc.steps: step2. ScrollTo bottom + * @tc.expected: Trigger scrollEdgeEvent/reachEndEvent + */ + ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); + EXPECT_EQ(pattern_->GetTotalOffset(), 630.0f); + EXPECT_EQ(isReachEnd, 1); + EXPECT_EQ(isToEdge, 1); + + ScrollBy(0, 10); + EXPECT_EQ(pattern_->GetTotalOffset(), 620.0f); + + ScrollBy(0, -10); + EXPECT_EQ(pattern_->GetTotalOffset(), 630.0f); + EXPECT_EQ(isReachEnd, 2); + EXPECT_EQ(isToEdge, 2); + + /** + * @tc.steps: step3. ScrollTo top + * @tc.expected: Trigger onScrollEvent/scrollEdgeEvent/reachStartEvent + */ + ScrollToEdge(ScrollEdgeType::SCROLL_TOP, false); + EXPECT_EQ(isReachStart, 2); + EXPECT_EQ(isReachEnd, 2); + EXPECT_EQ(isToEdge, 3); +} } // 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 c50101a4b171904bfe82705100b5fbe095991639..f0025941befe0f89df4ade5950d999ffc31c2b1b 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 @@ -427,4 +427,128 @@ HWTEST_F(ScrollPatternThreeTestNg, GetDefaultFrictionTest002, TestSize.Level1) MockContainer::Current()->SetApiTargetVersion(backupApiVersion); MockContainer::TearDown(); } + +/** + * @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_, 0.0f); + ScrollBy(0, -contentOffset * 0.5); + EXPECT_EQ(pattern_->currentOffset_, -contentOffset * 0.5); + + auto result = pattern_->GetOverScrollOffset(contentOffset); + EXPECT_DOUBLE_EQ(result.start, contentOffset * 0.5); + EXPECT_DOUBLE_EQ(result.end, 0.0); + + result = pattern_->GetOverScrollOffset(-contentOffset); + EXPECT_DOUBLE_EQ(result.start, 0.0); + 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_, 0.0f); + 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 529fba3894516501756394bde3292d1eed7de350..aad0dfcd370709767d2d783147378c696761d9f8 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 700270541ef03d3a394d080edc7eb7394f544793..7293af4519db7022dfef4d0ffc4863afad377f75 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 ae65b79b547f954ca954d30f3d2b6c22035848da..df80a6276d855546c7970ae7d5452fcbcba9dbea 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 cc12530bfa2520d448a4b02aa6111c6b617e5521..c49eff6782f1cfe0bba7075b2eff6ec2fd296bba 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 4251a725e5da6c1f5092fffd2994184a97ca5936..232e40017c1f7e190110fe4a94068db2ce236cb4 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 4b7f2a23c97e3c5312b14405c189d5715d2bf4d5..e17338c485d05bec1fcfcd4ffc55f664bb63a98b 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,70 @@ 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(WaterFlowTestNg, 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: InvalidContentOffset + * @tc.desc: Test contentStartOffset_ and contentEndOffset_ with invalid value + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, InvalidContentOffset, 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: ContentOffsetIsAtTopAndBottomTest + * @tc.desc: Test IsAtTop and IsAtBottom with contentStartOffset_ and contentEndOffset_ + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, ContentOffsetIsAtTopAndBottomTest, 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()); + + pattern_->UpdateCurrentOffset(-ITEM_MAIN_SIZE * 10, SCROLL_FROM_UPDATE); + FlushUITasks(); + EXPECT_FALSE(pattern_->IsAtTop()); + EXPECT_TRUE(pattern_->IsAtBottom()); +} } // namespace OHOS::Ace::NG diff --git a/test/unittest/core/pattern/web/ani/BUILD.gn b/test/unittest/core/pattern/web/ani/BUILD.gn index 7cf3afda5cf64c9e5983758428c9acac884f7d31..ed46b0151045fc285b3e59f327f390aedfbf96c9 100755 --- a/test/unittest/core/pattern/web/ani/BUILD.gn +++ b/test/unittest/core/pattern/web/ani/BUILD.gn @@ -418,6 +418,7 @@ ohos_unittest("web_pattern_static_unit_test_ohos") { "$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_ng_multi_thread.cpp", "$ace_root/frameworks/core/components_ng/pattern/scrollable/scrollable_model_static.cpp",