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