1 /*
2  * Copyright (c) 2022-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 #include "core/components_ng/pattern/toggle/switch_paint_method.h"
17 
18 #include "base/geometry/ng/offset_t.h"
19 #include "base/memory/referenced.h"
20 #include "base/utils/utils.h"
21 #include "core/components/checkable/checkable_theme.h"
22 #include "core/components/common/properties/color.h"
23 #include "core/components_ng/pattern/toggle/switch_layout_algorithm.h"
24 #include "core/components_ng/pattern/toggle/switch_modifier.h"
25 #include "core/components_ng/pattern/toggle/switch_paint_property.h"
26 #include "core/components_ng/render/drawing.h"
27 #include "core/components_ng/render/drawing_prop_convertor.h"
28 #include "core/components_ng/render/paint_property.h"
29 #include "core/pipeline/pipeline_base.h"
30 
31 namespace OHOS::Ace::NG {
32 
33 namespace {
34 constexpr uint8_t ENABLED_ALPHA = 255;
35 constexpr uint8_t DISABLED_ALPHA = 102;
36 const Color TMP_INACTIVE_COLOR = Color(0x337F7F7F);
37 } // namespace
38 
SwitchModifier(const SizeF & size,const OffsetF & offset,float pointOffset,bool isSelect,const Color & boardColor,float dragOffsetX)39 SwitchModifier::SwitchModifier(const SizeF& size, const OffsetF& offset, float pointOffset, bool isSelect,
40     const Color& boardColor, float dragOffsetX)
41 {
42     auto pipeline = PipelineBase::GetCurrentContext();
43     CHECK_NULL_VOID(pipeline);
44     auto switchTheme = pipeline->GetTheme<SwitchTheme>();
45     CHECK_NULL_VOID(switchTheme);
46     animatableBoardColor_ = AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(boardColor));
47     animateTouchHoverColor_ = AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(Color::TRANSPARENT));
48     animatePointColor_ = AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(switchTheme->GetPointColor()));
49     pointOffset_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(pointOffset);
50     dragOffsetX_ = AceType::MakeRefPtr<PropertyFloat>(dragOffsetX);
51     isSelect_ = AceType::MakeRefPtr<PropertyBool>(isSelect);
52     isHover_ = AceType::MakeRefPtr<PropertyBool>(false);
53     offset_ = AceType::MakeRefPtr<AnimatablePropertyOffsetF>(offset);
54     size_ = AceType::MakeRefPtr<AnimatablePropertySizeF>(size);
55     enabled_ = AceType::MakeRefPtr<PropertyBool>(true);
56     useContentModifier_ = AceType::MakeRefPtr<PropertyBool>(false);
57     animatePointRadius_ = AceType::MakeRefPtr<PropertyFloat>(SWITCH_ERROR_RADIUS);
58     animateTrackRadius_ = AceType::MakeRefPtr<PropertyFloat>(SWITCH_ERROR_RADIUS);
59 
60     AttachProperty(animatableBoardColor_);
61     AttachProperty(animateTouchHoverColor_);
62     AttachProperty(animatePointColor_);
63     AttachProperty(pointOffset_);
64     AttachProperty(dragOffsetX_);
65     AttachProperty(isSelect_);
66     AttachProperty(isHover_);
67     AttachProperty(offset_);
68     AttachProperty(size_);
69     AttachProperty(enabled_);
70     AttachProperty(animatePointRadius_);
71     AttachProperty(animateTrackRadius_);
72 }
73 
InitializeParam()74 void SwitchModifier::InitializeParam()
75 {
76     auto pipeline = PipelineBase::GetCurrentContext();
77     CHECK_NULL_VOID(pipeline);
78     auto switchTheme = pipeline->GetTheme<SwitchTheme>();
79     CHECK_NULL_VOID(switchTheme);
80     activeColor_ = switchTheme->GetActiveColor();
81     inactiveColor_ = TMP_INACTIVE_COLOR;
82     clickEffectColor_ = switchTheme->GetInteractivePressedColor();
83     hoverColor_ = switchTheme->GetInteractiveHoverColor();
84     userActiveColor_ = activeColor_;
85     hoverDuration_ = switchTheme->GetHoverDuration();
86     hoverToTouchDuration_ = switchTheme->GetHoverToTouchDuration();
87     touchDuration_ = switchTheme->GetTouchDuration();
88     colorAnimationDuration_ = switchTheme->GetColorAnimationDuration();
89     pointAnimationDuration_ = switchTheme->GetPointAnimationDuration();
90 }
91 
CalcActualWidth(float width,float height,double actualGap,double defaultWidthGap)92 float SwitchModifier::CalcActualWidth(float width, float height, double actualGap, double defaultWidthGap)
93 {
94     float result = 0.0f;
95     if (GreatOrEqual(width, height)) {
96         result = (pointRadius_ * NUM_TWO > height ? (width - (actualGap * NUM_TWO)) : width) + defaultWidthGap;
97     } else {
98         result = (pointRadius_ > actualTrackRadius_ ? (width + (pointRadius_ - actualTrackRadius_) * NUM_TWO) : width) +
99                  defaultWidthGap;
100     }
101     return result;
102 }
103 
PaintSwitch(RSCanvas & canvas,const OffsetF & contentOffset,const SizeF & contentSize)104 void SwitchModifier::PaintSwitch(RSCanvas& canvas, const OffsetF& contentOffset, const SizeF& contentSize)
105 {
106     auto pipelineContext = PipelineBase::GetCurrentContext();
107     CHECK_NULL_VOID(pipelineContext);
108     auto switchTheme = pipelineContext->GetTheme<SwitchTheme>();
109     CHECK_NULL_VOID(switchTheme);
110 
111     auto width = contentSize.Width();
112     auto height = contentSize.Height();
113     auto trackRadius =
114         (animateTrackRadius_->Get() < 0) ? (height / NUM_TWO) : animateTrackRadius_->Get();
115     auto radius = height / 2;
116     auto actualGap = radiusGap_.ConvertToPx() * height /
117                      (switchTheme->GetHeight() - switchTheme->GetHotZoneVerticalPadding() * 2).ConvertToPx();
118     auto xOffset = contentOffset.GetX();
119     auto yOffset = contentOffset.GetY();
120     if (animatePointRadius_->Get() < 0) {
121         pointRadius_ = radius - actualGap;
122     } else {
123         pointRadius_ = animatePointRadius_->Get();
124         actualGap = radius - pointRadius_;
125     }
126     auto defaultWidth = switchTheme->GetDefaultWidth().ConvertToPx();
127     auto defaultHeight = switchTheme->GetDefaultHeight().ConvertToPx();
128     auto defaultWidthGap =
129         defaultWidth - (switchTheme->GetWidth() - switchTheme->GetHotZoneHorizontalPadding() * 2).ConvertToPx();
130     auto defaultHeightGap =
131         defaultHeight - (switchTheme->GetHeight() - switchTheme->GetHotZoneVerticalPadding() * 2).ConvertToPx();
132     actualWidth_ = CalcActualWidth(width, height, actualGap, defaultWidthGap);
133     actualHeight_ = (pointRadius_ * NUM_TWO > height ? pointRadius_ * NUM_TWO : height) + defaultHeightGap;
134 
135     OffsetF hoverBoardOffset;
136     hoverBoardOffset.SetX(xOffset - (actualWidth_ - width) / 2.0);
137     hoverBoardOffset.SetY(yOffset - (actualHeight_ - height) / 2.0);
138 
139     RSRect rect;
140     rect.SetLeft(xOffset);
141     rect.SetTop(yOffset);
142     rect.SetRight(xOffset + width);
143     rect.SetBottom(yOffset + height);
144     RSRoundRect roundRect(rect, trackRadius, trackRadius);
145 
146     RSBrush brush;
147     if (!enabled_->Get()) {
148         brush.SetColor(
149             ToRSColor(animatableBoardColor_->Get().BlendOpacity(static_cast<float>(DISABLED_ALPHA) / ENABLED_ALPHA)));
150     } else {
151         brush.SetColor(ToRSColor(animatableBoardColor_->Get()));
152     }
153     brush.SetBlendMode(RSBlendMode::SRC_OVER);
154     brush.SetAntiAlias(true);
155     canvas.AttachBrush(brush);
156     canvas.DrawRoundRect(roundRect);
157     canvas.DetachBrush();
158     brush.SetColor(ToRSColor(animateTouchHoverColor_->Get()));
159     canvas.AttachBrush(brush);
160     canvas.DrawRoundRect(roundRect);
161     canvas.DetachBrush();
162 
163     brush.SetColor(ToRSColor(animatePointColor_->Get()));
164     brush.SetAntiAlias(true);
165     canvas.AttachBrush(brush);
166     RSPoint point;
167     if (GreatOrEqual(contentSize.Width(), contentSize.Height())) {
168         point.SetX(xOffset + actualGap + pointRadius_ + pointOffset_->Get());
169     } else {
170         point.SetX(xOffset + pointOffset_->Get());
171     }
172     point.SetY(yOffset + radius);
173     canvas.DrawCircle(point, pointRadius_);
174     canvas.DetachBrush();
175 }
176 
GetSwitchWidth(const SizeF & contentSize) const177 float SwitchModifier::GetSwitchWidth(const SizeF& contentSize) const
178 {
179     const float switchGap = 2.0f;
180     auto pipelineContext = PipelineBase::GetCurrentContext();
181     CHECK_NULL_RETURN(pipelineContext, false);
182     auto switchTheme = pipelineContext->GetTheme<SwitchTheme>();
183     auto actualGap = switchGap * contentSize.Height() /
184                      (switchTheme->GetHeight() - switchTheme->GetHotZoneVerticalPadding() * 2).ConvertToPx();
185     auto switchWidth = contentSize.Width() - contentSize.Height() + actualGap;
186     return switchWidth;
187 }
188 
189 } // namespace OHOS::Ace::NG
190