1 /*
2 * Copyright (c) 2021-2023 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 "bridge/declarative_frontend/jsview/js_checkbox.h"
17
18 #include <optional>
19 #include <string>
20
21 #include "base/log/ace_scoring_log.h"
22 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
23 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
24 #include "bridge/declarative_frontend/jsview/models/checkbox_model_impl.h"
25 #include "bridge/declarative_frontend/ark_theme/theme_apply/js_checkbox_theme.h"
26 #include "bridge/declarative_frontend/view_stack_processor.h"
27 #include "core/common/container.h"
28 #include "core/components/checkable/checkable_component.h"
29 #include "core/components_ng/base/view_abstract.h"
30 #include "core/components_ng/base/view_abstract_model.h"
31 #include "core/components_ng/base/view_stack_model.h"
32 #include "core/components_ng/base/view_stack_processor.h"
33 #include "core/components_ng/pattern/checkbox/checkbox_model_ng.h"
34 #include "core/components_v2/inspector/inspector_constants.h"
35
36 namespace OHOS::Ace {
37 namespace {
38 constexpr float CHECK_BOX_MARK_SIZE_INVALID_VALUE = -1.0f;
39 }
40 std::unique_ptr<CheckBoxModel> CheckBoxModel::instance_ = nullptr;
41 std::mutex CheckBoxModel::mutex_;
42
GetInstance()43 CheckBoxModel* CheckBoxModel::GetInstance()
44 {
45 if (!instance_) {
46 std::lock_guard<std::mutex> lock(mutex_);
47 if (!instance_) {
48 #ifdef NG_BUILD
49 instance_.reset(new NG::CheckBoxModelNG());
50 #else
51 if (Container::IsCurrentUseNewPipeline()) {
52 instance_.reset(new NG::CheckBoxModelNG());
53 } else {
54 instance_.reset(new Framework::CheckBoxModelImpl());
55 }
56 #endif
57 }
58 }
59 return instance_.get();
60 }
61 } // namespace OHOS::Ace
62
63 namespace OHOS::Ace::Framework {
Create(const JSCallbackInfo & info)64 void JSCheckbox::Create(const JSCallbackInfo& info)
65 {
66 auto checkboxName = std::optional<std::string>("");
67 auto checkboxGroup = std::optional<std::string>("");
68 std::optional<std::function<void()>> customBuilderFunc;
69 if ((info.Length() >= 1) && info[0]->IsObject()) {
70 auto paramObject = JSRef<JSObject>::Cast(info[0]);
71 auto name = paramObject->GetProperty("name");
72 auto group = paramObject->GetProperty("group");
73 if (name->IsString()) {
74 checkboxName = name->ToString();
75 }
76 if (group->IsString()) {
77 checkboxGroup = group->ToString();
78 }
79 auto builderObject = paramObject->GetProperty("indicatorBuilder");
80 if (builderObject->IsFunction()) {
81 auto builderFunc = AceType::MakeRefPtr<JsFunction>(info.This(), JSRef<JSFunc>::Cast(builderObject));
82 CHECK_NULL_VOID(builderFunc);
83 auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
84 auto callbackFunc = [execCtx = info.GetExecutionContext(),
85 func = std::move(builderFunc), node = targetNode]() {
86 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
87 ACE_SCORING_EVENT("CheckBox.builder");
88 PipelineContext::SetCallBackNode(node);
89 func->Execute();
90 };
91 customBuilderFunc = std::move(callbackFunc);
92 }
93 }
94 CheckBoxModel::GetInstance()->Create(checkboxName, checkboxGroup, V2::CHECK_BOX_ETS_TAG);
95 CheckBoxModel::GetInstance()->SetBuilder(customBuilderFunc);
96
97 JSCheckBoxTheme::ApplyTheme();
98 }
99
JSBind(BindingTarget globalObj)100 void JSCheckbox::JSBind(BindingTarget globalObj)
101 {
102 JSClass<JSCheckbox>::Declare("Checkbox");
103
104 JSClass<JSCheckbox>::StaticMethod("create", &JSCheckbox::Create);
105 JSClass<JSCheckbox>::StaticMethod("select", &JSCheckbox::SetSelect);
106 JSClass<JSCheckbox>::StaticMethod("shape", &JSCheckbox::SetCheckboxStyle);
107 JSClass<JSCheckbox>::StaticMethod("onChange", &JSCheckbox::SetOnChange);
108 JSClass<JSCheckbox>::StaticMethod("selectedColor", &JSCheckbox::SelectedColor);
109 JSClass<JSCheckbox>::StaticMethod("unselectedColor", &JSCheckbox::UnSelectedColor);
110 JSClass<JSCheckbox>::StaticMethod("mark", &JSCheckbox::Mark);
111 JSClass<JSCheckbox>::StaticMethod("responseRegion", &JSCheckbox::JsResponseRegion);
112 JSClass<JSCheckbox>::StaticMethod("padding", &JSCheckbox::JsPadding);
113 JSClass<JSCheckbox>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
114 JSClass<JSCheckbox>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
115 JSClass<JSCheckbox>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
116 JSClass<JSCheckbox>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
117 JSClass<JSCheckbox>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
118 JSClass<JSCheckbox>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
119 JSClass<JSCheckbox>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
120 JSClass<JSCheckbox>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
121 JSClass<JSCheckbox>::InheritAndBind<JSViewAbstract>(globalObj);
122 }
123
ParseSelectObject(const JSCallbackInfo & info,const JSRef<JSVal> & changeEventVal)124 void ParseSelectObject(const JSCallbackInfo& info, const JSRef<JSVal>& changeEventVal)
125 {
126 CHECK_NULL_VOID(changeEventVal->IsFunction());
127
128 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(changeEventVal));
129 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
130 auto changeEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](bool param) {
131 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
132 ACE_SCORING_EVENT("CheckBox.ChangeEvent");
133 PipelineContext::SetCallBackNode(node);
134 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(param));
135 func->ExecuteJS(1, &newJSVal);
136 };
137 CheckBoxModel::GetInstance()->SetChangeEvent(std::move(changeEvent));
138 }
139
SetSelect(const JSCallbackInfo & info)140 void JSCheckbox::SetSelect(const JSCallbackInfo& info)
141 {
142 auto length = info.Length();
143 if (length < 1 || length > 2) {
144 return;
145 }
146 bool select = false;
147 auto jsSelect = info[0];
148 if (length > 0 && jsSelect->IsBoolean()) {
149 select = jsSelect->ToBoolean();
150 }
151 TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "checkbox set select %{public}d", select);
152 CheckBoxModel::GetInstance()->SetSelect(select);
153 if (length > 1 && info[1]->IsFunction()) {
154 ParseSelectObject(info, info[1]);
155 }
156 }
157
SetOnChange(const JSCallbackInfo & args)158 void JSCheckbox::SetOnChange(const JSCallbackInfo& args)
159 {
160 if (!args[0]->IsFunction()) {
161 return;
162 }
163 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(args[0]));
164 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
165 auto onChange = [execCtx = args.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](bool select) {
166 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
167 ACE_SCORING_EVENT("CheckBox.onChange");
168 PipelineContext::SetCallBackNode(node);
169 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(select));
170 func->ExecuteJS(1, &newJSVal);
171 };
172 CheckBoxModel::GetInstance()->SetOnChange(onChange);
173 args.ReturnSelf();
174 }
175
JsResponseRegion(const JSCallbackInfo & info)176 void JSCheckbox::JsResponseRegion(const JSCallbackInfo& info)
177 {
178 if (!Container::IsCurrentUseNewPipeline()) {
179 JSViewAbstract::JsResponseRegion(info);
180 return;
181 }
182 if (info.Length() < 1) {
183 return;
184 }
185 std::vector<DimensionRect> result;
186 if (!JSViewAbstract::ParseJsResponseRegionArray(info[0], result)) {
187 return;
188 }
189 CheckBoxModel::GetInstance()->SetResponseRegion(result);
190 }
191
JsWidth(const JSCallbackInfo & info)192 void JSCheckbox::JsWidth(const JSCallbackInfo& info)
193 {
194 if (info.Length() < 1) {
195 return;
196 }
197
198 JsWidth(info[0]);
199 }
200
JsWidth(const JSRef<JSVal> & jsValue)201 void JSCheckbox::JsWidth(const JSRef<JSVal>& jsValue)
202 {
203 CalcDimension value;
204 ParseJsDimensionVp(jsValue, value);
205 if (value.IsNegative()) {
206 ViewAbstractModel::GetInstance()->ClearWidthOrHeight(true);
207 return;
208 }
209 CheckBoxModel::GetInstance()->SetWidth(value);
210 }
211
JsHeight(const JSCallbackInfo & info)212 void JSCheckbox::JsHeight(const JSCallbackInfo& info)
213 {
214 if (info.Length() < 1) {
215 return;
216 }
217
218 JsHeight(info[0]);
219 }
220
JsHeight(const JSRef<JSVal> & jsValue)221 void JSCheckbox::JsHeight(const JSRef<JSVal>& jsValue)
222 {
223 CalcDimension value;
224 ParseJsDimensionVp(jsValue, value);
225 if (value.IsNegative()) {
226 ViewAbstractModel::GetInstance()->ClearWidthOrHeight(false);
227 return;
228 }
229 CheckBoxModel::GetInstance()->SetHeight(value);
230 }
231
JsSize(const JSCallbackInfo & info)232 void JSCheckbox::JsSize(const JSCallbackInfo& info)
233 {
234 if (info.Length() < 1) {
235 return;
236 }
237
238 if (!info[0]->IsObject()) {
239 return;
240 }
241
242 JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
243 JsWidth(sizeObj->GetProperty("width"));
244 JsHeight(sizeObj->GetProperty("height"));
245 }
246
SelectedColor(const JSCallbackInfo & info)247 void JSCheckbox::SelectedColor(const JSCallbackInfo& info)
248 {
249 if (info.Length() < 1) {
250 return;
251 }
252 Color selectedColor;
253 auto theme = GetTheme<CheckboxTheme>();
254 if (!ParseJsColor(info[0], selectedColor)) {
255 selectedColor = theme->GetActiveColor();
256 }
257 CheckBoxModel::GetInstance()->SetSelectedColor(selectedColor);
258 }
259
UnSelectedColor(const JSCallbackInfo & info)260 void JSCheckbox::UnSelectedColor(const JSCallbackInfo& info)
261 {
262 if (info.Length() < 1) {
263 return;
264 }
265 Color unSelectedColor;
266 auto theme = GetTheme<CheckboxTheme>();
267 if (!ParseJsColor(info[0], unSelectedColor)) {
268 unSelectedColor = theme->GetInactiveColor();
269 }
270
271 CheckBoxModel::GetInstance()->SetUnSelectedColor(unSelectedColor);
272 }
273
SetCheckboxStyle(int32_t checkBoxStyle)274 void JSCheckbox::SetCheckboxStyle(int32_t checkBoxStyle)
275 {
276 CheckBoxStyle curCheckBoxStyle = static_cast<CheckBoxStyle>(checkBoxStyle);
277 CheckBoxModel::GetInstance()->SetCheckboxStyle(curCheckBoxStyle);
278 }
Mark(const JSCallbackInfo & info)279 void JSCheckbox::Mark(const JSCallbackInfo& info)
280 {
281 auto theme = GetTheme<CheckboxTheme>();
282 if (!info[0]->IsObject()) {
283 CheckBoxModel::GetInstance()->SetCheckMarkColor(theme->GetPointColor());
284 CheckBoxModel::GetInstance()->SetCheckMarkSize(Dimension(CHECK_BOX_MARK_SIZE_INVALID_VALUE));
285 CheckBoxModel::GetInstance()->SetCheckMarkWidth(theme->GetCheckStroke());
286 return;
287 }
288
289 auto markObj = JSRef<JSObject>::Cast(info[0]);
290 auto strokeColorValue = markObj->GetProperty("strokeColor");
291 Color strokeColor = theme->GetPointColor();
292 if (!ParseJsColor(strokeColorValue, strokeColor)) {
293 JSCheckBoxTheme::ObtainCheckMarkColor(strokeColor);
294 }
295 CheckBoxModel::GetInstance()->SetCheckMarkColor(strokeColor);
296 auto sizeValue = markObj->GetProperty("size");
297 CalcDimension size;
298 if ((ParseJsDimensionVp(sizeValue, size)) && (size.Unit() != DimensionUnit::PERCENT) && (size.ConvertToVp() >= 0)) {
299 CheckBoxModel::GetInstance()->SetCheckMarkSize(size);
300 } else {
301 CheckBoxModel::GetInstance()->SetCheckMarkSize(Dimension(CHECK_BOX_MARK_SIZE_INVALID_VALUE));
302 }
303
304 auto strokeWidthValue = markObj->GetProperty("strokeWidth");
305 CalcDimension strokeWidth;
306 if ((ParseJsDimensionVp(strokeWidthValue, strokeWidth)) && (strokeWidth.Unit() != DimensionUnit::PERCENT) &&
307 (strokeWidth.ConvertToVp() >= 0)) {
308 CheckBoxModel::GetInstance()->SetCheckMarkWidth(strokeWidth);
309 } else {
310 CheckBoxModel::GetInstance()->SetCheckMarkWidth(theme->GetCheckStroke());
311 }
312 }
313
JsPadding(const JSCallbackInfo & info)314 void JSCheckbox::JsPadding(const JSCallbackInfo& info)
315 {
316 if (info.Length() < 1) {
317 return;
318 }
319 NG::PaddingPropertyF oldPadding({ 0.0f, 0.0f, 0.0f, 0.0f });
320 bool flag = GetOldPadding(info, oldPadding);
321 NG::PaddingProperty newPadding = GetNewPadding(info);
322 CheckBoxModel::GetInstance()->SetPadding(oldPadding, newPadding, flag);
323 }
324
GetOldPadding(const JSCallbackInfo & info,NG::PaddingPropertyF & padding)325 bool JSCheckbox::GetOldPadding(const JSCallbackInfo& info, NG::PaddingPropertyF& padding)
326 {
327 if (info[0]->IsObject()) {
328 JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
329 if (jsObj->HasProperty("top") || jsObj->HasProperty("bottom")
330 || jsObj->HasProperty("left") || jsObj->HasProperty("right")) {
331 CalcDimension topDimen = CalcDimension(0.0, DimensionUnit::VP);
332 CalcDimension leftDimen = CalcDimension(0.0, DimensionUnit::VP);
333 CalcDimension rightDimen = CalcDimension(0.0, DimensionUnit::VP);
334 CalcDimension bottomDimen = CalcDimension(0.0, DimensionUnit::VP);
335 ParseJsDimensionVp(jsObj->GetProperty("top"), topDimen);
336 ParseJsDimensionVp(jsObj->GetProperty("left"), leftDimen);
337 ParseJsDimensionVp(jsObj->GetProperty("right"), rightDimen);
338 ParseJsDimensionVp(jsObj->GetProperty("bottom"), bottomDimen);
339 if (leftDimen == 0.0_vp) {
340 leftDimen = rightDimen;
341 }
342 if (topDimen == 0.0_vp) {
343 topDimen = bottomDimen;
344 }
345 if (leftDimen == 0.0_vp) {
346 leftDimen = topDimen;
347 }
348
349 padding.left = leftDimen.ConvertToPx();
350 padding.right = rightDimen.ConvertToPx();
351 padding.top = topDimen.ConvertToPx();
352 padding.bottom = bottomDimen.ConvertToPx();
353 return true;
354 }
355 }
356
357 CalcDimension length;
358 if (!ParseJsDimensionVp(info[0], length)) {
359 return false;
360 }
361
362 padding.left = length.ConvertToPx();
363 padding.right = length.ConvertToPx();
364 padding.top = length.ConvertToPx();
365 padding.bottom = length.ConvertToPx();
366 return true;
367 }
368
GetNewPadding(const JSCallbackInfo & info)369 NG::PaddingProperty JSCheckbox::GetNewPadding(const JSCallbackInfo& info)
370 {
371 NG::PaddingProperty padding({
372 NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp)
373 });
374 if (info[0]->IsObject()) {
375 std::optional<CalcDimension> left;
376 std::optional<CalcDimension> right;
377 std::optional<CalcDimension> top;
378 std::optional<CalcDimension> bottom;
379 JSRef<JSObject> paddingObj = JSRef<JSObject>::Cast(info[0]);
380
381 CalcDimension leftDimen;
382 if (ParseJsDimensionVp(paddingObj->GetProperty("left"), leftDimen)) {
383 left = leftDimen;
384 }
385 CalcDimension rightDimen;
386 if (ParseJsDimensionVp(paddingObj->GetProperty("right"), rightDimen)) {
387 right = rightDimen;
388 }
389 CalcDimension topDimen;
390 if (ParseJsDimensionVp(paddingObj->GetProperty("top"), topDimen)) {
391 top = topDimen;
392 }
393 CalcDimension bottomDimen;
394 if (ParseJsDimensionVp(paddingObj->GetProperty("bottom"), bottomDimen)) {
395 bottom = bottomDimen;
396 }
397 if (left.has_value() || right.has_value() || top.has_value() || bottom.has_value()) {
398 padding = GetPadding(top, bottom, left, right);
399 return padding;
400 }
401 }
402 CalcDimension length;
403 if (!ParseJsDimensionVp(info[0], length)) {
404 length.Reset();
405 }
406
407 padding.SetEdges(NG::CalcLength(length.IsNonNegative() ? length : CalcDimension()));
408 return padding;
409 }
410
GetPadding(const std::optional<CalcDimension> & top,const std::optional<CalcDimension> & bottom,const std::optional<CalcDimension> & left,const std::optional<CalcDimension> & right)411 NG::PaddingProperty JSCheckbox::GetPadding(const std::optional<CalcDimension>& top,
412 const std::optional<CalcDimension>& bottom, const std::optional<CalcDimension>& left,
413 const std::optional<CalcDimension>& right)
414 {
415 NG::PaddingProperty padding({
416 NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp)
417 });
418 if (left.has_value()) {
419 if (left.value().Unit() == DimensionUnit::CALC) {
420 padding.left =
421 NG::CalcLength(left.value().IsNonNegative() ? left.value().CalcValue() : CalcDimension().CalcValue());
422 } else {
423 padding.left = NG::CalcLength(left.value().IsNonNegative() ? left.value() : CalcDimension());
424 }
425 }
426 if (right.has_value()) {
427 if (right.value().Unit() == DimensionUnit::CALC) {
428 padding.right =
429 NG::CalcLength(right.value().IsNonNegative() ? right.value().CalcValue() : CalcDimension().CalcValue());
430 } else {
431 padding.right = NG::CalcLength(right.value().IsNonNegative() ? right.value() : CalcDimension());
432 }
433 }
434 if (top.has_value()) {
435 if (top.value().Unit() == DimensionUnit::CALC) {
436 padding.top =
437 NG::CalcLength(top.value().IsNonNegative() ? top.value().CalcValue() : CalcDimension().CalcValue());
438 } else {
439 padding.top = NG::CalcLength(top.value().IsNonNegative() ? top.value() : CalcDimension());
440 }
441 }
442 if (bottom.has_value()) {
443 if (bottom.value().Unit() == DimensionUnit::CALC) {
444 padding.bottom = NG::CalcLength(
445 bottom.value().IsNonNegative() ? bottom.value().CalcValue() : CalcDimension().CalcValue());
446 } else {
447 padding.bottom = NG::CalcLength(bottom.value().IsNonNegative() ? bottom.value() : CalcDimension());
448 }
449 }
450 return padding;
451 }
452 } // namespace OHOS::Ace::Framework
453