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