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