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