• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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/button/render_button.h"
17 
18 #include "base/log/event_report.h"
19 #include "base/log/log.h"
20 #include "base/memory/ace_type.h"
21 #include "core/accessibility/accessibility_node.h"
22 #include "core/animation/keyframe_animation.h"
23 #include "core/common/frontend.h"
24 #include "core/components/button/button_theme.h"
25 #include "core/components/common/properties/alignment.h"
26 #include "core/components/theme/theme_manager.h"
27 #include "core/event/ace_event_helper.h"
28 
29 namespace OHOS::Ace {
30 namespace {
31 
32 // Watch button definitions
33 constexpr Dimension TEXT_BUTTON_MAX_WIDTH = 116.5_vp;
34 
35 // Download button definitions
36 constexpr double DOWNLOAD_FULL_PERCENT = 100.0;
37 constexpr double MAX_TRANSITION_TIME = 5000.0;
38 constexpr double MIN_TRANSITION_TIME = 200.0;
39 constexpr double MILLISECOND_PER_PERCENT = 20.0;
40 constexpr double SECOND_TO_MILLISECOND = 1000.0;
41 
42 // Definition for animation
43 constexpr double TV_EXPAND_SCALE = 1.05;
44 constexpr double TV_REDUCE_SCALE = 0.95;
45 constexpr double WATCH_SCALE = 0.8;
46 constexpr double MASKING_ANIMATION_RATIO = 10.0;
47 constexpr float KEY_TIME_START = 0.0f;
48 constexpr float KEY_TIME_MID = 0.5f;
49 constexpr float KEY_TIME_END = 1.0f;
50 constexpr float PRESS_UP_OPACITY = 1.0f;
51 constexpr float PRESS_DOWN_OPACITY = 0.95f;
52 constexpr int32_t WATCH_DURATION_DOWN = 200;
53 constexpr int32_t WATCH_DURATION_UP = 250;
54 constexpr int32_t TV_CLICK_DURATION = 200;
55 constexpr int32_t TV_FOCUS_SCALE_DURATION = 100;
56 constexpr int32_t HOVER_ANIMATION_DURATION = 250;
57 constexpr int32_t PRESS_ANIMATION_DURATION = 100;
58 
59 } // namespace
60 
RenderButton()61 RenderButton::RenderButton()
62 {
63     Initialize();
64 }
65 
Initialize()66 void RenderButton::Initialize()
67 {
68     auto wp = AceType::WeakClaim(this);
69     touchRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
70     touchRecognizer_->SetOnTouchDown([wp](const TouchEventInfo&) {
71         auto button = wp.Upgrade();
72         if (button) {
73             button->HandleTouchEvent(true);
74         }
75     });
76     touchRecognizer_->SetOnTouchUp([wp](const TouchEventInfo&) {
77         auto button = wp.Upgrade();
78         if (button) {
79             button->HandleTouchEvent(false);
80         }
81     });
82     touchRecognizer_->SetOnTouchCancel([wp](const TouchEventInfo&) {
83         auto button = wp.Upgrade();
84         if (button) {
85             button->HandleTouchEvent(false);
86         }
87     });
88     touchRecognizer_->SetOnTouchMove([wp](const TouchEventInfo& info) {
89         auto button = wp.Upgrade();
90         if (button) {
91             button->HandleMoveEvent(info);
92         }
93     });
94 
95     clickRecognizer_ = AceType::MakeRefPtr<ClickRecognizer>();
96     clickRecognizer_->SetOnClick([wp](const ClickInfo& info) {
97         auto button = wp.Upgrade();
98         if (button) {
99             const auto context = button->GetContext().Upgrade();
100             if (context && context->GetIsDeclarative()) {
101                 button->HandleClickEvent(info);
102             } else {
103                 button->HandleClickEvent();
104             }
105         }
106     });
107     clickRecognizer_->SetRemoteMessage([wp](const ClickInfo& info) {
108         auto button = wp.Upgrade();
109         if (button) {
110             const auto context = button->GetContext().Upgrade();
111             if (context && context->GetIsDeclarative()) {
112                 button->HandleRemoteMessageEvent(info);
113             } else {
114                 button->HandleRemoteMessageEvent();
115             }
116         }
117     });
118 }
119 
InitAccessibilityEventListener()120 void RenderButton::InitAccessibilityEventListener()
121 {
122     auto refNode = accessibilityNode_.Upgrade();
123     if (!refNode) {
124         return;
125     }
126     refNode->AddSupportAction(AceAction::ACTION_ACCESSIBILITY_FOCUS);
127 
128     auto weakPtr = AceType::WeakClaim(this);
129     refNode->SetActionFocusImpl([weakPtr]() {
130         auto button = weakPtr.Upgrade();
131         if (button) {
132             button->HandleFocusEvent(true);
133         }
134     });
135 }
136 
OnPaintFinish()137 void RenderButton::OnPaintFinish()
138 {
139     if (isFocus_) {
140         if (isTv_) {
141             DisplayFocusAnimation();
142         }
143         if (isPhone_) {
144             UpdateFocusAnimation(INIT_SCALE);
145         }
146     }
147     UpdateAccessibility();
148     InitAccessibilityEventListener();
149 }
150 
UpdateAccessibility()151 void RenderButton::UpdateAccessibility()
152 {
153     const auto& context = context_.Upgrade();
154     if (!context) {
155         return;
156     }
157     auto viewScale = context->GetViewScale();
158     if (NearZero(viewScale)) {
159         return;
160     }
161     auto accessibilityNode = GetAccessibilityNode().Upgrade();
162     if (!accessibilityNode) {
163         return;
164     }
165 #if defined(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
166     Offset globalOffset = GetGlobalOffset();
167     if (isTv_ && isFocus_) {
168         Size size = GetLayoutSize();
169         Offset scaleCenter =
170             Offset(globalOffset.GetX() + size.Width() / 2.0, globalOffset.GetY() + size.Height() / 2.0);
171         accessibilityNode->SetScaleCenter(scaleCenter);
172         accessibilityNode->SetScale(TV_EXPAND_SCALE);
173     }
174 #endif
175     accessibilityNode->SetMarginSize(Size());
176     if (!GetAccessibilityText().empty()) {
177         accessibilityNode->SetAccessibilityLabel(GetAccessibilityText());
178     }
179 }
180 
HandleTouchEvent(bool isTouch)181 void RenderButton::HandleTouchEvent(bool isTouch)
182 {
183     isClicked_ = isTouch;
184     if (isClicked_) {
185         OnStatusStyleChanged(VisualState::PRESSED);
186         isMoveEventValid_ = true;
187     } else {
188         OnStatusStyleChanged(VisualState::NORMAL);
189     }
190     if (isMoveEventValid_ || isWatch_) {
191         PlayTouchAnimation();
192     }
193 }
194 
HandleMoveEvent(const TouchEventInfo & info)195 void RenderButton::HandleMoveEvent(const TouchEventInfo& info)
196 {
197     if (!isMoveEventValid_) {
198         return;
199     }
200     if (info.GetTouches().empty()) {
201         return;
202     }
203     const auto& locationInfo = info.GetTouches().front();
204     double moveDeltaX = locationInfo.GetLocalLocation().GetX();
205     double moveDeltaY = locationInfo.GetLocalLocation().GetY();
206     if ((moveDeltaX < 0 || moveDeltaX > buttonSize_.Width()) || (moveDeltaY < 0 || moveDeltaY > buttonSize_.Height())) {
207         isClicked_ = false;
208         MarkNeedRender();
209         OnStatusStyleChanged(VisualState::NORMAL);
210         isMoveEventValid_ = false;
211     }
212 }
213 
HandleClickEvent(const ClickInfo & info)214 void RenderButton::HandleClickEvent(const ClickInfo& info)
215 {
216     if (!buttonComponent_) {
217         return;
218     }
219     auto onClickWithInfo =
220         AceAsyncEvent<void(const ClickInfo&)>::Create(buttonComponent_->GetClickedEventId(), context_);
221     if (onClickWithInfo) {
222         onClickWithInfo(info);
223     }
224     PlayClickAnimation();
225 }
226 
HandleClickEvent()227 void RenderButton::HandleClickEvent()
228 {
229     if (!buttonComponent_) {
230         return;
231     }
232     auto onClick = AceAsyncEvent<void()>::Create(buttonComponent_->GetClickedEventId(), context_);
233     if (onClick) {
234         onClick();
235     }
236     PlayClickAnimation();
237 }
238 
HandleKeyEnterEvent(const ClickInfo & info)239 void RenderButton::HandleKeyEnterEvent(const ClickInfo& info)
240 {
241     if (!buttonComponent_) {
242         return;
243     }
244     auto onEnterWithInfo =
245         AceAsyncEvent<void(const ClickInfo&)>::Create(buttonComponent_->GetKeyEnterEventId(), context_);
246     if (onEnterWithInfo) {
247         onEnterWithInfo(info);
248     }
249 }
250 
HandleKeyEnterEvent()251 void RenderButton::HandleKeyEnterEvent()
252 {
253     if (!buttonComponent_) {
254         return;
255     }
256     auto onEnter = AceAsyncEvent<void()>::Create(buttonComponent_->GetKeyEnterEventId(), context_);
257     if (onEnter) {
258         onEnter();
259     }
260 }
261 
HandleRemoteMessageEvent(const ClickInfo & info)262 void RenderButton::HandleRemoteMessageEvent(const ClickInfo& info)
263 {
264     if (!buttonComponent_) {
265         return;
266     }
267     auto onRemoteMessagekWithInfo =
268         AceAsyncEvent<void(const ClickInfo&)>::Create(buttonComponent_->GetRemoteMessageEventId(), context_);
269     if (onRemoteMessagekWithInfo) {
270         onRemoteMessagekWithInfo(info);
271     }
272     PlayClickAnimation();
273 }
274 
HandleRemoteMessageEvent()275 void RenderButton::HandleRemoteMessageEvent()
276 {
277     if (!buttonComponent_) {
278         return;
279     }
280     auto onRemoteMessage = AceAsyncEvent<void()>::Create(buttonComponent_->GetRemoteMessageEventId(), context_);
281     if (onRemoteMessage) {
282         onRemoteMessage();
283     }
284     PlayClickAnimation();
285 }
286 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)287 void RenderButton::OnTouchTestHit(
288     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
289 {
290     if ((!touchRecognizer_) || (!clickRecognizer_)) {
291         return;
292     }
293     touchRecognizer_->SetCoordinateOffset(coordinateOffset);
294     result.emplace_back(touchRecognizer_);
295     result.emplace_back(clickRecognizer_);
296 }
297 
HandleFocusEvent(bool isFocus)298 void RenderButton::HandleFocusEvent(bool isFocus)
299 {
300     isFocus_ = isFocus;
301     needFocusColor_ = isFocus_ && isTv_;
302     MarkNeedRender();
303 }
304 
DisplayFocusAnimation()305 void RenderButton::DisplayFocusAnimation()
306 {
307     if (!animationRunning_ && isTv_) {
308         UpdateAnimationParam(TV_EXPAND_SCALE);
309     }
310 }
311 
CheckHoverNode()312 WeakPtr<RenderNode> RenderButton::CheckHoverNode()
313 {
314     return AceType::WeakClaim<RenderNode>(this);
315 }
316 
AnimateMouseHoverEnter()317 void RenderButton::AnimateMouseHoverEnter()
318 {
319     OnMouseHoverEnterTest();
320 }
OnMouseHoverEnterTest()321 void RenderButton::OnMouseHoverEnterTest()
322 {
323     if (!buttonComponent_) {
324         return;
325     }
326     ButtonType type = buttonComponent_->GetType();
327     if (isPhone_ && ((type == ButtonType::TEXT) || (type == ButtonType::NORMAL))) {
328         needHoverColor_ = true;
329         MarkNeedRender();
330     } else {
331         ResetController(hoverControllerExit_);
332         if (!hoverControllerEnter_) {
333             hoverControllerEnter_ = AceType::MakeRefPtr<Animator>(context_);
334         }
335         scaleAnimationEnter_ = AceType::MakeRefPtr<KeyframeAnimation<float>>();
336         CreateFloatAnimation(scaleAnimationEnter_, 1.0, 1.05);
337         hoverControllerEnter_->AddInterpolator(scaleAnimationEnter_);
338         hoverControllerEnter_->SetDuration(HOVER_ANIMATION_DURATION);
339         hoverControllerEnter_->Play();
340         hoverControllerEnter_->SetFillMode(FillMode::FORWARDS);
341     }
342 }
343 
AnimateMouseHoverExit()344 void RenderButton::AnimateMouseHoverExit()
345 {
346     OnMouseHoverExitTest();
347 }
OnMouseHoverExitTest()348 void RenderButton::OnMouseHoverExitTest()
349 {
350     if (needHoverColor_) {
351         needHoverColor_ = false;
352         MarkNeedRender();
353     } else {
354         ResetController(hoverControllerEnter_);
355         if (!hoverControllerExit_) {
356             hoverControllerExit_ = AceType::MakeRefPtr<Animator>(context_);
357         }
358         scaleAnimationExit_ = AceType::MakeRefPtr<KeyframeAnimation<float>>();
359         auto begin = scale_;
360         CreateFloatAnimation(scaleAnimationExit_, begin, 1.0);
361         hoverControllerExit_->AddInterpolator(scaleAnimationExit_);
362         hoverControllerExit_->SetDuration(HOVER_ANIMATION_DURATION);
363         hoverControllerExit_->Play();
364         hoverControllerExit_->SetFillMode(FillMode::FORWARDS);
365     }
366 }
367 
OnMouseClickDownAnimation()368 void RenderButton::OnMouseClickDownAnimation()
369 {
370     if (!needHoverColor_) {
371         ResetController(clickControllerUp_);
372         if (!clickControllerDown_) {
373             clickControllerDown_ = AceType::MakeRefPtr<Animator>(context_);
374         }
375         scaleAnimationDown_ = AceType::MakeRefPtr<KeyframeAnimation<float>>();
376         auto begin = scale_;
377         CreateFloatAnimation(scaleAnimationDown_, begin, 1.0);
378         clickControllerDown_->AddInterpolator(scaleAnimationDown_);
379         clickControllerDown_->SetDuration(HOVER_ANIMATION_DURATION);
380         clickControllerDown_->Play();
381         clickControllerDown_->SetFillMode(FillMode::FORWARDS);
382     }
383 }
384 
OnMouseClickUpAnimation()385 void RenderButton::OnMouseClickUpAnimation()
386 {
387     if (!needHoverColor_) {
388         ResetController(clickControllerDown_);
389         if (!clickControllerUp_) {
390             clickControllerUp_ = AceType::MakeRefPtr<Animator>(context_);
391         }
392         scaleAnimationUp_ = AceType::MakeRefPtr<KeyframeAnimation<float>>();
393         auto begin = scale_;
394         CreateFloatAnimation(scaleAnimationUp_, begin, 1.05);
395         clickControllerUp_->AddInterpolator(scaleAnimationUp_);
396         clickControllerUp_->SetDuration(HOVER_ANIMATION_DURATION);
397         clickControllerUp_->Play();
398         clickControllerUp_->SetFillMode(FillMode::FORWARDS);
399     }
400 }
401 
CreateFloatAnimation(RefPtr<KeyframeAnimation<float>> & floatAnimation,float beginValue,float endValue)402 void RenderButton::CreateFloatAnimation(
403     RefPtr<KeyframeAnimation<float>>& floatAnimation, float beginValue, float endValue)
404 {
405     if (!floatAnimation) {
406         return;
407     }
408     auto keyframeBegin = AceType::MakeRefPtr<Keyframe<float>>(0.0, beginValue);
409     auto keyframeEnd = AceType::MakeRefPtr<Keyframe<float>>(1.0, endValue);
410     floatAnimation->AddKeyframe(keyframeBegin);
411     floatAnimation->AddKeyframe(keyframeEnd);
412     floatAnimation->AddListener([weakButton = AceType::WeakClaim(this)](float value) {
413         auto button = weakButton.Upgrade();
414         if (button) {
415             button->isHover_ = true;
416             button->scale_ = value;
417             button->MarkNeedRender();
418         }
419     });
420 }
421 
ResetController(RefPtr<Animator> & controller)422 void RenderButton::ResetController(RefPtr<Animator>& controller)
423 {
424     if (controller) {
425         if (!controller->IsStopped()) {
426             controller->Stop();
427         }
428         controller->ClearInterpolators();
429     }
430 }
431 
Update(const RefPtr<Component> & component)432 void RenderButton::Update(const RefPtr<Component>& component)
433 {
434     const RefPtr<ButtonComponent> button = AceType::DynamicCast<ButtonComponent>(component);
435     if (!button) {
436         LOGE("Update error, button component is null");
437         EventReport::SendRenderException(RenderExcepType::RENDER_COMPONENT_ERR);
438         return;
439     }
440     buttonComponent_ = button;
441     if (!controller_) {
442         controller_ = AceType::MakeRefPtr<Animator>(GetContext());
443     }
444     auto theme = GetTheme<ButtonTheme>();
445     if (theme) {
446         defaultClickedColor_ = theme->GetClickedColor();
447     }
448 
449     width_ = buttonComponent_->GetWidth();
450     height_ = buttonComponent_->GetHeight();
451     layoutFlag_ = button->GetLayoutFlag();
452     // No animation happens on first setting, will animate from background color on click
453     clickedColor_ = AnimatableColor(button->GetBackgroundColor());
454     backgroundColor_.SetValue(button->GetBackgroundColor().GetValue());
455     stateEffect_ = button->GetStateEffect();
456     isWatch_ = (SystemProperties::GetDeviceType() == DeviceType::WATCH);
457     isTv_ = (SystemProperties::GetDeviceType() == DeviceType::TV);
458     isPhone_ = (SystemProperties::GetDeviceType() == DeviceType::PHONE);
459     auto catchMode =
460         buttonComponent_->GetClickedEventId().IsEmpty() || buttonComponent_->GetClickedEventId().GetCatchMode();
461     static const int32_t bubbleModeVersion = 6;
462     auto pipeline = context_.Upgrade();
463     if (!catchMode) {
464         if (pipeline && pipeline->GetMinPlatformVersion() >= bubbleModeVersion) {
465             catchMode = false;
466         } else {
467             catchMode = true;
468         }
469     }
470     auto catchModeButton = buttonComponent_->GetCatchMode();
471     clickRecognizer_->SetUseCatchMode(catchMode && catchModeButton);
472     SetAccessibilityText(button->GetAccessibilityText());
473     UpdateDownloadStyles(button);
474 
475     OnStatusStyleChanged(disabled_ ? VisualState::DISABLED : VisualState::NORMAL);
476     MarkNeedLayout();
477 }
478 
PerformLayout()479 void RenderButton::PerformLayout()
480 {
481     if (!buttonComponent_) {
482         LOGE("Fail to perform layout due to buttonComponent is null");
483         return;
484     }
485     minWidth_ = buttonComponent_->GetMinWidth();
486     type_ = buttonComponent_->GetType();
487     widthDefined_ = GreatOrEqual(buttonComponent_->GetWidth().Value(), 0.0);
488     heightDefined_ = GreatOrEqual(buttonComponent_->GetHeight().Value(), 0.0);
489     if (type_ == ButtonType::ARC) {
490         width_ = ARC_BUTTON_WIDTH;
491         height_ = ARC_BUTTON_HEIGHT;
492     }
493     buttonSize_ = Size(NormalizePercentToPx(width_, false), NormalizePercentToPx(height_, true));
494     rrectRadius_ = NormalizeToPx(buttonComponent_->GetRectRadius());
495     layoutSize_ = Measure();
496     SetChildrenLayoutSize();
497     SetLayoutSize(CalculateLayoutSize());
498     SetChildrenAlignment();
499     buttonSize_ = GetLayoutSize() - Size(widthDelta_, widthDelta_);
500 }
501 
SetChildrenLayoutSize()502 void RenderButton::SetChildrenLayoutSize()
503 {
504     LayoutParam innerLayoutParam;
505     bool isWatchText = (isWatch_ && (type_ == ButtonType::TEXT));
506     double maxWidth = buttonSize_.Width();
507     if (NearEqual(buttonSize_.Width(), 0.0)) {
508         maxWidth = isWatchText ? NormalizeToPx(TEXT_BUTTON_MAX_WIDTH) : GetLayoutParam().GetMaxSize().Width();
509         maxWidth -= widthDelta_;
510     }
511     double height = buttonSize_.Height();
512     if (buttonComponent_->GetDeclarativeFlag()) {
513         if (!heightDefined_ && type_ == ButtonType::NORMAL) {
514             height = GetLayoutParam().GetMaxSize().Height();
515         }
516     }
517     innerLayoutParam.SetMaxSize(Size(maxWidth, height));
518     if (GetChildren().empty()) {
519         childrenSize_ = Size();
520     }
521     for (const auto& child : GetChildren()) {
522         child->Layout(innerLayoutParam);
523         childrenSize_.SetWidth(child->GetLayoutSize().Width());
524         childrenSize_.SetHeight(child->GetLayoutSize().Height());
525     }
526 }
527 
CalculateLayoutSize()528 Size RenderButton::CalculateLayoutSize()
529 {
530     Size layoutSize;
531     if (NeedAdaptiveChild()) {
532         double layoutWidth = widthDefined_ ? layoutSize_.Width() : childrenSize_.Width();
533         double layoutHeight = heightDefined_ ? layoutSize_.Height() : childrenSize_.Height();
534         layoutSize = Size(layoutWidth, layoutHeight);
535     } else {
536         if (NearEqual(buttonSize_.Width(), 0.0)) {
537             double width =
538                 (childrenSize_.Width() > NormalizeToPx(minWidth_)) ? childrenSize_.Width() : NormalizeToPx(minWidth_);
539             layoutSize = Size(width, buttonSize_.Height()) + Size(widthDelta_, widthDelta_);
540         } else {
541             layoutSize = layoutSize_;
542         }
543     }
544     if (NeedConstrain()) {
545         layoutSize = GetLayoutParam().Constrain(layoutSize);
546     }
547     return layoutSize;
548 }
549 
NeedAdaptiveChild()550 bool RenderButton::NeedAdaptiveChild()
551 {
552     if ((type_ == ButtonType::TEXT) && isWatch_) {
553         return true;
554     }
555     if ((type_ == ButtonType::ICON) || (type_ == ButtonType::NORMAL)) {
556         return true;
557     }
558     return false;
559 }
560 
NeedConstrain()561 bool RenderButton::NeedConstrain()
562 {
563     if (type_ == ButtonType::CIRCLE) {
564         return false;
565     }
566     if (isWatch_) {
567         if ((type_ == ButtonType::DOWNLOAD) || (type_ == ButtonType::ARC)) {
568             return false;
569         }
570     }
571     return true;
572 }
573 
SetChildrenAlignment()574 void RenderButton::SetChildrenAlignment()
575 {
576     if (GetChildren().empty()) {
577         return;
578     }
579     auto& child = GetChildren().front();
580     Alignment alignment = (type_ == ButtonType::ARC) ? Alignment::TOP_CENTER : Alignment::CENTER;
581     child->SetPosition(Alignment::GetAlignPosition(GetLayoutSize(), child->GetLayoutSize(), alignment));
582 }
583 
SetProgress(uint32_t progress)584 void RenderButton::SetProgress(uint32_t progress)
585 {
586     if (isWatch_) {
587         if (progress >= static_cast<uint32_t>(round(DOWNLOAD_FULL_PERCENT))) {
588             progressDisplay_ = false;
589             return;
590         }
591         progressDisplay_ = progress > 0 ? true : false;
592         progressPercent_ = progress / DOWNLOAD_FULL_PERCENT;
593         progressWidth_ = buttonSize_.Width() * progressPercent_;
594         MarkNeedRender();
595         return;
596     }
597     needUpdateAnimation_ = false;
598     if (!NearEqual(progress, previousValue_)) {
599         animationDuring_ = std::chrono::steady_clock::now() - previousUpdateTime_;
600         percentChange_ = progress - previousValue_;
601         previousValue_ = progress;
602         previousUpdateTime_ = std::chrono::steady_clock::now();
603         needUpdateAnimation_ = true;
604     }
605     UpdateProgressAnimation();
606     MarkNeedLayout();
607 }
608 
UpdateDownloadStyles(const RefPtr<ButtonComponent> & button)609 void RenderButton::UpdateDownloadStyles(const RefPtr<ButtonComponent>& button)
610 {
611     if (button->GetType() != ButtonType::DOWNLOAD) {
612         return;
613     }
614     auto pipelineContext = GetContext().Upgrade();
615     if (pipelineContext) {
616         if (!progressController_) {
617             progressController_ = AceType::MakeRefPtr<Animator>(pipelineContext);
618         }
619     }
620     progressColor_ = button->GetProgressColor();
621     progressFocusColor_ = button->GetProgressFocusColor();
622     progressDiameter_ = NormalizeToPx(button->GetProgressDiameter());
623     const auto& buttonController = button->GetButtonController();
624     if (buttonController) {
625         buttonController->SetProgressCallback([weak = AceType::WeakClaim(this)](uint32_t progress) {
626             auto renderButton = weak.Upgrade();
627             if (renderButton) {
628                 renderButton->SetProgress(progress);
629             }
630         });
631     }
632 }
633 
UpdateProgressAnimation()634 void RenderButton::UpdateProgressAnimation()
635 {
636     if (!needUpdateAnimation_) {
637         return;
638     }
639     auto animation = AceType::MakeRefPtr<CurveAnimation<double>>(progressPercent_, previousValue_, Curves::EASE_OUT);
640     animation->AddListener([this](const double& value) {
641         progressDisplay_ = GreatNotEqual(value, 0.0) ? true : false;
642         progressPercent_ = value;
643         progressWidth_ = buttonSize_.Width() * progressPercent_ / DOWNLOAD_FULL_PERCENT;
644         if (GreatOrEqual(progressPercent_, DOWNLOAD_FULL_PERCENT)) {
645             progressDisplay_ = false;
646         }
647         MarkNeedRender();
648     });
649 
650     if (!progressController_) {
651         return;
652     }
653     double change = percentChange_ * MILLISECOND_PER_PERCENT;
654     double during = animationDuring_.count() * SECOND_TO_MILLISECOND;
655     double duration = GreatNotEqual(std::abs(change), during) ? std::abs(change) : during;
656     if (LessNotEqual(duration, MIN_TRANSITION_TIME) || (previousValue_ == DOWNLOAD_FULL_PERCENT)) {
657         duration = MIN_TRANSITION_TIME;
658     }
659     if (GreatNotEqual(duration, MAX_TRANSITION_TIME)) {
660         duration = MAX_TRANSITION_TIME;
661     }
662     progressController_->ClearInterpolators();
663     progressController_->AddInterpolator(animation);
664     progressController_->SetDuration(static_cast<int32_t>(round(duration)));
665     progressController_->SetIteration(1);
666     progressController_->Stop();
667     progressController_->Play();
668 }
669 
UpdateAnimationParam(double value)670 void RenderButton::UpdateAnimationParam(double value)
671 {
672     UpdateFocusAnimation(value);
673     if (!animationRunning_) {
674         return;
675     }
676 
677     if (isWatch_ || isTv_) {
678         scale_ = value;
679     }
680     if (isOpacityAnimation_) {
681         opacity_ = fabs((value - INIT_SCALE) * ratio_);
682     }
683     if (isTouchAnimation_) {
684         maskingOpacity_ = fabs((value - INIT_SCALE) * ratio_ / MASKING_ANIMATION_RATIO);
685     }
686     if (!valueChanged_ && (!NearEqual(value, startValue_))) {
687         valueChanged_ = true;
688     }
689     if ((!isClickAnimation_ && NearEqual(value, endValue_)) || (valueChanged_ && NearEqual(value, startValue_))) {
690         isLastFrame_ = true;
691         valueChanged_ = false;
692         isClickAnimation_ = false;
693     }
694     MarkNeedRender();
695 }
696 
UpdateFocusAnimation(double value)697 void RenderButton::UpdateFocusAnimation(double value)
698 {
699     auto context = context_.Upgrade();
700     if (!context || !isFocus_) {
701         return;
702     }
703     Size sizeDelta = buttonSize_ * (value - INIT_SCALE);
704     Size layoutSize = GetLayoutSize() + sizeDelta;
705     Offset buttonOffset = Offset(sizeDelta.Width() / 2.0, sizeDelta.Height() / 2.0);
706     double radius = rrectRadius_;
707     if (!buttonComponent_) {
708         return;
709     }
710     ButtonType type = buttonComponent_->GetType();
711     if ((type == ButtonType::CIRCLE) || (type == ButtonType::CAPSULE)) {
712         radius = layoutSize.Height() / 2.0;
713     }
714     if (isPhone_ && ((type == ButtonType::TEXT) || (type == ButtonType::NORMAL))) {
715         context->ShowFocusAnimation(RRect::MakeRRect(Rect(Offset(0, 0), layoutSize), Radius(radius)),
716             buttonComponent_->GetFocusAnimationColor(), GetGlobalOffset() - buttonOffset, true);
717         return;
718     }
719     context->ShowFocusAnimation(RRect::MakeRRect(Rect(Offset(0, 0), layoutSize), Radius(radius)),
720         buttonComponent_->GetFocusAnimationColor(), GetGlobalOffset() - buttonOffset);
721     context->ShowShadow(
722         RRect::MakeRRect(Rect(Offset(0, 0), layoutSize), Radius(radius)), GetGlobalOffset() - buttonOffset);
723 }
724 
PlayAnimation(double start,double end,int32_t duration,const FillMode & fillMode)725 void RenderButton::PlayAnimation(double start, double end, int32_t duration, const FillMode& fillMode)
726 {
727     animationRunning_ = true;
728     startValue_ = start;
729     endValue_ = end;
730     if (!NearEqual(start, end)) {
731         ratio_ = INIT_SCALE / (end - start);
732     }
733     auto animation = AceType::MakeRefPtr<CurveAnimation<double>>(start, end, Curves::FRICTION);
734     animation->AddListener(Animation<double>::ValueCallback([weak = AceType::WeakClaim(this)](double value) {
735         auto button = weak.Upgrade();
736         if (button) {
737             button->UpdateAnimationParam(value);
738         }
739     }));
740     if (!controller_) {
741         return;
742     }
743     controller_->ClearInterpolators();
744     controller_->SetDuration(duration);
745     controller_->SetFillMode(fillMode);
746     controller_->AddInterpolator(animation);
747     controller_->Play();
748 }
749 
PlayTouchAnimation()750 void RenderButton::PlayTouchAnimation()
751 {
752     if (isWatch_) {
753         isTouchAnimation_ = true;
754         if (isClicked_) {
755             PlayAnimation(INIT_SCALE, WATCH_SCALE, WATCH_DURATION_DOWN);
756         } else {
757             PlayAnimation(WATCH_SCALE, INIT_SCALE, WATCH_DURATION_UP);
758         }
759     } else {
760         isTouchAnimation_ = true;
761         if (isClicked_) {
762             PlayAnimation(PRESS_UP_OPACITY, PRESS_DOWN_OPACITY, PRESS_ANIMATION_DURATION);
763         } else {
764             PlayAnimation(PRESS_DOWN_OPACITY, PRESS_UP_OPACITY, PRESS_ANIMATION_DURATION);
765         }
766     }
767 }
768 
PlayClickScaleAnimation(float keyTime,int32_t duration)769 void RenderButton::PlayClickScaleAnimation(float keyTime, int32_t duration)
770 {
771     auto startFrame = AceType::MakeRefPtr<Keyframe<double>>(KEY_TIME_START, TV_EXPAND_SCALE);
772     auto midFrame = AceType::MakeRefPtr<Keyframe<double>>(keyTime, TV_REDUCE_SCALE);
773     auto endFrame = AceType::MakeRefPtr<Keyframe<double>>(KEY_TIME_END, TV_EXPAND_SCALE);
774     midFrame->SetCurve(Curves::FRICTION);
775     endFrame->SetCurve(Curves::FRICTION);
776     auto animation = AceType::MakeRefPtr<KeyframeAnimation<double>>();
777     animation->AddKeyframe(startFrame);
778     animation->AddKeyframe(midFrame);
779     animation->AddKeyframe(endFrame);
780     animation->AddListener([weak = AceType::WeakClaim(this)](double value) {
781         auto button = weak.Upgrade();
782         if (button) {
783             button->UpdateAnimationParam(value);
784         }
785     });
786 
787     if (!controller_) {
788         return;
789     }
790     controller_->ClearInterpolators();
791     controller_->AddInterpolator(animation);
792     controller_->SetDuration(duration);
793     controller_->Play();
794 }
795 
PlayClickAnimation()796 void RenderButton::PlayClickAnimation()
797 {
798     if (isPhone_ || isWatch_) {
799         return;
800     }
801     if (!isFocus_) {
802         return;
803     }
804     animationRunning_ = true;
805     isClickAnimation_ = true;
806     startValue_ = TV_EXPAND_SCALE;
807     endValue_ = TV_REDUCE_SCALE;
808     PlayClickScaleAnimation(KEY_TIME_MID, TV_CLICK_DURATION);
809 }
810 
PlayFocusAnimation(bool isFocus)811 void RenderButton::PlayFocusAnimation(bool isFocus)
812 {
813     if (isWatch_) {
814         return;
815     }
816     if (isPhone_) {
817         UpdateFocusAnimation(INIT_SCALE);
818         return;
819     }
820     if (!isOpacityAnimation_) {
821         isOpacityAnimation_ = true;
822     }
823     if (isFocus) {
824         PlayAnimation(INIT_SCALE, TV_EXPAND_SCALE, TV_FOCUS_SCALE_DURATION);
825     } else {
826         PlayAnimation(TV_EXPAND_SCALE, INIT_SCALE, TV_FOCUS_SCALE_DURATION);
827     }
828 }
829 
OnStatusStyleChanged(const VisualState state)830 void RenderButton::OnStatusStyleChanged(const VisualState state)
831 {
832     RenderNode::OnStatusStyleChanged(state);
833     if (buttonComponent_ == nullptr || !buttonComponent_->HasStateAttributes()) {
834         return;
835     }
836 
837     for (auto attribute : buttonComponent_->GetStateAttributes()->GetAttributesForState(state)) {
838         switch (attribute->id_) {
839             case ButtonStateAttribute::COLOR: {
840                 auto colorState =
841                     AceType::DynamicCast<StateAttributeValue<ButtonStateAttribute, AnimatableColor>>(attribute);
842                 if (state == VisualState::PRESSED) {
843                     LOGD("Click color start %{public}x  end %{public}x", backgroundColor_.GetValue(),
844                         colorState->value_.GetValue());
845                     SetClickedColor(backgroundColor_);  // starting animation color
846                     clickedColor_ = colorState->value_; // End color
847                     setClickColor_ = true;
848                 } else {
849                     LOGD("background color start %{public}x  end %{public}x", clickedColor_.GetValue(),
850                         colorState->value_.GetValue());
851                     backgroundColor_.SetValue(clickedColor_.GetValue()); // Start value
852                     backgroundColor_ = colorState->value_;               // End value and animate
853                 }
854             } break;
855 
856             case ButtonStateAttribute::RADIUS: {
857                 auto radiusState =
858                     AceType::DynamicCast<StateAttributeValue<ButtonStateAttribute, Dimension>>(attribute);
859                 buttonComponent_->SetRectRadius(radiusState->value_);
860             } break;
861 
862             case ButtonStateAttribute::HEIGHT: {
863                 auto valueState =
864                     AceType::DynamicCast<StateAttributeValue<ButtonStateAttribute, AnimatableDimension>>(attribute);
865                 buttonComponent_->SetHeight(valueState->value_);
866                 height_ = valueState->value_;
867             } break;
868 
869             case ButtonStateAttribute::WIDTH: {
870                 auto valueState =
871                     AceType::DynamicCast<StateAttributeValue<ButtonStateAttribute, AnimatableDimension>>(attribute);
872                 buttonComponent_->SetWidth(valueState->value_);
873                 width_ = valueState->value_;
874             } break;
875         }
876     }
877     MarkNeedLayout();
878 }
879 
SendAccessibilityEvent()880 void RenderButton::SendAccessibilityEvent()
881 {
882     auto accessibilityNode = GetAccessibilityNode().Upgrade();
883     if (!accessibilityNode) {
884         return;
885     }
886 
887     auto context = context_.Upgrade();
888     if (context) {
889         AccessibilityEvent radioEvent;
890         radioEvent.nodeId = accessibilityNode->GetNodeId();
891         radioEvent.eventType = "click";
892         context->SendEventToAccessibility(radioEvent);
893     }
894 }
895 
896 } // namespace OHOS::Ace
897