• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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