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