1 /*
2 * Copyright (c) 2021-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
16 #include "core/components/checkable/render_checkbox.h"
17
18 #include "base/log/event_report.h"
19
20 namespace OHOS::Ace {
21 namespace {
22
23 constexpr int32_t DEFUALT_CHECKBOX_ANIMATION_DURATION = 150;
24 constexpr double DEFAULT_MAX_CHECKBOX_SHAPE_SCALE = 1.0;
25 constexpr double DEFAULT_MID_CHECKBOX_SHAPE_SCALE = 0.5;
26 constexpr double DEFAULT_MIN_CHECKBOX_SHAPE_SCALE = 0.0;
27
28 const std::vector<double> CHECKBOX_SCALE = {
29 DEFAULT_MAX_CHECKBOX_SHAPE_SCALE, DEFAULT_MID_CHECKBOX_SHAPE_SCALE, DEFAULT_MIN_CHECKBOX_SHAPE_SCALE
30 };
31 const std::vector<CheckableStatus> CHECKABLE_STATUS = {
32 CheckableStatus::ALL, CheckableStatus::PART, CheckableStatus::NONE
33 };
34 } // namespace
35
Update(const RefPtr<Component> & component)36 void RenderCheckbox::Update(const RefPtr<Component>& component)
37 {
38 RenderCheckable::Update(component);
39 const auto& checkbox = AceType::DynamicCast<CheckboxComponent>(component);
40 if (!checkbox) {
41 LOGE("cast to checkbox component failed");
42 EventReport::SendRenderException(RenderExcepType::RENDER_COMPONENT_ERR);
43 return;
44 }
45 component_ = checkbox;
46
47 if (isDeclarative_) {
48 if (firstUpdate_) {
49 firstUpdate_ = false;
50 } else {
51 if (!component_->GetCheckboxList().empty() && component_->HasValue()) {
52 bool value = component_->GetValue();
53 component_->SetMember(value);
54 component_->SetGroupValue(value ? CheckableStatus::ALL : CheckableStatus::NONE);
55 }
56 }
57 UpdateGroupStatus();
58 component_->SetGroupValue(CHECKABLE_STATUS[static_cast<int32_t>(status_)]);
59
60 checkbox->SetGroupValueUpdateHandler([weak = AceType::WeakClaim(this)](CheckableStatus checked) {
61 auto renderCheckbox = weak.Upgrade();
62 if (renderCheckbox && renderCheckbox->UpdateGroupValue(checked)) {
63 renderCheckbox->MarkNeedRender();
64 }
65 });
66 checkbox->SetItemValueUpdateHandler([weak = AceType::WeakClaim(this)](bool checked) {
67 auto renderCheckbox = weak.Upgrade();
68 if (renderCheckbox && renderCheckbox->UpdateItemValue(checked)) {
69 renderCheckbox->MarkNeedRender();
70 }
71 });
72
73 onGroupChange_ = AceAsyncEvent<void(const std::shared_ptr<BaseEventInfo>&)>::Create(
74 checkbox->GetOnGroupChange(), context_);
75 }
76
77 if (!controller_) {
78 controller_ = CREATE_ANIMATOR(GetContext());
79 auto weak = AceType::WeakClaim(this);
80 controller_->AddStopListener(Animator::StatusCallback([weak]() {
81 auto checkBox = weak.Upgrade();
82 if (checkBox) {
83 checkBox->OnAnimationStop();
84 }
85 }));
86 }
87
88 auto theme = GetTheme<CheckboxTheme>();
89 if (theme) {
90 borderWidth_ = theme->GetBorderWidth();
91 borderRadius_ = theme->GetBorderRadius();
92 checkStroke_ = theme->GetCheckStroke();
93 }
94 if (checkbox->GetUpdateType() == UpdateType::ALL) {
95 checked_ = checkbox->GetValue();
96 }
97
98 ApplyRestoreInfo();
99
100 UpdateUIStatus();
101 UpdateAccessibilityAttr();
102 SetAccessibilityClickImpl();
103 }
104
105
UpdateItemValue(const bool itemValue)106 bool RenderCheckbox::UpdateItemValue(const bool itemValue)
107 {
108
109 if (!(component_->GetCheckboxName().empty())) {
110 UpdateUIStatus();
111 UpdateAnimation();
112 controller_->Play();
113 } else {
114 return false;
115 }
116
117 return true;
118 }
119
UpdateGroupValue(const CheckableStatus groupValue)120 bool RenderCheckbox::UpdateGroupValue(const CheckableStatus groupValue)
121 {
122 if (!(component_->GetGroupName().empty())) {
123 UpdateGroupStatus();
124 if (onGroupChange_) {
125 std::vector<std::string> result;
126 component_->GetSelectedCheckBoxName(result);
127 onGroupChange_(std::make_shared<CheckboxGroupResult>(result, static_cast<int32_t>(status_)));
128 }
129 if (component_->GetGroupValue() != groupValue) {
130 component_->SetGroupValue(groupValue);
131 lastStatus_ = status_;
132 UpdateUIStatus();
133 UpdateAnimation();
134 controller_->Play();
135 } else {
136 return false;
137 }
138 } else {
139 return false;
140 }
141
142 return true;
143 }
144
UpdateAccessibilityAttr()145 void RenderCheckbox::UpdateAccessibilityAttr()
146 {
147 auto accessibilityNode = GetAccessibilityNode().Upgrade();
148 if (!accessibilityNode) {
149 return;
150 }
151 accessibilityNode->SetCheckedState(checked_);
152 if (accessibilityNode->GetClicked()) {
153 accessibilityNode->SetClicked(false);
154 auto context = context_.Upgrade();
155 if (context) {
156 AccessibilityEvent checkboxEvent;
157 checkboxEvent.nodeId = accessibilityNode->GetNodeId();
158 checkboxEvent.eventType = "click";
159 context->SendEventToAccessibility(checkboxEvent);
160 }
161 }
162 }
163
HandleClick()164 void RenderCheckbox::HandleClick()
165 {
166 auto context = context_.Upgrade();
167 if (isDeclarative_) {
168 if (!(component_->GetGroupName().empty())) {
169 lastStatus_ = status_;
170 auto value = (component_->GetGroupValue() ==
171 CheckableStatus::NONE) ? CheckableStatus::ALL : CheckableStatus::NONE;
172 component_->SetGroupValue(value);
173 component_->SetMember(value == CheckableStatus::ALL);
174 UpdateGroupStatus();
175
176 if (onGroupChange_) {
177 std::vector<std::string> result;
178 component_->GetSelectedCheckBoxName(result);
179 onGroupChange_(std::make_shared<CheckboxGroupResult>(result, static_cast<int32_t>(status_)));
180 }
181 } else if (!(component_->GetCheckboxName().empty())) {
182 component_->SetValue(!component_->GetValue());
183 if (component_->GetGroup()) {
184 component_->GetGroup()->SetGroupStatus();
185 }
186 }
187 }
188 UpdateUIStatus();
189 UpdateAnimation();
190 MarkNeedRender();
191 if (controller_) {
192 controller_->Play();
193 }
194 auto accessibilityNode = accessibilityNode_.Upgrade();
195 if (accessibilityNode) {
196 accessibilityNode->SetCheckedState(checked_);
197 }
198 }
199
UpdateAnimation()200 void RenderCheckbox::UpdateAnimation()
201 {
202 if (!controller_) {
203 LOGE("the controller is nullptr");
204 return;
205 }
206 double from = 0.0;
207 double to = 0.0;
208 if (!(component_->GetGroupName().empty())) {
209 from = CHECKBOX_SCALE[static_cast<int32_t>(lastStatus_)];
210 to = CHECKBOX_SCALE[static_cast<int32_t>(status_)];
211 } else if (!(component_->GetCheckboxName().empty())) {
212 if (component_->GetValue()) {
213 from = DEFAULT_MIN_CHECKBOX_SHAPE_SCALE;
214 to = DEFAULT_MAX_CHECKBOX_SHAPE_SCALE;
215 } else {
216 from = DEFAULT_MAX_CHECKBOX_SHAPE_SCALE;
217 to = DEFAULT_MIN_CHECKBOX_SHAPE_SCALE;
218 }
219 } else {
220 if (checked_) {
221 from = DEFAULT_MAX_CHECKBOX_SHAPE_SCALE;
222 to = DEFAULT_MIN_CHECKBOX_SHAPE_SCALE;
223 } else {
224 from = DEFAULT_MAX_CHECKBOX_SHAPE_SCALE;
225 to = DEFAULT_MIN_CHECKBOX_SHAPE_SCALE;
226 }
227 }
228
229 if (translate_) {
230 controller_->RemoveInterpolator(translate_);
231 }
232 translate_ = AceType::MakeRefPtr<CurveAnimation<double>>(from, to, Curves::FRICTION);
233 auto weak = AceType::WeakClaim(this);
234 translate_->AddListener(Animation<double>::ValueCallback([weak](double value) {
235 auto checkBox = weak.Upgrade();
236 if (checkBox) {
237 checkBox->UpdateCheckBoxShape(value);
238 }
239 }));
240 controller_->SetDuration(DEFUALT_CHECKBOX_ANIMATION_DURATION);
241 controller_->AddInterpolator(translate_);
242 }
243
UpdateCheckBoxShape(const double value)244 void RenderCheckbox::UpdateCheckBoxShape(const double value)
245 {
246 if (value < DEFAULT_MIN_CHECKBOX_SHAPE_SCALE || value > DEFAULT_MAX_CHECKBOX_SHAPE_SCALE) {
247 return;
248 }
249 shapeScale_ = value;
250 if (!(component_->GetGroupName().empty())) {
251 if (lastStatus_ == SelectStatus::ALL && status_ == SelectStatus::PART) {
252 uiStatus_ = UIStatus::ON_TO_PART;
253 } else if (lastStatus_ == SelectStatus::ALL && status_ == SelectStatus::NONE) {
254 uiStatus_ = UIStatus::ON_TO_OFF;
255 } else if (lastStatus_ == SelectStatus::PART && status_ == SelectStatus::ALL) {
256 uiStatus_ = UIStatus::PART_TO_ON;
257 } else if (lastStatus_ == SelectStatus::PART && status_ == SelectStatus::NONE) {
258 uiStatus_ = UIStatus::PART_TO_OFF;
259 } else if (lastStatus_ == SelectStatus::NONE && status_ == SelectStatus::ALL) {
260 uiStatus_ = UIStatus::OFF_TO_ON;
261 } else if (lastStatus_ == SelectStatus::NONE && status_ == SelectStatus::PART) {
262 uiStatus_ = UIStatus::OFF_TO_PART;
263 }
264 } else if (!(component_->GetCheckboxName().empty())) {
265 uiStatus_ = (component_->GetValue()) ? UIStatus::OFF_TO_ON : UIStatus::ON_TO_OFF;
266 } else {
267 uiStatus_ = checked_ ? UIStatus::ON_TO_OFF : UIStatus::OFF_TO_ON;
268 }
269
270 MarkNeedRender();
271 }
272
UpdateGroupStatus()273 void RenderCheckbox::UpdateGroupStatus()
274 {
275 if (!component_) {
276 return;
277 }
278
279 auto checkboxList = component_->GetCheckboxList();
280 int count = 0;
281 isGroup_ = !checkboxList.empty();
282 if (isGroup_) {
283 for (auto& item : checkboxList) {
284 if (item->GetValue()) {
285 count++;
286 }
287 }
288 if (count == (int)checkboxList.size()) {
289 status_ = SelectStatus::ALL;
290 } else if (count > 0 && (int)checkboxList.size() > count) {
291 status_ = SelectStatus::PART;
292 } else {
293 status_ = SelectStatus::NONE;
294 }
295 }
296 }
297
OnAnimationStop()298 void RenderCheckbox::OnAnimationStop()
299 {
300 // after the animation stopped,we need to update the check status
301 RenderCheckable::HandleClick();
302 UpdateAccessibilityAttr();
303 }
304
SetAccessibilityClickImpl()305 void RenderCheckbox::SetAccessibilityClickImpl()
306 {
307 auto accessibilityNode = accessibilityNode_.Upgrade();
308 if (accessibilityNode) {
309 auto weakPtr = AceType::WeakClaim(AceType::RawPtr(clickRecognizer_));
310 accessibilityNode->AddSupportAction(AceAction::ACTION_CLICK);
311 accessibilityNode->SetClickableState(true);
312 accessibilityNode->SetCheckableState(true);
313 accessibilityNode->SetActionClickImpl([weakPtr]() {
314 auto click = weakPtr.Upgrade();
315 if (click) {
316 click->OnAccepted();
317 }
318 });
319 }
320 }
321
ProvideRestoreInfo()322 std::string RenderCheckbox::ProvideRestoreInfo()
323 {
324 return std::to_string(checked_);
325 }
326
ApplyRestoreInfo()327 void RenderCheckbox::ApplyRestoreInfo()
328 {
329 if (GetRestoreInfo().empty()) {
330 return;
331 }
332 checked_ = static_cast<size_t>(StringUtils::StringToInt(GetRestoreInfo()));
333 SetRestoreInfo("");
334 }
335
336 } // namespace OHOS::Ace
337