1 /* 2 * Copyright (c) 2022-2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SCROLL_SCROLL_PATTERN_H 17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SCROLL_SCROLL_PATTERN_H 18 19 #include "base/geometry/axis.h" 20 #include "core/components/common/layout/constants.h" 21 #include "core/components_ng/gestures/recognizers/pan_recognizer.h" 22 #include "core/components_ng/gestures/recognizers/parallel_recognizer.h" 23 #include "core/components_ng/pattern/scroll/free_scroll_controller.h" 24 #include "core/components_ng/pattern/scroll/inner/scroll_bar_2d.h" 25 #include "core/components_ng/pattern/scroll/scroll_accessibility_property.h" 26 #include "core/components_ng/pattern/scroll/scroll_content_modifier.h" 27 #include "core/components_ng/pattern/scroll/scroll_edge_effect.h" 28 #include "core/components_ng/pattern/scroll/scroll_event_hub.h" 29 #include "core/components_ng/pattern/scroll/scroll_layout_algorithm.h" 30 #include "core/components_ng/pattern/scroll/scroll_layout_property.h" 31 #include "core/components_ng/pattern/scroll/scroll_paint_method.h" 32 #include "core/components_ng/pattern/scroll_bar/proxy/scroll_bar_proxy.h" 33 #include "core/components_ng/pattern/scroll/zoom_controller.h" 34 #include "core/components_ng/pattern/scrollable/scrollable_pattern.h" 35 #include "core/components_ng/pattern/scrollable/scrollable_properties.h" 36 37 #ifdef SUPPORT_DIGITAL_CROWN 38 #include "core/common/vibrator/vibrator_utils.h" 39 #endif 40 41 namespace OHOS::Ace::NG { 42 class InspectorFilter; 43 44 class ScrollPattern : public ScrollablePattern { 45 DECLARE_ACE_TYPE(ScrollPattern, ScrollablePattern); 46 47 public: ScrollPattern()48 ScrollPattern() : ScrollablePattern(EdgeEffect::NONE, true) {} 49 50 ~ScrollPattern() override = default; 51 UsResRegion()52 bool UsResRegion() override 53 { 54 return false; 55 } 56 CreateLayoutProperty()57 RefPtr<LayoutProperty> CreateLayoutProperty() override 58 { 59 return MakeRefPtr<ScrollLayoutProperty>(); 60 } 61 CreateAccessibilityProperty()62 RefPtr<AccessibilityProperty> CreateAccessibilityProperty() override 63 { 64 return MakeRefPtr<ScrollAccessibilityProperty>(); 65 } 66 CreateLayoutAlgorithm()67 RefPtr<LayoutAlgorithm> CreateLayoutAlgorithm() override 68 { 69 if (freeScroll_) { 70 return MakeRefPtr<ScrollLayoutAlgorithm>(freeScroll_->GetOffset().GetX(), freeScroll_->GetOffset().GetY()); 71 } 72 return MakeRefPtr<ScrollLayoutAlgorithm>(currentOffset_); 73 } 74 75 RefPtr<PaintProperty> CreatePaintProperty() override; 76 77 RefPtr<NodePaintMethod> CreateNodePaintMethod() override; 78 OpIncType()79 OPINC_TYPE_E OpIncType() override 80 { 81 return OPINC_PARENT_POSSIBLE; 82 } 83 CreateEventHub()84 RefPtr<EventHub> CreateEventHub() override 85 { 86 return MakeRefPtr<ScrollEventHub>(); 87 } 88 IsScrollable()89 bool IsScrollable() const override 90 { 91 return GetAxis() != Axis::NONE; 92 } 93 IsPositiveScrollableDistance()94 bool IsPositiveScrollableDistance() 95 { 96 return Positive(scrollableDistance_); 97 } 98 99 bool OnScrollCallback(float offset, int32_t source) override; 100 101 void OnScrollEndCallback() override; 102 GetCurrentPosition()103 double GetCurrentPosition() const 104 { 105 return currentOffset_; 106 } 107 GetTotalOffset()108 double GetTotalOffset() const override 109 { 110 return -currentOffset_; 111 } 112 113 void ResetPosition(); 114 GetCurrentOffset()115 Offset GetCurrentOffset() const 116 { 117 if (GetAxis() == Axis::HORIZONTAL) { 118 return Offset { currentOffset_, 0 }; 119 } 120 return Offset { 0, currentOffset_ }; 121 } 122 GetScrollableDistance()123 float GetScrollableDistance() const 124 { 125 return scrollableDistance_; 126 } 127 IsRowReverse()128 bool IsRowReverse() const 129 { 130 return direction_ == FlexDirection::ROW_REVERSE; 131 } 132 IsColReverse()133 bool IsColReverse() const 134 { 135 return direction_ == FlexDirection::COLUMN_REVERSE; 136 } 137 GetScrollPositionController()138 RefPtr<ScrollableController> GetScrollPositionController() const 139 { 140 return positionController_; 141 } 142 SetDirection(FlexDirection direction)143 void SetDirection(FlexDirection direction) 144 { 145 direction_ = direction; 146 } 147 GetFocusPattern()148 FocusPattern GetFocusPattern() const override 149 { 150 return { FocusType::SCOPE, true }; 151 } 152 153 bool ScrollToNode(const RefPtr<FrameNode>& focusFrameNode) override; 154 ScrollOffsetAbility GetScrollOffsetAbility() override; 155 156 bool IsAtTop() const override; 157 bool IsAtBottom(bool considerRepeat = false) const override; 158 bool IsOutOfBoundary(bool useCurrentDelta = true) override; 159 OverScrollOffset GetOverScrollOffset(double delta) const override; 160 161 void OnAnimateStop() override; 162 bool UpdateCurrentOffset(float offset, int32_t source) override; 163 void ScrollToEdge(ScrollEdgeType scrollEdgeType, bool smooth) override; 164 165 void CheckScrollToEdge(); 166 GetScrollEdgeType()167 ScrollEdgeType GetScrollEdgeType() const override 168 { 169 return scrollEdgeType_; 170 } 171 SetScrollEdgeType(ScrollEdgeType scrollEdgeType)172 void SetScrollEdgeType(ScrollEdgeType scrollEdgeType) override 173 { 174 scrollEdgeType_ = scrollEdgeType; 175 } 176 177 void ScrollBy(float pixelX, float pixelY, bool smooth, const std::function<void()>& onFinish = nullptr); 178 void ScrollPage(bool reverse, bool smooth = false, 179 AccessibilityScrollType scrollType = AccessibilityScrollType::SCROLL_FULL) override; 180 void ScrollTo(float position) override; 181 void JumpToPosition(float position, int32_t source = SCROLL_FROM_JUMP); GetMainContentSize()182 float GetMainContentSize() const override 183 { 184 return viewPortLength_; 185 } SupportScrollToIndex()186 bool SupportScrollToIndex() const override 187 { 188 return false; 189 } 190 bool ScrollPageCheck(float delta, int32_t source); 191 void AdjustOffset(float& delta, int32_t source); 192 Rect GetItemRect(int32_t index) const override; 193 194 // scrollSnap 195 std::optional<float> CalcPredictSnapOffset(float delta, float dragDistance = 0.f, float velocity = 0.f, 196 SnapDirection snapDirection = SnapDirection::NONE) override; 197 std::optional<float> CalcPredictNextSnapOffset(float delta, SnapDirection snapDirection); 198 bool NeedScrollSnapToSide(float delta) override; 199 void CaleSnapOffsets(const RefPtr<FrameNode>& host); 200 void CaleSnapOffsetsByInterval(ScrollSnapAlign scrollSnapAlign, const RefPtr<FrameNode>& host); 201 void CaleSnapOffsetsByPaginations(ScrollSnapAlign scrollSnapAlign); 202 203 float GetSelectScrollWidth(); 204 IsSnapToInterval()205 bool IsSnapToInterval() const 206 { 207 return snapPaginations_.empty(); 208 } 209 GetSnapOffsets()210 std::vector<float> GetSnapOffsets() const 211 { 212 return snapOffsets_; 213 } 214 SetSnapOffsets(const std::vector<float> & snapOffset)215 void SetSnapOffsets(const std::vector<float>& snapOffset) 216 { 217 snapOffsets_ = snapOffset; 218 } 219 SetIntervalSize(const Dimension & intervalSize)220 void SetIntervalSize(const Dimension& intervalSize) 221 { 222 if (intervalSize_ != intervalSize) { 223 intervalSize_ = intervalSize; 224 TAG_LOGI(AceLogTag::ACE_SCROLL, "scroll setIntervalSize:%{public}f", intervalSize.Value()); 225 scrollSnapUpdate_ = true; 226 } 227 } 228 229 #ifdef SUPPORT_DIGITAL_CROWN 230 void StartVibrateFeedback(); 231 SetReachBoundary(bool flag)232 void SetReachBoundary(bool flag) 233 { 234 reachBoundary_ = flag; 235 } 236 #endif 237 GetIntervalSize()238 Dimension GetIntervalSize() const 239 { 240 return intervalSize_; 241 } 242 SetSnapPaginations(const std::vector<Dimension> & snapPaginations)243 void SetSnapPaginations(const std::vector<Dimension>& snapPaginations) 244 { 245 if (snapPaginations_ != snapPaginations) { 246 snapPaginations_ = snapPaginations; 247 scrollSnapUpdate_ = true; 248 } 249 } 250 GetSnapPaginations()251 std::vector<Dimension> GetSnapPaginations() const 252 { 253 return snapPaginations_; 254 } 255 SetEnableSnapToSide(const std::pair<bool,bool> & enableSnapToSide)256 void SetEnableSnapToSide(const std::pair<bool, bool>& enableSnapToSide) 257 { 258 enableSnapToSide_ = enableSnapToSide; 259 } 260 GetEnableSnapToSide()261 std::pair<bool, bool> GetEnableSnapToSide() const 262 { 263 return enableSnapToSide_; 264 } 265 SetScrollSnapUpdate(bool scrollSnapUpdate)266 void SetScrollSnapUpdate(bool scrollSnapUpdate) 267 { 268 scrollSnapUpdate_ = scrollSnapUpdate; 269 } 270 GetScrollSnapUpdate()271 bool GetScrollSnapUpdate() const 272 { 273 return scrollSnapUpdate_; 274 } 275 GetScrollSnapAlign()276 ScrollSnapAlign GetScrollSnapAlign() const 277 { 278 auto host = GetHost(); 279 CHECK_NULL_RETURN(host, ScrollSnapAlign::NONE); 280 auto scrollLayoutProperty = host->GetLayoutProperty<ScrollLayoutProperty>(); 281 CHECK_NULL_RETURN(scrollLayoutProperty, ScrollSnapAlign::NONE); 282 return scrollLayoutProperty->GetScrollSnapAlign().value_or(ScrollSnapAlign::NONE); 283 } 284 285 ScrollSnapAlign GetScrollSnapAlign(const RefPtr<FrameNode>& host) const; 286 287 std::string ProvideRestoreInfo() override; 288 void OnRestoreInfo(const std::string& restoreInfo) override; 289 SetIsWidthModifiedBySelect(bool isModified)290 void SetIsWidthModifiedBySelect(bool isModified) 291 { 292 isWidthModifiedBySelect_ = isModified; 293 } 294 IsWidthModifiedBySelect()295 bool IsWidthModifiedBySelect() const 296 { 297 return isWidthModifiedBySelect_; 298 } 299 SetIsSelectScroll(bool isSelect)300 void SetIsSelectScroll(bool isSelect) 301 { 302 isSelectScroll_ = isSelect; 303 } 304 IsSelectScroll()305 bool IsSelectScroll() const 306 { 307 return isSelectScroll_; 308 } 309 SetHasOptionWidth(bool hasOptionWidth)310 void SetHasOptionWidth(bool hasOptionWidth) 311 { 312 hasOptionWidth_ = hasOptionWidth; 313 } 314 GetHasOptionWidth()315 bool GetHasOptionWidth() 316 { 317 return hasOptionWidth_; 318 } 319 SetEnablePaging(ScrollPagingStatus status)320 void SetEnablePaging(ScrollPagingStatus status) 321 { 322 enablePagingStatus_ = status; 323 } 324 GetEnablePaging()325 ScrollPagingStatus GetEnablePaging() 326 { 327 return enablePagingStatus_; 328 } 329 IsScrollSnap()330 bool IsScrollSnap() override 331 { 332 return !snapOffsets_.empty() && 333 (GetScrollSnapAlign() != ScrollSnapAlign::NONE || enablePagingStatus_ == ScrollPagingStatus::VALID); 334 } 335 GetSnapType()336 SnapType GetSnapType() override 337 { 338 return (!snapOffsets_.empty() && 339 (GetScrollSnapAlign() != ScrollSnapAlign::NONE || enablePagingStatus_ == ScrollPagingStatus::VALID)) 340 ? SnapType::SCROLL_SNAP 341 : SnapType::NONE_SNAP; 342 } 343 344 void TriggerModifyDone(); 345 SetInitialOffset(const OffsetT<CalcDimension> & offset)346 void SetInitialOffset(const OffsetT<CalcDimension>& offset) 347 { 348 initialOffset_ = offset; 349 } 350 GetInitialOffset()351 OffsetT<CalcDimension> GetInitialOffset() const 352 { 353 return initialOffset_.has_value() ? initialOffset_.value() : OffsetT(CalcDimension(), CalcDimension()); 354 } 355 NeedSetInitialOffset()356 bool NeedSetInitialOffset() 357 { 358 return !isInitialized_ && initialOffset_.has_value(); 359 } 360 361 void AddScrollMeasureInfo(const std::optional<LayoutConstraintF>& parentConstraint, 362 const std::optional<LayoutConstraintF>& childConstraint, const SizeF& selfSize, const SizeF& childSize); 363 364 void AddScrollLayoutInfo(); 365 366 void GetScrollSnapAlignDumpInfo(); 367 void GetScrollSnapAlignDumpInfo(std::unique_ptr<JsonValue>& json); 368 369 void GetScrollPagingStatusDumpInfo(); 370 void GetScrollPagingStatusDumpInfo(std::unique_ptr<JsonValue>& json); 371 void DumpAdvanceInfo() override; 372 void DumpAdvanceInfo(std::unique_ptr<JsonValue>& json) override; 373 GetViewSize()374 const SizeF& GetViewSize() const 375 { 376 return viewSize_; 377 } 378 GetViewPortExtent()379 const SizeF& GetViewPortExtent() const 380 { 381 return viewPortExtent_; 382 } 383 384 void ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const override; 385 386 bool StartSnapAnimation(SnapAnimationOptions snapAnimationOptions) override; 387 388 void StartScrollSnapAnimation(float scrollSnapDelta, float scrollSnapVelocity, bool fromScrollBar); 389 390 SizeF GetChildrenExpandedSize() override; 391 392 void TriggerScrollBarDisplay(); 393 IsEnablePagingValid()394 bool IsEnablePagingValid() override 395 { 396 return enablePagingStatus_ == ScrollPagingStatus::VALID && GetScrollSnapAlign() == ScrollSnapAlign::NONE; 397 } 398 399 protected: 400 void DoJump(float position, int32_t source = SCROLL_FROM_JUMP); 401 402 private: 403 void OnModifyDone() override; 404 bool OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config) override; 405 406 bool IsCrashTop() const; 407 bool IsCrashBottom() const; 408 bool ReachStart(bool firstLayout) const; 409 bool ReachEnd(bool firstLayout) const; 410 bool IsScrollOutOnEdge(float delta) const; 411 void HandleCrashTop(); 412 void HandleCrashBottom(); 413 414 void RegisterScrollBarEventTask(); 415 void HandleScrollEffect(); 416 void ValidateOffset(int32_t source); 417 float ValidateOffset(int32_t source, float willScrollOffset); 418 void HandleScrollPosition(float scroll); 419 float FireTwoDimensionOnWillScroll(float scroll); 420 TwoDimensionScrollResult FireObserverTwoDimensionOnWillScroll(Dimension xOffset, Dimension yOffset, 421 ScrollState state, ScrollSource source); 422 void FireOnDidScroll(float scroll); 423 void FireOnReachStart(const OnReachEvent& onReachStart, const OnReachEvent& onJSFrameNodeReachStart) override; 424 void FireOnReachEnd(const OnReachEvent& onReachEnd, const OnReachEvent& onJSFrameNodeReachEnd) override; 425 void SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect) override; 426 void UpdateScrollBarOffset() override; 427 void SetAccessibilityAction() override; 428 bool ScrollSnapTrigger(); 429 void CheckScrollable(); 430 OffsetF GetOffsetToScroll(const RefPtr<FrameNode>& childFrame) const; 431 bool SetScrollProperties(const RefPtr<LayoutWrapper>& dirty, const RefPtr<FrameNode>& host); 432 std::string GetScrollSnapPagination() const; 433 void OnColorModeChange(uint32_t colorMode) override; 434 435 float currentOffset_ = 0.0f; 436 float lastOffset_ = 0.0f; 437 // keep lastOffset_ for compatibility, use prevOffset_ for onReachStart/onReachEnd 438 float prevOffset_ = 0.0f; 439 float scrollableDistance_ = 0.0f; 440 float viewPortLength_ = 0.0f; 441 SizeF viewPort_; 442 SizeF viewSize_; 443 SizeF viewPortExtent_; 444 FlexDirection direction_ { FlexDirection::COLUMN }; 445 446 /* ============================= zoom Enhancements ============================= */ 447 public: 448 void SetMaxZoomScale(float scale); 449 float GetMaxZoomScale() const; 450 void SetMinZoomScale(float scale); 451 float GetMinZoomScale() const; 452 void SetZoomScale(std::optional<float> scale); 453 float GetZoomScale() const; 454 void UpdateZoomScale(float scale); 455 void SetEnableBouncesZoom(bool enable); 456 bool GetEnableBouncesZoom() const; 457 void ProcessZoomScale(); 458 void SetChildScale(std::optional<float> scale); 459 private: 460 void UpdatePinchGesture(); 461 friend class ZoomController; 462 RefPtr<ZoomController> zoomCtrl_; 463 float maxZoomScale_ = 1.0f; 464 float minZoomScale_ = 1.0f; 465 std::optional<float> zoomScale_; 466 std::optional<float> childScale_; 467 bool enableBouncesZoom_ = true; 468 /* ============================================================================== */ 469 470 /* ============================= Free Scroll Enhancements ============================= */ 471 public: 472 /** 473 * @return Pan gesture recognizer configured for Axis::FREE mode 474 */ 475 RefPtr<NGGestureRecognizer> GetOverrideRecognizer(); Get2DScrollBar()476 RefPtr<ScrollBar2D> Get2DScrollBar() const 477 { 478 return scrollBar2d_; 479 } 480 481 Offset GetFreeScrollOffset() const final; 482 bool FreeScrollBy(const OffsetF& delta) final; 483 bool FreeScrollPage(bool reverse, bool smooth) final; 484 bool FreeScrollToEdge(ScrollEdgeType type, bool smooth, std::optional<float> velocity) final; 485 void FreeScrollTo(const ScrollControllerBase::ScrollToParam& param) final; 486 487 private: 488 RefPtr<ParallelRecognizer> gestureGroup_; 489 RefPtr<FreeScrollController> freeScroll_; 490 RefPtr<ScrollBar2D> scrollBar2d_; 491 /* ============================================================================== */ 492 493 // scrollSnap 494 std::vector<float> snapOffsets_; 495 std::vector<Dimension> snapPaginations_; 496 std::pair<bool, bool> enableSnapToSide_ = { true, true }; 497 Dimension intervalSize_; 498 bool scrollSnapUpdate_ = false; 499 500 bool isWidthModifiedBySelect_ = false; 501 bool isSelectScroll_ = false; 502 bool hasOptionWidth_ = false; 503 504 // enablePaging 505 ScrollPagingStatus enablePagingStatus_ = ScrollPagingStatus::NONE; 506 float lastPageLength_ = 0.0f; 507 float GetPagingOffset(float delta, float dragDistance, float velocity) const; 508 float GetPagingDelta(float dragDistance, float velocity, float pageLength) const; 509 510 RefPtr<ScrollContentModifier> scrollContentModifier_; 511 512 // initialOffset 513 std::optional<OffsetT<CalcDimension>> initialOffset_; 514 515 // scrollToEdge 516 ScrollEdgeType scrollEdgeType_ = ScrollEdgeType::SCROLL_NONE; 517 518 // dump info 519 std::list<ScrollLayoutInfo> scrollLayoutInfos_; 520 std::list<ScrollMeasureInfo> scrollMeasureInfos_; 521 522 #ifdef SUPPORT_DIGITAL_CROWN 523 int32_t crownEventNum_ = 0; 524 bool reachBoundary_ = false; 525 int64_t lastTime_ = 0; 526 #endif 527 }; 528 529 } // namespace OHOS::Ace::NG 530 531 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SCROLL_SCROLL_PATTERN_H 532