• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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