1 /*
2 * Copyright (c) 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 #include "base/geometry/ng/offset_t.h"
16 #include "base/utils/utils.h"
17 #include "core/components/checkable/checkable_theme.h"
18 #include "core/components/common/properties/color.h"
19 #include "core/components_ng/pattern/radio/radio_modifier.h"
20 #include "core/components_ng/render/animation_utils.h"
21 #include "core/components_ng/render/drawing.h"
22 #include "core/components_ng/render/drawing_prop_convertor.h"
23 #include "core/pipeline/pipeline_base.h"
24
25 namespace OHOS::Ace::NG {
26 namespace {
27 constexpr uint8_t ENABLED_ALPHA = 255;
28 constexpr uint8_t DISABLED_ALPHA = 102;
29 constexpr float CALC_RADIUS = 2.0f;
30 constexpr float DEFAULT_POINT_SCALE = 0.5f;
31 constexpr float DEFAULT_TOTAL_SCALE = 1.0f;
32 constexpr float DEFAULT_SHRINK_SCALE = 0.9f;
33 constexpr int32_t DEFAULT_RADIO_ANIMATION_DURATION = 300;
34 } // namespace
35
RadioModifier()36 RadioModifier::RadioModifier()
37 {
38 auto pipeline = PipelineBase::GetCurrentContext();
39 CHECK_NULL_VOID(pipeline);
40 auto radioTheme = pipeline->GetTheme<RadioTheme>();
41
42 pointColor_ = AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(radioTheme->GetPointColor()));
43 AttachProperty(pointColor_);
44
45 activeColor_ = AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(radioTheme->GetActiveColor()));
46 AttachProperty(activeColor_);
47
48 inactiveColor_ = AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(radioTheme->GetInactiveColor()));
49 AttachProperty(inactiveColor_);
50 isOnAnimationFlag_ = AceType::MakeRefPtr<PropertyBool>(false);
51 enabled_ = AceType::MakeRefPtr<PropertyBool>(true);
52 isCheck_ = AceType::MakeRefPtr<PropertyBool>(false);
53 uiStatus_ = AceType::MakeRefPtr<PropertyInt>(static_cast<int32_t>(UIStatus::UNSELECTED));
54 offset_ = AceType::MakeRefPtr<AnimatablePropertyOffsetF>(OffsetF());
55 size_ = AceType::MakeRefPtr<AnimatablePropertySizeF>(SizeF());
56 totalScale_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(DEFAULT_TOTAL_SCALE);
57 pointScale_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(DEFAULT_POINT_SCALE);
58 ringPointScale_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0f);
59 animateTouchHoverColor_ = AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(Color::TRANSPARENT));
60
61 AttachProperty(enabled_);
62 AttachProperty(isCheck_);
63 AttachProperty(uiStatus_);
64 AttachProperty(offset_);
65 AttachProperty(size_);
66 AttachProperty(totalScale_);
67 AttachProperty(pointScale_);
68 AttachProperty(ringPointScale_);
69 AttachProperty(animateTouchHoverColor_);
70
71 InitializeParam();
72 }
73
InitializeParam()74 void RadioModifier::InitializeParam()
75 {
76 auto pipeline = PipelineBase::GetCurrentContext();
77 CHECK_NULL_VOID(pipeline);
78 auto radioTheme = pipeline->GetTheme<RadioTheme>();
79 shadowWidth_ = radioTheme->GetShadowWidth().ConvertToPx();
80 borderWidth_ = radioTheme->GetBorderWidth().ConvertToPx();
81 inactivePointColor_ = radioTheme->GetInactivePointColor();
82 shadowColor_ = radioTheme->GetShadowColor();
83 clickEffectColor_ = radioTheme->GetClickEffectColor();
84 hoverColor_ = radioTheme->GetHoverColor();
85 hotZoneHorizontalPadding_ = radioTheme->GetHotZoneHorizontalPadding();
86 hoverDuration_ = radioTheme->GetHoverDuration();
87 hoverToTouchDuration_ = radioTheme->GetHoverToTouchDuration();
88 touchDuration_ = radioTheme->GetTouchDuration();
89 }
90
UpdateAnimatableProperty()91 void RadioModifier::UpdateAnimatableProperty()
92 {
93 switch (touchHoverType_) {
94 case TouchHoverAnimationType::HOVER:
95 SetBoardColor(LinearColor(hoverColor_), hoverDuration_, Curves::FRICTION);
96 break;
97 case TouchHoverAnimationType::PRESS_TO_HOVER:
98 SetBoardColor(LinearColor(hoverColor_), hoverToTouchDuration_, Curves::SHARP);
99 break;
100 case TouchHoverAnimationType::NONE:
101 SetBoardColor(LinearColor(hoverColor_.BlendOpacity(0)), hoverDuration_, Curves::FRICTION);
102 break;
103 case TouchHoverAnimationType::HOVER_TO_PRESS:
104 SetBoardColor(LinearColor(clickEffectColor_), hoverToTouchDuration_, Curves::SHARP);
105 break;
106 case TouchHoverAnimationType::PRESS:
107 SetBoardColor(LinearColor(clickEffectColor_), hoverDuration_, Curves::FRICTION);
108 break;
109 default:
110 break;
111 }
112 }
113
UpdateIsOnAnimatableProperty(bool isCheck)114 void RadioModifier::UpdateIsOnAnimatableProperty(bool isCheck)
115 {
116 AnimationOption delayOption;
117 delayOption.SetDelay(DEFAULT_RADIO_ANIMATION_DURATION / 2);
118 delayOption.SetDuration(DEFAULT_RADIO_ANIMATION_DURATION / 2);
119 delayOption.SetCurve(Curves::FRICTION);
120
121 AnimationOption halfDurationOption;
122 halfDurationOption.SetDuration(DEFAULT_RADIO_ANIMATION_DURATION / 2);
123 halfDurationOption.SetCurve(Curves::FRICTION);
124
125 if (isOnAnimationFlag_->Get()) {
126 pointScale_->Set(0);
127 AnimationUtils::Animate(delayOption, [&]() { pointScale_->Set(DEFAULT_POINT_SCALE); });
128 ringPointScale_->Set(1);
129 AnimationUtils::Animate(halfDurationOption, [&]() { ringPointScale_->Set(0); });
130 } else {
131 pointScale_->Set(DEFAULT_POINT_SCALE);
132 AnimationUtils::Animate(halfDurationOption, [&]() { pointScale_->Set(0); });
133 ringPointScale_->Set(0);
134 AnimationUtils::Animate(delayOption, [&]() { ringPointScale_->Set(1); });
135 }
136
137 totalScale_->Set(DEFAULT_TOTAL_SCALE);
138 AnimationUtils::Animate(halfDurationOption, [&]() { totalScale_->Set(DEFAULT_SHRINK_SCALE); });
139 totalScale_->Set(DEFAULT_SHRINK_SCALE);
140 AnimationUtils::Animate(
141 delayOption, [&]() { totalScale_->Set(1); },
142 [isCheck, weakUiStatus = AceType::WeakClaim(AceType::RawPtr(uiStatus_))]() {
143 auto uiStatus = weakUiStatus.Upgrade();
144 if (uiStatus) {
145 uiStatus->Set(static_cast<int32_t>(isCheck ? UIStatus::SELECTED : UIStatus::UNSELECTED));
146 }
147 auto context = PipelineBase::GetCurrentContext();
148 if (context) {
149 context->RequestFrame();
150 }
151 });
152 }
153
SetBoardColor(LinearColor color,int32_t duratuion,const RefPtr<CubicCurve> & curve)154 void RadioModifier::SetBoardColor(LinearColor color, int32_t duratuion, const RefPtr<CubicCurve>& curve)
155 {
156 if (animateTouchHoverColor_) {
157 AnimationOption option = AnimationOption();
158 option.SetDuration(duratuion);
159 option.SetCurve(curve);
160 AnimationUtils::Animate(option, [&]() { animateTouchHoverColor_->Set(color); });
161 }
162 }
163
PaintRadio(RSCanvas & canvas,bool,const SizeF & contentSize,const OffsetF & contentOffset) const164 void RadioModifier::PaintRadio(
165 RSCanvas& canvas, bool /* checked */, const SizeF& contentSize, const OffsetF& contentOffset) const
166 {
167 DrawTouchAndHoverBoard(canvas, contentSize, contentOffset);
168 float outCircleRadius = contentSize.Width() / CALC_RADIUS;
169 float centerX = contentOffset.GetX() + outCircleRadius;
170 float centerY = contentOffset.GetY() + outCircleRadius;
171 RSPen pen;
172 RSPen outPen;
173 RSBrush brush;
174 RSBrush shadowBrush;
175 pen.SetAntiAlias(true);
176 pen.SetWidth(borderWidth_);
177 outPen.SetAntiAlias(true);
178 brush.SetAntiAlias(true);
179 shadowBrush.SetAntiAlias(true);
180 shadowBrush.SetColor(ToRSColor(shadowColor_));
181 if (uiStatus_->Get() == static_cast<int32_t>(UIStatus::SELECTED)) {
182 if (!enabled_->Get()) {
183 brush.SetColor(
184 ToRSColor(pointColor_->Get().BlendOpacity(static_cast<float>(DISABLED_ALPHA) / ENABLED_ALPHA)));
185 } else {
186 brush.SetColor(ToRSColor(pointColor_->Get()));
187 }
188 if (!NearZero(pointScale_->Get())) {
189 // draw shadow
190 canvas.AttachBrush(shadowBrush);
191 canvas.DrawCircle(RSPoint(centerX, centerY), outCircleRadius * pointScale_->Get() + shadowWidth_);
192 // draw inner circle
193 canvas.AttachBrush(brush);
194 canvas.DrawCircle(RSPoint(centerX, centerY), outCircleRadius * pointScale_->Get());
195 }
196 // draw ring circle
197 if (!enabled_->Get()) {
198 brush.SetColor(
199 ToRSColor(inactivePointColor_.BlendOpacity(static_cast<float>(DISABLED_ALPHA) / ENABLED_ALPHA)));
200 } else {
201 brush.SetColor(ToRSColor(inactivePointColor_));
202 }
203 if (!NearZero(ringPointScale_->Get())) {
204 canvas.AttachBrush(brush);
205 canvas.DrawCircle(RSPoint(centerX, centerY), outCircleRadius * ringPointScale_->Get());
206 }
207 // draw out circular ring
208 if (!enabled_->Get()) {
209 outPen.SetColor(
210 ToRSColor(activeColor_->Get().BlendOpacity(static_cast<float>(DISABLED_ALPHA) / ENABLED_ALPHA)));
211 } else {
212 outPen.SetColor(ToRSColor(activeColor_->Get()));
213 }
214 auto outWidth = outCircleRadius * (totalScale_->Get() - pointScale_->Get() - ringPointScale_->Get());
215 if (outWidth < borderWidth_) {
216 outWidth = borderWidth_;
217 }
218 outPen.SetWidth(outWidth);
219 canvas.AttachPen(outPen);
220 canvas.DrawCircle(RSPoint(centerX, centerY), outCircleRadius * totalScale_->Get() - outWidth / CALC_RADIUS);
221 } else if (uiStatus_->Get() == static_cast<int32_t>(UIStatus::UNSELECTED)) {
222 auto alphaCalculate = static_cast<float>(DISABLED_ALPHA) / ENABLED_ALPHA;
223 if (!enabled_->Get()) {
224 brush.SetColor(ToRSColor(inactivePointColor_.BlendOpacity(alphaCalculate)));
225 pen.SetColor(ToRSColor(inactiveColor_->Get().BlendOpacity(alphaCalculate)));
226 } else {
227 brush.SetColor(ToRSColor(inactivePointColor_));
228 pen.SetColor(ToRSColor(inactiveColor_->Get()));
229 }
230 canvas.AttachBrush(brush);
231 canvas.DrawCircle(RSPoint(centerX, centerY), outCircleRadius - borderWidth_);
232 // draw border with unselected color
233 canvas.AttachPen(pen);
234 canvas.DrawCircle(RSPoint(centerX, centerY), outCircleRadius - borderWidth_ / CALC_RADIUS);
235 }
236 }
237
DrawTouchAndHoverBoard(RSCanvas & canvas,const SizeF & contentSize,const OffsetF & offset) const238 void RadioModifier::DrawTouchAndHoverBoard(RSCanvas& canvas, const SizeF& contentSize, const OffsetF& offset) const
239 {
240 float outCircleRadius = contentSize.Width() / CALC_RADIUS;
241 float centerX = outCircleRadius + offset.GetX();
242 float centerY = outCircleRadius + offset.GetY();
243 outCircleRadius += hotZoneHorizontalPadding_.ConvertToPx();
244 RSBrush brush;
245 brush.SetColor(ToRSColor(animateTouchHoverColor_->Get()));
246 brush.SetAntiAlias(true);
247 canvas.AttachBrush(brush);
248 canvas.DrawCircle(RSPoint(centerX, centerY), outCircleRadius);
249 }
250
251 } // namespace OHOS::Ace::NG
252