• 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 
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