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 #include "core/components_ng/pattern/slider/slider_content_modifier.h"
17
18 #include <optional>
19 #include <utility>
20
21 #include "base/geometry/ng/offset_t.h"
22 #include "base/utils/utils.h"
23 #include "core/animation/curves.h"
24 #include "core/components/common/properties/color.h"
25 #include "core/components/slider/slider_theme.h"
26 #include "core/components_ng/render/drawing_prop_convertor.h"
27 #include "core/pipeline/pipeline_base.h"
28
29 namespace OHOS::Ace::NG {
30 namespace {
31 constexpr float HALF = 0.5f;
32 constexpr Dimension CIRCLE_SHADOW_WIDTH = 1.0_vp;
33 constexpr float SPRING_MOTION_RESPONSE = 0.314f;
34 constexpr float SPRING_MOTION_DAMPING_FRACTION = 0.95f;
35 } // namespace
SliderContentModifier(const Parameters & parameters)36 SliderContentModifier::SliderContentModifier(const Parameters& parameters)
37 : boardColor_(AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(Color::TRANSPARENT)))
38 {
39 // animatable property
40 selectStart_ = AceType::MakeRefPtr<AnimatablePropertyOffsetF>(parameters.selectStart - PointF());
41 selectEnd_ = AceType::MakeRefPtr<AnimatablePropertyOffsetF>(parameters.selectEnd - PointF());
42 backStart_ = AceType::MakeRefPtr<AnimatablePropertyOffsetF>(parameters.backStart - PointF());
43 backEnd_ = AceType::MakeRefPtr<AnimatablePropertyOffsetF>(parameters.backEnd - PointF());
44 circleCenter_ = AceType::MakeRefPtr<AnimatablePropertyOffsetF>(parameters.circleCenter - PointF());
45 trackThickness_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(parameters.trackThickness);
46 trackBackgroundColor_ = AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(parameters.trackBackgroundColor));
47 selectColor_ = AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(parameters.selectColor));
48 blockColor_ = AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(parameters.blockColor));
49 // non-animatable property
50 blockDiameter_ = AceType::MakeRefPtr<PropertyFloat>(parameters.blockDiameter);
51 stepRatio_ = AceType::MakeRefPtr<PropertyFloat>(parameters.stepRatio);
52 isShowStep_ = AceType::MakeRefPtr<PropertyBool>(false);
53 // others
54 UpdateData(parameters);
55 UpdateThemeColor();
56
57 AttachProperty(selectStart_);
58 AttachProperty(selectEnd_);
59 AttachProperty(backStart_);
60 AttachProperty(backEnd_);
61 AttachProperty(circleCenter_);
62 AttachProperty(trackThickness_);
63 AttachProperty(trackBackgroundColor_);
64 AttachProperty(selectColor_);
65 AttachProperty(blockColor_);
66 AttachProperty(boardColor_);
67 AttachProperty(blockDiameter_);
68 AttachProperty(stepRatio_);
69 AttachProperty(isShowStep_);
70 }
71
onDraw(DrawingContext & context)72 void SliderContentModifier::onDraw(DrawingContext& context)
73 {
74 DrawBackground(context);
75 DrawStep(context);
76 DrawSelect(context);
77 DrawDefaultBlock(context);
78 DrawShadow(context);
79 DrawHoverOrPress(context);
80 }
81
DrawBackground(DrawingContext & context)82 void SliderContentModifier::DrawBackground(DrawingContext& context)
83 {
84 auto& canvas = context.canvas;
85 RSPen backgroundPen;
86 backgroundPen.SetAntiAlias(true);
87 backgroundPen.SetWidth(trackThickness_->Get());
88 backgroundPen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
89 backgroundPen.SetColor(ToRSColor(trackBackgroundColor_->Get()));
90 canvas.AttachPen(backgroundPen);
91 canvas.DrawLine(RSPoint(backStart_->Get().GetX(), backStart_->Get().GetY()),
92 RSPoint(backEnd_->Get().GetX(), backEnd_->Get().GetY()));
93 canvas.DetachPen();
94 }
95
DrawStep(DrawingContext & context)96 void SliderContentModifier::DrawStep(DrawingContext& context)
97 {
98 if (!isShowStep_->Get()) {
99 return;
100 }
101 auto& canvas = context.canvas;
102 auto stepSize = stepSize_;
103 auto stepColor = stepColor_;
104 auto backStart = backStart_->Get();
105 auto backEnd = backEnd_->Get();
106 auto stepRatio = stepRatio_->Get();
107 if (NearEqual(stepRatio, .0f)) {
108 return;
109 }
110 RSBrush brush;
111 brush.SetAntiAlias(true);
112 brush.SetColor(ToRSColor(stepColor));
113 canvas.AttachBrush(brush);
114 // Distance between slide track and Content boundary
115 auto centerWidth = directionAxis_ == Axis::HORIZONTAL ? context.height : context.width;
116 centerWidth *= HALF;
117 if (directionAxis_ == Axis::HORIZONTAL) {
118 auto stepsLength = (backEnd.GetX() - backStart.GetX()) * stepRatio;
119 float dyOffset = backEnd.GetY();
120 float start = backStart.GetX();
121 float end = backEnd.GetX();
122 float current = start;
123 while (LessOrEqual(current, end)) {
124 float dxOffset = std::clamp(current, start, end);
125 canvas.DrawCircle(RSPoint(dxOffset, dyOffset), stepSize * HALF);
126 current += stepsLength;
127 }
128 } else {
129 auto stepsLength = (backEnd.GetY() - backStart.GetY()) * stepRatio;
130 float dxOffset = backEnd.GetX();
131 float start = backStart.GetY();
132 float end = backEnd.GetY();
133 float current = start;
134 while (LessOrEqual(current, end)) {
135 float dyOffset = std::clamp(current, start, end);
136 canvas.DrawCircle(RSPoint(dxOffset, dyOffset), stepSize * HALF);
137 current += stepsLength;
138 }
139 }
140
141 canvas.DetachBrush();
142 }
143
DrawSelect(DrawingContext & context)144 void SliderContentModifier::DrawSelect(DrawingContext& context)
145 {
146 auto& canvas = context.canvas;
147 if (selectStart_->Get() != selectEnd_->Get()) {
148 RSPen selectPen;
149 selectPen.SetAntiAlias(true);
150 selectPen.SetWidth(trackThickness_->Get());
151 selectPen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
152 selectPen.SetColor(ToRSColor(selectColor_->Get()));
153 canvas.AttachPen(selectPen);
154 canvas.DrawLine(RSPoint(selectStart_->Get().GetX(), selectStart_->Get().GetY()),
155 RSPoint(selectEnd_->Get().GetX(), selectEnd_->Get().GetY()));
156 }
157 }
158
DrawDefaultBlock(DrawingContext & context)159 void SliderContentModifier::DrawDefaultBlock(DrawingContext& context)
160 {
161 auto& canvas = context.canvas;
162 RSPen circlePen;
163 circlePen.SetAntiAlias(true);
164 circlePen.SetColor(ToRSColor(blockColor_->Get()));
165 circlePen.SetWidth(blockDiameter_->Get() * HALF);
166 canvas.AttachPen(circlePen);
167 auto penRadius = blockDiameter_->Get() * HALF * HALF;
168 canvas.DrawCircle(RSPoint(circleCenter_->Get().GetX(), circleCenter_->Get().GetY()), penRadius);
169 canvas.DetachPen();
170 }
171
DrawHoverOrPress(DrawingContext & context)172 void SliderContentModifier::DrawHoverOrPress(DrawingContext& context)
173 {
174 auto& canvas = context.canvas;
175 RSPen circleStatePen;
176 circleStatePen.SetAntiAlias(true);
177 // add animate color
178 circleStatePen.SetColor(ToRSColor(boardColor_->Get()));
179 circleStatePen.SetWidth(hotCircleShadowWidth_);
180 canvas.AttachPen(circleStatePen);
181 auto penRadius = (blockDiameter_->Get() + hotCircleShadowWidth_) * HALF;
182 canvas.DrawCircle(RSPoint(circleCenter_->Get().GetX(), circleCenter_->Get().GetY()), penRadius);
183 canvas.DetachPen();
184 }
185
DrawShadow(DrawingContext & context)186 void SliderContentModifier::DrawShadow(DrawingContext& context)
187 {
188 auto& canvas = context.canvas;
189 if (!mouseHoverFlag_ && !mousePressedFlag_) {
190 RSPen circleShadowPen;
191 circleShadowPen.SetAntiAlias(true);
192 circleShadowPen.SetColor(ToRSColor(blockOuterEdgeColor_));
193 circleShadowPen.SetWidth(static_cast<float>(CIRCLE_SHADOW_WIDTH.ConvertToPx()));
194 canvas.AttachPen(circleShadowPen);
195 auto penRadius = (blockDiameter_->Get() + static_cast<float>(CIRCLE_SHADOW_WIDTH.ConvertToPx())) * HALF;
196 canvas.DrawCircle(
197 RSPoint(circleCenter_->Get().GetX(), circleCenter_->Get().GetY()), penRadius);
198 canvas.DetachPen();
199 }
200 }
201
SetBoardColor()202 void SliderContentModifier::SetBoardColor()
203 {
204 CHECK_NULL_VOID(boardColor_);
205 auto pipeline = PipelineBase::GetCurrentContext();
206 CHECK_NULL_VOID(pipeline);
207 auto theme = pipeline->GetTheme<SliderTheme>();
208 CHECK_NULL_VOID(theme);
209 Color shadowColor = Color::TRANSPARENT;
210 shadowColor = mouseHoverFlag_ ? theme->GetBlockHoverColor() : shadowColor;
211 shadowColor = mousePressedFlag_ ? theme->GetBlockPressedColor() : shadowColor;
212 auto duration = mousePressedFlag_ ? static_cast<int32_t>(theme->GetPressAnimationDuration())
213 : static_cast<int32_t>(theme->GetHoverAnimationDuration());
214 auto curve = mousePressedFlag_ ? Curves::SHARP : Curves::FRICTION;
215 AnimationOption option = AnimationOption();
216 option.SetDuration(duration);
217 option.SetCurve(curve);
218 AnimationUtils::Animate(option, [&]() { boardColor_->Set(LinearColor(shadowColor)); });
219 }
220
UpdateData(const Parameters & parameters)221 void SliderContentModifier::UpdateData(const Parameters& parameters)
222 {
223 mouseHoverFlag_ = parameters.mouseHoverFlag_;
224 mousePressedFlag_ = parameters.mousePressedFlag_;
225 hotCircleShadowWidth_ = parameters.hotCircleShadowWidth;
226 }
227
JudgeNeedAimate(const RefPtr<SliderPaintProperty> & property)228 void SliderContentModifier::JudgeNeedAimate(const RefPtr<SliderPaintProperty>& property)
229 {
230 auto reverse = property->GetReverseValue(false);
231 // when reverse is changed, slider block position changes do not animated.
232 if (reverse_ != reverse) {
233 SetNotAnimated();
234 reverse_ = reverse;
235 }
236 }
237
SetSelectSize(const PointF & start,const PointF & end)238 void SliderContentModifier::SetSelectSize(const PointF& start, const PointF& end)
239 {
240 if (selectStart_) {
241 selectStart_->Set(start - PointF());
242 }
243 CHECK_NULL_VOID(selectEnd_);
244 if (needAnimate_) {
245 AnimationOption option = AnimationOption();
246 auto motion =
247 AceType::MakeRefPtr<ResponsiveSpringMotion>(SPRING_MOTION_RESPONSE, SPRING_MOTION_DAMPING_FRACTION);
248 option.SetCurve(motion);
249 AnimationUtils::Animate(option, [&]() { selectEnd_->Set(end - PointF()); });
250 } else {
251 selectEnd_->Set(end - PointF());
252 }
253 }
254
SetCircleCenter(const PointF & center)255 void SliderContentModifier::SetCircleCenter(const PointF& center)
256 {
257 CHECK_NULL_VOID(circleCenter_);
258 if (needAnimate_) {
259 AnimationOption option = AnimationOption();
260 auto motion =
261 AceType::MakeRefPtr<ResponsiveSpringMotion>(SPRING_MOTION_RESPONSE, SPRING_MOTION_DAMPING_FRACTION);
262 option.SetCurve(motion);
263 AnimationUtils::Animate(option, [&]() { circleCenter_->Set(center - PointF()); });
264 } else {
265 circleCenter_->Set(center - PointF());
266 }
267 }
268 } // namespace OHOS::Ace::NG
269