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