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