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
UpdateItemValue(const bool itemValue)105 bool RenderCheckbox::UpdateItemValue(const bool itemValue)
106 {
107 if (!(component_->GetCheckboxName().empty())) {
108 UpdateUIStatus();
109 UpdateAnimation();
110 controller_->Play();
111 } else {
112 return false;
113 }
114
115 return true;
116 }
117
UpdateGroupValue(const CheckableStatus groupValue)118 bool RenderCheckbox::UpdateGroupValue(const CheckableStatus groupValue)
119 {
120 if (!(component_->GetGroupName().empty())) {
121 UpdateGroupStatus();
122 if (onGroupChange_) {
123 std::vector<std::string> result;
124 component_->GetSelectedCheckBoxName(result);
125 onGroupChange_(std::make_shared<CheckboxGroupResult>(result, static_cast<int32_t>(status_)));
126 }
127 if (component_->GetGroupValue() != groupValue) {
128 component_->SetGroupValue(groupValue);
129 lastStatus_ = status_;
130 UpdateUIStatus();
131 UpdateAnimation();
132 controller_->Play();
133 } else {
134 return false;
135 }
136 } else {
137 return false;
138 }
139
140 return true;
141 }
142
UpdateAccessibilityAttr()143 void RenderCheckbox::UpdateAccessibilityAttr()
144 {
145 auto accessibilityNode = GetAccessibilityNode().Upgrade();
146 if (!accessibilityNode) {
147 return;
148 }
149 accessibilityNode->SetCheckedState(checked_);
150 if (accessibilityNode->GetClicked()) {
151 accessibilityNode->SetClicked(false);
152 auto context = context_.Upgrade();
153 if (context) {
154 AccessibilityEvent checkboxEvent;
155 checkboxEvent.nodeId = accessibilityNode->GetNodeId();
156 checkboxEvent.eventType = "click";
157 context->SendEventToAccessibility(checkboxEvent);
158 }
159 }
160 }
161
HandleClick()162 void RenderCheckbox::HandleClick()
163 {
164 auto context = context_.Upgrade();
165 if (isDeclarative_) {
166 if (!(component_->GetGroupName().empty())) {
167 lastStatus_ = status_;
168 auto value = (component_->GetGroupValue() ==
169 CheckableStatus::NONE) ? CheckableStatus::ALL : CheckableStatus::NONE;
170 component_->SetGroupValue(value);
171 component_->SetMember(value == CheckableStatus::ALL);
172 UpdateGroupStatus();
173
174 if (onGroupChange_) {
175 std::vector<std::string> result;
176 component_->GetSelectedCheckBoxName(result);
177 onGroupChange_(std::make_shared<CheckboxGroupResult>(result, static_cast<int32_t>(status_)));
178 }
179 } else if (!(component_->GetCheckboxName().empty())) {
180 component_->SetValue(!component_->GetValue());
181 if (component_->GetGroup()) {
182 component_->GetGroup()->SetGroupStatus();
183 }
184 }
185 }
186 UpdateUIStatus();
187 UpdateAnimation();
188 MarkNeedRender();
189 if (controller_) {
190 controller_->Play();
191 }
192 auto accessibilityNode = accessibilityNode_.Upgrade();
193 if (accessibilityNode) {
194 accessibilityNode->SetCheckedState(checked_);
195 }
196 }
197
UpdateAnimation()198 void RenderCheckbox::UpdateAnimation()
199 {
200 if (!controller_) {
201 LOGE("the controller is nullptr");
202 return;
203 }
204 double from = 0.0;
205 double to = 0.0;
206 if (!(component_->GetGroupName().empty())) {
207 from = CHECKBOX_SCALE[static_cast<int32_t>(lastStatus_)];
208 to = CHECKBOX_SCALE[static_cast<int32_t>(status_)];
209 } else if (!(component_->GetCheckboxName().empty())) {
210 if (component_->GetValue()) {
211 from = DEFAULT_MIN_CHECKBOX_SHAPE_SCALE;
212 to = DEFAULT_MAX_CHECKBOX_SHAPE_SCALE;
213 } else {
214 from = DEFAULT_MAX_CHECKBOX_SHAPE_SCALE;
215 to = DEFAULT_MIN_CHECKBOX_SHAPE_SCALE;
216 }
217 } else {
218 if (checked_) {
219 from = DEFAULT_MAX_CHECKBOX_SHAPE_SCALE;
220 to = DEFAULT_MIN_CHECKBOX_SHAPE_SCALE;
221 } else {
222 from = DEFAULT_MAX_CHECKBOX_SHAPE_SCALE;
223 to = DEFAULT_MIN_CHECKBOX_SHAPE_SCALE;
224 }
225 }
226
227 if (translate_) {
228 controller_->RemoveInterpolator(translate_);
229 }
230 translate_ = AceType::MakeRefPtr<CurveAnimation<double>>(from, to, Curves::FRICTION);
231 auto weak = AceType::WeakClaim(this);
232 translate_->AddListener(Animation<double>::ValueCallback([weak](double value) {
233 auto checkBox = weak.Upgrade();
234 if (checkBox) {
235 checkBox->UpdateCheckBoxShape(value);
236 }
237 }));
238 controller_->SetDuration(DEFUALT_CHECKBOX_ANIMATION_DURATION);
239 controller_->AddInterpolator(translate_);
240 }
241
UpdateCheckBoxShape(const double value)242 void RenderCheckbox::UpdateCheckBoxShape(const double value)
243 {
244 if (value < DEFAULT_MIN_CHECKBOX_SHAPE_SCALE || value > DEFAULT_MAX_CHECKBOX_SHAPE_SCALE) {
245 return;
246 }
247 shapeScale_ = value;
248 if (!(component_->GetGroupName().empty())) {
249 if (lastStatus_ == SelectStatus::ALL && status_ == SelectStatus::PART) {
250 uiStatus_ = UIStatus::ON_TO_PART;
251 } else if (lastStatus_ == SelectStatus::ALL && status_ == SelectStatus::NONE) {
252 uiStatus_ = UIStatus::ON_TO_OFF;
253 } else if (lastStatus_ == SelectStatus::PART && status_ == SelectStatus::ALL) {
254 uiStatus_ = UIStatus::PART_TO_ON;
255 } else if (lastStatus_ == SelectStatus::PART && status_ == SelectStatus::NONE) {
256 uiStatus_ = UIStatus::PART_TO_OFF;
257 } else if (lastStatus_ == SelectStatus::NONE && status_ == SelectStatus::ALL) {
258 uiStatus_ = UIStatus::OFF_TO_ON;
259 } else if (lastStatus_ == SelectStatus::NONE && status_ == SelectStatus::PART) {
260 uiStatus_ = UIStatus::OFF_TO_PART;
261 }
262 } else if (!(component_->GetCheckboxName().empty())) {
263 uiStatus_ = (component_->GetValue()) ? UIStatus::OFF_TO_ON : UIStatus::ON_TO_OFF;
264 } else {
265 uiStatus_ = checked_ ? UIStatus::ON_TO_OFF : UIStatus::OFF_TO_ON;
266 }
267
268 MarkNeedRender();
269 }
270
UpdateGroupStatus()271 void RenderCheckbox::UpdateGroupStatus()
272 {
273 if (!component_) {
274 return;
275 }
276
277 auto checkboxList = component_->GetCheckboxList();
278 int count = 0;
279 isGroup_ = !checkboxList.empty();
280 if (isGroup_) {
281 for (auto& item : checkboxList) {
282 if (item->GetValue()) {
283 count++;
284 }
285 }
286 if (count == (int)checkboxList.size()) {
287 status_ = SelectStatus::ALL;
288 } else if (count > 0 && (int)checkboxList.size() > count) {
289 status_ = SelectStatus::PART;
290 } else {
291 status_ = SelectStatus::NONE;
292 }
293 }
294 }
295
OnAnimationStop()296 void RenderCheckbox::OnAnimationStop()
297 {
298 // after the animation stopped,we need to update the check status
299 RenderCheckable::HandleClick();
300 UpdateAccessibilityAttr();
301 }
302
SetAccessibilityClickImpl()303 void RenderCheckbox::SetAccessibilityClickImpl()
304 {
305 auto accessibilityNode = accessibilityNode_.Upgrade();
306 if (accessibilityNode) {
307 auto weakPtr = AceType::WeakClaim(AceType::RawPtr(clickRecognizer_));
308 accessibilityNode->AddSupportAction(AceAction::ACTION_CLICK);
309 accessibilityNode->SetClickableState(true);
310 accessibilityNode->SetCheckableState(true);
311 accessibilityNode->SetActionClickImpl([weakPtr]() {
312 auto click = weakPtr.Upgrade();
313 if (click) {
314 click->OnAccepted();
315 }
316 });
317 }
318 }
319
ProvideRestoreInfo()320 std::string RenderCheckbox::ProvideRestoreInfo()
321 {
322 return std::to_string(checked_);
323 }
324
ApplyRestoreInfo()325 void RenderCheckbox::ApplyRestoreInfo()
326 {
327 if (GetRestoreInfo().empty()) {
328 return;
329 }
330 checked_ = static_cast<size_t>(StringUtils::StringToInt(GetRestoreInfo()));
331 SetRestoreInfo("");
332 }
333
334 } // namespace OHOS::Ace
335