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