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 progressChild_ = FrameNode::CreateFrameNode(V2::LOADING_PROGRESS_ETS_TAG,
220 ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<LoadingProgressPattern>());
221 CHECK_NULL_VOID(progressChild_);
222 auto gestureHub = progressChild_->GetEventHub<EventHub>();
223 if (gestureHub) {
224 gestureHub->SetEnabled(false);
225 }
226 auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
227 CHECK_NULL_VOID(progressLayoutProperty);
228 progressLayoutProperty->UpdateUserDefinedIdealSize(
229 CalcSize(CalcLength(LOADING_PROGRESS_SIZE.ConvertToPx()), CalcLength(LOADING_PROGRESS_SIZE.ConvertToPx())));
230 auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
231 CHECK_NULL_VOID(progressPaintProperty);
232 progressPaintProperty->UpdateLoadingProgressOwner(LoadingProgressOwner::REFRESH);
233 host->AddChild(progressChild_, 0);
234 progressChild_->MarkDirtyNode();
235 auto context = host->GetContext();
236 CHECK_NULL_VOID(context);
237 auto theme = context->GetTheme<RefreshTheme>();
238 CHECK_NULL_VOID(theme);
239 progressPaintProperty->UpdateColor(theme->GetProgressColor());
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 UpdateFirstChildPlacement();
441 FireOnOffsetChange(scrollOffset_);
442 if (!isSourceFromAnimation_) {
443 if (isRefreshing_) {
444 UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, GetFollowRatio());
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 } else {
455 HandleDragUpdateLowVersion(delta);
456 }
457 return { 0.f, true };
458 }
459
HandleDragEnd(float speed)460 void RefreshPattern::HandleDragEnd(float speed)
461 {
462 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
463 SpeedTriggerAnimation(speed);
464 } else {
465 HandleDragEndLowVersion();
466 }
467 }
468
HandleDragCancel()469 void RefreshPattern::HandleDragCancel()
470 {
471 HandleDragEnd(0.0f);
472 }
473
CalculatePullDownRatio()474 float RefreshPattern::CalculatePullDownRatio()
475 {
476 auto host = GetHost();
477 CHECK_NULL_RETURN(host, 1.0f);
478 auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
479 CHECK_NULL_RETURN(layoutProperty, 1.f);
480 if (layoutProperty->GetPullDownRatio().has_value()) {
481 return layoutProperty->GetPullDownRatio().value();
482 }
483 auto geometryNode = host->GetGeometryNode();
484 CHECK_NULL_RETURN(geometryNode, 1.0f);
485 auto contentHeight = geometryNode->GetPaddingSize().Height();
486 return NearZero(contentHeight) ? 1.0f : ScrollablePattern::CalculateFriction(scrollOffset_ / contentHeight);
487 }
488
GetFollowRatio()489 float RefreshPattern::GetFollowRatio()
490 {
491 auto loadingVisibleHeight = GetLoadingVisibleHeight();
492 auto ratio = 0.0f;
493 if (!NearEqual(static_cast<float>(refreshOffset_.ConvertToPx()), loadingVisibleHeight)) {
494 ratio = static_cast<float>(
495 (scrollOffset_ - loadingVisibleHeight) / (refreshOffset_.ConvertToPx() - loadingVisibleHeight));
496 }
497 return std::clamp(ratio, 0.0f, 1.0f);
498 }
499
FireStateChange(int32_t value)500 void RefreshPattern::FireStateChange(int32_t value)
501 {
502 auto refreshEventHub = GetEventHub<RefreshEventHub>();
503 CHECK_NULL_VOID(refreshEventHub);
504 refreshEventHub->FireOnStateChange(value);
505 if (refreshStatus_ == RefreshStatus::REFRESH && Recorder::EventRecorder::Get().IsComponentRecordEnable()) {
506 auto host = GetHost();
507 CHECK_NULL_VOID(host);
508 auto inspectorId = host->GetInspectorId().value_or("");
509 Recorder::EventParamsBuilder builder;
510 builder.SetId(inspectorId)
511 .SetType(host->GetTag())
512 .SetEventType(Recorder::EventType::REFRESH)
513 .SetDescription(host->GetAutoEventParamValue(""));
514 Recorder::EventRecorder::Get().OnEvent(std::move(builder));
515 }
516 }
517
FireRefreshing()518 void RefreshPattern::FireRefreshing()
519 {
520 auto refreshEventHub = GetEventHub<RefreshEventHub>();
521 CHECK_NULL_VOID(refreshEventHub);
522 refreshEventHub->FireOnRefreshing();
523 }
524
FireChangeEvent(const std::string & value)525 void RefreshPattern::FireChangeEvent(const std::string& value)
526 {
527 auto refreshEventHub = GetEventHub<RefreshEventHub>();
528 CHECK_NULL_VOID(refreshEventHub);
529 refreshEventHub->FireChangeEvent(value);
530 }
531
FireOnOffsetChange(float value)532 void RefreshPattern::FireOnOffsetChange(float value)
533 {
534 if (NearZero(value)) {
535 value = 0.0f;
536 }
537 if (!NearEqual(lastScrollOffset_, value)) {
538 auto refreshEventHub = GetEventHub<RefreshEventHub>();
539 CHECK_NULL_VOID(refreshEventHub);
540 refreshEventHub->FireOnOffsetChange(Dimension(value).ConvertToVp());
541 lastScrollOffset_ = value;
542 }
543 }
544
AddCustomBuilderNode(const RefPtr<NG::UINode> & builder)545 void RefreshPattern::AddCustomBuilderNode(const RefPtr<NG::UINode>& builder)
546 {
547 auto host = GetHost();
548 CHECK_NULL_VOID(host);
549 if (!builder) {
550 if (isCustomBuilderExist_) {
551 host->RemoveChild(customBuilder_);
552 isCustomBuilderExist_ = false;
553 customBuilder_ = nullptr;
554 isRemoveCustomBuilder_ = true;
555 }
556 return;
557 }
558
559 if (!isCustomBuilderExist_) {
560 if (progressChild_) {
561 if (columnNode_) {
562 host->RemoveChild(columnNode_);
563 columnNode_ = nullptr;
564 loadingTextNode_ = nullptr;
565 }
566 host->RemoveChild(progressChild_);
567 progressChild_ = nullptr;
568 }
569 host->AddChild(builder, 0);
570 UpdateFirstChildPlacement();
571 UpdateScrollTransition(0.f);
572 } else {
573 auto customNodeChild = host->GetFirstChild();
574 CHECK_NULL_VOID(customNodeChild);
575 if (customNodeChild != builder) {
576 host->ReplaceChild(customNodeChild, builder);
577 host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
578 }
579 }
580 customBuilder_ = AceType::DynamicCast<FrameNode>(builder);
581 isCustomBuilderExist_ = true;
582 }
583
SetAccessibilityAction()584 void RefreshPattern::SetAccessibilityAction()
585 {
586 auto host = GetHost();
587 CHECK_NULL_VOID(host);
588 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
589 CHECK_NULL_VOID(accessibilityProperty);
590 accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
591 const auto& pattern = weakPtr.Upgrade();
592 CHECK_NULL_VOID(pattern);
593 if (pattern->IsRefreshing()) {
594 return;
595 }
596 pattern->HandleDragStart(true, 0.0f);
597 for (float delta = 0.0f; LessNotEqual(delta, static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx()));
598 delta += TRIGGER_LOADING_DISTANCE.ConvertToPx()) {
599 pattern->HandleDragUpdate(delta, 0.0f);
600 }
601 pattern->HandleDragEnd(0.0f);
602 });
603 }
604
InitCoordinationEvent(RefPtr<ScrollableCoordinationEvent> & coordinationEvent)605 void RefreshPattern::InitCoordinationEvent(RefPtr<ScrollableCoordinationEvent>& coordinationEvent)
606 {
607 auto onScrollEvent = [weak = WeakClaim(this)](float offset, float mainSpeed) -> bool {
608 auto pattern = weak.Upgrade();
609 CHECK_NULL_RETURN(pattern, false);
610 pattern->HandleDragUpdate(offset, mainSpeed);
611 return Positive(pattern->scrollOffset_) || NonNegative(offset);
612 };
613 coordinationEvent->SetOnScrollEvent(onScrollEvent);
614 auto onScrollStartEvent = [weak = WeakClaim(this)](bool isDrag, float mainSpeed) {
615 auto pattern = weak.Upgrade();
616 CHECK_NULL_VOID(pattern);
617 pattern->HandleDragStart(isDrag, mainSpeed);
618 };
619 coordinationEvent->SetOnScrollStartEvent(onScrollStartEvent);
620 auto onScrollEndEvent = [weak = WeakClaim(this)](float speed) {
621 auto pattern = weak.Upgrade();
622 CHECK_NULL_VOID(pattern);
623 pattern->HandleDragEnd(speed);
624 };
625 coordinationEvent->SetOnScrollEndEvent(onScrollEndEvent);
626 }
627
UpdateRefreshStatus(RefreshStatus newStatus)628 void RefreshPattern::UpdateRefreshStatus(RefreshStatus newStatus)
629 {
630 if (refreshStatus_ == newStatus) {
631 return;
632 }
633 refreshStatus_ = newStatus;
634 if (refreshStatus_ == RefreshStatus::REFRESH) {
635 isRefreshing_ = true;
636 // the two-way binding of 'refreshing' variable need to be changed before 'onRefreshing' function is triggered
637 FireChangeEvent("true");
638 FireRefreshing();
639 } else {
640 isRefreshing_ = false;
641 FireChangeEvent("false");
642 }
643 FireStateChange(static_cast<int>(refreshStatus_));
644 TAG_LOGD(AceLogTag::ACE_REFRESH, "refresh status changed %{public}d", static_cast<int32_t>(refreshStatus_));
645 }
646
SwitchToFinish()647 void RefreshPattern::SwitchToFinish()
648 {
649 if (refreshStatus_ != RefreshStatus::REFRESH && refreshStatus_ != RefreshStatus::DONE) {
650 UpdateRefreshStatus(RefreshStatus::INACTIVE);
651 } else {
652 UpdateRefreshStatus(RefreshStatus::DONE);
653 }
654 }
655
UpdateLoadingProgressStatus(RefreshAnimationState state,float ratio)656 void RefreshPattern::UpdateLoadingProgressStatus(RefreshAnimationState state, float ratio)
657 {
658 // Need to check loadingProgress mode
659 CHECK_NULL_VOID(progressChild_);
660 auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
661 CHECK_NULL_VOID(progressPaintProperty);
662 progressPaintProperty->UpdateRefreshAnimationState(state);
663 switch (state) {
664 case RefreshAnimationState::FOLLOW_HAND:
665 case RefreshAnimationState::RECYCLE:
666 progressPaintProperty->UpdateRefreshSizeScaleRatio(ratio);
667 break;
668 default:
669 break;
670 }
671 if (CheckNeedRender(progressPaintProperty->GetPropertyChangeFlag())) {
672 progressChild_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
673 }
674 }
675
InitOffsetProperty()676 void RefreshPattern::InitOffsetProperty()
677 {
678 if (!offsetProperty_) {
679 auto propertyCallback = [weak = AceType::WeakClaim(this)](float scrollOffset) {
680 auto pattern = weak.Upgrade();
681 CHECK_NULL_VOID(pattern);
682 pattern->scrollOffset_ = scrollOffset;
683 pattern->UpdateFirstChildPlacement();
684 pattern->FireOnOffsetChange(scrollOffset);
685 };
686 offsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
687 auto host = GetHost();
688 CHECK_NULL_VOID(host);
689 auto renderContext = host->GetRenderContext();
690 CHECK_NULL_VOID(renderContext);
691 renderContext->AttachNodeAnimatableProperty(offsetProperty_);
692 offsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
693 }
694 }
695
UpdateFirstChildPlacement()696 void RefreshPattern::UpdateFirstChildPlacement()
697 {
698 auto host = GetHost();
699 CHECK_NULL_VOID(host);
700 auto geometryNode = host->GetGeometryNode();
701 CHECK_NULL_VOID(geometryNode);
702 auto refreshHeight = geometryNode->GetFrameSize().Height();
703 auto scrollOffset = std::clamp(scrollOffset_, 0.0f, refreshHeight);
704 if (progressChild_) {
705 if (isSourceFromAnimation_) {
706 UpdateLoadingProgressTranslate(0.0f);
707 UpdateScrollTransition(scrollOffset);
708 } else {
709 UpdateLoadingProgressTranslate(scrollOffset);
710 UpdateScrollTransition(scrollOffset);
711 UpdateLoadingProgressStatus(GetLoadingProgressStatus(), GetFollowRatio());
712 }
713 } else {
714 UpdateBuilderHeight(scrollOffset);
715 }
716 }
717
UpdateScrollTransition(float scrollOffset)718 void RefreshPattern::UpdateScrollTransition(float scrollOffset)
719 {
720 auto host = GetHost();
721 CHECK_NULL_VOID(host);
722 int32_t childCount = host->TotalChildCount();
723 // If the refresh has no children without loadingProgress and text, it does not need to update offset.
724 if (childCount < 2 || (childCount == 2 && columnNode_)) { // 2 means loadingProgress and text child components.
725 return;
726 }
727 // Need to search for frameNode and skip ComponentNode
728 auto childNode = host->GetLastChild();
729 CHECK_NULL_VOID(childNode);
730 while (!AceType::InstanceOf<FrameNode>(childNode) && !childNode->GetChildren().empty()) {
731 childNode = childNode->GetFirstChild();
732 }
733 auto scrollableNode = AceType::DynamicCast<FrameNode>(childNode);
734 CHECK_NULL_VOID(scrollableNode);
735 auto scrollableRenderContext = scrollableNode->GetRenderContext();
736 CHECK_NULL_VOID(scrollableRenderContext);
737 scrollableRenderContext->UpdateTransformTranslate({ 0.0f, scrollOffset, 0.0f });
738 }
739
UpdateBuilderHeight(float builderHeight)740 void RefreshPattern::UpdateBuilderHeight(float builderHeight)
741 {
742 auto host = GetHost();
743 CHECK_NULL_VOID(host);
744 auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
745 CHECK_NULL_VOID(layoutProperty);
746 builderMeasureBaseHeight_ = builderHeight;
747 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
748 }
749
UpdateLoadingProgressTranslate(float scrollOffset)750 void RefreshPattern::UpdateLoadingProgressTranslate(float scrollOffset)
751 {
752 CHECK_NULL_VOID(progressChild_);
753 auto renderContext = progressChild_->GetRenderContext();
754 CHECK_NULL_VOID(renderContext);
755 auto loadingVisibleHeight = GetLoadingVisibleHeight();
756 if (GreatOrEqual(scrollOffset, loadingVisibleHeight) &&
757 !NearEqual(loadingVisibleHeight, static_cast<float>(GetTriggerRefreshDisTance().ConvertToPx()))) {
758 auto ratio = static_cast<float>(
759 (scrollOffset - loadingVisibleHeight) / (GetTriggerRefreshDisTance().ConvertToPx() - loadingVisibleHeight));
760 renderContext->UpdateOpacity(std::clamp(ratio, 0.0f, 1.0f));
761 renderContext->UpdateTransformTranslate({ 0.0f, (scrollOffset - loadingVisibleHeight) * HALF, 0.0f });
762 if (loadingTextNode_) {
763 UpdateLoadingTextOpacity(std::clamp(ratio, 0.0f, 1.0f));
764 auto loadingTextRenderContext = loadingTextNode_->GetRenderContext();
765 CHECK_NULL_VOID(loadingTextRenderContext);
766 loadingTextRenderContext->UpdateTransformTranslate({ 0.0f,
767 scrollOffset_ - TRIGGER_LOADING_DISTANCE.ConvertToPx() - LOADING_PROGRESS_SIZE.ConvertToPx() -
768 LOADING_TEXT_TOP_MARGIN.ConvertToPx(),
769 0.0f });
770 }
771 } else {
772 renderContext->UpdateOpacity(0.0f);
773 UpdateLoadingTextOpacity(0.0f);
774 }
775 }
776
GetLoadingVisibleHeight()777 float RefreshPattern::GetLoadingVisibleHeight()
778 {
779 float loadingHeight = 0.0f;
780 CHECK_NULL_RETURN(progressChild_, 0.0f);
781 auto renderContext = progressChild_->GetRenderContext();
782 CHECK_NULL_RETURN(renderContext, 0.0f);
783 auto geometryNode = progressChild_->GetGeometryNode();
784 CHECK_NULL_RETURN(geometryNode, 0.0f);
785 if (loadingTextNode_) {
786 auto loadingTextGeometryNode = loadingTextNode_->GetGeometryNode();
787 CHECK_NULL_RETURN(loadingTextGeometryNode, 0.0f);
788 loadingHeight = geometryNode->GetFrameSize().Height() + loadingTextGeometryNode->GetFrameSize().Height() +
789 LOADING_TEXT_TOP_MARGIN.ConvertToPx();
790 } else {
791 loadingHeight = geometryNode->GetFrameSize().Height();
792 }
793 return (HALF + BASE_SCALE * HALF) * loadingHeight;
794 }
795
SpeedTriggerAnimation(float speed)796 void RefreshPattern::SpeedTriggerAnimation(float speed)
797 {
798 auto targetOffset = (isSourceFromAnimation_ ||
799 LessNotEqual(scrollOffset_, refreshOffset_.ConvertToPx()) || !pullToRefresh_)
800 ? 0.0f
801 : refreshOffset_.ConvertToPx();
802 auto dealSpeed = 0.0f;
803 if (!NearEqual(scrollOffset_, targetOffset)) {
804 dealSpeed = speed / (targetOffset - scrollOffset_);
805 }
806 bool recycle = true;
807 if (pullToRefresh_ && !isSourceFromAnimation_ && refreshStatus_ == RefreshStatus::OVER_DRAG) {
808 UpdateRefreshStatus(RefreshStatus::REFRESH);
809 UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_TO_RECYCLE, GetFollowRatio());
810 } else if (NearZero(targetOffset)) {
811 recycle = false;
812 SwitchToFinish();
813 }
814 ResetAnimation();
815 AnimationOption option;
816 auto curve = AceType::MakeRefPtr<InterpolatingSpring>(dealSpeed, 1.0f, 228.0f, 30.0f);
817 option.SetCurve(curve);
818 animation_ = AnimationUtils::StartAnimation(
819 option,
820 [&, weak = AceType::WeakClaim(this)]() {
821 auto pattern = weak.Upgrade();
822 CHECK_NULL_VOID(pattern);
823 pattern->offsetProperty_->Set(targetOffset);
824 },
825 [weak = AceType::WeakClaim(this), recycle]() {
826 auto pattern = weak.Upgrade();
827 CHECK_NULL_VOID(pattern);
828 if (recycle) {
829 pattern->UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, pattern->GetFollowRatio());
830 }
831 });
832 auto context = PipelineContext::GetCurrentContextSafely();
833 CHECK_NULL_VOID(context);
834 context->RequestFrame();
835 }
836
GetTargetOffset()837 float RefreshPattern::GetTargetOffset()
838 {
839 if (isSourceFromAnimation_) {
840 return 0.0f;
841 }
842 auto targetOffset = 0.0f;
843 switch (refreshStatus_) {
844 case RefreshStatus::OVER_DRAG:
845 case RefreshStatus::REFRESH:
846 targetOffset = refreshOffset_.ConvertToPx();
847 break;
848 default:
849 targetOffset = 0.0f;
850 break;
851 }
852 return targetOffset;
853 }
854
SpeedAnimationFinish()855 void RefreshPattern::SpeedAnimationFinish()
856 {
857 if (isRefreshing_) {
858 UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, GetFollowRatio());
859 } else {
860 UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_HAND, GetFollowRatio());
861 }
862 }
863
QuickFirstChildAppear()864 void RefreshPattern::QuickFirstChildAppear()
865 {
866 isSourceFromAnimation_ = false;
867 UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, GetFollowRatio());
868 ResetAnimation();
869 AnimationOption option;
870 option.SetCurve(DEFAULT_CURVE);
871 option.SetDuration(LOADING_ANIMATION_DURATION);
872 animation_ = AnimationUtils::StartAnimation(
873 option, [&]() { offsetProperty_->Set(static_cast<float>(refreshOffset_.ConvertToPx())); });
874 }
875
QuickFirstChildDisappear()876 void RefreshPattern::QuickFirstChildDisappear()
877 {
878 ResetAnimation();
879 AnimationOption option;
880 option.SetCurve(DEFAULT_CURVE);
881 option.SetDuration(LOADING_ANIMATION_DURATION);
882 animation_ = AnimationUtils::StartAnimation(
883 option, [&]() { offsetProperty_->Set(0.0f); },
884 [weak = AceType::WeakClaim(this)]() {
885 auto pattern = weak.Upgrade();
886 CHECK_NULL_VOID(pattern);
887 pattern->SpeedAnimationFinish();
888 });
889 }
890
GetLoadingProgressStatus()891 RefreshAnimationState RefreshPattern::GetLoadingProgressStatus()
892 {
893 auto defaultValue = RefreshAnimationState::FOLLOW_HAND;
894 CHECK_NULL_RETURN(progressChild_, defaultValue);
895 auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
896 CHECK_NULL_RETURN(progressPaintProperty, defaultValue);
897 return progressPaintProperty->GetRefreshAnimationState().value_or(defaultValue);
898 }
899
ResetAnimation()900 void RefreshPattern::ResetAnimation()
901 {
902 float currentOffset = scrollOffset_;
903 AnimationUtils::StopAnimation(animation_);
904 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
905 CHECK_NULL_VOID(offsetProperty_);
906 offsetProperty_->Set(currentOffset);
907 } else {
908 CHECK_NULL_VOID(lowVersionOffset_);
909 lowVersionOffset_->Set(currentOffset);
910 }
911 }
912
UpdateDragFRCSceneInfo(const std::string & scene,float speed,SceneStatus sceneStatus)913 void RefreshPattern::UpdateDragFRCSceneInfo(const std::string& scene, float speed, SceneStatus sceneStatus)
914 {
915 auto host = GetHost();
916 CHECK_NULL_VOID(host);
917 host->AddFRCSceneInfo(scene, std::abs(speed), sceneStatus);
918 }
919
InitLowVersionOffset()920 void RefreshPattern::InitLowVersionOffset()
921 {
922 if (!lowVersionOffset_) {
923 auto propertyCallback = [weak = AceType::WeakClaim(this)](float scrollOffset) {
924 auto pattern = weak.Upgrade();
925 CHECK_NULL_VOID(pattern);
926 pattern->scrollOffset_ = scrollOffset;
927 pattern->UpdateChild();
928 };
929 lowVersionOffset_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
930 auto host = GetHost();
931 CHECK_NULL_VOID(host);
932 auto renderContext = host->GetRenderContext();
933 CHECK_NULL_VOID(renderContext);
934 renderContext->AttachNodeAnimatableProperty(lowVersionOffset_);
935 }
936 }
937
UpdateChild()938 void RefreshPattern::UpdateChild()
939 {
940 if (customBuilder_) {
941 UpdateCustomBuilderProperty();
942 } else {
943 UpdateLoadingProgress();
944 }
945 }
946
HandleDragStartLowVersion()947 void RefreshPattern::HandleDragStartLowVersion()
948 {
949 if (isRefreshing_) {
950 return;
951 }
952 scrollOffset_ = 0.0f;
953 UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_HAND, 0.0f);
954 }
955
HandleDragUpdateLowVersion(float delta)956 void RefreshPattern::HandleDragUpdateLowVersion(float delta)
957 {
958 if (isRefreshing_) {
959 return;
960 }
961 scrollOffset_ = GetScrollOffset(delta);
962 if (LessNotEqual(scrollOffset_, static_cast<float>(GetTriggerRefreshDisTance().ConvertToPx()))) {
963 UpdateRefreshStatus(RefreshStatus::DRAG);
964 } else {
965 UpdateRefreshStatus(RefreshStatus::OVER_DRAG);
966 }
967 if (customBuilder_) {
968 HandleCustomBuilderDragUpdateStage();
969 return;
970 }
971 UpdateLoadingProgress();
972 if (GreatNotEqual(scrollOffset_, triggerLoadingDistance_)) {
973 CHECK_NULL_VOID(progressChild_);
974 auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
975 CHECK_NULL_VOID(progressPaintProperty);
976 float triggerRefreshDistance = GetTriggerRefreshDisTance().ConvertToPx();
977 float ratio =
978 NearEqual(triggerRefreshDistance, triggerLoadingDistance_)
979 ? 1.0f
980 : (scrollOffset_ - triggerLoadingDistance_) / (triggerRefreshDistance - triggerLoadingDistance_);
981 progressPaintProperty->UpdateRefreshSizeScaleRatio(std::clamp(ratio, 0.0f, 1.0f));
982 }
983 }
984
HandleDragEndLowVersion()985 void RefreshPattern::HandleDragEndLowVersion()
986 {
987 if (isRefreshing_) {
988 return;
989 }
990 if (customBuilder_) {
991 HandleCustomBuilderDragEndStage();
992 return;
993 }
994 if (refreshStatus_ == RefreshStatus::OVER_DRAG) {
995 UpdateRefreshStatus(RefreshStatus::REFRESH);
996 LoadingProgressRefreshingAnimation(true);
997 } else {
998 SwitchToFinish();
999 LoadingProgressExit();
1000 }
1001 // AccessibilityEventType::SCROLL_END
1002 }
1003
LoadingProgressRefreshingAnimation(bool isDrag)1004 void RefreshPattern::LoadingProgressRefreshingAnimation(bool isDrag)
1005 {
1006 UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, 1.0f);
1007 ResetAnimation();
1008 CHECK_NULL_VOID(lowVersionOffset_);
1009 AnimationOption option;
1010 if (isDrag) {
1011 option.SetCurve(AceType::MakeRefPtr<SpringCurve>(0.0f, 1.0f, 228.0f, 30.0f));
1012 option.SetDuration(FOLLOW_TO_RECYCLE_DURATION);
1013 } else {
1014 option.SetCurve(DEFAULT_CURVE);
1015 option.SetDuration(LOADING_ANIMATION_DURATION);
1016 }
1017 animation_ = AnimationUtils::StartAnimation(
1018 option, [&]() { lowVersionOffset_->Set(GetTriggerRefreshDisTance().ConvertToPx()); });
1019 }
1020
LoadingProgressExit()1021 void RefreshPattern::LoadingProgressExit()
1022 {
1023 ResetAnimation();
1024 CHECK_NULL_VOID(lowVersionOffset_);
1025 AnimationOption option;
1026 option.SetCurve(DEFAULT_CURVE);
1027 option.SetDuration(LOADING_ANIMATION_DURATION);
1028 animation_ = AnimationUtils::StartAnimation(
1029 option, [&]() { lowVersionOffset_->Set(0.0f); },
1030 [weak = AceType::WeakClaim(this)]() {
1031 auto pattern = weak.Upgrade();
1032 CHECK_NULL_VOID(pattern);
1033 pattern->UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_HAND, 0.0f);
1034 });
1035 }
1036
UpdateLoadingProgress()1037 void RefreshPattern::UpdateLoadingProgress()
1038 {
1039 float loadingProgressOffset =
1040 std::clamp(scrollOffset_, triggerLoadingDistance_, static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx()));
1041 UpdateLoadingMarginTop(loadingProgressOffset);
1042 float triggerRefreshDistance = GetTriggerRefreshDisTance().ConvertToPx();
1043 float ratio = NearEqual(triggerRefreshDistance, triggerLoadingDistance_)
1044 ? 1.0f
1045 : (loadingProgressOffset - triggerLoadingDistance_) /
1046 (GetTriggerRefreshDisTance().ConvertToPx() - triggerLoadingDistance_);
1047 auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
1048 CHECK_NULL_VOID(progressPaintProperty);
1049 progressPaintProperty->UpdateRefreshSizeScaleRatio(ratio);
1050 auto progressContext = progressChild_->GetRenderContext();
1051 CHECK_NULL_VOID(progressContext);
1052 progressContext->UpdateOpacity(std::clamp(ratio, 0.0f, 1.0f));
1053 UpdateLoadingTextOpacity(std::clamp(ratio, 0.0f, 1.0f));
1054 progressChild_->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1055 }
1056
CustomBuilderRefreshingAnimation(bool isDrag)1057 void RefreshPattern::CustomBuilderRefreshingAnimation(bool isDrag)
1058 {
1059 ResetAnimation();
1060 CHECK_NULL_VOID(lowVersionOffset_);
1061 AnimationOption option;
1062 if (isDrag) {
1063 option.SetCurve(AceType::MakeRefPtr<SpringCurve>(0.0f, 1.0f, 228.0f, 30.0f));
1064 option.SetDuration(FOLLOW_TO_RECYCLE_DURATION);
1065 } else {
1066 option.SetCurve(DEFAULT_CURVE);
1067 option.SetDuration(CUSTOM_BUILDER_ANIMATION_DURATION);
1068 }
1069 animation_ = AnimationUtils::StartAnimation(
1070 option, [&]() { lowVersionOffset_->Set(GetTriggerRefreshDisTance().ConvertToPx()); });
1071 }
1072
CustomBuilderExit()1073 void RefreshPattern::CustomBuilderExit()
1074 {
1075 ResetAnimation();
1076 CHECK_NULL_VOID(lowVersionOffset_);
1077 AnimationOption option;
1078 option.SetDuration(CUSTOM_BUILDER_ANIMATION_DURATION);
1079 option.SetCurve(DEFAULT_CURVE);
1080 animation_ = AnimationUtils::StartAnimation(option, [&]() { lowVersionOffset_->Set(0.0f); });
1081 }
1082
UpdateCustomBuilderProperty()1083 void RefreshPattern::UpdateCustomBuilderProperty()
1084 {
1085 auto customBuilderSize = customBuilder_->GetGeometryNode()->GetFrameSize();
1086 auto maxScroll = static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx());
1087 customBuilderOffset_ = std::clamp(scrollOffset_, triggerLoadingDistance_, maxScroll - customBuilderSize.Height());
1088 float triggerRefreshDistance = GetTriggerRefreshDisTance().ConvertToPx();
1089 float ratio = NearEqual(triggerRefreshDistance, triggerLoadingDistance_)
1090 ? 1.0f
1091 : (customBuilderOffset_ - triggerLoadingDistance_) /
1092 (GetTriggerRefreshDisTance().ConvertToPx() - triggerLoadingDistance_);
1093 auto customBuilderContext = customBuilder_->GetRenderContext();
1094 CHECK_NULL_VOID(customBuilderContext);
1095 customBuilderContext->UpdateOpacity(std::clamp(ratio, 0.0f, 1.0f));
1096 auto host = GetHost();
1097 CHECK_NULL_VOID(host);
1098 host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1099 }
1100
HandleCustomBuilderDragUpdateStage()1101 void RefreshPattern::HandleCustomBuilderDragUpdateStage()
1102 {
1103 auto customBuilderSize = customBuilder_->GetGeometryNode()->GetMarginFrameSize();
1104 auto maxScroll = MAX_SCROLL_DISTANCE.ConvertToPx();
1105 if (NearZero(static_cast<double>(customBuilder_->GetGeometryNode()->GetMarginFrameSize().Height()))) {
1106 return;
1107 }
1108 if (LessNotEqual(static_cast<double>(maxScroll - customBuilderSize.Height()),
1109 static_cast<double>(triggerLoadingDistance_))) {
1110 return;
1111 }
1112 UpdateCustomBuilderProperty();
1113 }
1114
HandleCustomBuilderDragEndStage()1115 void RefreshPattern::HandleCustomBuilderDragEndStage()
1116 {
1117 if (refreshStatus_ == RefreshStatus::OVER_DRAG) {
1118 UpdateRefreshStatus(RefreshStatus::REFRESH);
1119 CustomBuilderRefreshingAnimation(true);
1120 } else {
1121 SwitchToFinish();
1122 CustomBuilderExit();
1123 }
1124 }
1125
UpdateLoadingMarginTop(float top)1126 void RefreshPattern::UpdateLoadingMarginTop(float top)
1127 {
1128 CHECK_NULL_VOID(progressChild_);
1129 auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
1130 CHECK_NULL_VOID(progressLayoutProperty);
1131 MarginProperty marginProperty;
1132 marginProperty.left = CalcLength(0.0f);
1133 marginProperty.right = CalcLength(0.0f);
1134 marginProperty.bottom = CalcLength(0.0f);
1135 marginProperty.top = CalcLength(top);
1136 progressLayoutProperty->UpdateMargin(marginProperty);
1137 }
1138
GetScrollOffset(float delta)1139 float RefreshPattern::GetScrollOffset(float delta)
1140 {
1141 auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
1142 CHECK_NULL_RETURN(layoutProperty, 0.0f);
1143 auto frictionRatio = static_cast<float>(layoutProperty->GetFriction().value_or(DEFAULT_FRICTION)) * PERCENT;
1144 auto scrollY = delta * frictionRatio;
1145 return std::clamp(scrollOffset_ + scrollY, 0.0f, static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx()));
1146 }
1147
HandleScroll(float offset,int32_t source,NestedState state,float velocity)1148 ScrollResult RefreshPattern::HandleScroll(float offset, int32_t source, NestedState state, float velocity)
1149 {
1150 ScrollResult result = { offset, true };
1151 auto nestedScroll = GetNestedScroll();
1152 if (NearZero(offset)) {
1153 return result;
1154 }
1155 isSourceFromAnimation_ = (source == SCROLL_FROM_ANIMATION);
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