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