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