• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2024 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 #include "core/components_ng/pattern/refresh/refresh_pattern.h"
17 
18 #include "base/geometry/dimension.h"
19 #include "base/geometry/ng/offset_t.h"
20 #include "base/log/dump_log.h"
21 #include "base/memory/ace_type.h"
22 #include "base/utils/utils.h"
23 #include "core/animation/spring_curve.h"
24 #include "core/common/container.h"
25 #include "core/components/common/properties/animation_option.h"
26 #include "core/components/refresh/refresh_theme.h"
27 #include "core/components_ng/base/frame_node.h"
28 #include "core/components_ng/event/event_hub.h"
29 #include "core/components_ng/pattern/loading_progress/loading_progress_layout_property.h"
30 #include "core/components_ng/pattern/loading_progress/loading_progress_paint_property.h"
31 #include "core/components_ng/pattern/refresh/refresh_animation_state.h"
32 #include "core/components_ng/pattern/refresh/refresh_layout_property.h"
33 #include "core/components_ng/pattern/scrollable/scrollable_pattern.h"
34 #include "core/components_ng/property/property.h"
35 #include "core/components_ng/render/animation_utils.h"
36 #include "core/pipeline/base/element_register.h"
37 #include "core/pipeline_ng/pipeline_context.h"
38 #include "frameworks/base/i18n/localization.h"
39 #include "frameworks/base/utils/time_util.h"
40 #include "frameworks/base/utils/utils.h"
41 #include "frameworks/core/components/common/layout/constants.h"
42 #include "frameworks/core/components_ng/pattern/loading_progress/loading_progress_pattern.h"
43 #include "frameworks/core/components_ng/pattern/text/text_pattern.h"
44 
45 namespace OHOS::Ace::NG {
46 
47 namespace {
48 constexpr float PERCENT = 0.01f; // Percent
49 constexpr float FOLLOW_TO_RECYCLE_DURATION = 600.0f;
50 constexpr float CUSTOM_BUILDER_ANIMATION_DURATION = 100.0f;
51 constexpr float LOADING_ANIMATION_DURATION = 350.0f;
52 constexpr float MAX_OFFSET = 100000.0f;
53 constexpr float HALF = 0.5f;
54 constexpr float BASE_SCALE = 0.707f; // std::sqrt(2)/2
55 constexpr Dimension TRIGGER_LOADING_DISTANCE = 16.0_vp;
56 constexpr Dimension TRIGGER_REFRESH_WITH_TEXT_DISTANCE = 96.0_vp;
57 constexpr Dimension TRIGGER_REFRESH_DISTANCE = 64.0_vp;
58 constexpr Dimension MAX_SCROLL_DISTANCE = 128.0_vp;
59 constexpr Dimension LOADING_PROGRESS_SIZE = 32.0_vp;
60 constexpr float DEFAULT_FRICTION = 62.0f;
61 const RefPtr<Curve> DEFAULT_CURVE = AceType::MakeRefPtr<CubicCurve>(0.2f, 0.0f, 0.1f, 1.0f);
62 const std::string REFRESH_DRAG_SCENE = "refresh_drag_scene";
63 constexpr Dimension LOADING_TEXT_TOP_MARGIN = 16.0_vp;
64 constexpr Dimension LOADING_TEXT_DISPLAY_DISTANCE = 80.0_vp;
65 } // namespace
66 
67 
GetTriggerRefreshDisTance()68 Dimension RefreshPattern::GetTriggerRefreshDisTance()
69 {
70     if (hasLoadingText_) {
71         return TRIGGER_REFRESH_WITH_TEXT_DISTANCE;
72     } else {
73         return TRIGGER_REFRESH_DISTANCE;
74     }
75 }
76 
OnAttachToFrameNode()77 void RefreshPattern::OnAttachToFrameNode()
78 {
79     auto host = GetHost();
80     CHECK_NULL_VOID(host);
81     host->GetRenderContext()->SetClipToBounds(true);
82     host->GetRenderContext()->UpdateClipEdge(true);
83 }
84 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)85 bool RefreshPattern::OnDirtyLayoutWrapperSwap(
86     const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
87 {
88     if (isRemoveCustomBuilder_ || isTextNodeChanged_) {
89         UpdateFirstChildPlacement();
90         if (isRefreshing_) {
91             UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, GetFollowRatio());
92         }
93         isRemoveCustomBuilder_ = false;
94         isTextNodeChanged_ = false;
95     } else if (progressChild_) {
96         auto host = GetHost();
97         CHECK_NULL_RETURN(host, false);
98         auto geometryNode = host->GetGeometryNode();
99         CHECK_NULL_RETURN(geometryNode, false);
100         auto refreshHeight = geometryNode->GetFrameSize().Height();
101         auto scrollOffset = std::clamp(scrollOffset_, 0.0f, refreshHeight);
102         UpdateScrollTransition(scrollOffset);
103     }
104     return false;
105 }
106 
OnModifyDone()107 void RefreshPattern::OnModifyDone()
108 {
109     Pattern::OnModifyDone();
110     auto host = GetHost();
111     CHECK_NULL_VOID(host);
112     auto hub = host->GetEventHub<EventHub>();
113     CHECK_NULL_VOID(hub);
114     auto gestureHub = hub->GetOrCreateGestureEventHub();
115     CHECK_NULL_VOID(gestureHub);
116     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
117     CHECK_NULL_VOID(layoutProperty);
118     hasLoadingText_ = layoutProperty->HasLoadingText();
119     refreshOffset_ = layoutProperty->GetRefreshOffset().value_or(GetTriggerRefreshDisTance());
120     if (LessOrEqual(refreshOffset_.Value(), 0)) {
121         refreshOffset_ = GetTriggerRefreshDisTance();
122     }
123     pullToRefresh_ = layoutProperty->GetPullToRefresh().value_or(true);
124     InitPanEvent(gestureHub);
125     InitOnKeyEvent();
126     InitChildNode();
127     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
128         InitOffsetProperty();
129     } else {
130         triggerLoadingDistance_ = static_cast<float>(
131             std::clamp(layoutProperty->GetIndicatorOffset().value_or(TRIGGER_LOADING_DISTANCE).ConvertToPx(),
132                 -1.0f * TRIGGER_LOADING_DISTANCE.ConvertToPx(), GetTriggerRefreshDisTance().ConvertToPx()));
133         InitLowVersionOffset();
134     }
135     RefreshStatusChangeEffect();
136     SetAccessibilityAction();
137 }
138 
CreateLayoutAlgorithm()139 RefPtr<LayoutAlgorithm> RefreshPattern::CreateLayoutAlgorithm()
140 {
141     auto refreshLayoutAlgorithm = MakeRefPtr<RefreshLayoutAlgorithm>();
142     if (isCustomBuilderExist_) {
143         refreshLayoutAlgorithm->SetCustomBuilderIndex(0);
144         if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
145             refreshLayoutAlgorithm->SetBuilderMeasureBaseHeight(builderMeasureBaseHeight_);
146         } else {
147             refreshLayoutAlgorithm->SetCustomBuilderOffset(customBuilderOffset_);
148             refreshLayoutAlgorithm->SetScrollOffset(scrollOffset_);
149         }
150     }
151     return refreshLayoutAlgorithm;
152 }
153 
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)154 void RefreshPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
155 {
156     if (panEvent_) {
157         return;
158     }
159     auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& info) {
160         auto pattern = weak.Upgrade();
161         CHECK_NULL_VOID(pattern);
162         auto speed = static_cast<float>(info.GetMainVelocity());
163         pattern->UpdateDragFRCSceneInfo(REFRESH_DRAG_SCENE, speed, SceneStatus::START);
164         pattern->HandleDragStart(true, speed);
165     };
166     auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
167         auto pattern = weak.Upgrade();
168         CHECK_NULL_VOID(pattern);
169         pattern->HandleDragUpdate(static_cast<float>(info.GetMainDelta()), static_cast<float>(info.GetMainVelocity()));
170     };
171     auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
172         auto pattern = weak.Upgrade();
173         CHECK_NULL_VOID(pattern);
174         auto speed = static_cast<float>(info.GetMainVelocity());
175         pattern->UpdateDragFRCSceneInfo(REFRESH_DRAG_SCENE, speed, SceneStatus::END);
176         pattern->HandleDragEnd(speed);
177     };
178     auto actionCancelTask = [weak = WeakClaim(this)]() {
179         auto pattern = weak.Upgrade();
180         CHECK_NULL_VOID(pattern);
181         pattern->HandleDragCancel();
182     };
183     PanDirection panDirection;
184     panDirection.type = PanDirection::VERTICAL;
185     if (panEvent_) {
186         gestureHub->RemovePanEvent(panEvent_);
187     }
188 
189     panEvent_ = MakeRefPtr<PanEvent>(
190         std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
191     gestureHub->AddPanEvent(panEvent_, panDirection, 1, DEFAULT_PAN_DISTANCE);
192     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_THIRTEEN)) {
193         gestureHub->SetIsAllowMouse(false);
194     }
195 }
196 
InitOnKeyEvent()197 void RefreshPattern::InitOnKeyEvent()
198 {
199     if (isKeyEventRegisted_) {
200         return;
201     }
202     auto host = GetHost();
203     CHECK_NULL_VOID(host);
204     auto focusHub = host->GetFocusHub();
205     CHECK_NULL_VOID(focusHub);
206     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
207         auto pattern = wp.Upgrade();
208         CHECK_NULL_RETURN(pattern, false);
209         return pattern->OnKeyEvent(event);
210     };
211     isKeyEventRegisted_ = true;
212     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
213 }
214 
InitProgressNode()215 void RefreshPattern::InitProgressNode()
216 {
217     auto host = GetHost();
218     CHECK_NULL_VOID(host);
219     auto context = host->GetContext();
220     CHECK_NULL_VOID(context);
221     auto theme = context->GetTheme<RefreshTheme>();
222     CHECK_NULL_VOID(theme);
223     progressChild_ = FrameNode::CreateFrameNode(V2::LOADING_PROGRESS_ETS_TAG,
224         ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<LoadingProgressPattern>());
225     CHECK_NULL_VOID(progressChild_);
226     auto gestureHub = progressChild_->GetEventHub<EventHub>();
227     if (gestureHub) {
228         gestureHub->SetEnabled(false);
229     }
230     auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
231     CHECK_NULL_VOID(progressLayoutProperty);
232     progressLayoutProperty->UpdateUserDefinedIdealSize(
233         CalcSize(CalcLength(LOADING_PROGRESS_SIZE.ConvertToPx()), CalcLength(LOADING_PROGRESS_SIZE.ConvertToPx())));
234     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
235     CHECK_NULL_VOID(progressPaintProperty);
236     progressPaintProperty->UpdateLoadingProgressOwner(LoadingProgressOwner::REFRESH);
237     progressPaintProperty->UpdateColor(theme->GetProgressColor());
238     host->AddChild(progressChild_, 0);
239     progressChild_->MarkDirtyNode();
240 }
241 
UpdateLoadingTextOpacity(float opacity)242 void RefreshPattern::UpdateLoadingTextOpacity(float opacity)
243 {
244     CHECK_NULL_VOID(loadingTextNode_);
245     auto loadingTextRenderContext = loadingTextNode_->GetRenderContext();
246     CHECK_NULL_VOID(loadingTextRenderContext);
247     if (opacity > 0.0f) {
248         opacity = std::clamp(scrollOffset_ - static_cast<float>(LOADING_TEXT_DISPLAY_DISTANCE.ConvertToPx()), 0.0f,
249             static_cast<float>(TRIGGER_REFRESH_WITH_TEXT_DISTANCE.ConvertToPx() -
250                                          LOADING_TEXT_DISPLAY_DISTANCE.ConvertToPx())) /
251                   static_cast<float>(
252                       TRIGGER_REFRESH_WITH_TEXT_DISTANCE.ConvertToPx() - LOADING_TEXT_DISPLAY_DISTANCE.ConvertToPx());
253     }
254     loadingTextRenderContext->UpdateOpacity(opacity);
255 }
256 
InitProgressColumn()257 void RefreshPattern::InitProgressColumn()
258 {
259     auto host = GetHost();
260     CHECK_NULL_VOID(host);
261     columnNode_ = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
262         AceType::MakeRefPtr<LinearLayoutPattern>(true));
263     loadingTextNode_ = FrameNode::CreateFrameNode(
264         V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
265     auto loadingTextLayoutProperty = loadingTextNode_->GetLayoutProperty<TextLayoutProperty>();
266     CHECK_NULL_VOID(loadingTextLayoutProperty);
267     auto layoutProperty = host->GetLayoutProperty<RefreshLayoutProperty>();
268     CHECK_NULL_VOID(layoutProperty);
269     loadingTextLayoutProperty->UpdateContent(layoutProperty->GetLoadingTextValue());
270     loadingTextLayoutProperty->UpdateMaxLines(1);
271     loadingTextLayoutProperty->UpdateMaxFontScale(2.0f);
272     loadingTextLayoutProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
273     auto context = PipelineContext::GetCurrentContextSafely();
274     CHECK_NULL_VOID(context);
275     auto theme = context->GetTheme<RefreshTheme>();
276     CHECK_NULL_VOID(theme);
277     loadingTextLayoutProperty->UpdateTextColor(theme->GetTextStyle().GetTextColor());
278     loadingTextLayoutProperty->UpdateFontSize(theme->GetTextStyle().GetFontSize());
279 
280     PaddingProperty textpadding;
281     textpadding.top = CalcLength(TRIGGER_LOADING_DISTANCE.ConvertToPx() + LOADING_TEXT_TOP_MARGIN.ConvertToPx());
282     auto prop = columnNode_->GetLayoutProperty<LinearLayoutProperty>();
283     prop->UpdatePadding(textpadding);
284     UpdateLoadingTextOpacity(0.0f);
285 
286     columnNode_->AddChild(loadingTextNode_, -1);
287     host->AddChild(columnNode_, 0);
288 }
289 
OnColorConfigurationUpdate()290 void RefreshPattern::OnColorConfigurationUpdate()
291 {
292     if (isCustomBuilderExist_) {
293         return;
294     }
295     CHECK_NULL_VOID(progressChild_);
296     auto pipeline = PipelineContext::GetCurrentContextSafely();
297     CHECK_NULL_VOID(pipeline);
298     auto themeManager = pipeline->GetThemeManager();
299     CHECK_NULL_VOID(themeManager);
300     auto theme = themeManager->GetTheme<RefreshTheme>();
301     CHECK_NULL_VOID(theme);
302     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
303     CHECK_NULL_VOID(layoutProperty);
304     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
305     CHECK_NULL_VOID(progressPaintProperty);
306     progressPaintProperty->UpdateColor(theme->GetProgressColor());
307     if (hasLoadingText_) {
308         CHECK_NULL_VOID(loadingTextNode_);
309         auto textLayoutProperty = loadingTextNode_->GetLayoutProperty<TextLayoutProperty>();
310         CHECK_NULL_VOID(textLayoutProperty);
311         textLayoutProperty->UpdateFontSize(theme->GetTextStyle().GetFontSize());
312         textLayoutProperty->UpdateTextColor(theme->GetTextStyle().GetTextColor());
313     }
314 }
315 
InitChildNode()316 void RefreshPattern::InitChildNode()
317 {
318     if (isCustomBuilderExist_) {
319         return;
320     }
321     auto host = GetHost();
322     CHECK_NULL_VOID(host);
323     auto accessibilityProperty = host->GetAccessibilityProperty<NG::AccessibilityProperty>();
324     CHECK_NULL_VOID(accessibilityProperty);
325     auto accessibilityLevel = accessibilityProperty->GetAccessibilityLevel();
326     if (!progressChild_) {
327         InitProgressNode();
328         if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
329             auto progressContext = progressChild_->GetRenderContext();
330             CHECK_NULL_VOID(progressContext);
331             progressContext->UpdateOpacity(0.0f);
332         } else {
333             UpdateLoadingProgress();
334         }
335     }
336     auto progressAccessibilityProperty = progressChild_->GetAccessibilityProperty<AccessibilityProperty>();
337     CHECK_NULL_VOID(progressAccessibilityProperty);
338     progressAccessibilityProperty->SetAccessibilityLevel(accessibilityLevel);
339 
340     if (hasLoadingText_ && !loadingTextNode_) {
341         InitProgressColumn();
342         isTextNodeChanged_ = true;
343     } else if (!hasLoadingText_ && loadingTextNode_) {
344         host->RemoveChild(columnNode_);
345         columnNode_ = nullptr;
346         loadingTextNode_ = nullptr;
347         isTextNodeChanged_ = true;
348         host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
349     }
350 
351     if (hasLoadingText_ && loadingTextNode_) {
352         auto loadingTextLayoutProperty = loadingTextNode_->GetLayoutProperty<TextLayoutProperty>();
353         CHECK_NULL_VOID(loadingTextLayoutProperty);
354         auto layoutProperty = host->GetLayoutProperty<RefreshLayoutProperty>();
355         CHECK_NULL_VOID(layoutProperty);
356         loadingTextLayoutProperty->UpdateContent(layoutProperty->GetLoadingTextValue());
357         loadingTextNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
358         auto textAccessibilityProperty = loadingTextNode_->GetAccessibilityProperty<AccessibilityProperty>();
359         CHECK_NULL_VOID(textAccessibilityProperty);
360         textAccessibilityProperty->SetAccessibilityLevel(accessibilityLevel);
361     }
362 }
363 
RefreshStatusChangeEffect()364 void RefreshPattern::RefreshStatusChangeEffect()
365 {
366     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
367     CHECK_NULL_VOID(layoutProperty);
368     auto refreshingProp = layoutProperty->GetIsRefreshing().value_or(false);
369     if (isRefreshing_ != refreshingProp) {
370         if (refreshingProp) {
371             QuickStartFresh();
372         } else {
373             QuickEndFresh();
374         }
375     }
376 }
377 
QuickStartFresh()378 void RefreshPattern::QuickStartFresh()
379 {
380     UpdateRefreshStatus(RefreshStatus::REFRESH);
381     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
382         QuickFirstChildAppear();
383         return;
384     }
385 
386     if (isCustomBuilderExist_) {
387         CustomBuilderRefreshingAnimation(false);
388     } else {
389         LoadingProgressRefreshingAnimation(false);
390     }
391 }
392 
QuickEndFresh()393 void RefreshPattern::QuickEndFresh()
394 {
395     SwitchToFinish();
396     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
397         QuickFirstChildDisappear();
398         return;
399     }
400 
401     if (isCustomBuilderExist_) {
402         CustomBuilderExit();
403     } else {
404         LoadingProgressExit();
405     }
406 }
407 
OnKeyEvent(const KeyEvent & event)408 bool RefreshPattern::OnKeyEvent(const KeyEvent& event)
409 {
410     if (event.code == KeyCode::KEY_F5 || (event.IsCombinationKey() && event.IsCtrlWith(KeyCode::KEY_R))) {
411         if (!isRefreshing_) {
412             QuickStartFresh();
413         }
414         return true;
415     }
416     return false;
417 }
418 
HandleDragStart(bool isDrag,float mainSpeed)419 void RefreshPattern::HandleDragStart(bool isDrag, float mainSpeed)
420 {
421     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
422         isSourceFromAnimation_ = !isDrag;
423         ResetAnimation();
424     } else {
425         HandleDragStartLowVersion();
426     }
427     // AccessibilityEventType::SCROLL_START
428 }
429 
HandleDragUpdate(float delta,float mainSpeed)430 ScrollResult RefreshPattern::HandleDragUpdate(float delta, float mainSpeed)
431 {
432     UpdateDragFRCSceneInfo(REFRESH_DRAG_SCENE, mainSpeed, SceneStatus::RUNNING);
433     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
434         // If dragging does not expand the refresh, there is no need to continue executing the code
435         if (NearZero(scrollOffset_) && NonPositive(delta)) {
436             return { delta, true };
437         }
438         auto pullDownRatio = CalculatePullDownRatio();
439         scrollOffset_ = std::clamp(scrollOffset_ + delta * pullDownRatio, 0.0f, MAX_OFFSET);
440         if (!isSourceFromAnimation_) {
441             FireOnOffsetChange(scrollOffset_);
442             if (isRefreshing_) {
443                 UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, GetFollowRatio());
444                 UpdateFirstChildPlacement();
445                 return { 0.f, true };
446             }
447             UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_HAND, GetFollowRatio());
448             if (LessNotEqual(scrollOffset_, static_cast<float>(refreshOffset_.ConvertToPx()))) {
449                 UpdateRefreshStatus(RefreshStatus::DRAG);
450             } else {
451                 UpdateRefreshStatus(RefreshStatus::OVER_DRAG);
452             }
453         }
454         UpdateFirstChildPlacement();
455     } else {
456         HandleDragUpdateLowVersion(delta);
457     }
458     return { 0.f, true };
459 }
460 
HandleDragEnd(float speed)461 void RefreshPattern::HandleDragEnd(float speed)
462 {
463     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
464         SpeedTriggerAnimation(speed);
465     } else {
466         HandleDragEndLowVersion();
467     }
468 }
469 
HandleDragCancel()470 void RefreshPattern::HandleDragCancel()
471 {
472     HandleDragEnd(0.0f);
473 }
474 
CalculatePullDownRatio()475 float RefreshPattern::CalculatePullDownRatio()
476 {
477     auto host = GetHost();
478     CHECK_NULL_RETURN(host, 1.0f);
479     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
480     CHECK_NULL_RETURN(layoutProperty, 1.f);
481     if (layoutProperty->GetPullDownRatio().has_value()) {
482         return layoutProperty->GetPullDownRatio().value();
483     }
484     auto geometryNode = host->GetGeometryNode();
485     CHECK_NULL_RETURN(geometryNode, 1.0f);
486     auto contentHeight = geometryNode->GetPaddingSize().Height();
487     return NearZero(contentHeight) ? 1.0f : ScrollablePattern::CalculateFriction(scrollOffset_ / contentHeight);
488 }
489 
GetFollowRatio()490 float RefreshPattern::GetFollowRatio()
491 {
492     auto loadingVisibleHeight = GetLoadingVisibleHeight();
493     auto ratio = 0.0f;
494     if (!NearEqual(static_cast<float>(refreshOffset_.ConvertToPx()), loadingVisibleHeight)) {
495         ratio = static_cast<float>(
496             (scrollOffset_ - loadingVisibleHeight) / (refreshOffset_.ConvertToPx() - loadingVisibleHeight));
497     }
498     return std::clamp(ratio, 0.0f, 1.0f);
499 }
500 
FireStateChange(int32_t value)501 void RefreshPattern::FireStateChange(int32_t value)
502 {
503     auto refreshEventHub = GetEventHub<RefreshEventHub>();
504     CHECK_NULL_VOID(refreshEventHub);
505     refreshEventHub->FireOnStateChange(value);
506     if (refreshStatus_ == RefreshStatus::REFRESH && Recorder::EventRecorder::Get().IsComponentRecordEnable()) {
507         auto host = GetHost();
508         CHECK_NULL_VOID(host);
509         auto inspectorId = host->GetInspectorId().value_or("");
510         Recorder::EventParamsBuilder builder;
511         builder.SetId(inspectorId)
512             .SetType(host->GetTag())
513             .SetEventType(Recorder::EventType::REFRESH)
514             .SetDescription(host->GetAutoEventParamValue(""));
515         Recorder::EventRecorder::Get().OnEvent(std::move(builder));
516     }
517 }
518 
FireRefreshing()519 void RefreshPattern::FireRefreshing()
520 {
521     auto refreshEventHub = GetEventHub<RefreshEventHub>();
522     CHECK_NULL_VOID(refreshEventHub);
523     refreshEventHub->FireOnRefreshing();
524 }
525 
FireChangeEvent(const std::string & value)526 void RefreshPattern::FireChangeEvent(const std::string& value)
527 {
528     auto refreshEventHub = GetEventHub<RefreshEventHub>();
529     CHECK_NULL_VOID(refreshEventHub);
530     refreshEventHub->FireChangeEvent(value);
531 }
532 
FireOnOffsetChange(float value)533 void RefreshPattern::FireOnOffsetChange(float value)
534 {
535     if (NearZero(value)) {
536         value = 0.0f;
537     }
538     if (!NearEqual(lastScrollOffset_, value)) {
539         auto refreshEventHub = GetEventHub<RefreshEventHub>();
540         CHECK_NULL_VOID(refreshEventHub);
541         refreshEventHub->FireOnOffsetChange(Dimension(value).ConvertToVp());
542         lastScrollOffset_ = value;
543     }
544 }
545 
AddCustomBuilderNode(const RefPtr<NG::UINode> & builder)546 void RefreshPattern::AddCustomBuilderNode(const RefPtr<NG::UINode>& builder)
547 {
548     auto host = GetHost();
549     CHECK_NULL_VOID(host);
550     if (!builder) {
551         if (isCustomBuilderExist_) {
552             host->RemoveChild(customBuilder_);
553             isCustomBuilderExist_ = false;
554             customBuilder_ = nullptr;
555             isRemoveCustomBuilder_ = true;
556         }
557         return;
558     }
559 
560     if (!isCustomBuilderExist_) {
561         if (progressChild_) {
562             if (columnNode_) {
563                 host->RemoveChild(columnNode_);
564                 columnNode_ = nullptr;
565                 loadingTextNode_ = nullptr;
566             }
567             host->RemoveChild(progressChild_);
568             progressChild_ = nullptr;
569         }
570         host->AddChild(builder, 0);
571         UpdateFirstChildPlacement();
572         UpdateScrollTransition(0.f);
573     } else {
574         auto customNodeChild = host->GetFirstChild();
575         CHECK_NULL_VOID(customNodeChild);
576         if (customNodeChild != builder) {
577             host->ReplaceChild(customNodeChild, builder);
578             host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
579         }
580     }
581     customBuilder_ = AceType::DynamicCast<FrameNode>(builder);
582     isCustomBuilderExist_ = true;
583 }
584 
SetAccessibilityAction()585 void RefreshPattern::SetAccessibilityAction()
586 {
587     auto host = GetHost();
588     CHECK_NULL_VOID(host);
589     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
590     CHECK_NULL_VOID(accessibilityProperty);
591     accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
592         const auto& pattern = weakPtr.Upgrade();
593         CHECK_NULL_VOID(pattern);
594         if (pattern->IsRefreshing()) {
595             return;
596         }
597         pattern->HandleDragStart(true, 0.0f);
598         for (float delta = 0.0f; LessNotEqual(delta, static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx()));
599              delta += TRIGGER_LOADING_DISTANCE.ConvertToPx()) {
600             pattern->HandleDragUpdate(delta, 0.0f);
601         }
602         pattern->HandleDragEnd(0.0f);
603     });
604 }
605 
InitCoordinationEvent(RefPtr<ScrollableCoordinationEvent> & coordinationEvent)606 void RefreshPattern::InitCoordinationEvent(RefPtr<ScrollableCoordinationEvent>& coordinationEvent)
607 {
608     auto onScrollEvent = [weak = WeakClaim(this)](float offset, float mainSpeed) -> bool {
609         auto pattern = weak.Upgrade();
610         CHECK_NULL_RETURN(pattern, false);
611         pattern->HandleDragUpdate(offset, mainSpeed);
612         return Positive(pattern->scrollOffset_) || NonNegative(offset);
613     };
614     coordinationEvent->SetOnScrollEvent(onScrollEvent);
615     auto onScrollStartEvent = [weak = WeakClaim(this)](bool isDrag, float mainSpeed) {
616         auto pattern = weak.Upgrade();
617         CHECK_NULL_VOID(pattern);
618         pattern->HandleDragStart(isDrag, mainSpeed);
619     };
620     coordinationEvent->SetOnScrollStartEvent(onScrollStartEvent);
621     auto onScrollEndEvent = [weak = WeakClaim(this)](float speed) {
622         auto pattern = weak.Upgrade();
623         CHECK_NULL_VOID(pattern);
624         pattern->HandleDragEnd(speed);
625     };
626     coordinationEvent->SetOnScrollEndEvent(onScrollEndEvent);
627 }
628 
UpdateRefreshStatus(RefreshStatus newStatus)629 void RefreshPattern::UpdateRefreshStatus(RefreshStatus newStatus)
630 {
631     if (refreshStatus_ == newStatus) {
632         return;
633     }
634     refreshStatus_ = newStatus;
635     if (refreshStatus_ == RefreshStatus::REFRESH) {
636         isRefreshing_ = true;
637         // the two-way binding of 'refreshing' variable need to be changed before 'onRefreshing' function is triggered
638         FireChangeEvent("true");
639         FireRefreshing();
640     } else {
641         isRefreshing_ = false;
642         FireChangeEvent("false");
643     }
644     FireStateChange(static_cast<int>(refreshStatus_));
645     TAG_LOGD(AceLogTag::ACE_REFRESH, "refresh status changed %{public}d", static_cast<int32_t>(refreshStatus_));
646 }
647 
SwitchToFinish()648 void RefreshPattern::SwitchToFinish()
649 {
650     if (refreshStatus_ != RefreshStatus::REFRESH && refreshStatus_ != RefreshStatus::DONE) {
651         UpdateRefreshStatus(RefreshStatus::INACTIVE);
652     } else {
653         UpdateRefreshStatus(RefreshStatus::DONE);
654     }
655 }
656 
UpdateLoadingProgressStatus(RefreshAnimationState state,float ratio)657 void RefreshPattern::UpdateLoadingProgressStatus(RefreshAnimationState state, float ratio)
658 {
659     // Need to check loadingProgress mode
660     CHECK_NULL_VOID(progressChild_);
661     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
662     CHECK_NULL_VOID(progressPaintProperty);
663     progressPaintProperty->UpdateRefreshAnimationState(state);
664     switch (state) {
665         case RefreshAnimationState::FOLLOW_HAND:
666         case RefreshAnimationState::RECYCLE:
667             progressPaintProperty->UpdateRefreshSizeScaleRatio(ratio);
668             break;
669         default:
670             break;
671     }
672     if (CheckNeedRender(progressPaintProperty->GetPropertyChangeFlag())) {
673         progressChild_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
674     }
675 }
676 
InitOffsetProperty()677 void RefreshPattern::InitOffsetProperty()
678 {
679     if (!offsetProperty_) {
680         auto propertyCallback = [weak = AceType::WeakClaim(this)](float scrollOffset) {
681             auto pattern = weak.Upgrade();
682             CHECK_NULL_VOID(pattern);
683             pattern->scrollOffset_ = scrollOffset;
684             pattern->UpdateFirstChildPlacement();
685             pattern->FireOnOffsetChange(scrollOffset);
686         };
687         offsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
688         auto host = GetHost();
689         CHECK_NULL_VOID(host);
690         auto renderContext = host->GetRenderContext();
691         CHECK_NULL_VOID(renderContext);
692         renderContext->AttachNodeAnimatableProperty(offsetProperty_);
693         offsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
694     }
695 }
696 
UpdateFirstChildPlacement()697 void RefreshPattern::UpdateFirstChildPlacement()
698 {
699     auto host = GetHost();
700     CHECK_NULL_VOID(host);
701     auto geometryNode = host->GetGeometryNode();
702     CHECK_NULL_VOID(geometryNode);
703     auto refreshHeight = geometryNode->GetFrameSize().Height();
704     auto scrollOffset = std::clamp(scrollOffset_, 0.0f, refreshHeight);
705     if (progressChild_) {
706         if (isSourceFromAnimation_) {
707             UpdateLoadingProgressTranslate(0.0f);
708             UpdateScrollTransition(scrollOffset);
709         } else {
710             UpdateLoadingProgressTranslate(scrollOffset);
711             UpdateScrollTransition(scrollOffset);
712             UpdateLoadingProgressStatus(GetLoadingProgressStatus(), GetFollowRatio());
713         }
714     } else {
715         UpdateBuilderHeight(scrollOffset);
716     }
717 }
718 
UpdateScrollTransition(float scrollOffset)719 void RefreshPattern::UpdateScrollTransition(float scrollOffset)
720 {
721     auto host = GetHost();
722     CHECK_NULL_VOID(host);
723     int32_t childCount = host->TotalChildCount();
724     // If the refresh has no children without loadingProgress and text, it does not need to update offset.
725     if (childCount < 2 || (childCount == 2 && columnNode_)) { // 2 means loadingProgress and text child components.
726         return;
727     }
728     // Need to search for frameNode and skip ComponentNode
729     auto childNode = host->GetLastChild();
730     CHECK_NULL_VOID(childNode);
731     while (!AceType::InstanceOf<FrameNode>(childNode) && !childNode->GetChildren().empty()) {
732         childNode = childNode->GetFirstChild();
733     }
734     auto scrollableNode = AceType::DynamicCast<FrameNode>(childNode);
735     CHECK_NULL_VOID(scrollableNode);
736     auto scrollableRenderContext = scrollableNode->GetRenderContext();
737     CHECK_NULL_VOID(scrollableRenderContext);
738     scrollableRenderContext->UpdateTransformTranslate({ 0.0f, scrollOffset, 0.0f });
739 }
740 
UpdateBuilderHeight(float builderHeight)741 void RefreshPattern::UpdateBuilderHeight(float builderHeight)
742 {
743     auto host = GetHost();
744     CHECK_NULL_VOID(host);
745     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
746     CHECK_NULL_VOID(layoutProperty);
747     builderMeasureBaseHeight_ = builderHeight;
748     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
749 }
750 
UpdateLoadingProgressTranslate(float scrollOffset)751 void RefreshPattern::UpdateLoadingProgressTranslate(float scrollOffset)
752 {
753     CHECK_NULL_VOID(progressChild_);
754     auto renderContext = progressChild_->GetRenderContext();
755     CHECK_NULL_VOID(renderContext);
756     auto loadingVisibleHeight = GetLoadingVisibleHeight();
757     if (GreatOrEqual(scrollOffset, loadingVisibleHeight) &&
758         !NearEqual(loadingVisibleHeight, static_cast<float>(GetTriggerRefreshDisTance().ConvertToPx()))) {
759         auto ratio = static_cast<float>(
760             (scrollOffset - loadingVisibleHeight) / (GetTriggerRefreshDisTance().ConvertToPx() - loadingVisibleHeight));
761         renderContext->UpdateOpacity(std::clamp(ratio, 0.0f, 1.0f));
762         renderContext->UpdateTransformTranslate({ 0.0f, (scrollOffset - loadingVisibleHeight) * HALF, 0.0f });
763         if (loadingTextNode_) {
764             UpdateLoadingTextOpacity(std::clamp(ratio, 0.0f, 1.0f));
765             auto loadingTextRenderContext = loadingTextNode_->GetRenderContext();
766             CHECK_NULL_VOID(loadingTextRenderContext);
767             loadingTextRenderContext->UpdateTransformTranslate({ 0.0f,
768                 scrollOffset_ - TRIGGER_LOADING_DISTANCE.ConvertToPx() - LOADING_PROGRESS_SIZE.ConvertToPx() -
769                     LOADING_TEXT_TOP_MARGIN.ConvertToPx(),
770                 0.0f });
771         }
772     } else {
773         renderContext->UpdateOpacity(0.0f);
774         UpdateLoadingTextOpacity(0.0f);
775     }
776 }
777 
GetLoadingVisibleHeight()778 float RefreshPattern::GetLoadingVisibleHeight()
779 {
780     float loadingHeight = 0.0f;
781     CHECK_NULL_RETURN(progressChild_, 0.0f);
782     auto renderContext = progressChild_->GetRenderContext();
783     CHECK_NULL_RETURN(renderContext, 0.0f);
784     auto geometryNode = progressChild_->GetGeometryNode();
785     CHECK_NULL_RETURN(geometryNode, 0.0f);
786     if (loadingTextNode_) {
787         auto loadingTextGeometryNode = loadingTextNode_->GetGeometryNode();
788         CHECK_NULL_RETURN(loadingTextGeometryNode, 0.0f);
789         loadingHeight = geometryNode->GetFrameSize().Height() + loadingTextGeometryNode->GetFrameSize().Height() +
790                         LOADING_TEXT_TOP_MARGIN.ConvertToPx();
791     } else {
792         loadingHeight = geometryNode->GetFrameSize().Height();
793     }
794     return (HALF + BASE_SCALE * HALF) * loadingHeight;
795 }
796 
SpeedTriggerAnimation(float speed)797 void RefreshPattern::SpeedTriggerAnimation(float speed)
798 {
799     auto targetOffset = (isSourceFromAnimation_ ||
800                             LessNotEqual(scrollOffset_, refreshOffset_.ConvertToPx()) || !pullToRefresh_)
801                             ? 0.0f
802                             : refreshOffset_.ConvertToPx();
803     auto dealSpeed = 0.0f;
804     if (!NearEqual(scrollOffset_, targetOffset)) {
805         dealSpeed = speed / (targetOffset - scrollOffset_);
806     }
807     bool recycle = true;
808     if (pullToRefresh_ && !isSourceFromAnimation_ && refreshStatus_ == RefreshStatus::OVER_DRAG) {
809         UpdateRefreshStatus(RefreshStatus::REFRESH);
810         UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_TO_RECYCLE, GetFollowRatio());
811     } else if (NearZero(targetOffset)) {
812         recycle = false;
813         SwitchToFinish();
814     }
815     ResetAnimation();
816     AnimationOption option;
817     auto curve = AceType::MakeRefPtr<InterpolatingSpring>(dealSpeed, 1.0f, 228.0f, 30.0f);
818     option.SetCurve(curve);
819     animation_ = AnimationUtils::StartAnimation(
820         option,
821         [&, weak = AceType::WeakClaim(this)]() {
822             auto pattern = weak.Upgrade();
823             CHECK_NULL_VOID(pattern);
824             pattern->offsetProperty_->Set(targetOffset);
825         },
826         [weak = AceType::WeakClaim(this), recycle]() {
827             auto pattern = weak.Upgrade();
828             CHECK_NULL_VOID(pattern);
829             if (recycle) {
830                 pattern->UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, pattern->GetFollowRatio());
831             }
832         });
833     auto context = PipelineContext::GetCurrentContextSafely();
834     CHECK_NULL_VOID(context);
835     context->RequestFrame();
836 }
837 
GetTargetOffset()838 float RefreshPattern::GetTargetOffset()
839 {
840     if (isSourceFromAnimation_) {
841         return 0.0f;
842     }
843     auto targetOffset = 0.0f;
844     switch (refreshStatus_) {
845         case RefreshStatus::OVER_DRAG:
846         case RefreshStatus::REFRESH:
847             targetOffset = refreshOffset_.ConvertToPx();
848             break;
849         default:
850             targetOffset = 0.0f;
851             break;
852     }
853     return targetOffset;
854 }
855 
SpeedAnimationFinish()856 void RefreshPattern::SpeedAnimationFinish()
857 {
858     if (isRefreshing_) {
859         UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, GetFollowRatio());
860     } else {
861         UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_HAND, GetFollowRatio());
862     }
863 }
864 
QuickFirstChildAppear()865 void RefreshPattern::QuickFirstChildAppear()
866 {
867     isSourceFromAnimation_ = false;
868     UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, GetFollowRatio());
869     ResetAnimation();
870     AnimationOption option;
871     option.SetCurve(DEFAULT_CURVE);
872     option.SetDuration(LOADING_ANIMATION_DURATION);
873     animation_ = AnimationUtils::StartAnimation(
874         option, [&]() { offsetProperty_->Set(static_cast<float>(refreshOffset_.ConvertToPx())); });
875 }
876 
QuickFirstChildDisappear()877 void RefreshPattern::QuickFirstChildDisappear()
878 {
879     ResetAnimation();
880     AnimationOption option;
881     option.SetCurve(DEFAULT_CURVE);
882     option.SetDuration(LOADING_ANIMATION_DURATION);
883     animation_ = AnimationUtils::StartAnimation(
884         option, [&]() { offsetProperty_->Set(0.0f); },
885         [weak = AceType::WeakClaim(this)]() {
886             auto pattern = weak.Upgrade();
887             CHECK_NULL_VOID(pattern);
888             pattern->SpeedAnimationFinish();
889         });
890 }
891 
GetLoadingProgressStatus()892 RefreshAnimationState RefreshPattern::GetLoadingProgressStatus()
893 {
894     auto defaultValue = RefreshAnimationState::FOLLOW_HAND;
895     CHECK_NULL_RETURN(progressChild_, defaultValue);
896     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
897     CHECK_NULL_RETURN(progressPaintProperty, defaultValue);
898     return progressPaintProperty->GetRefreshAnimationState().value_or(defaultValue);
899 }
900 
ResetAnimation()901 void RefreshPattern::ResetAnimation()
902 {
903     float currentOffset = scrollOffset_;
904     AnimationUtils::StopAnimation(animation_);
905     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
906         CHECK_NULL_VOID(offsetProperty_);
907         offsetProperty_->Set(currentOffset);
908     } else {
909         CHECK_NULL_VOID(lowVersionOffset_);
910         lowVersionOffset_->Set(currentOffset);
911     }
912 }
913 
UpdateDragFRCSceneInfo(const std::string & scene,float speed,SceneStatus sceneStatus)914 void RefreshPattern::UpdateDragFRCSceneInfo(const std::string& scene, float speed, SceneStatus sceneStatus)
915 {
916     auto host = GetHost();
917     CHECK_NULL_VOID(host);
918     host->AddFRCSceneInfo(scene, std::abs(speed), sceneStatus);
919 }
920 
InitLowVersionOffset()921 void RefreshPattern::InitLowVersionOffset()
922 {
923     if (!lowVersionOffset_) {
924         auto propertyCallback = [weak = AceType::WeakClaim(this)](float scrollOffset) {
925             auto pattern = weak.Upgrade();
926             CHECK_NULL_VOID(pattern);
927             pattern->scrollOffset_ = scrollOffset;
928             pattern->UpdateChild();
929         };
930         lowVersionOffset_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
931         auto host = GetHost();
932         CHECK_NULL_VOID(host);
933         auto renderContext = host->GetRenderContext();
934         CHECK_NULL_VOID(renderContext);
935         renderContext->AttachNodeAnimatableProperty(lowVersionOffset_);
936     }
937 }
938 
UpdateChild()939 void RefreshPattern::UpdateChild()
940 {
941     if (customBuilder_) {
942         UpdateCustomBuilderProperty();
943     } else {
944         UpdateLoadingProgress();
945     }
946 }
947 
HandleDragStartLowVersion()948 void RefreshPattern::HandleDragStartLowVersion()
949 {
950     if (isRefreshing_) {
951         return;
952     }
953     scrollOffset_ = 0.0f;
954     UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_HAND, 0.0f);
955 }
956 
HandleDragUpdateLowVersion(float delta)957 void RefreshPattern::HandleDragUpdateLowVersion(float delta)
958 {
959     if (isRefreshing_) {
960         return;
961     }
962     scrollOffset_ = GetScrollOffset(delta);
963     if (LessNotEqual(scrollOffset_, static_cast<float>(GetTriggerRefreshDisTance().ConvertToPx()))) {
964         UpdateRefreshStatus(RefreshStatus::DRAG);
965     } else {
966         UpdateRefreshStatus(RefreshStatus::OVER_DRAG);
967     }
968     if (customBuilder_) {
969         HandleCustomBuilderDragUpdateStage();
970         return;
971     }
972     UpdateLoadingProgress();
973     if (GreatNotEqual(scrollOffset_, triggerLoadingDistance_)) {
974         CHECK_NULL_VOID(progressChild_);
975         auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
976         CHECK_NULL_VOID(progressPaintProperty);
977         float triggerRefreshDistance = GetTriggerRefreshDisTance().ConvertToPx();
978         float ratio =
979             NearEqual(triggerRefreshDistance, triggerLoadingDistance_)
980                 ? 1.0f
981                 : (scrollOffset_ - triggerLoadingDistance_) / (triggerRefreshDistance - triggerLoadingDistance_);
982         progressPaintProperty->UpdateRefreshSizeScaleRatio(std::clamp(ratio, 0.0f, 1.0f));
983     }
984 }
985 
HandleDragEndLowVersion()986 void RefreshPattern::HandleDragEndLowVersion()
987 {
988     if (isRefreshing_) {
989         return;
990     }
991     if (customBuilder_) {
992         HandleCustomBuilderDragEndStage();
993         return;
994     }
995     if (refreshStatus_ == RefreshStatus::OVER_DRAG) {
996         UpdateRefreshStatus(RefreshStatus::REFRESH);
997         LoadingProgressRefreshingAnimation(true);
998     } else {
999         SwitchToFinish();
1000         LoadingProgressExit();
1001     }
1002     // AccessibilityEventType::SCROLL_END
1003 }
1004 
LoadingProgressRefreshingAnimation(bool isDrag)1005 void RefreshPattern::LoadingProgressRefreshingAnimation(bool isDrag)
1006 {
1007     UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, 1.0f);
1008     ResetAnimation();
1009     CHECK_NULL_VOID(lowVersionOffset_);
1010     AnimationOption option;
1011     if (isDrag) {
1012         option.SetCurve(AceType::MakeRefPtr<SpringCurve>(0.0f, 1.0f, 228.0f, 30.0f));
1013         option.SetDuration(FOLLOW_TO_RECYCLE_DURATION);
1014     } else {
1015         option.SetCurve(DEFAULT_CURVE);
1016         option.SetDuration(LOADING_ANIMATION_DURATION);
1017     }
1018     animation_ = AnimationUtils::StartAnimation(
1019         option, [&]() { lowVersionOffset_->Set(GetTriggerRefreshDisTance().ConvertToPx()); });
1020 }
1021 
LoadingProgressExit()1022 void RefreshPattern::LoadingProgressExit()
1023 {
1024     ResetAnimation();
1025     CHECK_NULL_VOID(lowVersionOffset_);
1026     AnimationOption option;
1027     option.SetCurve(DEFAULT_CURVE);
1028     option.SetDuration(LOADING_ANIMATION_DURATION);
1029     animation_ = AnimationUtils::StartAnimation(
1030         option, [&]() { lowVersionOffset_->Set(0.0f); },
1031         [weak = AceType::WeakClaim(this)]() {
1032             auto pattern = weak.Upgrade();
1033             CHECK_NULL_VOID(pattern);
1034             pattern->UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_HAND, 0.0f);
1035         });
1036 }
1037 
UpdateLoadingProgress()1038 void RefreshPattern::UpdateLoadingProgress()
1039 {
1040     float loadingProgressOffset =
1041         std::clamp(scrollOffset_, triggerLoadingDistance_, static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx()));
1042     UpdateLoadingMarginTop(loadingProgressOffset);
1043     float triggerRefreshDistance = GetTriggerRefreshDisTance().ConvertToPx();
1044     float ratio = NearEqual(triggerRefreshDistance, triggerLoadingDistance_)
1045                       ? 1.0f
1046                       : (loadingProgressOffset - triggerLoadingDistance_) /
1047                             (GetTriggerRefreshDisTance().ConvertToPx() - triggerLoadingDistance_);
1048     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
1049     CHECK_NULL_VOID(progressPaintProperty);
1050     progressPaintProperty->UpdateRefreshSizeScaleRatio(ratio);
1051     auto progressContext = progressChild_->GetRenderContext();
1052     CHECK_NULL_VOID(progressContext);
1053     progressContext->UpdateOpacity(std::clamp(ratio, 0.0f, 1.0f));
1054     UpdateLoadingTextOpacity(std::clamp(ratio, 0.0f, 1.0f));
1055     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1056 }
1057 
CustomBuilderRefreshingAnimation(bool isDrag)1058 void RefreshPattern::CustomBuilderRefreshingAnimation(bool isDrag)
1059 {
1060     ResetAnimation();
1061     CHECK_NULL_VOID(lowVersionOffset_);
1062     AnimationOption option;
1063     if (isDrag) {
1064         option.SetCurve(AceType::MakeRefPtr<SpringCurve>(0.0f, 1.0f, 228.0f, 30.0f));
1065         option.SetDuration(FOLLOW_TO_RECYCLE_DURATION);
1066     } else {
1067         option.SetCurve(DEFAULT_CURVE);
1068         option.SetDuration(CUSTOM_BUILDER_ANIMATION_DURATION);
1069     }
1070     animation_ = AnimationUtils::StartAnimation(
1071         option, [&]() { lowVersionOffset_->Set(GetTriggerRefreshDisTance().ConvertToPx()); });
1072 }
1073 
CustomBuilderExit()1074 void RefreshPattern::CustomBuilderExit()
1075 {
1076     ResetAnimation();
1077     CHECK_NULL_VOID(lowVersionOffset_);
1078     AnimationOption option;
1079     option.SetDuration(CUSTOM_BUILDER_ANIMATION_DURATION);
1080     option.SetCurve(DEFAULT_CURVE);
1081     animation_ = AnimationUtils::StartAnimation(option, [&]() { lowVersionOffset_->Set(0.0f); });
1082 }
1083 
UpdateCustomBuilderProperty()1084 void RefreshPattern::UpdateCustomBuilderProperty()
1085 {
1086     auto customBuilderSize = customBuilder_->GetGeometryNode()->GetFrameSize();
1087     auto maxScroll = static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx());
1088     customBuilderOffset_ = std::clamp(scrollOffset_, triggerLoadingDistance_, maxScroll - customBuilderSize.Height());
1089     float triggerRefreshDistance = GetTriggerRefreshDisTance().ConvertToPx();
1090     float ratio = NearEqual(triggerRefreshDistance, triggerLoadingDistance_)
1091                       ? 1.0f
1092                       : (customBuilderOffset_ - triggerLoadingDistance_) /
1093                             (GetTriggerRefreshDisTance().ConvertToPx() - triggerLoadingDistance_);
1094     auto customBuilderContext = customBuilder_->GetRenderContext();
1095     CHECK_NULL_VOID(customBuilderContext);
1096     customBuilderContext->UpdateOpacity(std::clamp(ratio, 0.0f, 1.0f));
1097     auto host = GetHost();
1098     CHECK_NULL_VOID(host);
1099     host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1100 }
1101 
HandleCustomBuilderDragUpdateStage()1102 void RefreshPattern::HandleCustomBuilderDragUpdateStage()
1103 {
1104     auto customBuilderSize = customBuilder_->GetGeometryNode()->GetMarginFrameSize();
1105     auto maxScroll = MAX_SCROLL_DISTANCE.ConvertToPx();
1106     if (NearZero(static_cast<double>(customBuilder_->GetGeometryNode()->GetMarginFrameSize().Height()))) {
1107         return;
1108     }
1109     if (LessNotEqual(static_cast<double>(maxScroll - customBuilderSize.Height()),
1110             static_cast<double>(triggerLoadingDistance_))) {
1111         return;
1112     }
1113     UpdateCustomBuilderProperty();
1114 }
1115 
HandleCustomBuilderDragEndStage()1116 void RefreshPattern::HandleCustomBuilderDragEndStage()
1117 {
1118     if (refreshStatus_ == RefreshStatus::OVER_DRAG) {
1119         UpdateRefreshStatus(RefreshStatus::REFRESH);
1120         CustomBuilderRefreshingAnimation(true);
1121     } else {
1122         SwitchToFinish();
1123         CustomBuilderExit();
1124     }
1125 }
1126 
UpdateLoadingMarginTop(float top)1127 void RefreshPattern::UpdateLoadingMarginTop(float top)
1128 {
1129     CHECK_NULL_VOID(progressChild_);
1130     auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
1131     CHECK_NULL_VOID(progressLayoutProperty);
1132     MarginProperty marginProperty;
1133     marginProperty.left = CalcLength(0.0f);
1134     marginProperty.right = CalcLength(0.0f);
1135     marginProperty.bottom = CalcLength(0.0f);
1136     marginProperty.top = CalcLength(top);
1137     progressLayoutProperty->UpdateMargin(marginProperty);
1138 }
1139 
GetScrollOffset(float delta)1140 float RefreshPattern::GetScrollOffset(float delta)
1141 {
1142     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
1143     CHECK_NULL_RETURN(layoutProperty, 0.0f);
1144     auto frictionRatio = static_cast<float>(layoutProperty->GetFriction().value_or(DEFAULT_FRICTION)) * PERCENT;
1145     auto scrollY = delta * frictionRatio;
1146     return std::clamp(scrollOffset_ + scrollY, 0.0f, static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx()));
1147 }
1148 
HandleScroll(float offset,int32_t source,NestedState state,float velocity)1149 ScrollResult RefreshPattern::HandleScroll(float offset, int32_t source, NestedState state, float velocity)
1150 {
1151     ScrollResult result = { offset, true };
1152     auto nestedScroll = GetNestedScroll();
1153     if (NearZero(offset)) {
1154         return result;
1155     }
1156     auto parent = GetNestedScrollParent();
1157     if (state == NestedState::CHILD_SCROLL) {
1158         if (Negative(offset) && Positive(scrollOffset_)) {
1159             if (parent && nestedScroll.forward == NestedScrollMode::PARENT_FIRST) {
1160                 result = parent->HandleScroll(offset, source, NestedState::CHILD_SCROLL, velocity);
1161                 result = HandleDragUpdate(result.remain, velocity);
1162             } else if (parent && nestedScroll.forward == NestedScrollMode::SELF_FIRST) {
1163                 result = HandleDragUpdate(offset, velocity);
1164                 result = parent->HandleScroll(result.remain, source, NestedState::CHILD_SCROLL, velocity);
1165             } else {
1166                 result = HandleDragUpdate(offset, velocity);
1167             }
1168         } else {
1169             if (!parent || ((Negative(offset) && (nestedScroll.forward == NestedScrollMode::SELF_ONLY ||
1170                                                      nestedScroll.forward == NestedScrollMode::PARALLEL)) ||
1171                                (Positive(offset) && (nestedScroll.backward == NestedScrollMode::SELF_ONLY ||
1172                                                         nestedScroll.backward == NestedScrollMode::PARALLEL)))) {
1173                 return result;
1174             } else {
1175                 result = parent->HandleScroll(offset, source, NestedState::CHILD_SCROLL, velocity);
1176             }
1177         }
1178         return result;
1179     } else if (state == NestedState::CHILD_OVER_SCROLL) {
1180         if (parent && ((Negative(offset) && nestedScroll.forward == NestedScrollMode::SELF_FIRST) ||
1181                           (Positive(offset) && nestedScroll.backward == NestedScrollMode::SELF_FIRST))) {
1182             result = parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL, velocity);
1183             if (!NearZero(result.remain)) {
1184                 result = HandleDragUpdate(result.remain, velocity);
1185             }
1186             return { 0.f, true };
1187         } else {
1188             result = HandleDragUpdate(offset, velocity);
1189         }
1190     }
1191     return result;
1192 }
1193 
OnScrollStartRecursive(WeakPtr<NestableScrollContainer> child,float position,float velocity)1194 void RefreshPattern::OnScrollStartRecursive(WeakPtr<NestableScrollContainer> child, float position, float velocity)
1195 {
1196     SetIsNestedInterrupt(false);
1197     if (!GetIsFixedNestedScrollMode()) {
1198         SetParentScrollable();
1199     }
1200     auto nestedScroll = GetNestedScroll();
1201     HandleDragStart(true, velocity);
1202     auto parent = GetNestedScrollParent();
1203     if (parent && nestedScroll.NeedParent() &&
1204         (nestedScroll.forward != NestedScrollMode::PARALLEL || nestedScroll.backward != NestedScrollMode::PARALLEL)) {
1205         parent->OnScrollStartRecursive(child, position, velocity);
1206     }
1207 }
1208 
HandleScrollVelocity(float velocity,const RefPtr<NestableScrollContainer> & child)1209 bool RefreshPattern::HandleScrollVelocity(float velocity, const RefPtr<NestableScrollContainer>& child)
1210 {
1211     auto parent = GetNestedScrollParent();
1212     auto nestedScroll = GetNestedScroll();
1213     bool result = false;
1214     if (parent && ((Negative(velocity) && nestedScroll.forward == NestedScrollMode::PARENT_FIRST) ||
1215                       (Positive(velocity) && nestedScroll.backward == NestedScrollMode::PARENT_FIRST))) {
1216         result = parent->HandleScrollVelocity(velocity);
1217         if (result) {
1218             return true;
1219         }
1220     }
1221     if (Positive(scrollOffset_) || Positive(velocity)) {
1222         HandleDragEnd(velocity);
1223         result = true;
1224     } else if (parent && ((Negative(velocity) && nestedScroll.forward == NestedScrollMode::SELF_FIRST) ||
1225                              (Positive(velocity) && nestedScroll.backward == NestedScrollMode::SELF_FIRST))) {
1226         result = parent->HandleScrollVelocity(velocity);
1227     }
1228     return result;
1229 }
1230 
OnScrollEndRecursive(const std::optional<float> & velocity)1231 void RefreshPattern::OnScrollEndRecursive(const std::optional<float>& velocity)
1232 {
1233     HandleDragEnd(velocity.value_or(0.f));
1234     auto parent = GetNestedScrollParent();
1235     auto nestedScroll = GetNestedScroll();
1236     if (parent && (nestedScroll.NeedParent() || GetIsNestedInterrupt())) {
1237         parent->OnScrollEndRecursive(velocity);
1238     }
1239     SetIsNestedInterrupt(false);
1240 }
1241 
DumpInfo()1242 void RefreshPattern::DumpInfo()
1243 {
1244     DumpLog::GetInstance().AddDesc(
1245         std::string("RefreshStatus: ").append(std::to_string(static_cast<int32_t>(refreshStatus_))));
1246 }
1247 } // namespace OHOS::Ace::NG
1248