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/base/view_stack_processor.h"
25 #include "core/components_ng/pattern/radio/radio_model_ng.h"
26
27 namespace OHOS::Ace {
28
29 std::unique_ptr<RadioModel> RadioModel::instance_ = nullptr;
30 std::mutex RadioModel::mutex_;
31
GetInstance()32 RadioModel* RadioModel::GetInstance()
33 {
34 if (!instance_) {
35 std::lock_guard<std::mutex> lock(mutex_);
36 if (!instance_) {
37 #ifdef NG_BUILD
38 instance_.reset(new NG::RadioModelNG());
39 #else
40 if (Container::IsCurrentUseNewPipeline()) {
41 instance_.reset(new NG::RadioModelNG());
42 } else {
43 instance_.reset(new Framework::RadioModelImpl());
44 }
45 #endif
46 }
47 }
48 return instance_.get();
49 }
50
51 } // namespace OHOS::Ace
52 namespace OHOS::Ace::Framework {
Create(const JSCallbackInfo & info)53 void JSRadio::Create(const JSCallbackInfo& info)
54 {
55 if (info.Length() < 1) {
56 return;
57 }
58
59 std::optional<std::string> value;
60 std::optional<std::string> group;
61 if ((info.Length() >= 1) && info[0]->IsObject()) {
62 auto paramObject = JSRef<JSObject>::Cast(info[0]);
63 auto valueTemp = paramObject->GetProperty("value");
64 auto groupTemp = paramObject->GetProperty("group");
65 if (valueTemp->IsString()) {
66 value = valueTemp->ToString();
67 } else {
68 value = "";
69 }
70 if (groupTemp->IsString()) {
71 group = groupTemp->ToString();
72 } else {
73 group = "";
74 }
75 }
76 RadioModel::GetInstance()->Create(value, group);
77 }
78
JSBind(BindingTarget globalObj)79 void JSRadio::JSBind(BindingTarget globalObj)
80 {
81 JSClass<JSRadio>::Declare("Radio");
82
83 JSClass<JSRadio>::StaticMethod("create", &JSRadio::Create);
84 JSClass<JSRadio>::StaticMethod("checked", &JSRadio::Checked);
85 JSClass<JSRadio>::StaticMethod("width", &JSRadio::JsWidth);
86 JSClass<JSRadio>::StaticMethod("height", &JSRadio::JsHeight);
87 JSClass<JSRadio>::StaticMethod("size", &JSRadio::JsSize);
88 JSClass<JSRadio>::StaticMethod("padding", &JSRadio::JsPadding);
89 JSClass<JSRadio>::StaticMethod("radioStyle", &JSRadio::JsRadioStyle);
90 JSClass<JSRadio>::StaticMethod("responseRegion", &JSRadio::JsResponseRegion);
91 JSClass<JSRadio>::StaticMethod("hoverEffect", &JSRadio::JsHoverEffect);
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 WeakPtr<NG::FrameNode> targetNode = NG::ViewStackProcessor::GetInstance()->GetMainFrameNode();
108 auto onChecked = [execCtx = args.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](bool check) {
109 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
110 ACE_SCORING_EVENT("Radio.onChangeEvent");
111 PipelineContext::SetCallBackNode(node);
112 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(check));
113 func->ExecuteJS(1, &newJSVal);
114 };
115 RadioModel::GetInstance()->SetOnChangeEvent(std::move(onChecked));
116 }
117
Checked(const JSCallbackInfo & info)118 void JSRadio::Checked(const JSCallbackInfo& info)
119 {
120 if (info.Length() < 1 || info.Length() > 2) {
121 return;
122 }
123
124 if (info.Length() > 0 && info[0]->IsBoolean()) {
125 RadioModel::GetInstance()->SetChecked(info[0]->ToBoolean());
126 } else {
127 RadioModel::GetInstance()->SetChecked(false);
128 }
129
130 if (info.Length() > 1 && info[1]->IsFunction()) {
131 ParseCheckedObject(info, info[1]);
132 }
133 }
134
JsWidth(const JSCallbackInfo & info)135 void JSRadio::JsWidth(const JSCallbackInfo& info)
136 {
137 if (info.Length() < 1) {
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 return;
165 }
166
167 JsHeight(info[0]);
168 }
169
JsHeight(const JSRef<JSVal> & jsValue)170 void JSRadio::JsHeight(const JSRef<JSVal>& jsValue)
171 {
172 auto pipeline = PipelineBase::GetCurrentContext();
173 CHECK_NULL_VOID(pipeline);
174 auto radioTheme = pipeline->GetTheme<RadioTheme>();
175 CHECK_NULL_VOID(radioTheme);
176 auto defaultHeight = radioTheme->GetDefaultHeight();
177 auto verticalPadding = radioTheme->GetHotZoneVerticalPadding();
178 auto height = defaultHeight - verticalPadding * 2;
179 CalcDimension value(height);
180 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
181 if (!ParseJsDimensionVpNG(jsValue, value)) {
182 value = height;
183 }
184 } else {
185 ParseJsDimensionVp(jsValue, value);
186 if (value.IsNegative()) {
187 value = height;
188 }
189 }
190 RadioModel::GetInstance()->SetHeight(value);
191 }
192
JsSize(const JSCallbackInfo & info)193 void JSRadio::JsSize(const JSCallbackInfo& info)
194 {
195 if (!info[0]->IsObject()) {
196 return;
197 }
198
199 JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
200 JsWidth(sizeObj->GetProperty("width"));
201 JsHeight(sizeObj->GetProperty("height"));
202 }
203
JsPadding(const JSCallbackInfo & info)204 void JSRadio::JsPadding(const JSCallbackInfo& info)
205 {
206 if (info.Length() < 1) {
207 return;
208 }
209 NG::PaddingPropertyF oldPadding = GetOldPadding(info);
210 NG::PaddingProperty newPadding = GetNewPadding(info);
211 RadioModel::GetInstance()->SetPadding(oldPadding, newPadding);
212 }
213
GetOldPadding(const JSCallbackInfo & info)214 NG::PaddingPropertyF JSRadio::GetOldPadding(const JSCallbackInfo& info)
215 {
216 NG::PaddingPropertyF padding({ 0.0f, 0.0f, 0.0f, 0.0f });
217 if (info[0]->IsObject()) {
218 JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
219 if (jsObj->HasProperty("top") || jsObj->HasProperty("bottom")
220 || jsObj->HasProperty("left") || jsObj->HasProperty("right")) {
221 CalcDimension topDimen = CalcDimension(0.0, DimensionUnit::VP);
222 CalcDimension leftDimen = CalcDimension(0.0, DimensionUnit::VP);
223 CalcDimension rightDimen = CalcDimension(0.0, DimensionUnit::VP);
224 CalcDimension bottomDimen = CalcDimension(0.0, DimensionUnit::VP);
225 ParseJsDimensionVp(jsObj->GetProperty("top"), topDimen);
226 ParseJsDimensionVp(jsObj->GetProperty("left"), leftDimen);
227 ParseJsDimensionVp(jsObj->GetProperty("right"), rightDimen);
228 ParseJsDimensionVp(jsObj->GetProperty("bottom"), bottomDimen);
229 if (leftDimen == 0.0_vp) {
230 leftDimen = rightDimen;
231 }
232 if (topDimen == 0.0_vp) {
233 topDimen = bottomDimen;
234 }
235 if (leftDimen == 0.0_vp) {
236 leftDimen = topDimen;
237 }
238
239 padding.left = leftDimen.ConvertToPx();
240 padding.right = rightDimen.ConvertToPx();
241 padding.top = topDimen.ConvertToPx();
242 padding.bottom = bottomDimen.ConvertToPx();
243 return padding;
244 }
245 }
246
247 CalcDimension length;
248 if (!ParseJsDimensionVp(info[0], length)) {
249 return padding;
250 }
251
252 padding.left = length.ConvertToPx();
253 padding.right = length.ConvertToPx();
254 padding.top = length.ConvertToPx();
255 padding.bottom = length.ConvertToPx();
256 return padding;
257 }
258
GetNewPadding(const JSCallbackInfo & info)259 NG::PaddingProperty JSRadio::GetNewPadding(const JSCallbackInfo& info)
260 {
261 NG::PaddingProperty padding(
262 { NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp) });
263 if (info[0]->IsObject()) {
264 std::optional<CalcDimension> left;
265 std::optional<CalcDimension> right;
266 std::optional<CalcDimension> top;
267 std::optional<CalcDimension> bottom;
268 JSRef<JSObject> paddingObj = JSRef<JSObject>::Cast(info[0]);
269
270 CalcDimension leftDimen;
271 if (ParseJsDimensionVp(paddingObj->GetProperty("left"), leftDimen)) {
272 left = leftDimen;
273 }
274 CalcDimension rightDimen;
275 if (ParseJsDimensionVp(paddingObj->GetProperty("right"), rightDimen)) {
276 right = rightDimen;
277 }
278 CalcDimension topDimen;
279 if (ParseJsDimensionVp(paddingObj->GetProperty("top"), topDimen)) {
280 top = topDimen;
281 }
282 CalcDimension bottomDimen;
283 if (ParseJsDimensionVp(paddingObj->GetProperty("bottom"), bottomDimen)) {
284 bottom = bottomDimen;
285 }
286 if (left.has_value() || right.has_value() || top.has_value() || bottom.has_value()) {
287 padding = GetPadding(top, bottom, left, right);
288 return padding;
289 }
290 }
291 CalcDimension length;
292 if (!ParseJsDimensionVp(info[0], length)) {
293 length.Reset();
294 }
295
296 padding.SetEdges(NG::CalcLength(length.IsNonNegative() ? length : CalcDimension()));
297 return padding;
298 }
299
GetPadding(const std::optional<CalcDimension> & top,const std::optional<CalcDimension> & bottom,const std::optional<CalcDimension> & left,const std::optional<CalcDimension> & right)300 NG::PaddingProperty JSRadio::GetPadding(const std::optional<CalcDimension>& top,
301 const std::optional<CalcDimension>& bottom, const std::optional<CalcDimension>& left,
302 const std::optional<CalcDimension>& right)
303 {
304 NG::PaddingProperty padding(
305 { NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp) });
306 if (left.has_value() && left.value().IsNonNegative()) {
307 padding.left = NG::CalcLength(left.value());
308 }
309 if (right.has_value() && right.value().IsNonNegative()) {
310 padding.right = NG::CalcLength(right.value());
311 }
312 if (top.has_value() && top.value().IsNonNegative()) {
313 padding.top = NG::CalcLength(top.value());
314 }
315 if (bottom.has_value() && bottom.value().IsNonNegative()) {
316 padding.bottom = NG::CalcLength(bottom.value());
317 }
318 return padding;
319 }
320
JsRadioStyle(const JSCallbackInfo & info)321 void JSRadio::JsRadioStyle(const JSCallbackInfo& info)
322 {
323 if (info[0]->IsObject()) {
324 JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
325 JSRef<JSVal> checkedBackgroundColor = obj->GetProperty("checkedBackgroundColor");
326 JSRef<JSVal> uncheckedBorderColor = obj->GetProperty("uncheckedBorderColor");
327 JSRef<JSVal> indicatorColor = obj->GetProperty("indicatorColor");
328 Color checkedBackgroundColorVal;
329 auto theme = GetTheme<RadioTheme>();
330 if (!ParseJsColor(checkedBackgroundColor, checkedBackgroundColorVal)) {
331 checkedBackgroundColorVal = theme->GetActiveColor();
332 }
333 RadioModel::GetInstance()->SetCheckedBackgroundColor(checkedBackgroundColorVal);
334 Color uncheckedBorderColorVal;
335 if (!ParseJsColor(uncheckedBorderColor, uncheckedBorderColorVal)) {
336 uncheckedBorderColorVal = theme->GetInactiveColor();
337 }
338 RadioModel::GetInstance()->SetUncheckedBorderColor(uncheckedBorderColorVal);
339 Color indicatorColorVal;
340 if (!ParseJsColor(indicatorColor, indicatorColorVal)) {
341 indicatorColorVal = theme->GetPointColor();
342 }
343 RadioModel::GetInstance()->SetIndicatorColor(indicatorColorVal);
344 }
345 }
346
JsResponseRegion(const JSCallbackInfo & info)347 void JSRadio::JsResponseRegion(const JSCallbackInfo& info)
348 {
349 if (info.Length() < 1) {
350 return;
351 }
352
353 std::vector<DimensionRect> result;
354 if (!JSViewAbstract::ParseJsResponseRegionArray(info[0], result)) {
355 return;
356 }
357
358 RadioModel::GetInstance()->SetResponseRegion(result);
359 }
360
OnChange(const JSCallbackInfo & args)361 void JSRadio::OnChange(const JSCallbackInfo& args)
362 {
363 if (!args[0]->IsFunction()) {
364 return;
365 }
366 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(args[0]));
367 WeakPtr<NG::FrameNode> targetNode = NG::ViewStackProcessor::GetInstance()->GetMainFrameNode();
368 auto onChange = [execCtx = args.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](bool check) {
369 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
370 ACE_SCORING_EVENT("Radio.onChange");
371 PipelineContext::SetCallBackNode(node);
372 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(check));
373 func->ExecuteJS(1, &newJSVal);
374 };
375 RadioModel::GetInstance()->SetOnChange(std::move(onChange));
376 args.ReturnSelf();
377 }
378
JsOnClick(const JSCallbackInfo & args)379 void JSRadio::JsOnClick(const JSCallbackInfo& args)
380 {
381 if (Container::IsCurrentUseNewPipeline()) {
382 JSViewAbstract::JsOnClick(args);
383 return;
384 }
385
386 RadioModel::GetInstance()->SetOnClickEvent(
387 JsEventCallback<void()>(args.GetExecutionContext(), JSRef<JSFunc>::Cast(args[0])));
388
389 args.ReturnSelf();
390 }
391
JsHoverEffect(const JSCallbackInfo & info)392 void JSRadio::JsHoverEffect(const JSCallbackInfo& info)
393 {
394 if (info[0]->IsNumber()) {
395 RadioModel::GetInstance()->SetHoverEffect(static_cast<HoverEffectType>(info[0]->ToNumber<int32_t>()));
396 }
397 }
398 } // namespace OHOS::Ace::Framework
399