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