1 /*
2 * Copyright (c) 2021-2022 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_radio.h"
17
18 #include "base/log/ace_scoring_log.h"
19 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
20 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
21 #include "bridge/declarative_frontend/jsview/models/radio_model_impl.h"
22 #include "core/components/checkable/checkable_theme.h"
23 #include "core/components_ng/base/view_abstract.h"
24 #include "core/components_ng/pattern/radio/radio_model_ng.h"
25
26 namespace OHOS::Ace {
27
28 std::unique_ptr<RadioModel> RadioModel::instance_ = nullptr;
29 std::mutex RadioModel::mutex_;
30
GetInstance()31 RadioModel* RadioModel::GetInstance()
32 {
33 if (!instance_) {
34 std::lock_guard<std::mutex> lock(mutex_);
35 if (!instance_) {
36 #ifdef NG_BUILD
37 instance_.reset(new NG::RadioModelNG());
38 #else
39 if (Container::IsCurrentUseNewPipeline()) {
40 instance_.reset(new NG::RadioModelNG());
41 } else {
42 instance_.reset(new Framework::RadioModelImpl());
43 }
44 #endif
45 }
46 }
47 return instance_.get();
48 }
49
50 } // namespace OHOS::Ace
51 namespace OHOS::Ace::Framework {
52 const static int32_t PLATFORM_VERSION_TEN = 10;
Create(const JSCallbackInfo & info)53 void JSRadio::Create(const JSCallbackInfo& info)
54 {
55 if (info.Length() < 1) {
56 LOGE("radio create error, info is not-valid");
57 return;
58 }
59
60 std::optional<std::string> value;
61 std::optional<std::string> group;
62 if ((info.Length() >= 1) && info[0]->IsObject()) {
63 auto paramObject = JSRef<JSObject>::Cast(info[0]);
64 auto valueTemp = paramObject->GetProperty("value");
65 auto groupTemp = paramObject->GetProperty("group");
66 if (valueTemp->IsString()) {
67 value = valueTemp->ToString();
68 } else {
69 value = "";
70 }
71 if (groupTemp->IsString()) {
72 group = groupTemp->ToString();
73 } else {
74 group = "";
75 }
76 }
77 RadioModel::GetInstance()->Create(value, group);
78 }
79
JSBind(BindingTarget globalObj)80 void JSRadio::JSBind(BindingTarget globalObj)
81 {
82 JSClass<JSRadio>::Declare("Radio");
83
84 JSClass<JSRadio>::StaticMethod("create", &JSRadio::Create);
85 JSClass<JSRadio>::StaticMethod("checked", &JSRadio::Checked);
86 JSClass<JSRadio>::StaticMethod("width", &JSRadio::JsWidth);
87 JSClass<JSRadio>::StaticMethod("height", &JSRadio::JsHeight);
88 JSClass<JSRadio>::StaticMethod("size", &JSRadio::JsSize);
89 JSClass<JSRadio>::StaticMethod("padding", &JSRadio::JsPadding);
90 JSClass<JSRadio>::StaticMethod("radioStyle", &JSRadio::JsRadioStyle);
91 JSClass<JSRadio>::StaticMethod("responseRegion", &JSRadio::JsResponseRegion);
92 JSClass<JSRadio>::StaticMethod("onChange", &JSRadio::OnChange);
93 JSClass<JSRadio>::StaticMethod("onClick", &JSRadio::JsOnClick);
94 JSClass<JSRadio>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
95 JSClass<JSRadio>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
96 JSClass<JSRadio>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
97 JSClass<JSRadio>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
98 JSClass<JSRadio>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
99 JSClass<JSRadio>::InheritAndBind<JSViewAbstract>(globalObj);
100 }
101
ParseCheckedObject(const JSCallbackInfo & args,const JSRef<JSVal> & changeEventVal)102 void ParseCheckedObject(const JSCallbackInfo& args, const JSRef<JSVal>& changeEventVal)
103 {
104 CHECK_NULL_VOID(changeEventVal->IsFunction());
105
106 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(changeEventVal));
107 auto onChecked = [execCtx = args.GetExecutionContext(), func = std::move(jsFunc)](bool check) {
108 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
109 ACE_SCORING_EVENT("Radio.onChangeEvent");
110 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(check));
111 func->ExecuteJS(1, &newJSVal);
112 };
113 RadioModel::GetInstance()->SetOnChangeEvent(std::move(onChecked));
114 }
115
Checked(const JSCallbackInfo & info)116 void JSRadio::Checked(const JSCallbackInfo& info)
117 {
118 if (info.Length() < 1 || info.Length() > 2) {
119 LOGE("The arg is wrong, it is supposed to have 1 or 2 arguments");
120 return;
121 }
122
123 if (info.Length() > 0 && info[0]->IsBoolean()) {
124 RadioModel::GetInstance()->SetChecked(info[0]->ToBoolean());
125 } else {
126 RadioModel::GetInstance()->SetChecked(false);
127 }
128
129 if (info.Length() > 1 && info[1]->IsFunction()) {
130 ParseCheckedObject(info, info[1]);
131 }
132 }
133
JsWidth(const JSCallbackInfo & info)134 void JSRadio::JsWidth(const JSCallbackInfo& info)
135 {
136 if (info.Length() < 1) {
137 LOGE("The arg is wrong, it is supposed to have atleast 1 arguments");
138 return;
139 }
140
141 JsWidth(info[0]);
142 }
143
JsWidth(const JSRef<JSVal> & jsValue)144 void JSRadio::JsWidth(const JSRef<JSVal>& jsValue)
145 {
146 auto pipeline = PipelineBase::GetCurrentContext();
147 CHECK_NULL_VOID(pipeline);
148 auto radioTheme = pipeline->GetTheme<RadioTheme>();
149 CHECK_NULL_VOID(radioTheme);
150 auto defaultWidth = radioTheme->GetDefaultWidth();
151 auto horizontalPadding = radioTheme->GetHotZoneHorizontalPadding();
152 auto width = defaultWidth - horizontalPadding * 2;
153 CalcDimension value(width);
154 ParseJsDimensionVp(jsValue, value);
155 if (value.IsNegative()) {
156 value = width;
157 }
158 RadioModel::GetInstance()->SetWidth(value);
159 }
160
JsHeight(const JSCallbackInfo & info)161 void JSRadio::JsHeight(const JSCallbackInfo& info)
162 {
163 if (info.Length() < 1) {
164 LOGE("The arg is wrong, it is supposed to have atleast 1 arguments");
165 return;
166 }
167
168 JsHeight(info[0]);
169 }
170
JsHeight(const JSRef<JSVal> & jsValue)171 void JSRadio::JsHeight(const JSRef<JSVal>& jsValue)
172 {
173 auto pipeline = PipelineBase::GetCurrentContext();
174 CHECK_NULL_VOID(pipeline);
175 auto radioTheme = pipeline->GetTheme<RadioTheme>();
176 CHECK_NULL_VOID(radioTheme);
177 auto defaultHeight = radioTheme->GetDefaultHeight();
178 auto verticalPadding = radioTheme->GetHotZoneVerticalPadding();
179 auto height = defaultHeight - verticalPadding * 2;
180 CalcDimension value(height);
181 if (PipelineBase::GetCurrentContext() &&
182 PipelineBase::GetCurrentContext()->GetMinPlatformVersion() >= PLATFORM_VERSION_TEN) {
183 if (!ParseJsDimensionVpNG(jsValue, value)) {
184 value = height;
185 }
186 } else {
187 ParseJsDimensionVp(jsValue, value);
188 if (value.IsNegative()) {
189 value = height;
190 }
191 }
192 RadioModel::GetInstance()->SetHeight(value);
193 }
194
JsSize(const JSCallbackInfo & info)195 void JSRadio::JsSize(const JSCallbackInfo& info)
196 {
197 if (info.Length() < 1) {
198 LOGE("The arg is wrong, it is supposed to have atleast 1 arguments");
199 return;
200 }
201
202 if (!info[0]->IsObject()) {
203 LOGE("arg is not Object or String.");
204 return;
205 }
206
207 JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
208 JsWidth(sizeObj->GetProperty("width"));
209 JsHeight(sizeObj->GetProperty("height"));
210 }
211
JsPadding(const JSCallbackInfo & info)212 void JSRadio::JsPadding(const JSCallbackInfo& info)
213 {
214 if (info.Length() < 1) {
215 LOGE("The arg is wrong, it is supposed to have atleast 1 arguments");
216 return;
217 }
218 NG::PaddingPropertyF oldPadding = GetOldPadding(info);
219 NG::PaddingProperty newPadding = GetNewPadding(info);
220 RadioModel::GetInstance()->SetPadding(oldPadding, newPadding);
221 }
222
GetOldPadding(const JSCallbackInfo & info)223 NG::PaddingPropertyF JSRadio::GetOldPadding(const JSCallbackInfo& info)
224 {
225 NG::PaddingPropertyF padding({ 0.0f, 0.0f, 0.0f, 0.0f });
226 if (info[0]->IsObject()) {
227 auto argsPtrItem = JsonUtil::ParseJsonString(info[0]->ToString());
228 if (!argsPtrItem || argsPtrItem->IsNull()) {
229 LOGE("Js Parse object failed. argsPtr is null. %s", info[0]->ToString().c_str());
230 return padding;
231 }
232 if (argsPtrItem->Contains("top") || argsPtrItem->Contains("bottom") || argsPtrItem->Contains("left") ||
233 argsPtrItem->Contains("right")) {
234 CalcDimension topDimen = CalcDimension(0.0, DimensionUnit::VP);
235 CalcDimension leftDimen = CalcDimension(0.0, DimensionUnit::VP);
236 CalcDimension rightDimen = CalcDimension(0.0, DimensionUnit::VP);
237 CalcDimension bottomDimen = CalcDimension(0.0, DimensionUnit::VP);
238 ParseJsonDimensionVp(argsPtrItem->GetValue("top"), topDimen);
239 ParseJsonDimensionVp(argsPtrItem->GetValue("left"), leftDimen);
240 ParseJsonDimensionVp(argsPtrItem->GetValue("right"), rightDimen);
241 ParseJsonDimensionVp(argsPtrItem->GetValue("bottom"), bottomDimen);
242 if (leftDimen == 0.0_vp) {
243 leftDimen = rightDimen;
244 }
245 if (topDimen == 0.0_vp) {
246 topDimen = bottomDimen;
247 }
248 if (leftDimen == 0.0_vp) {
249 leftDimen = topDimen;
250 }
251
252 padding.left = leftDimen.ConvertToPx();
253 padding.right = rightDimen.ConvertToPx();
254 padding.top = topDimen.ConvertToPx();
255 padding.bottom = bottomDimen.ConvertToPx();
256 return padding;
257 }
258 }
259
260 CalcDimension length;
261 if (!ParseJsDimensionVp(info[0], length)) {
262 return padding;
263 }
264
265 padding.left = length.ConvertToPx();
266 padding.right = length.ConvertToPx();
267 padding.top = length.ConvertToPx();
268 padding.bottom = length.ConvertToPx();
269 return padding;
270 }
271
GetNewPadding(const JSCallbackInfo & info)272 NG::PaddingProperty JSRadio::GetNewPadding(const JSCallbackInfo& info)
273 {
274 NG::PaddingProperty padding(
275 { NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp) });
276 if (info[0]->IsObject()) {
277 std::optional<CalcDimension> left;
278 std::optional<CalcDimension> right;
279 std::optional<CalcDimension> top;
280 std::optional<CalcDimension> bottom;
281 JSRef<JSObject> paddingObj = JSRef<JSObject>::Cast(info[0]);
282
283 CalcDimension leftDimen;
284 if (ParseJsDimensionVp(paddingObj->GetProperty("left"), leftDimen)) {
285 left = leftDimen;
286 }
287 CalcDimension rightDimen;
288 if (ParseJsDimensionVp(paddingObj->GetProperty("right"), rightDimen)) {
289 right = rightDimen;
290 }
291 CalcDimension topDimen;
292 if (ParseJsDimensionVp(paddingObj->GetProperty("top"), topDimen)) {
293 top = topDimen;
294 }
295 CalcDimension bottomDimen;
296 if (ParseJsDimensionVp(paddingObj->GetProperty("bottom"), bottomDimen)) {
297 bottom = bottomDimen;
298 }
299 if (left.has_value() || right.has_value() || top.has_value() || bottom.has_value()) {
300 padding = GetPadding(top, bottom, left, right);
301 return padding;
302 }
303 }
304 CalcDimension length;
305 if (!ParseJsDimensionVp(info[0], length)) {
306 length.Reset();
307 }
308
309 padding.SetEdges(NG::CalcLength(length.IsNonNegative() ? length : CalcDimension()));
310 return padding;
311 }
312
GetPadding(const std::optional<CalcDimension> & top,const std::optional<CalcDimension> & bottom,const std::optional<CalcDimension> & left,const std::optional<CalcDimension> & right)313 NG::PaddingProperty JSRadio::GetPadding(const std::optional<CalcDimension>& top,
314 const std::optional<CalcDimension>& bottom, const std::optional<CalcDimension>& left,
315 const std::optional<CalcDimension>& right)
316 {
317 NG::PaddingProperty padding(
318 { NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp) });
319 if (left.has_value() && left.value().IsNonNegative()) {
320 padding.left = NG::CalcLength(left.value());
321 }
322 if (right.has_value() && right.value().IsNonNegative()) {
323 padding.right = NG::CalcLength(right.value());
324 }
325 if (top.has_value() && top.value().IsNonNegative()) {
326 padding.top = NG::CalcLength(top.value());
327 }
328 if (bottom.has_value() && bottom.value().IsNonNegative()) {
329 padding.bottom = NG::CalcLength(bottom.value());
330 }
331 return padding;
332 }
333
JsRadioStyle(const JSCallbackInfo & info)334 void JSRadio::JsRadioStyle(const JSCallbackInfo& info)
335 {
336 if (info[0]->IsObject()) {
337 JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
338 JSRef<JSVal> checkedBackgroundColor = obj->GetProperty("checkedBackgroundColor");
339 JSRef<JSVal> uncheckedBorderColor = obj->GetProperty("uncheckedBorderColor");
340 JSRef<JSVal> indicatorColor = obj->GetProperty("indicatorColor");
341 Color checkedBackgroundColorVal;
342 auto theme = GetTheme<RadioTheme>();
343 if (!ParseJsColor(checkedBackgroundColor, checkedBackgroundColorVal)) {
344 checkedBackgroundColorVal = theme->GetActiveColor();
345 }
346 RadioModel::GetInstance()->SetCheckedBackgroundColor(checkedBackgroundColorVal);
347 Color uncheckedBorderColorVal;
348 if (!ParseJsColor(uncheckedBorderColor, uncheckedBorderColorVal)) {
349 uncheckedBorderColorVal = theme->GetInactiveColor();
350 }
351 RadioModel::GetInstance()->SetUncheckedBorderColor(uncheckedBorderColorVal);
352 Color indicatorColorVal;
353 if (!ParseJsColor(indicatorColor, indicatorColorVal)) {
354 indicatorColorVal = theme->GetPointColor();
355 }
356 RadioModel::GetInstance()->SetIndicatorColor(indicatorColorVal);
357 }
358 }
359
JsResponseRegion(const JSCallbackInfo & info)360 void JSRadio::JsResponseRegion(const JSCallbackInfo& info)
361 {
362 if (info.Length() < 1) {
363 LOGE("The arg is wrong, it is supposed to have at least 1 arguments");
364 return;
365 }
366
367 std::vector<DimensionRect> result;
368 if (!JSViewAbstract::ParseJsResponseRegionArray(info[0], result)) {
369 return;
370 }
371
372 RadioModel::GetInstance()->SetResponseRegion(result);
373 }
374
OnChange(const JSCallbackInfo & args)375 void JSRadio::OnChange(const JSCallbackInfo& args)
376 {
377 if (!args[0]->IsFunction()) {
378 return;
379 }
380 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(args[0]));
381 auto onChange = [execCtx = args.GetExecutionContext(), func = std::move(jsFunc)](bool check) {
382 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
383 ACE_SCORING_EVENT("Radio.onChange");
384 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(check));
385 func->ExecuteJS(1, &newJSVal);
386 };
387 RadioModel::GetInstance()->SetOnChange(std::move(onChange));
388 args.ReturnSelf();
389 }
390
JsOnClick(const JSCallbackInfo & args)391 void JSRadio::JsOnClick(const JSCallbackInfo& args)
392 {
393 if (Container::IsCurrentUseNewPipeline()) {
394 JSViewAbstract::JsOnClick(args);
395 return;
396 }
397
398 RadioModel::GetInstance()->SetOnClickEvent(
399 JsEventCallback<void()>(args.GetExecutionContext(), JSRef<JSFunc>::Cast(args[0])));
400
401 args.ReturnSelf();
402 }
403
404 } // namespace OHOS::Ace::Framework
405