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