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