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