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/checkboxgroup/checkboxgroup_modifier.h"
17
18 #include "base/utils/utils.h"
19 #include "core/common/container.h"
20 #include "core/components/checkable/checkable_theme.h"
21 #include "core/components_ng/base/modifier.h"
22 #include "core/components_ng/pattern/checkboxgroup/checkboxgroup_paint_property.h"
23 #include "core/components_ng/render/drawing.h"
24 #include "core/components_ng/render/drawing_prop_convertor.h"
25
26 namespace OHOS::Ace::NG {
27 namespace {
28 constexpr uint8_t ENABLED_ALPHA = 255;
29 constexpr uint8_t DISABLED_ALPHA = 102;
30 constexpr float CHECK_MARK_START_X_POSITION = 0.25f;
31 constexpr float CHECK_MARK_START_Y_POSITION = 0.49f;
32 constexpr float CHECK_MARK_MIDDLE_X_POSITION = 0.44f;
33 constexpr float CHECK_MARK_MIDDLE_Y_POSITION = 0.68f;
34 constexpr float CHECK_MARK_END_X_POSITION = 0.76f;
35 constexpr float CHECK_MARK_END_Y_POSITION = 0.33f;
36 constexpr float CHECK_MARK_PART_START_X_POSITION = 0.20f;
37 constexpr float CHECK_MARK_PART_END_Y_POSITION = 0.80f;
38 constexpr float CHECK_MARK_PART_Y_POSITION = 0.50f;
39 constexpr float CHECKBOX_GROUP_DOUBLE_RATIO = 2.0f;
40 constexpr float CHECKBOX_GROUP_LENGTH_ZERO = 0.0f;
41 } // namespace
42
CheckBoxGroupModifier(const Parameters & parameters)43 CheckBoxGroupModifier::CheckBoxGroupModifier(const Parameters& parameters)
44 {
45 activeColor_ = AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(parameters.activeColor));
46 pointColor_ = AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(parameters.pointColor));
47 inactiveColor_ = AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(parameters.inactiveColor));
48 checkMarkPaintSize_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(parameters.checkMarkPaintSize);
49 checkStroke_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(parameters.checkStroke);
50 enabled_ = AceType::MakeRefPtr<PropertyBool>(true);
51 uiStatus_ = AceType::MakeRefPtr<PropertyInt>(static_cast<int>(parameters.uiStatus));
52 status_ = AceType::MakeRefPtr<PropertyInt>(static_cast<int>(UIStatus::UNSELECTED));
53 offset_ = AceType::MakeRefPtr<PropertyOffsetF>(OffsetF());
54 size_ = AceType::MakeRefPtr<PropertySizeF>(SizeF());
55 animateTouchHoverColor_ = AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(Color::TRANSPARENT));
56
57 borderWidth_ = parameters.borderWidth;
58 borderRadius_ = parameters.borderRadius;
59 shadowColor_ = parameters.shadowColor;
60 clickEffectColor_ = parameters.clickEffectColor;
61 hoverColor_ = parameters.hoverColor;
62 inactivePointColor_ = parameters.inactivePointColor;
63 hoverRadius_ = parameters.hoverRadius;
64 hotZoneHorizontalPadding_ = parameters.hotZoneHorizontalPadding;
65 defaultPaddingSize_ = parameters.defaultPaddingSize;
66 hotZoneVerticalPadding_ = parameters.hotZoneVerticalPadding;
67 shadowWidth_ = parameters.shadowWidth;
68 hoverDuration_ = parameters.hoverDuration;
69 hoverToTouchDuration_ = parameters.hoverToTouchDuration;
70 checkBoxGroupShape_ = AceType::MakeRefPtr<PropertyInt>(static_cast<int32_t>(CheckBoxStyle::CIRCULAR_STYLE));
71 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
72 checkBoxGroupStyle_ = CheckBoxStyle::CIRCULAR_STYLE;
73 } else {
74 checkBoxGroupStyle_ = CheckBoxStyle::SQUARE_STYLE;
75 }
76 AttachProperty(activeColor_);
77 AttachProperty(pointColor_);
78 AttachProperty(inactiveColor_);
79 AttachProperty(checkMarkPaintSize_);
80 AttachProperty(checkStroke_);
81 AttachProperty(enabled_);
82 AttachProperty(uiStatus_);
83 AttachProperty(status_);
84 AttachProperty(offset_);
85 AttachProperty(size_);
86 AttachProperty(animateTouchHoverColor_);
87 AttachProperty(checkBoxGroupShape_);
88 }
89
PaintCheckBox(DrawingContext & context,const OffsetF & paintOffset,const SizeF & contentSize) const90 void CheckBoxGroupModifier::PaintCheckBox(
91 DrawingContext& context, const OffsetF& paintOffset, const SizeF& contentSize) const
92 {
93 auto& canvas = context.canvas;
94 auto color = activeColor_;
95
96 RSPen pen;
97 RSBrush brush;
98 pen.SetWidth(borderWidth_);
99 pen.SetAntiAlias(true);
100 DrawTouchAndHoverBoard(canvas, contentSize, paintOffset);
101 RSPen shadowPen = RSPen(pen);
102 shadowPen.SetColor(ToRSColor(shadowColor_));
103 if (static_cast<CheckBoxGroupPaintProperty::SelectStatus>(status_->Get()) ==
104 CheckBoxGroupPaintProperty::SelectStatus::PART) {
105 PaintCheckBoxGroupPartStatus(canvas, paintOffset, brush, pen, contentSize);
106 return;
107 }
108 if (static_cast<UIStatus>(uiStatus_->Get()) == UIStatus::OFF_TO_ON ||
109 static_cast<UIStatus>(uiStatus_->Get()) == UIStatus::PART_TO_ON) {
110 brush.SetColor(ToRSColor(color->Get()));
111 brush.SetAntiAlias(true);
112 pen.SetColor(ToRSColor(pointColor_->Get()));
113 if (!enabled_->Get()) {
114 brush.SetColor(ToRSColor(color->Get().BlendOpacity(static_cast<float>(DISABLED_ALPHA) / ENABLED_ALPHA)));
115 }
116 DrawActiveBorder(canvas, paintOffset, brush, contentSize);
117 DrawCheck(canvas, paintOffset, pen, shadowPen, contentSize);
118 } else {
119 brush.SetColor(ToRSColor(inactivePointColor_));
120 pen.SetColor(ToRSColor(inactiveColor_->Get()));
121 if (!enabled_->Get()) {
122 brush.SetColor(
123 ToRSColor(inactivePointColor_.BlendOpacity(static_cast<float>(DISABLED_ALPHA) / ENABLED_ALPHA)));
124 pen.SetColor(
125 ToRSColor(inactiveColor_->Get().BlendOpacity(static_cast<float>(DISABLED_ALPHA) / ENABLED_ALPHA)));
126 }
127 DrawUnselectedBorder(canvas, paintOffset, brush, contentSize);
128 DrawUnselected(canvas, paintOffset, pen, contentSize);
129 }
130 }
131
PaintCheckBoxGroupPartStatus(RSCanvas & canvas,const OffsetF & paintOffset,RSBrush & brush,RSPen & pen,const SizeF & paintSize) const132 void CheckBoxGroupModifier::PaintCheckBoxGroupPartStatus(
133 RSCanvas& canvas, const OffsetF& paintOffset, RSBrush& brush, RSPen& pen, const SizeF& paintSize) const
134 {
135 auto color = activeColor_;
136 brush.SetColor(ToRSColor(color->Get()));
137 brush.SetAntiAlias(true);
138 pen.SetColor(ToRSColor(pointColor_->Get()));
139 if (!enabled_->Get()) {
140 brush.SetColor(ToRSColor(color->Get().BlendOpacity(static_cast<float>(DISABLED_ALPHA) / ENABLED_ALPHA)));
141 }
142 DrawActiveBorder(canvas, paintOffset, brush, paintSize);
143 DrawPart(canvas, paintOffset, pen, paintSize);
144 }
145
DrawCheck(RSCanvas & canvas,const OffsetF & origin,RSPen & pen,RSPen & shadowPen,const SizeF & paintSize) const146 void CheckBoxGroupModifier::DrawCheck(
147 RSCanvas& canvas, const OffsetF& origin, RSPen& pen, RSPen& shadowPen, const SizeF& paintSize) const
148 {
149 if (checkMarkPaintSize_->Get() == CHECKBOX_GROUP_LENGTH_ZERO || checkStroke_->Get() == CHECKBOX_GROUP_LENGTH_ZERO) {
150 return;
151 }
152 #ifndef USE_ROSEN_DRAWING
153 RSPath path;
154 #else
155 RSRecordingPath path;
156 #endif
157 float originX = origin.GetX();
158 float originY = origin.GetY();
159 float strokeSize = checkMarkPaintSize_->Get();
160 const Offset start = Offset(strokeSize * CHECK_MARK_START_X_POSITION, strokeSize * CHECK_MARK_START_Y_POSITION);
161 const Offset middle = Offset(strokeSize * CHECK_MARK_MIDDLE_X_POSITION, strokeSize * CHECK_MARK_MIDDLE_Y_POSITION);
162 const Offset end = Offset(strokeSize * CHECK_MARK_END_X_POSITION, strokeSize * CHECK_MARK_END_Y_POSITION);
163 path.MoveTo(originX + start.GetX() + (paintSize.Width() - strokeSize) / CHECKBOX_GROUP_DOUBLE_RATIO,
164 originY + start.GetY() + (paintSize.Height() - strokeSize) / CHECKBOX_GROUP_DOUBLE_RATIO);
165 path.LineTo(originX + middle.GetX() + (paintSize.Width() - strokeSize) / CHECKBOX_GROUP_DOUBLE_RATIO,
166 originY + middle.GetY() + (paintSize.Height() - strokeSize) / CHECKBOX_GROUP_DOUBLE_RATIO);
167 path.MoveTo(originX + middle.GetX() + (paintSize.Width() - strokeSize) / CHECKBOX_GROUP_DOUBLE_RATIO,
168 originY + middle.GetY() + (paintSize.Height() - strokeSize) / CHECKBOX_GROUP_DOUBLE_RATIO);
169 path.LineTo(originX + end.GetX() + (paintSize.Width() - strokeSize) / CHECKBOX_GROUP_DOUBLE_RATIO,
170 originY + end.GetY() + (paintSize.Height() - strokeSize) / CHECKBOX_GROUP_DOUBLE_RATIO);
171 shadowPen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
172 shadowPen.SetWidth(checkStroke_->Get() + shadowWidth_.ConvertToPx() * CHECKBOX_GROUP_DOUBLE_RATIO);
173 pen.SetWidth(checkStroke_->Get());
174 pen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
175 canvas.AttachPen(shadowPen);
176 canvas.DrawPath(path);
177 canvas.AttachPen(pen);
178 canvas.DrawPath(path);
179 #ifdef USE_ROSEN_DRAWING
180 canvas.DetachPen();
181 #endif
182 }
183
DrawUnselected(RSCanvas & canvas,const OffsetF & origin,RSPen & pen,const SizeF & paintSize) const184 void CheckBoxGroupModifier::DrawUnselected(
185 RSCanvas& canvas, const OffsetF& origin, RSPen& pen, const SizeF& paintSize) const
186 {
187 float originX = origin.GetX() + borderWidth_ / CHECKBOX_GROUP_DOUBLE_RATIO;
188 float originY = origin.GetY() + borderWidth_ / CHECKBOX_GROUP_DOUBLE_RATIO;
189 float endX = originX + paintSize.Width() - borderWidth_;
190 float endY = originY + paintSize.Height() - borderWidth_;
191 RSRect rect(originX, originY, endX, endY);
192 auto rrect = RSRoundRect(rect, borderRadius_, borderRadius_);
193 canvas.AttachPen(pen);
194 DrawRectOrCircle(canvas, rrect);
195 #ifdef USE_ROSEN_DRAWING
196 canvas.DetachPen();
197 #endif
198 }
199
DrawActiveBorder(RSCanvas & canvas,const OffsetF & paintOffset,RSBrush & brush,const SizeF & paintSize) const200 void CheckBoxGroupModifier::DrawActiveBorder(
201 RSCanvas& canvas, const OffsetF& paintOffset, RSBrush& brush, const SizeF& paintSize) const
202 {
203 float originX = paintOffset.GetX();
204 float originY = paintOffset.GetY();
205 float endX = originX + paintSize.Width();
206 float endY = originY + paintSize.Height();
207 RSRect rect(originX, originY, endX, endY);
208 auto rrect = RSRoundRect(rect, borderRadius_, borderRadius_);
209 canvas.AttachBrush(brush);
210 DrawRectOrCircle(canvas, rrect);
211 canvas.DetachBrush();
212 }
213
DrawUnselectedBorder(RSCanvas & canvas,const OffsetF & paintOffset,RSBrush & brush,const SizeF & paintSize) const214 void CheckBoxGroupModifier::DrawUnselectedBorder(
215 RSCanvas& canvas, const OffsetF& paintOffset, RSBrush& brush, const SizeF& paintSize) const
216 {
217 float originX = paintOffset.GetX() + borderWidth_;
218 float originY = paintOffset.GetY() + borderWidth_;
219 float endX = originX + paintSize.Width() - CHECKBOX_GROUP_DOUBLE_RATIO * borderWidth_;
220 float endY = originY + paintSize.Height() - CHECKBOX_GROUP_DOUBLE_RATIO * borderWidth_;
221 RSRect rect(originX, originY, endX, endY);
222 auto rrect = RSRoundRect(rect, borderRadius_, borderRadius_);
223 canvas.AttachBrush(brush);
224 DrawRectOrCircle(canvas, rrect);
225 #ifdef USE_ROSEN_DRAWING
226 canvas.DetachBrush();
227 #endif
228 }
229
DrawPart(RSCanvas & canvas,const OffsetF & origin,RSPen & pen,const SizeF & paintSize) const230 void CheckBoxGroupModifier::DrawPart(RSCanvas& canvas, const OffsetF& origin, RSPen& pen, const SizeF& paintSize) const
231 {
232 if (checkMarkPaintSize_->Get() == CHECKBOX_GROUP_LENGTH_ZERO || checkStroke_->Get() == CHECKBOX_GROUP_LENGTH_ZERO) {
233 return;
234 }
235 #ifndef USE_ROSEN_DRAWING
236 RSPath path;
237 #else
238 RSRecordingPath path;
239 #endif
240 RSPen shadowPen;
241 float originX = origin.GetX();
242 float originY = origin.GetY();
243 const Offset start = Offset(checkMarkPaintSize_->Get() * CHECK_MARK_PART_START_X_POSITION,
244 checkMarkPaintSize_->Get() * CHECK_MARK_PART_Y_POSITION);
245 const Offset end = Offset(checkMarkPaintSize_->Get() * CHECK_MARK_PART_END_Y_POSITION,
246 checkMarkPaintSize_->Get() * CHECK_MARK_PART_Y_POSITION);
247 path.MoveTo(originX + start.GetX() + (paintSize.Width() - checkMarkPaintSize_->Get()) / CHECKBOX_GROUP_DOUBLE_RATIO,
248 originY + start.GetY() + (paintSize.Height() - checkMarkPaintSize_->Get()) / CHECKBOX_GROUP_DOUBLE_RATIO);
249 path.LineTo(originX + end.GetX() + (paintSize.Width() - checkMarkPaintSize_->Get()) / CHECKBOX_GROUP_DOUBLE_RATIO,
250 originY + end.GetY() + (paintSize.Height() - checkMarkPaintSize_->Get()) / CHECKBOX_GROUP_DOUBLE_RATIO);
251 shadowPen.SetColor(ToRSColor(shadowColor_));
252 shadowPen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
253 shadowPen.SetWidth(checkStroke_->Get() + shadowWidth_.ConvertToPx() * CHECKBOX_GROUP_DOUBLE_RATIO);
254 canvas.AttachPen(shadowPen);
255 canvas.DrawPath(path);
256 pen.SetWidth(checkStroke_->Get());
257 pen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
258 canvas.AttachPen(pen);
259 canvas.DrawPath(path);
260 #ifdef USE_ROSEN_DRAWING
261 canvas.DetachPen();
262 #endif
263 }
264
DrawTouchAndHoverBoard(RSCanvas & canvas,const SizeF & size,const OffsetF & offset) const265 void CheckBoxGroupModifier::DrawTouchAndHoverBoard(RSCanvas& canvas, const SizeF& size, const OffsetF& offset) const
266 {
267 if (HoverEffectType::NONE == hoverEffectType_) {
268 return;
269 }
270 RSBrush brush;
271 brush.SetColor(ToRSColor(animateTouchHoverColor_->Get()));
272 brush.SetAntiAlias(true);
273 float originX;
274 float originY;
275 float endX;
276 float endY;
277 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
278 originX = offset.GetX() - defaultPaddingSize_.ConvertToPx();
279 originY = offset.GetY() - defaultPaddingSize_.ConvertToPx();
280 endX = size.Width() + originX + CHECKBOX_GROUP_DOUBLE_RATIO * defaultPaddingSize_.ConvertToPx();
281 endY = size.Height() + originY + CHECKBOX_GROUP_DOUBLE_RATIO * defaultPaddingSize_.ConvertToPx();
282 } else {
283 originX = offset.GetX() - hotZoneHorizontalPadding_.ConvertToPx();
284 originY = offset.GetY() - hotZoneVerticalPadding_.ConvertToPx();
285 endX = size.Width() + originX + CHECKBOX_GROUP_DOUBLE_RATIO * hotZoneHorizontalPadding_.ConvertToPx();
286 endY = size.Height() + originY + CHECKBOX_GROUP_DOUBLE_RATIO * hotZoneVerticalPadding_.ConvertToPx();
287 }
288 auto rrect = RSRoundRect({ originX, originY, endX, endY }, hoverRadius_.ConvertToPx(), hoverRadius_.ConvertToPx());
289 canvas.AttachBrush(brush);
290 DrawRectOrCircle(canvas, rrect);
291 #ifdef USE_ROSEN_DRAWING
292 canvas.DetachBrush();
293 #endif
294 }
295
DrawRectOrCircle(RSCanvas & canvas,const RSRoundRect & rrect) const296 void CheckBoxGroupModifier::DrawRectOrCircle(RSCanvas& canvas, const RSRoundRect& rrect) const
297 {
298 if (CheckBoxStyle::SQUARE_STYLE == checkBoxGroupStyle_) {
299 canvas.DrawRoundRect(rrect);
300 } else {
301 RSScalar halfDenominator = 2.0f;
302 RSRect rect = rrect.GetRect();
303 RSScalar x = (rect.GetLeft() + rect.GetRight()) / halfDenominator;
304 RSScalar y = (rect.GetTop() + rect.GetBottom()) / halfDenominator;
305 RSPoint centerPt(x, y);
306 RSScalar radius = std::min(rect.GetWidth(), rect.GetHeight()) / halfDenominator;
307 canvas.DrawCircle(centerPt, radius);
308 }
309 }
310 } // namespace OHOS::Ace::NG
311