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("margin", &JSCheckbox::JsMargin);
106 JSClass<JSCheckbox>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
107 JSClass<JSCheckbox>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
108 JSClass<JSCheckbox>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
109 JSClass<JSCheckbox>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
110 JSClass<JSCheckbox>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
111 JSClass<JSCheckbox>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
112 JSClass<JSCheckbox>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
113 JSClass<JSCheckbox>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
114 JSClass<JSCheckbox>::InheritAndBind<JSViewAbstract>(globalObj);
115 }
116
ParseSelectObject(const JSCallbackInfo & info,const JSRef<JSVal> & changeEventVal)117 void ParseSelectObject(const JSCallbackInfo& info, const JSRef<JSVal>& changeEventVal)
118 {
119 CHECK_NULL_VOID(changeEventVal->IsFunction());
120
121 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(changeEventVal));
122 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
123 auto changeEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](bool param) {
124 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
125 ACE_SCORING_EVENT("CheckBox.ChangeEvent");
126 PipelineContext::SetCallBackNode(node);
127 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(param));
128 func->ExecuteJS(1, &newJSVal);
129 };
130 CheckBoxModel::GetInstance()->SetChangeEvent(std::move(changeEvent));
131 }
132
SetSelect(const JSCallbackInfo & info)133 void JSCheckbox::SetSelect(const JSCallbackInfo& info)
134 {
135 auto length = info.Length();
136 if (length < 1 || length > 2) {
137 return;
138 }
139 bool select = false;
140
141 JSRef<JSVal> changeEventVal;
142 auto selectedVal = info[0];
143 if (selectedVal->IsObject()) {
144 JSRef<JSObject> obj = JSRef<JSObject>::Cast(selectedVal);
145 selectedVal = obj->GetProperty("value");
146 changeEventVal = obj->GetProperty("$value");
147 } else if (info.Length() > 1) {
148 changeEventVal = info[1];
149 }
150 if (selectedVal->IsBoolean()) {
151 select = selectedVal->ToBoolean();
152 }
153 TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "checkbox set select %{public}d", select);
154 CheckBoxModel::GetInstance()->SetSelect(select);
155 if (changeEventVal->IsFunction()) {
156 ParseSelectObject(info, changeEventVal);
157 }
158 }
159
SetOnChange(const JSCallbackInfo & args)160 void JSCheckbox::SetOnChange(const JSCallbackInfo& args)
161 {
162 if (!args[0]->IsFunction()) {
163 return;
164 }
165 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(args[0]));
166 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
167 auto onChange = [execCtx = args.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](bool select) {
168 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
169 ACE_SCORING_EVENT("CheckBox.onChange");
170 PipelineContext::SetCallBackNode(node);
171 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(select));
172 func->ExecuteJS(1, &newJSVal);
173 };
174 CheckBoxModel::GetInstance()->SetOnChange(onChange);
175 args.ReturnSelf();
176 }
177
JsResponseRegion(const JSCallbackInfo & info)178 void JSCheckbox::JsResponseRegion(const JSCallbackInfo& info)
179 {
180 if (!Container::IsCurrentUseNewPipeline()) {
181 JSViewAbstract::JsResponseRegion(info);
182 return;
183 }
184 if (info.Length() < 1) {
185 return;
186 }
187 std::vector<DimensionRect> result;
188 if (!JSViewAbstract::ParseJsResponseRegionArray(info[0], result)) {
189 return;
190 }
191 CheckBoxModel::GetInstance()->SetResponseRegion(result);
192 }
193
JsWidth(const JSCallbackInfo & info)194 void JSCheckbox::JsWidth(const JSCallbackInfo& info)
195 {
196 if (info.Length() < 1) {
197 return;
198 }
199
200 JsWidth(info[0]);
201 }
202
JsWidth(const JSRef<JSVal> & jsValue)203 void JSCheckbox::JsWidth(const JSRef<JSVal>& jsValue)
204 {
205 CalcDimension value;
206 ParseJsDimensionVp(jsValue, value);
207 if (value.IsNegative()) {
208 ViewAbstractModel::GetInstance()->ClearWidthOrHeight(true);
209 return;
210 }
211 CheckBoxModel::GetInstance()->SetWidth(value);
212 }
213
JsHeight(const JSCallbackInfo & info)214 void JSCheckbox::JsHeight(const JSCallbackInfo& info)
215 {
216 if (info.Length() < 1) {
217 return;
218 }
219
220 JsHeight(info[0]);
221 }
222
JsHeight(const JSRef<JSVal> & jsValue)223 void JSCheckbox::JsHeight(const JSRef<JSVal>& jsValue)
224 {
225 CalcDimension value;
226 ParseJsDimensionVp(jsValue, value);
227 if (value.IsNegative()) {
228 ViewAbstractModel::GetInstance()->ClearWidthOrHeight(false);
229 return;
230 }
231 CheckBoxModel::GetInstance()->SetHeight(value);
232 }
233
JsSize(const JSCallbackInfo & info)234 void JSCheckbox::JsSize(const JSCallbackInfo& info)
235 {
236 if (info.Length() < 1) {
237 return;
238 }
239
240 if (!info[0]->IsObject()) {
241 return;
242 }
243
244 JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
245 JsWidth(sizeObj->GetProperty("width"));
246 JsHeight(sizeObj->GetProperty("height"));
247 }
248
SelectedColor(const JSCallbackInfo & info)249 void JSCheckbox::SelectedColor(const JSCallbackInfo& info)
250 {
251 if (info.Length() < 1) {
252 return;
253 }
254 Color selectedColor;
255 RefPtr<ResourceObject> resObj;
256 if (!ParseJsColor(info[0], selectedColor, resObj)) {
257 CheckBoxModel::GetInstance()->ResetSelectedColor();
258 } else {
259 CheckBoxModel::GetInstance()->SetSelectedColor(selectedColor);
260 }
261 if (SystemProperties::ConfigChangePerform()) {
262 CheckBoxModel::GetInstance()->CreateWithColorResourceObj(resObj, CheckBoxColorType::SELECTED_COLOR);
263 }
264 }
265
UnSelectedColor(const JSCallbackInfo & info)266 void JSCheckbox::UnSelectedColor(const JSCallbackInfo& info)
267 {
268 if (info.Length() < 1) {
269 return;
270 }
271 Color unSelectedColor;
272 RefPtr<ResourceObject> resObj;
273 if (!ParseJsColor(info[0], unSelectedColor, resObj)) {
274 CheckBoxModel::GetInstance()->ResetUnSelectedColor();
275 } else {
276 CheckBoxModel::GetInstance()->SetUnSelectedColor(unSelectedColor);
277 }
278 if (SystemProperties::ConfigChangePerform()) {
279 CheckBoxModel::GetInstance()->CreateWithColorResourceObj(resObj, CheckBoxColorType::UN_SELECTED_COLOR);
280 }
281 }
282
SetCheckboxStyle(int32_t checkBoxStyle)283 void JSCheckbox::SetCheckboxStyle(int32_t checkBoxStyle)
284 {
285 CheckBoxStyle curCheckBoxStyle = static_cast<CheckBoxStyle>(checkBoxStyle);
286 CheckBoxModel::GetInstance()->SetCheckboxStyle(curCheckBoxStyle);
287 }
Mark(const JSCallbackInfo & info)288 void JSCheckbox::Mark(const JSCallbackInfo& info)
289 {
290 auto theme = GetTheme<CheckboxTheme>();
291 auto defaultStroke = theme ? theme->GetCheckStroke() : Dimension();
292 if (!info[0]->IsObject()) {
293 CheckBoxModel::GetInstance()->ResetCheckMarkColor();
294 CheckBoxModel::GetInstance()->SetCheckMarkSize(Dimension(CHECK_BOX_MARK_SIZE_INVALID_VALUE));
295 CheckBoxModel::GetInstance()->SetCheckMarkWidth(defaultStroke);
296 return;
297 }
298
299 auto markObj = JSRef<JSObject>::Cast(info[0]);
300 auto strokeColorValue = markObj->GetProperty("strokeColor");
301 Color strokeColor;
302 if (!ParseJsColor(strokeColorValue, strokeColor)) {
303 CheckBoxModel::GetInstance()->ResetCheckMarkColor();
304 } else {
305 CheckBoxModel::GetInstance()->SetCheckMarkColor(strokeColor);
306 }
307 auto sizeValue = markObj->GetProperty("size");
308 CalcDimension size;
309 if ((ParseJsDimensionVp(sizeValue, size)) && (size.Unit() != DimensionUnit::PERCENT) && (size.ConvertToVp() >= 0)) {
310 CheckBoxModel::GetInstance()->SetCheckMarkSize(size);
311 } else {
312 CheckBoxModel::GetInstance()->SetCheckMarkSize(Dimension(CHECK_BOX_MARK_SIZE_INVALID_VALUE));
313 }
314
315 auto strokeWidthValue = markObj->GetProperty("strokeWidth");
316 CalcDimension strokeWidth;
317 if ((ParseJsDimensionVp(strokeWidthValue, strokeWidth)) && (strokeWidth.Unit() != DimensionUnit::PERCENT) &&
318 (strokeWidth.ConvertToVp() >= 0)) {
319 CheckBoxModel::GetInstance()->SetCheckMarkWidth(strokeWidth);
320 } else {
321 CheckBoxModel::GetInstance()->SetCheckMarkWidth(defaultStroke);
322 }
323 }
324
JsPadding(const JSCallbackInfo & info)325 void JSCheckbox::JsPadding(const JSCallbackInfo& info)
326 {
327 if (info.Length() < 1) {
328 return;
329 }
330 NG::PaddingPropertyF oldPadding({ 0.0f, 0.0f, 0.0f, 0.0f });
331 bool flag = GetOldPadding(info, oldPadding);
332 NG::PaddingProperty newPadding = GetNewPadding(info);
333 CheckBoxModel::GetInstance()->SetPadding(oldPadding, newPadding, flag);
334 }
335
JsMargin(const JSCallbackInfo & info)336 void JSCheckbox::JsMargin(const JSCallbackInfo& info)
337 {
338 CheckBoxModel::GetInstance()->SetIsUserSetMargin(true);
339 JSViewAbstract::JsMargin(info);
340 }
341
GetOldPadding(const JSCallbackInfo & info,NG::PaddingPropertyF & padding)342 bool JSCheckbox::GetOldPadding(const JSCallbackInfo& info, NG::PaddingPropertyF& padding)
343 {
344 if (info[0]->IsObject()) {
345 JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
346 if (jsObj->HasProperty("top") || jsObj->HasProperty("bottom")
347 || jsObj->HasProperty("left") || jsObj->HasProperty("right")) {
348 CalcDimension topDimen = CalcDimension(0.0, DimensionUnit::VP);
349 CalcDimension leftDimen = CalcDimension(0.0, DimensionUnit::VP);
350 CalcDimension rightDimen = CalcDimension(0.0, DimensionUnit::VP);
351 CalcDimension bottomDimen = CalcDimension(0.0, DimensionUnit::VP);
352 ParseJsDimensionVp(jsObj->GetProperty("top"), topDimen);
353 ParseJsDimensionVp(jsObj->GetProperty("left"), leftDimen);
354 ParseJsDimensionVp(jsObj->GetProperty("right"), rightDimen);
355 ParseJsDimensionVp(jsObj->GetProperty("bottom"), bottomDimen);
356 if (leftDimen == 0.0_vp) {
357 leftDimen = rightDimen;
358 }
359 if (topDimen == 0.0_vp) {
360 topDimen = bottomDimen;
361 }
362 if (leftDimen == 0.0_vp) {
363 leftDimen = topDimen;
364 }
365
366 padding.left = leftDimen.ConvertToPx();
367 padding.right = rightDimen.ConvertToPx();
368 padding.top = topDimen.ConvertToPx();
369 padding.bottom = bottomDimen.ConvertToPx();
370 return true;
371 }
372 }
373
374 CalcDimension length;
375 if (!ParseJsDimensionVp(info[0], length)) {
376 return false;
377 }
378
379 padding.left = length.ConvertToPx();
380 padding.right = length.ConvertToPx();
381 padding.top = length.ConvertToPx();
382 padding.bottom = length.ConvertToPx();
383 return true;
384 }
385
GetNewPadding(const JSCallbackInfo & info)386 NG::PaddingProperty JSCheckbox::GetNewPadding(const JSCallbackInfo& info)
387 {
388 NG::PaddingProperty padding({ NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp),
389 NG::CalcLength(0.0_vp), std::nullopt, std::nullopt });
390 if (info[0]->IsObject()) {
391 JSRef<JSObject> paddingObj = JSRef<JSObject>::Cast(info[0]);
392 CommonCalcDimension commonCalcDimension;
393 ParseCommonMarginOrPaddingCorner(paddingObj, commonCalcDimension);
394 if (commonCalcDimension.left.has_value() || commonCalcDimension.right.has_value() ||
395 commonCalcDimension.top.has_value() || commonCalcDimension.bottom.has_value()) {
396 padding = GetPadding(commonCalcDimension.top, commonCalcDimension.bottom, commonCalcDimension.left,
397 commonCalcDimension.right);
398 return padding;
399 }
400 }
401 CalcDimension length;
402 if (!ParseJsDimensionVp(info[0], length)) {
403 length.Reset();
404 }
405
406 padding.SetEdges(NG::CalcLength(length.IsNonNegative() ? length : CalcDimension()));
407 return padding;
408 }
409
GetPadding(const std::optional<CalcDimension> & top,const std::optional<CalcDimension> & bottom,const std::optional<CalcDimension> & left,const std::optional<CalcDimension> & right)410 NG::PaddingProperty JSCheckbox::GetPadding(const std::optional<CalcDimension>& top,
411 const std::optional<CalcDimension>& bottom, const std::optional<CalcDimension>& left,
412 const std::optional<CalcDimension>& right)
413 {
414 NG::PaddingProperty padding({ NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp),
415 NG::CalcLength(0.0_vp), std::nullopt, std::nullopt });
416 if (left.has_value()) {
417 if (left.value().Unit() == DimensionUnit::CALC) {
418 padding.left =
419 NG::CalcLength(left.value().IsNonNegative() ? left.value().CalcValue() : CalcDimension().CalcValue());
420 } else {
421 padding.left = NG::CalcLength(left.value().IsNonNegative() ? left.value() : CalcDimension());
422 }
423 }
424 if (right.has_value()) {
425 if (right.value().Unit() == DimensionUnit::CALC) {
426 padding.right =
427 NG::CalcLength(right.value().IsNonNegative() ? right.value().CalcValue() : CalcDimension().CalcValue());
428 } else {
429 padding.right = NG::CalcLength(right.value().IsNonNegative() ? right.value() : CalcDimension());
430 }
431 }
432 if (top.has_value()) {
433 if (top.value().Unit() == DimensionUnit::CALC) {
434 padding.top =
435 NG::CalcLength(top.value().IsNonNegative() ? top.value().CalcValue() : CalcDimension().CalcValue());
436 } else {
437 padding.top = NG::CalcLength(top.value().IsNonNegative() ? top.value() : CalcDimension());
438 }
439 }
440 if (bottom.has_value()) {
441 if (bottom.value().Unit() == DimensionUnit::CALC) {
442 padding.bottom = NG::CalcLength(
443 bottom.value().IsNonNegative() ? bottom.value().CalcValue() : CalcDimension().CalcValue());
444 } else {
445 padding.bottom = NG::CalcLength(bottom.value().IsNonNegative() ? bottom.value() : CalcDimension());
446 }
447 }
448 return padding;
449 }
450 } // namespace OHOS::Ace::Framework
451