• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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/refresh/render_refresh.h"
17 
18 #include <chrono>
19 #include <ctime>
20 
21 #include "base/i18n/localization.h"
22 #include "base/utils/string_utils.h"
23 #include "base/utils/utils.h"
24 #include "core/animation/curve_animation.h"
25 #include "core/event/ace_event_helper.h"
26 
27 namespace OHOS::Ace {
28 namespace {
29 
30 constexpr int32_t ANIMATION_DURATION = 350; // Default animation duration
31 constexpr int32_t MAX_ALPHA = 255;
32 constexpr int32_t BASE_YEAR = 1900;
33 constexpr double DEFAULT_TIME_BOX_BOTTOM_SIZE = 8.0; // Default box height for time
34 constexpr double PERCENT = 0.01;                     // Percent
35 constexpr double HALF = 0.5;
36 const char REFRESH_LAST_UPDATED[] = "refresh.last_updated"; // I18n for last updated
37 const char LAST_UPDATE_FORMAT[] = "yyyy/M/d HH:mm";         // Date format for last updated
38 const char PULL_DOWN_START[] = "start";                     // Pull-down event, state is start
39 const char PULL_DOWN_END[] = "end";                         // Pull-down event, state is end
40 
41 } // namespace
42 
RenderRefresh()43 RenderRefresh::RenderRefresh()
44 {
45     loadingComponent_ = AceType::MakeRefPtr<LoadingProgressComponent>();
46     loading_ = AceType::DynamicCast<RenderLoadingProgress>(loadingComponent_->CreateRenderNode());
47     loading_->SetLoadingMode(MODE_DRAG);
48 
49     loadingBackgroundBoxComponent_ = AceType::MakeRefPtr<BoxComponent>();
50     loadingBackgroundBoxComponent_->SetEnableDebugBoundary(true);
51 
52     decoration_ = AceType::MakeRefPtr<Decoration>();
53     loadingBackgroundBox_ = AceType::DynamicCast<RenderBox>(loadingBackgroundBoxComponent_->CreateRenderNode());
54 
55     loadingBoxComponent_ = AceType::MakeRefPtr<BoxComponent>();
56     loadingBoxComponent_->SetFlex(BoxFlex::FLEX_X);
57 
58     loadingBox_ = AceType::DynamicCast<RenderBox>(loadingBoxComponent_->CreateRenderNode());
59 
60     loadingBackgroundBox_->AddChild(loading_);
61     loadingBox_->AddChild(loadingBackgroundBox_);
62 
63     timeBoxComponent_ = AceType::MakeRefPtr<BoxComponent>();
64     timeBoxComponent_->SetFlex(BoxFlex::FLEX_X);
65     timeBox_ = AceType::DynamicCast<RenderBox>(timeBoxComponent_->CreateRenderNode());
66 
67     lastTimeText_ = Localization::GetInstance()->GetEntryLetters(REFRESH_LAST_UPDATED);
68     timeText_ = StringUtils::FormatString(lastTimeText_.c_str(), "");
69     timeComponent_ = AceType::MakeRefPtr<TextComponent>(timeText_);
70 
71     displayComponent_ = AceType::MakeRefPtr<DisplayComponent>();
72 
73     display_ = AceType::DynamicCast<RenderDisplay>(displayComponent_->CreateRenderNode());
74     time_ = AceType::DynamicCast<RenderText>(timeComponent_->CreateRenderNode());
75     display_->AddChild(timeBox_);
76     timeBox_->AddChild(time_);
77 }
78 
Update(const RefPtr<Component> & component)79 void RenderRefresh::Update(const RefPtr<Component>& component)
80 {
81     const RefPtr<RefreshComponent> refresh = AceType::DynamicCast<RefreshComponent>(component);
82     if (!refresh) {
83         LOGW("RefreshComponent is null");
84         return;
85     }
86     if (refresh->GetOnStateChange()) {
87         onStateChange_ = *refresh->GetOnStateChange();
88     }
89     if (refresh->GetOnRefreshing()) {
90         onRefreshing_ = *refresh->GetOnRefreshing();
91     }
92 
93     refreshComponent_ = AceType::WeakClaim(AceType::RawPtr(component));
94     refreshing_ = refresh->IsRefreshing();
95     showLastTime_ = refresh->IsShowLastTime();
96     isUseOffset_ = refresh->IsUseOffset();
97     refreshType_ = refresh->GetRefreshType();
98     progressColor_ = refresh->GetProgressColor();
99     backgroundColor_ = refresh->GetBackgroundColor();
100     frictionRatio_ = refresh->GetFriction() * PERCENT;
101     isRefresh_ = refresh->GetIsRefresh();
102     if (!refresh->GetChangeEvent().IsEmpty()) {
103         changeEvent_ =
104             AceAsyncEvent<void(const std::string&)>::Create(refresh->GetChangeEvent(), context_);
105     }
106 
107     loadingComponent_->SetProgressColor(progressColor_);
108     loadingComponent_->SetDiameter(Dimension(GetLoadingDiameter()));
109 
110     // Set the progress background color
111     decoration_->SetBackgroundColor(backgroundColor_);
112 
113     if (refresh->GetTextDirection() == TextDirection::LTR) {
114         SetTextDirection(TextDirection::LTR);
115         time_->SetTextDirection(TextDirection::LTR);
116     } else {
117         SetTextDirection(TextDirection::RTL);
118         time_->SetTextDirection(TextDirection::RTL);
119     }
120 
121     if (!isInitialized_) {
122         loading_->Attach(GetContext());
123         loadingBackgroundBox_->Attach(GetContext());
124         loadingBox_->Attach(GetContext());
125         AddChild(loadingBox_);
126 
127         timeBox_->Attach(GetContext());
128         display_->Attach(GetContext());
129         time_->Attach(GetContext());
130         timeText_ = StringUtils::FormatString(lastTimeText_.c_str(), GetFormatDateTime().c_str());
131         timeComponent_->SetData(timeText_);
132         timeComponent_->SetTextStyle(refresh->GetTextStyle());
133         time_->Update(timeComponent_);
134         display_->Update(displayComponent_);
135         timeBox_->Update(timeBoxComponent_);
136         AddChild(display_);
137 
138         // Just run on the first time
139         isInitialized_ = true;
140     }
141 
142     loading_->Update(loadingComponent_);
143     loadingBackgroundBox_->Update(loadingBackgroundBoxComponent_);
144     loadingBox_->Update(loadingBoxComponent_);
145 
146     refreshEvent_ = AceAsyncEvent<void(const std::string&)>::Create(refresh->GetRefreshEventId(), context_);
147     pullDownEvent_ = AceAsyncEvent<void(const std::string&)>::Create(refresh->GetPulldownEventId(), context_);
148     CalcLoadingParams(component);
149     Initialize();
150     MarkNeedLayout();
151 }
152 
CalcLoadingParams(const RefPtr<Component> & component)153 void RenderRefresh::CalcLoadingParams(const RefPtr<Component>& component)
154 {
155     auto refresh = AceType::DynamicCast<RefreshComponent>(component);
156     if (refresh == nullptr) {
157         LOGW("RefreshComponent is null");
158         return;
159     }
160     auto context = context_.Upgrade();
161     if (context == nullptr) {
162         LOGW("context is nullptr!");
163         return;
164     }
165     scale_ = context->GetDipScale();
166     triggerLoadingDistance_ = NormalizeToPx(refresh->GetLoadingDistance());
167     triggerShowTimeDistance_ = NormalizeToPx(refresh->GetShowTimeDistance());
168     if (showLastTime_) {
169         timeDistance_ = NormalizeToPx(Dimension(DEFAULT_TIME_BOX_BOTTOM_SIZE, DimensionUnit::VP));
170         triggerRefreshDistance_ = triggerShowTimeDistance_;
171     } else {
172         triggerRefreshDistance_ = NormalizeToPx(refresh->GetRefreshDistance());
173         inspectorOffset_ = refresh->GetRefreshDistance();
174     }
175     loadingDiameter_ = NormalizeToPx(refresh->GetProgressDiameter());
176     maxScrollOffset_ = NormalizeToPx(refresh->GetMaxDistance());
177     indicatorOffset_ = NormalizeToPx(refresh->GetIndicatorOffset());
178     timeOffset_ = NormalizeToPx(refresh->GetTimeOffset());
179     loading_->SetDiameter(refresh->GetProgressDiameter());
180     loading_->SetDragRange(triggerLoadingDistance_, triggerRefreshDistance_);
181     loadingBox_->SetHeight(loadingDiameter_);
182     decoration_->SetBorderRadius(Radius(loadingDiameter_ * HALF));
183     loadingBackgroundBox_->SetBackDecoration(decoration_);
184     loadingBackgroundBox_->SetWidth(loadingDiameter_);
185 }
186 
Initialize()187 void RenderRefresh::Initialize()
188 {
189     LOGI("RenderRefresh Initialize state:%{public}d", refreshing_);
190     if (!dragDetector_) {
191         dragDetector_ = AceType::MakeRefPtr<VerticalDragRecognizer>();
192         dragDetector_->SetOnDragUpdate([weakFresh = AceType::WeakClaim(this)](const DragUpdateInfo& info) {
193             auto refresh = weakFresh.Upgrade();
194             if (refresh) {
195                 refresh->HandleDragUpdate(info.GetMainDelta());
196             }
197         });
198         dragDetector_->SetOnDragEnd([weakFresh = AceType::WeakClaim(this)](const DragEndInfo& info) {
199             auto refresh = weakFresh.Upgrade();
200             if (refresh) {
201                 refresh->HandleDragEnd();
202             }
203         });
204 
205         dragDetector_->SetOnDragCancel([weakFresh = AceType::WeakClaim(this)]() {
206             auto refresh = weakFresh.Upgrade();
207             if (refresh) {
208                 refresh->HandleDragCancel();
209             }
210         });
211     }
212     if (!animator_) {
213         animator_ = AceType::MakeRefPtr<Animator>(GetContext());
214     }
215     if (!refreshController_) {
216         refreshController_ = AceType::MakeRefPtr<RefreshController>();
217         refreshController_->SetRefresh(AceType::WeakClaim(this));
218     }
219     InitAccessibilityEventListener();
220 }
221 
InitAccessibilityEventListener()222 void RenderRefresh::InitAccessibilityEventListener()
223 {
224     auto refNode = accessibilityNode_.Upgrade();
225     if (!refNode) {
226         return;
227     }
228     refNode->AddSupportAction(AceAction::ACTION_SCROLL_FORWARD);
229     auto weakPtr = AceType::WeakClaim(this);
230     refNode->SetActionScrollForward([weakPtr]() {
231         auto refresh = weakPtr.Upgrade();
232         if (refresh) {
233             refresh->SetRefreshStatus(true);
234             refresh->MarkNeedLayout();
235         }
236         return true;
237     });
238 }
239 
UpdateTouchRect()240 void RenderRefresh::UpdateTouchRect()
241 {
242     touchRect_.SetSize(viewPort_);
243     touchRect_.SetOffset(GetPosition());
244     touchRectList_.emplace_back(touchRect_);
245     SetTouchRectList(touchRectList_);
246 }
247 
HandleDragUpdate(double delta)248 void RenderRefresh::HandleDragUpdate(double delta)
249 {
250     if (isRefresh_) {
251         return;
252     }
253     LOGD("RenderRefresh HandleDragUpdate delta is %{public}lf, offset is %{public}lf", delta, scrollableOffset_.GetY());
254     if (NearZero(delta)) {
255         LOGD("Delta is near zero!");
256         return;
257     }
258     if (refreshStatus_ == RefreshStatus::REFRESH && delta > 0.0) {
259         LOGD("The refresh status is refreshing!");
260         return;
261     }
262     Offset deltaOffset(0, delta);
263     if (refreshStatus_ == RefreshStatus::DRAG || refreshStatus_ == RefreshStatus::OVER_DRAG ||
264         refreshStatus_ == RefreshStatus::DONE) {
265         deltaOffset.SetY(GetOffset(delta));
266     }
267     scrollableOffset_ += deltaOffset;
268     scrollableOffset_.SetY(std::clamp(scrollableOffset_.GetY(), 0.0, maxScrollOffset_));
269     MarkNeedLayout();
270 }
271 
HandleDragEnd()272 void RenderRefresh::HandleDragEnd()
273 {
274     LOGD("RenderRefresh HandleDragEnd");
275     if (NearEqual(scrollableOffset_.GetY(), 0.0f)) {
276         ResetStatus();
277         return;
278     }
279     if (refreshStatus_ == RefreshStatus::INACTIVE) {
280         return;
281     }
282     if (refreshStatus_ == RefreshStatus::DRAG || refreshStatus_ == RefreshStatus::DONE) {
283         double start = scrollableOffset_.GetY();
284         double end = 0.0;
285         StartAnimation(start, end, false);
286         return;
287     }
288     if (refreshStatus_ == RefreshStatus::OVER_DRAG) {
289         double start = scrollableOffset_.GetY();
290         double end = triggerRefreshDistance_;
291         loading_->SetLoadingMode(MODE_LOOP);
292         StartAnimation(start, end, false);
293     }
294 }
295 
HandleDragCancel()296 void RenderRefresh::HandleDragCancel()
297 {
298     LOGD("RenderRefresh HandleDragCancel");
299     ResetStatus();
300 }
301 
ResetStatus()302 void RenderRefresh::ResetStatus()
303 {
304     refreshing_ = false;
305     if (changeEvent_) {
306         changeEvent_("false");
307     }
308     refreshStatus_ = RefreshStatus::INACTIVE;
309     scrollableOffset_.Reset();
310     loading_->SetLoadingMode(MODE_DRAG);
311     loading_->SetDragDistance(scrollableOffset_.GetY());
312     MarkNeedLayout();
313 }
314 
UpdateScrollOffset(double value)315 void RenderRefresh::UpdateScrollOffset(double value)
316 {
317     scrollableOffset_.SetY(value);
318     MarkNeedLayout();
319 }
320 
FireRefreshEvent() const321 void RenderRefresh::FireRefreshEvent() const
322 {
323     if (refreshEvent_) {
324         LOGI("RefreshEvent, refreshing = %{public}d.", refreshing_);
325         std::string param =
326             std::string(R"("refresh",{"refreshing":)").append(refreshing_ ? "true" : "false").append("},null");
327         refreshEvent_(param);
328     }
329     if (onRefreshing_) {
330         onRefreshing_();
331     }
332 }
333 
FirePullDownEvent(const std::string & state) const334 void RenderRefresh::FirePullDownEvent(const std::string& state) const
335 {
336     if (pullDownEvent_) {
337         LOGI("PullDown Event, state is %{public}s", state.c_str());
338         std::string param = std::string(R"("pulldown",{"state":")").append(state).append("\"},null");
339         pullDownEvent_(param);
340     }
341 }
342 
StartAnimation(double start,double end,bool isFinished)343 void RenderRefresh::StartAnimation(double start, double end, bool isFinished)
344 {
345     animator_->ClearInterpolators();
346     animator_->ClearStopListeners();
347     translate_ = AceType::MakeRefPtr<CurveAnimation<double>>(start, end, Curves::FRICTION);
348     auto weak = AceType::WeakClaim(this);
349     translate_->AddListener(Animation<double>::ValueCallback([weak](double value) {
350         auto scroll = weak.Upgrade();
351         if (scroll) {
352             scroll->UpdateScrollOffset(value);
353         }
354     }));
355     animator_->SetDuration(ANIMATION_DURATION);
356     animator_->AddInterpolator(translate_);
357     animator_->AddStopListener([weak, isFinished]() {
358         auto refresh = weak.Upgrade();
359         if (refresh) {
360             refresh->HandleStopListener(isFinished);
361         }
362     });
363 
364     animator_->Play();
365 }
366 
HandleStopListener(const bool isFinished)367 void RenderRefresh::HandleStopListener(const bool isFinished)
368 {
369     // Update the last loading time
370     if (isFinished) {
371         timeComponent_->SetData(timeText_);
372         time_->Update(timeComponent_);
373         return;
374     }
375 
376     if (NearEqual(scrollableOffset_.GetY(), triggerRefreshDistance_)) {
377         if (refreshing_) {
378             loading_->SetLoadingMode(MODE_LOOP);
379         }
380         refreshing_ = true;
381         if (changeEvent_) {
382             changeEvent_("true");
383         }
384         FireRefreshEvent();
385     } else if (NearEqual(scrollableOffset_.GetY(), 0.0f)) {
386         ResetStatus();
387     } else {
388         loading_->SetLoadingMode(MODE_DRAG);
389     }
390 }
391 
GetNextStatus()392 RefreshStatus RenderRefresh::GetNextStatus()
393 {
394     RefreshStatus nextStatus;
395     auto context = context_.Upgrade();
396     switch (refreshStatus_) {
397         case RefreshStatus::INACTIVE:
398             if (refreshing_) {
399                 StartAnimation(0.0, triggerRefreshDistance_, false);
400                 nextStatus = RefreshStatus::REFRESH;
401                 break;
402             }
403             if (LessOrEqual(scrollableOffset_.GetY(), 0.0)) {
404                 nextStatus = RefreshStatus::INACTIVE;
405                 break;
406             }
407             // No break here, continue next case
408             FirePullDownEvent(PULL_DOWN_START);
409             [[fallthrough]];
410         case RefreshStatus::DRAG:
411             if (LessOrEqual(scrollableOffset_.GetY(), 0.0)) {
412                 FirePullDownEvent(PULL_DOWN_END);
413                 nextStatus = RefreshStatus::INACTIVE;
414             } else if (scrollableOffset_.GetY() < triggerRefreshDistance_) {
415                 nextStatus = RefreshStatus::DRAG;
416             } else {
417                 nextStatus = RefreshStatus::OVER_DRAG;
418             }
419             break;
420         case RefreshStatus::OVER_DRAG:
421             if (!refreshEvent_ && !context->GetIsDeclarative()) {
422                 nextStatus = RefreshStatus::DONE;
423                 break;
424             }
425             if (scrollableOffset_.GetY() > triggerRefreshDistance_) {
426                 nextStatus = RefreshStatus::OVER_DRAG;
427                 break;
428             }
429             // No break here, continue get next status.
430             [[fallthrough]];
431         case RefreshStatus::REFRESH:
432             if (!refreshing_) {
433                 timeText_ = StringUtils::FormatString(lastTimeText_.c_str(), GetFormatDateTime().c_str());
434                 StartAnimation(scrollableOffset_.GetY(), 0.0, true);
435                 nextStatus = RefreshStatus::DONE;
436                 break;
437             }
438             nextStatus = RefreshStatus::REFRESH;
439             break;
440         case RefreshStatus::DONE:
441             if (scrollableOffset_.GetY() > 0.0) {
442                 nextStatus = RefreshStatus::DONE;
443             } else {
444                 FirePullDownEvent(PULL_DOWN_END);
445                 nextStatus = RefreshStatus::INACTIVE;
446             }
447             break;
448         default:
449             nextStatus = RefreshStatus::INACTIVE;
450             break;
451     }
452     if (onStateChange_ && (refreshStatus_ != nextStatus)) {
453         onStateChange_(static_cast<int>(nextStatus));
454     }
455     return nextStatus;
456 }
457 
GetFriction(double percentage) const458 double RenderRefresh::GetFriction(double percentage) const
459 {
460     if (NearEqual(percentage, 1.0)) {
461         return 0.0;
462     } else {
463         return frictionRatio_ * std::pow(1.0 - percentage, SQUARE);
464     }
465 }
466 
GetOffset(double delta) const467 double RenderRefresh::GetOffset(double delta) const
468 {
469     double height = GetLayoutSize().Height();
470     if (!NearZero(height)) {
471         double friction = GetFriction(std::abs(scrollableOffset_.GetY() / height));
472         return friction * delta;
473     }
474     return delta;
475 }
476 
MaxScrollableHeight() const477 double RenderRefresh::MaxScrollableHeight() const
478 {
479     return GetLayoutParam().GetMaxSize().Height();
480 }
481 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)482 void RenderRefresh::OnTouchTestHit(
483     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
484 {
485     if (!dragDetector_ || hasScrollableChild_) {
486         return;
487     }
488     dragDetector_->SetCoordinateOffset(coordinateOffset);
489     result.emplace_back(dragDetector_);
490 }
491 
GetLoadingDiameter() const492 double RenderRefresh::GetLoadingDiameter() const
493 {
494     double diameter = 0.0;
495     if (scrollableOffset_.GetY() < triggerLoadingDistance_) {
496         return diameter;
497     } else if (scrollableOffset_.GetY() < triggerRefreshDistance_) {
498         double maxDistance = triggerRefreshDistance_ - triggerLoadingDistance_;
499         double actualDistance = scrollableOffset_.GetY() - triggerLoadingDistance_;
500 
501         // Get the diameter by actual distance
502         diameter = ((actualDistance * loadingDiameter_ * HALF) / maxDistance) + loadingDiameter_ * HALF;
503     } else {
504         diameter = loadingDiameter_;
505     }
506     return diameter;
507 }
508 
GetLoadingOffset() const509 Offset RenderRefresh::GetLoadingOffset() const
510 {
511     auto pipelineContext = GetContext().Upgrade();
512     if (!pipelineContext) {
513         LOGE("pipelineContext update with nullptr");
514         return Offset::Zero();
515     }
516 
517     if (scrollableOffset_.GetY() < triggerLoadingDistance_) {
518         return Offset::Zero();
519     }
520     if (!isUseOffset_) {
521         return scrollableOffset_ * HALF - Offset(0.0, GetLoadingDiameter() * HALF);
522     }
523     double factor =
524         (scrollableOffset_.GetY() - triggerLoadingDistance_) / (triggerRefreshDistance_ - triggerLoadingDistance_);
525     return Offset(0.0, indicatorOffset_ * factor);
526 }
527 
GetShowTimeOffset() const528 Offset RenderRefresh::GetShowTimeOffset() const
529 {
530     auto pipelineContext = GetContext().Upgrade();
531     if (!pipelineContext) {
532         LOGE("pipelineContext update with nullptr");
533         return Offset::Zero();
534     }
535 
536     double bottomOffset = timeBox_->GetLayoutSize().Height() +
537                           pipelineContext->NormalizeToPx(Dimension(DEFAULT_TIME_BOX_BOTTOM_SIZE, DimensionUnit::VP));
538     return scrollableOffset_ - Offset(0.0, bottomOffset + timeOffset_);
539 }
540 
GetOpacity() const541 double RenderRefresh::GetOpacity() const
542 {
543     double factor = 0.0;
544     if (scrollableOffset_.GetY() < triggerRefreshDistance_ - timeDistance_) {
545         return factor;
546     } else if (scrollableOffset_.GetY() < triggerRefreshDistance_) {
547         double actualDistance = scrollableOffset_.GetY() - triggerRefreshDistance_ + timeDistance_;
548 
549         // Get the factor, timeDistance_ never be zero
550         if (!NearZero(timeDistance_)) {
551             factor = actualDistance / timeDistance_;
552         }
553     } else {
554         factor = 1.0;
555     }
556     return factor;
557 }
558 
GetFormatDateTime()559 std::string RenderRefresh::GetFormatDateTime()
560 {
561     auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
562     auto local = std::localtime(&now);
563     if (local == nullptr) {
564         LOGE("Get localtime failed.");
565         return "";
566     }
567 
568     // This is for i18n date time
569     DateTime dateTime;
570     dateTime.year = static_cast<uint32_t>(local->tm_year + BASE_YEAR);
571     dateTime.month = static_cast<uint32_t>(local->tm_mon);
572     dateTime.day = static_cast<uint32_t>(local->tm_mday);
573     dateTime.hour = static_cast<uint32_t>(local->tm_hour);
574     dateTime.minute = static_cast<uint32_t>(local->tm_min);
575     std::string time = Localization::GetInstance()->FormatDateTime(dateTime, LAST_UPDATE_FORMAT);
576     LOGD("Last update refresh time is %{public}s", time.c_str());
577     return time;
578 }
579 
UpdateScrollableOffset(double delta)580 void RenderRefresh::UpdateScrollableOffset(double delta)
581 {
582     LOGD("Update offset is %{public}lf", delta);
583     if (NearZero(delta)) {
584         LOGW("Delta is near zero!");
585         return;
586     }
587     if (refreshStatus_ == RefreshStatus::REFRESH) {
588         LOGW("The refresh status is refreshing!");
589         return;
590     }
591     Offset offset = Offset(0.0, GetOffset(delta));
592     scrollableOffset_ = scrollableOffset_ + offset;
593     scrollableOffset_.SetY(std::min(scrollableOffset_.GetY(), maxScrollOffset_));
594     MarkNeedLayout();
595 }
596 
OnHiddenChanged(bool hidden)597 void RenderRefresh::OnHiddenChanged(bool hidden)
598 {
599     if (hidden) {
600         return;
601     }
602     ResetStatus();
603 }
604 
PerformLayout()605 void RenderRefresh::PerformLayout()
606 {
607     const auto& children = GetChildren();
608     if (children.empty()) {
609         LOGW("Refresh has no child!");
610         return;
611     }
612     auto context = context_.Upgrade();
613     if (context == nullptr) {
614         LOGW("context is nullptr!");
615         return;
616     }
617 
618     RefreshStatus nextState = GetNextStatus();
619     if (nextState != RefreshStatus::REFRESH && refreshStatus_ == RefreshStatus::REFRESH) {
620         loading_->SetLoadingMode(MODE_EXIT);
621     }
622     refreshStatus_ = nextState;
623     LayoutParam innerLayout = GetLayoutParam();
624     innerLayout.SetMinSize(Size(0.0, 0.0));
625     if (!NearEqual(scale_, context->GetDipScale())) {
626         // Notify loading to updated when window size changed.
627         CalcLoadingParams(refreshComponent_.Upgrade());
628         loading_->Layout(innerLayout);
629     }
630 
631     loading_->SetDragDistance(scrollableOffset_.GetY());
632     loadingBox_->SetPosition(GetLoadingOffset());
633 
634     display_->UpdateOpacity(GetOpacity() * MAX_ALPHA);
635     display_->SetPosition(GetShowTimeOffset());
636     loadingBox_->SetHidden(scrollableOffset_.GetY() < triggerLoadingDistance_);
637     loadingBox_->SetVisible(scrollableOffset_.GetY() > triggerLoadingDistance_);
638 
639     columnChild_ = children.back();
640     columnChild_->Layout(innerLayout);
641     if (refreshType_ == RefreshType::PULL_DOWN && GreatNotEqual(scrollableOffset_.GetY(), 0.0)) {
642         columnChild_->SetPosition(scrollableOffset_);
643     } else {
644         columnChild_->SetPosition(Offset::Zero());
645     }
646 
647     display_->Layout(innerLayout);
648     loadingBox_->Layout(innerLayout);
649     timeBox_->Layout(innerLayout);
650     SetLayoutSize(GetLayoutParam().GetMaxSize());
651 }
652 
653 } // namespace OHOS::Ace
654