1 /*
2  * Copyright (c) 2023 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 #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SWITCH_SWITCH_MODIFIER_H
17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SWITCH_SWITCH_MODIFIER_H
18 
19 #include <algorithm>
20 #include <vector>
21 
22 #include "base/geometry/ng/offset_t.h"
23 #include "core/animation/spring_curve.h"
24 #include "core/common/container.h"
25 #include "core/components_ng/base/modifier.h"
26 #include "core/components_ng/pattern/radio/radio_modifier.h"
27 #include "core/components_ng/pattern/toggle/switch_paint_property.h"
28 #include "core/components_ng/property/property.h"
29 #include "core/components_ng/render/animation_utils.h"
30 #include "core/components_ng/render/canvas_image.h"
31 #include "core/components_ng/render/drawing_forward.h"
32 #include "core/components_ng/render/paint_wrapper.h"
33 namespace OHOS::Ace::NG {
34 class SwitchModifier : public ContentModifier {
35     DECLARE_ACE_TYPE(SwitchModifier, ContentModifier);
36 
37 public:
38     SwitchModifier(const SizeF& size, const OffsetF& offset, float pointOffset, bool isSelect, const Color& boardColor,
39         float dragOffsetX);
40     ~SwitchModifier() override = default;
41 
onDraw(DrawingContext & context)42     void onDraw(DrawingContext& context) override
43     {
44         if (useContentModifier_->Get()) {
45             return;
46         }
47         RSCanvas& canvas = context.canvas;
48         PaintSwitch(canvas, offset_->Get(), size_->Get());
49     }
50 
UpdateAnimatableProperty()51     void UpdateAnimatableProperty()
52     {
53         switch (touchHoverType_) {
54             case TouchHoverAnimationType::HOVER:
55                 SetBoardColor(LinearColor(hoverColor_), hoverDuration_, Curves::FRICTION);
56                 break;
57             case TouchHoverAnimationType::PRESS_TO_HOVER:
58                 SetBoardColor(LinearColor(hoverColor_), hoverToTouchDuration_, Curves::SHARP);
59                 break;
60             case TouchHoverAnimationType::NONE:
61                 SetBoardColor(LinearColor(hoverColor_.BlendOpacity(0)), hoverDuration_, Curves::FRICTION);
62                 break;
63             case TouchHoverAnimationType::HOVER_TO_PRESS:
64                 SetBoardColor(LinearColor(clickEffectColor_), hoverToTouchDuration_, Curves::SHARP);
65                 break;
66             case TouchHoverAnimationType::PRESS:
67                 SetBoardColor(LinearColor(clickEffectColor_), hoverDuration_, Curves::FRICTION);
68                 break;
69             default:
70                 break;
71         }
72         if (!actualSize_.IsPositive()) {
73             return;
74         }
75         AnimationOption colorOption = AnimationOption();
76         colorOption.SetDuration(colorAnimationDuration_);
77         colorOption.SetCurve(Curves::FAST_OUT_SLOW_IN);
78         AnimationUtils::Animate(colorOption, [&]() {
79             animatableBoardColor_->Set(isSelect_->Get() ? LinearColor(userActiveColor_) : LinearColor(inactiveColor_));
80         });
81         UpdatePointOffsetAnimation();
82     }
83 
UpdatePointOffsetAnimation()84     void UpdatePointOffsetAnimation()
85     {
86         AnimationOption pointOption = AnimationOption();
87         pointOption.SetDuration(pointAnimationDuration_);
88         pointOption.SetCurve(Curves::FAST_OUT_SLOW_IN);
89         float newPointOffset = 0.0f;
90         bool isRtl = direction_ == TextDirection::AUTO ? AceApplicationInfo::GetInstance().IsRightToLeft()
91                                                        : direction_ == TextDirection::RTL;
92         auto offsetNotRtl = GreatOrEqual(actualSize_.Width(), actualSize_.Height())
93                                 ? (isSelect_->Get() ? actualSize_.Width() - actualSize_.Height() : 0.0f)
94                                 : (isSelect_->Get() ? actualSize_.Width() - actualTrackRadius_ : actualTrackRadius_);
95         auto offsetIsRtl = GreatOrEqual(actualSize_.Width(), actualSize_.Height())
96                                ? (isSelect_->Get() ? 0.0f : actualSize_.Width() - actualSize_.Height())
97                                : (isSelect_->Get() ? actualTrackRadius_ : actualSize_.Width() - actualTrackRadius_);
98         if (!isDragEvent_) {
99             if (isRtl) {
100                 newPointOffset = offsetIsRtl;
101             } else {
102                 newPointOffset = offsetNotRtl;
103             }
104         } else {
105             if (GreatOrEqual(actualSize_.Width(), actualSize_.Height())) {
106                 newPointOffset = std::clamp(
107                     dragOffsetX_->Get() - offset_->Get().GetX(), 0.0f, actualSize_.Width() - actualSize_.Height());
108             } else {
109                 newPointOffset = std::clamp(dragOffsetX_->Get() - offset_->Get().GetX(), actualTrackRadius_,
110                     actualSize_.Width() - actualTrackRadius_);
111             }
112         }
113         AnimationUtils::Animate(pointOption, [&]() { pointOffset_->Set(newPointOffset); });
114     }
115 
SetBoardColor(LinearColor color,int32_t duratuion,const RefPtr<CubicCurve> & curve)116     void SetBoardColor(LinearColor color, int32_t duratuion, const RefPtr<CubicCurve>& curve)
117     {
118         if (animateTouchHoverColor_) {
119             AnimationOption option = AnimationOption();
120             option.SetDuration(duratuion);
121             option.SetCurve(curve);
122             AnimationUtils::Animate(option, [&]() { animateTouchHoverColor_->Set(color); });
123         }
124     }
125 
126     void InitializeParam();
127     void PaintSwitch(RSCanvas& canvas, const OffsetF& contentOffset, const SizeF& contentSize);
128     float GetSwitchWidth(const SizeF& contentSize) const;
129     float CalcActualWidth(float width, float height, double actualGap, double defaultWidthGap);
130 
SetUserActiveColor(const Color & color)131     void SetUserActiveColor(const Color& color)
132     {
133         userActiveColor_ = color;
134         animatableBoardColor_->Set(isSelect_->Get() ? LinearColor(userActiveColor_) : LinearColor(inactiveColor_));
135     }
136 
SetPointColor(const Color & color)137     void SetPointColor(const Color& color)
138     {
139         animatePointColor_->Set(LinearColor(color));
140     }
141 
SetEnabled(bool enabled)142     void SetEnabled(bool enabled)
143     {
144         if (enabled_) {
145             enabled_->Set(enabled);
146         }
147     }
148 
SetIsHover(bool isHover)149     void SetIsHover(bool isHover)
150     {
151         if (isHover_) {
152             isHover_->Set(isHover);
153         }
154     }
155 
SetIsSelect(bool isSelect)156     void SetIsSelect(bool isSelect)
157     {
158         if (isSelect_) {
159             isSelect_->Set(isSelect);
160         }
161     }
162 
SetHotZoneOffset(OffsetF & hotZoneOffset)163     void SetHotZoneOffset(OffsetF& hotZoneOffset)
164     {
165         hotZoneOffset_ = hotZoneOffset;
166     }
167 
SetHotZoneSize(SizeF & hotZoneSize)168     void SetHotZoneSize(SizeF& hotZoneSize)
169     {
170         hotZoneSize_ = hotZoneSize;
171     }
172 
SetOffset(OffsetF & offset)173     void SetOffset(OffsetF& offset)
174     {
175         if (offset_) {
176             offset_->Set(offset);
177         }
178     }
179 
SetUseContentModifier(bool useContentModifier)180     void SetUseContentModifier(bool useContentModifier)
181     {
182         if (useContentModifier_) {
183             useContentModifier_->Set(useContentModifier);
184         }
185     }
186 
SetSize(SizeF & size)187     void SetSize(SizeF& size)
188     {
189         actualSize_ = size;
190         if (size_) {
191             size_->Set(size);
192         }
193     }
194 
SetDragOffsetX(float dragOffsetX)195     void SetDragOffsetX(float dragOffsetX)
196     {
197         if (dragOffsetX_) {
198             dragOffsetX_->Set(dragOffsetX);
199         }
200     }
201 
SetPointOffset(float pointOffset)202     void SetPointOffset(float pointOffset)
203     {
204         if (pointOffset_) {
205             pointOffset_->Set(pointOffset);
206         }
207     }
208 
SetTouchHoverAnimationType(const TouchHoverAnimationType touchHoverType)209     void SetTouchHoverAnimationType(const TouchHoverAnimationType touchHoverType)
210     {
211         touchHoverType_ = touchHoverType;
212     }
213 
SetIsDragEvent(bool isDragEvent)214     void SetIsDragEvent(bool isDragEvent)
215     {
216         isDragEvent_ = isDragEvent;
217     }
218 
SetShowHoverEffect(bool showHoverEffect)219     void SetShowHoverEffect(bool showHoverEffect)
220     {
221         showHoverEffect_ = showHoverEffect;
222     }
223 
SetDirection(TextDirection direction)224     void SetDirection(TextDirection direction)
225     {
226         if (direction_ != direction) {
227             direction_ = direction;
228             isFirstCreated_ = true;
229         }
230     }
231 
SetPointRadius(float pointRadius)232     void SetPointRadius(float pointRadius)
233     {
234         animatePointRadius_->Set(pointRadius);
235     }
236 
GetPointRadius()237     float GetPointRadius()
238     {
239         return pointRadius_;
240     }
241 
SetInactiveColor(const Color & color)242     void SetInactiveColor(const Color& color)
243     {
244         inactiveColor_ = color;
245     }
246 
SetTrackRadius(float borderRadius)247     void SetTrackRadius(float borderRadius)
248     {
249         animateTrackRadius_->Set(borderRadius);
250     }
251 
GetTrackRadius()252     float GetTrackRadius()
253     {
254         return actualTrackRadius_;
255     }
256 
SetActualTrackRadius(float borderRadius)257     void SetActualTrackRadius(float borderRadius)
258     {
259         actualTrackRadius_ = borderRadius;
260     }
261 
262 private:
263     float actualWidth_ = 0.0f;
264     float actualHeight_ = 0.0f;
265     float pointRadius_ = 0.0f;
266     const Dimension radiusGap_ = 2.0_vp;
267     Color clickEffectColor_;
268     Color hoverColor_;
269     Color activeColor_;
270     Color inactiveColor_;
271     Color userActiveColor_;
272     Dimension hoverRadius_ = 8.0_vp;
273     float hoverDuration_ = 0.0f;
274     float hoverToTouchDuration_ = 0.0f;
275     float touchDuration_ = 0.0f;
276     float colorAnimationDuration_ = 0.0f;
277     float pointAnimationDuration_ = 0.0f;
278     bool isDragEvent_ = false;
279     bool isFirstCreated_ = true;
280     bool showHoverEffect_ = true;
281     float actualTrackRadius_ = 0.0f;
282 
283     OffsetF hotZoneOffset_;
284     SizeF hotZoneSize_;
285     SizeF actualSize_;
286     TouchHoverAnimationType touchHoverType_ = TouchHoverAnimationType::NONE;
287     TextDirection direction_ = TextDirection::AUTO;
288 
289     RefPtr<AnimatablePropertyColor> animatableBoardColor_;
290     RefPtr<AnimatablePropertyColor> animateTouchHoverColor_;
291     RefPtr<AnimatablePropertyColor> animatePointColor_;
292     RefPtr<AnimatablePropertyFloat> pointOffset_;
293     RefPtr<PropertyFloat> dragOffsetX_;
294     RefPtr<PropertyBool> isSelect_;
295     RefPtr<PropertyBool> isHover_;
296     RefPtr<AnimatablePropertyOffsetF> offset_;
297     RefPtr<AnimatablePropertySizeF> size_;
298     RefPtr<PropertyBool> enabled_;
299     RefPtr<PropertyBool> useContentModifier_;
300     RefPtr<PropertyFloat> animatePointRadius_;
301     RefPtr<PropertyFloat> animateTrackRadius_;
302 
303     ACE_DISALLOW_COPY_AND_MOVE(SwitchModifier);
304 };
305 } // namespace OHOS::Ace::NG
306 
307 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SWITCH_SWITCH_MODIFIER_H
308