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 "core/components_ng/pattern/checkboxgroup/checkboxgroup_paint_method.h"
16
17 #include <optional>
18
19 #include "base/geometry/ng/offset_t.h"
20 #include "base/geometry/ng/rect_t.h"
21 #include "base/geometry/rect.h"
22 #include "base/geometry/rrect.h"
23 #include "base/utils/utils.h"
24 #include "core/components/checkable/checkable_theme.h"
25 #include "core/components/common/properties/alignment.h"
26 #include "core/components/common/properties/color.h"
27 #include "core/components/theme/theme_manager.h"
28 #include "core/components_ng/pattern/checkboxgroup/checkboxgroup_paint_property.h"
29 #include "core/components_ng/pattern/checkboxgroup/checkboxgroup_pattern.h"
30 #include "core/components_ng/render/drawing.h"
31 #include "core/components_ng/render/drawing_prop_convertor.h"
32
33 namespace OHOS::Ace::NG {
34 namespace {
35 constexpr uint8_t ENABLED_ALPHA = 255;
36 constexpr uint8_t DISABLED_ALPHA = 102;
37 constexpr float CHECK_MARK_START_X_POSITION = 0.25f;
38 constexpr float CHECK_MARK_START_Y_POSITION = 0.49f;
39 constexpr float CHECK_MARK_MIDDLE_X_POSITION = 0.44f;
40 constexpr float CHECK_MARK_MIDDLE_Y_POSITION = 0.68f;
41 constexpr float CHECK_MARK_END_X_POSITION = 0.76f;
42 constexpr float CHECK_MARK_END_Y_POSITION = 0.33f;
43 constexpr float CHECK_MARK_PART_START_X_POSITION = 0.20f;
44 constexpr float CHECK_MARK_PART_END_Y_POSITION = 0.80f;
45 constexpr float CHECK_MARK_PART_Y_POSITION = 0.50f;
46 constexpr float CHECK_MARK_LEFT_ANIMATION_PERCENT = 0.45;
47 constexpr float CHECK_MARK_RIGHT_ANIMATION_PERCENT = 0.55;
48 constexpr float DEFAULT_MAX_CHECKBOX_SHAPE_SCALE = 1.0;
49 constexpr float DEFAULT_MIN_CHECKBOX_SHAPE_SCALE = 0.0;
50 } // namespace
51
GetContentDrawFunction(PaintWrapper * paintWrapper)52 CanvasDrawFunction CheckBoxGroupPaintMethod::GetContentDrawFunction(PaintWrapper* paintWrapper)
53 {
54 InitializeParam();
55 auto paintFunc = [weak = WeakClaim(this), paintWrapper](RSCanvas& canvas) {
56 auto checkboxgroup = weak.Upgrade();
57 if (checkboxgroup) {
58 checkboxgroup->PaintCheckBox(canvas, paintWrapper);
59 }
60 };
61 return paintFunc;
62 }
63
InitializeParam()64 void CheckBoxGroupPaintMethod::InitializeParam()
65 {
66 auto pipeline = PipelineContext::GetCurrentContext();
67 CHECK_NULL_VOID(pipeline);
68 auto checkBoxTheme = pipeline->GetTheme<CheckboxTheme>();
69 CHECK_NULL_VOID(checkBoxTheme);
70 borderWidth_ = checkBoxTheme->GetBorderWidth().ConvertToPx();
71 borderRadius_ = checkBoxTheme->GetBorderRadius().ConvertToPx();
72 checkStroke_ = checkBoxTheme->GetCheckStroke().ConvertToPx();
73 pointColor_ = checkBoxTheme->GetPointColor();
74 activeColor_ = checkBoxTheme->GetActiveColor();
75 inactiveColor_ = checkBoxTheme->GetInactiveColor();
76 inactivePointColor_ = checkBoxTheme->GetInactivePointColor();
77 shadowColor_ = checkBoxTheme->GetShadowColor();
78 clickEffectColor_ = checkBoxTheme->GetClickEffectColor();
79 hoverColor_ = checkBoxTheme->GetHoverColor();
80 hoverRadius_ = checkBoxTheme->GetHoverRadius();
81 hotZoneHorizontalPadding_ = checkBoxTheme->GetHotZoneHorizontalPadding();
82 hotZoneVerticalPadding_ = checkBoxTheme->GetHotZoneVerticalPadding();
83 shadowWidth_ = checkBoxTheme->GetShadowWidth();
84 }
85
PaintCheckBox(RSCanvas & canvas,PaintWrapper * paintWrapper) const86 void CheckBoxGroupPaintMethod::PaintCheckBox(RSCanvas& canvas, PaintWrapper* paintWrapper) const
87 {
88 CHECK_NULL_VOID(paintWrapper);
89 auto paintProperty = DynamicCast<CheckBoxGroupPaintProperty>(paintWrapper->GetPaintProperty());
90 CHECK_NULL_VOID(paintProperty);
91 auto contentSize = paintWrapper->GetContentSize();
92 auto status = paintProperty->GetSelectStatus();
93 auto color = activeColor_;
94 if (paintProperty->HasCheckBoxGroupSelectedColor()) {
95 color = paintProperty->GetCheckBoxGroupSelectedColorValue();
96 }
97 auto paintOffset = paintWrapper->GetContentOffset();
98 RSPen pen;
99 RSBrush brush;
100 pen.SetWidth(borderWidth_);
101 pen.SetAntiAlias(true);
102 if (isTouch_) {
103 LOGI("Touch effect is to be realized here");
104 }
105 if (isHover_) {
106 DrawHoverBoard(canvas, contentSize, paintOffset);
107 }
108 if (status == CheckBoxGroupPaintProperty::SelectStatus::PART) {
109 brush.SetColor(ToRSColor(color));
110 brush.SetAntiAlias(true);
111 pen.SetColor(ToRSColor(pointColor_));
112 if (!enabled_) {
113 brush.SetColor(ToRSColor(color.BlendOpacity(static_cast<float>(DISABLED_ALPHA) / ENABLED_ALPHA)));
114 }
115 DrawActiveBorder(canvas, paintOffset, brush, contentSize);
116 DrawPart(canvas, paintOffset, pen, contentSize);
117 return;
118 }
119 if (uiStatus_ == UIStatus::OFF_TO_ON) {
120 brush.SetColor(ToRSColor(color));
121 brush.SetAntiAlias(true);
122 pen.SetColor(ToRSColor(pointColor_));
123 if (!enabled_) {
124 brush.SetColor(ToRSColor(color.BlendOpacity(static_cast<float>(DISABLED_ALPHA) / ENABLED_ALPHA)));
125 }
126 DrawActiveBorder(canvas, paintOffset, brush, contentSize);
127 DrawAnimationOffToOn(canvas, paintOffset, pen, contentSize);
128 } else if (uiStatus_ == UIStatus::ON_TO_OFF) {
129 brush.SetColor(ToRSColor(color));
130 brush.SetAntiAlias(true);
131 pen.SetColor(ToRSColor(pointColor_));
132 if (!enabled_) {
133 brush.SetColor(ToRSColor(color.BlendOpacity(static_cast<float>(DISABLED_ALPHA) / ENABLED_ALPHA)));
134 }
135 DrawActiveBorder(canvas, paintOffset, brush, contentSize);
136 DrawAnimationOnToOff(canvas, paintOffset, pen, contentSize);
137 } else if (uiStatus_ == UIStatus::UNSELECTED || uiStatus_ == UIStatus::PART_TO_OFF) {
138 brush.SetColor(ToRSColor(inactivePointColor_));
139 pen.SetColor(ToRSColor(inactiveColor_));
140 if (!enabled_) {
141 brush.SetColor(
142 ToRSColor(inactivePointColor_.BlendOpacity(static_cast<float>(DISABLED_ALPHA) / ENABLED_ALPHA)));
143 pen.SetColor(ToRSColor(inactiveColor_.BlendOpacity(static_cast<float>(DISABLED_ALPHA) / ENABLED_ALPHA)));
144 }
145 DrawUnselectedBorder(canvas, paintOffset, brush, contentSize);
146 DrawUnselected(canvas, paintOffset, pen, contentSize);
147 }
148 }
149
DrawUnselected(RSCanvas & canvas,const OffsetF & origin,RSPen & pen,SizeF & paintSize) const150 void CheckBoxGroupPaintMethod::DrawUnselected(
151 RSCanvas& canvas, const OffsetF& origin, RSPen& pen, SizeF& paintSize) const
152 {
153 float originX = origin.GetX() + borderWidth_ / 2.0;
154 float originY = origin.GetY() + borderWidth_ / 2.0;
155 float endX = originX + paintSize.Width() - borderWidth_;
156 float endY = originY + paintSize.Height() - borderWidth_;
157 auto rrect = RSRoundRect({ originX, originY, endX, endY }, borderRadius_, borderRadius_);
158 canvas.AttachPen(pen);
159 canvas.DrawRoundRect(rrect);
160 }
161
DrawActiveBorder(RSCanvas & canvas,const OffsetF & paintOffset,RSBrush & brush,const SizeF & paintSize) const162 void CheckBoxGroupPaintMethod::DrawActiveBorder(
163 RSCanvas& canvas, const OffsetF& paintOffset, RSBrush& brush, const SizeF& paintSize) const
164 {
165 float originX = paintOffset.GetX();
166 float originY = paintOffset.GetY();
167 float endX = originX + paintSize.Width();
168 float endY = originY + paintSize.Height();
169 auto rrect = RSRoundRect({ originX, originY, endX, endY }, borderRadius_, borderRadius_);
170 canvas.AttachBrush(brush);
171 canvas.DrawRoundRect(rrect);
172 }
173
DrawUnselectedBorder(RSCanvas & canvas,const OffsetF & paintOffset,RSBrush & brush,const SizeF & paintSize) const174 void CheckBoxGroupPaintMethod::DrawUnselectedBorder(
175 RSCanvas& canvas, const OffsetF& paintOffset, RSBrush& brush, const SizeF& paintSize) const
176 {
177 float originX = paintOffset.GetX() + borderWidth_;
178 float originY = paintOffset.GetY() + borderWidth_;
179 float endX = originX + paintSize.Width() - 2 * borderWidth_;
180 float endY = originY + paintSize.Height() - 2 * borderWidth_;
181 auto rrect = RSRoundRect({ originX, originY, endX, endY }, borderRadius_, borderRadius_);
182 canvas.AttachBrush(brush);
183 canvas.DrawRoundRect(rrect);
184 }
185
DrawPart(RSCanvas & canvas,const OffsetF & origin,RSPen & pen,const SizeF & paintSize) const186 void CheckBoxGroupPaintMethod::DrawPart(
187 RSCanvas& canvas, const OffsetF& origin, RSPen& pen, const SizeF& paintSize) const
188 {
189 RSPath path;
190 RSPen shadowPen;
191 float originX = origin.GetX();
192 float originY = origin.GetY();
193 const Offset start =
194 Offset(paintSize.Width() * CHECK_MARK_PART_START_X_POSITION, paintSize.Width() * CHECK_MARK_PART_Y_POSITION);
195 const Offset end =
196 Offset(paintSize.Width() * CHECK_MARK_PART_END_Y_POSITION, paintSize.Width() * CHECK_MARK_PART_Y_POSITION);
197 path.MoveTo(originX + start.GetX(), originY + start.GetY());
198 path.LineTo(originX + end.GetX(), originY + end.GetY());
199 shadowPen.SetColor(ToRSColor(shadowColor_));
200 shadowPen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
201 shadowPen.SetWidth(checkStroke_ + shadowWidth_.ConvertToPx() * 2);
202 canvas.AttachPen(shadowPen);
203 canvas.DrawPath(path);
204 pen.SetWidth(checkStroke_);
205 pen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
206 canvas.AttachPen(pen);
207 canvas.DrawPath(path);
208 }
209
DrawAnimationOffToOn(RSCanvas & canvas,const OffsetF & origin,RSPen & pen,const SizeF & paintSize) const210 void CheckBoxGroupPaintMethod::DrawAnimationOffToOn(
211 RSCanvas& canvas, const OffsetF& origin, RSPen& pen, const SizeF& paintSize) const
212 {
213 RSPath path;
214 RSPen shadowPen = RSPen(pen);
215 float originX = origin.GetX();
216 float originY = origin.GetY();
217 const Offset start =
218 Offset(paintSize.Width() * CHECK_MARK_START_X_POSITION, paintSize.Height() * CHECK_MARK_START_Y_POSITION);
219 const Offset middle =
220 Offset(paintSize.Width() * CHECK_MARK_MIDDLE_X_POSITION, paintSize.Height() * CHECK_MARK_MIDDLE_Y_POSITION);
221 const Offset end =
222 Offset(paintSize.Width() * CHECK_MARK_END_X_POSITION, paintSize.Height() * CHECK_MARK_END_Y_POSITION);
223
224 float deltaX = middle.GetX() - start.GetX();
225 float deltaY = middle.GetY() - start.GetY();
226 path.MoveTo(originX + start.GetX(), originY + start.GetY());
227 shadowPen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
228 shadowPen.SetWidth(checkStroke_ + shadowWidth_.ConvertToPx() * 2);
229 shadowPen.SetColor(ToRSColor(shadowColor_));
230 pen.SetWidth(checkStroke_);
231 pen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
232 float ratio = DEFAULT_MIN_CHECKBOX_SHAPE_SCALE;
233 if (shapeScale_ < CHECK_MARK_LEFT_ANIMATION_PERCENT) {
234 ratio = shapeScale_ / CHECK_MARK_LEFT_ANIMATION_PERCENT;
235 path.LineTo(originX + start.GetX() + deltaX * ratio, originY + start.GetY() + deltaY * ratio);
236 } else {
237 path.LineTo(originX + middle.GetX(), originY + middle.GetY());
238 }
239 canvas.AttachPen(shadowPen);
240 canvas.DrawPath(path);
241 canvas.AttachPen(pen);
242 canvas.DrawPath(path);
243 if (shapeScale_ > CHECK_MARK_LEFT_ANIMATION_PERCENT) {
244 deltaX = end.GetX() - middle.GetX();
245 deltaY = middle.GetY() - end.GetY();
246 path.MoveTo(originX + middle.GetX(), originY + middle.GetY());
247 if (shapeScale_ == DEFAULT_MAX_CHECKBOX_SHAPE_SCALE) {
248 path.LineTo(originX + end.GetX(), originY + end.GetY());
249 } else {
250 ratio = (shapeScale_ - CHECK_MARK_LEFT_ANIMATION_PERCENT) / CHECK_MARK_RIGHT_ANIMATION_PERCENT;
251 path.LineTo(originX + middle.GetX() + deltaX * ratio, originY + middle.GetY() - deltaY * ratio);
252 }
253 canvas.AttachPen(shadowPen);
254 canvas.DrawPath(path);
255 canvas.AttachPen(pen);
256 canvas.DrawPath(path);
257 }
258 }
259
DrawAnimationOnToOff(RSCanvas & canvas,const OffsetF & origin,RSPen & pen,const SizeF & paintSize) const260 void CheckBoxGroupPaintMethod::DrawAnimationOnToOff(
261 RSCanvas& canvas, const OffsetF& origin, RSPen& pen, const SizeF& paintSize) const
262 {
263 if (shapeScale_ == DEFAULT_MIN_CHECKBOX_SHAPE_SCALE) {
264 return;
265 }
266 RSPath path;
267 RSPen shadowPen = RSPen(pen);
268 float originX = origin.GetX();
269 float originY = origin.GetY();
270 const Offset start =
271 Offset(paintSize.Width() * CHECK_MARK_START_X_POSITION, paintSize.Height() * CHECK_MARK_START_Y_POSITION);
272 const Offset middle =
273 Offset(paintSize.Width() * CHECK_MARK_MIDDLE_X_POSITION, paintSize.Height() * CHECK_MARK_MIDDLE_Y_POSITION);
274 const Offset end =
275 Offset(paintSize.Width() * CHECK_MARK_END_X_POSITION, paintSize.Height() * CHECK_MARK_END_Y_POSITION);
276 const Offset middlePoint = Offset(paintSize.Width() / 2, paintSize.Height() / 2);
277 float deltaX = middlePoint.GetX() - start.GetX();
278 float deltaY = middlePoint.GetY() - start.GetY();
279 float ratio = DEFAULT_MAX_CHECKBOX_SHAPE_SCALE - shapeScale_;
280 shadowPen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
281 shadowPen.SetWidth(checkStroke_ + shadowWidth_.ConvertToPx() * 2);
282 shadowPen.SetColor(ToRSColor(shadowColor_));
283 pen.SetWidth(checkStroke_);
284 pen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
285 path.MoveTo(originX + start.GetX() + deltaX * ratio, originY + start.GetY() + deltaY * ratio);
286 deltaX = middlePoint.GetX() - middle.GetX();
287 deltaY = middle.GetY() - middlePoint.GetY();
288 path.LineTo(originX + middle.GetX() + deltaX * ratio, originY + middle.GetY() - deltaY * ratio);
289 canvas.AttachPen(shadowPen);
290 canvas.DrawPath(path);
291 canvas.AttachPen(pen);
292 canvas.DrawPath(path);
293 path.MoveTo(originX + middle.GetX() + deltaX * ratio, originY + middle.GetY() - deltaY * ratio);
294 deltaX = end.GetX() - middlePoint.GetX();
295 deltaY = middlePoint.GetY() - end.GetY();
296 path.LineTo(originX + end.GetX() - deltaX * ratio, originY + end.GetY() + deltaY * ratio);
297 canvas.AttachPen(shadowPen);
298 canvas.DrawPath(path);
299 canvas.AttachPen(pen);
300 canvas.DrawPath(path);
301 }
302
DrawTouchBoard(RSCanvas & canvas,const SizeF & size,const OffsetF & offset) const303 void CheckBoxGroupPaintMethod::DrawTouchBoard(RSCanvas& canvas, const SizeF& size, const OffsetF& offset) const
304 {
305 RSBrush brush;
306 brush.SetColor(ToRSColor(Color(clickEffectColor_)));
307 brush.SetAntiAlias(true);
308 float originX = offset.GetX() - hotZoneHorizontalPadding_.ConvertToPx();
309 float originY = offset.GetY() - hotZoneVerticalPadding_.ConvertToPx();
310 float endX = size.Width() + originX + 2 * hotZoneHorizontalPadding_.ConvertToPx();
311 float endY = size.Height() + originY + 2 * hotZoneVerticalPadding_.ConvertToPx();
312 auto rrect = RSRoundRect({ originX, originY, endX, endY }, hoverRadius_.ConvertToPx(), hoverRadius_.ConvertToPx());
313 canvas.AttachBrush(brush);
314 canvas.DrawRoundRect(rrect);
315 }
316
DrawHoverBoard(RSCanvas & canvas,const SizeF & size,const OffsetF & offset) const317 void CheckBoxGroupPaintMethod::DrawHoverBoard(RSCanvas& canvas, const SizeF& size, const OffsetF& offset) const
318 {
319 RSBrush brush;
320 brush.SetColor(ToRSColor(Color(hoverColor_)));
321 brush.SetAntiAlias(true);
322 float originX = offset.GetX() - hotZoneHorizontalPadding_.ConvertToPx();
323 float originY = offset.GetY() - hotZoneVerticalPadding_.ConvertToPx();
324 float endX = size.Width() + originX + 2 * hotZoneHorizontalPadding_.ConvertToPx();
325 float endY = size.Height() + originY + 2 * hotZoneVerticalPadding_.ConvertToPx();
326 auto rrect = RSRoundRect({ originX, originY, endX, endY }, hoverRadius_.ConvertToPx(), hoverRadius_.ConvertToPx());
327 canvas.AttachBrush(brush);
328 canvas.DrawRoundRect(rrect);
329 }
330
331 } // namespace OHOS::Ace::NG
332