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 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
21
22 #include "base/log/ace_scoring_log.h"
23 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
24 #include "bridge/declarative_frontend/jsview/js_button.h"
25 #include "bridge/declarative_frontend/jsview/models/toggle_model_impl.h"
26 #include "core/common/container.h"
27 #include "core/components/common/properties/color.h"
28 #include "core/components/toggle/toggle_theme.h"
29 #include "core/components_ng/base/view_stack_model.h"
30 #include "core/components_ng/base/view_stack_processor.h"
31 #include "core/components_ng/pattern/button/toggle_button_model_ng.h"
32 #include "core/components_ng/pattern/toggle/toggle_model_ng.h"
33
34 namespace OHOS::Ace {
35
36 std::unique_ptr<ToggleModel> ToggleModel::instance_ = nullptr;
37 std::mutex ToggleModel::mutex_;
38
GetInstance()39 ToggleModel* ToggleModel::GetInstance()
40 {
41 if (!instance_) {
42 std::lock_guard<std::mutex> lock(mutex_);
43 if (!instance_) {
44 #ifdef NG_BUILD
45 instance_.reset(new NG::ToggleModelNG());
46 #else
47 if (Container::IsCurrentUseNewPipeline()) {
48 instance_.reset(new NG::ToggleModelNG());
49 } else {
50 instance_.reset(new Framework::ToggleModelImpl());
51 }
52 #endif
53 }
54 }
55 return instance_.get();
56 }
57
58 } // namespace OHOS::Ace
59
60 namespace OHOS::Ace::Framework {
61 int32_t JSToggle::toggleType_ = 1;
JSBind(BindingTarget globalObj)62 void JSToggle::JSBind(BindingTarget globalObj)
63 {
64 JSClass<JSToggle>::Declare("Toggle");
65 JSClass<JSToggle>::StaticMethod("create", &JSToggle::Create);
66 JSClass<JSToggle>::StaticMethod("onChange", &JSToggle::OnChange);
67 JSClass<JSToggle>::StaticMethod("selectedColor", &JSToggle::SelectedColor);
68 JSClass<JSToggle>::StaticMethod("width", &JSToggle::JsWidth);
69 JSClass<JSToggle>::StaticMethod("height", &JSToggle::JsHeight);
70 JSClass<JSToggle>::StaticMethod("responseRegion", &JSToggle::JsResponseRegion);
71 JSClass<JSToggle>::StaticMethod("size", &JSToggle::JsSize);
72 JSClass<JSToggle>::StaticMethod("padding", &JSToggle::JsPadding);
73 JSClass<JSToggle>::StaticMethod("pop", &JSToggle::Pop);
74 JSClass<JSToggle>::StaticMethod("switchPointColor", &JSToggle::SwitchPointColor);
75 JSClass<JSToggle>::StaticMethod("backgroundColor", &JSToggle::SetBackgroundColor);
76 JSClass<JSToggle>::StaticMethod("hoverEffect", &JSToggle::JsHoverEffect);
77 JSClass<JSToggle>::StaticMethod("switchStyle", &JSToggle::SwitchStyle);
78 JSClass<JSToggle>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
79 JSClass<JSToggle>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
80 JSClass<JSToggle>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
81 JSClass<JSToggle>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
82 JSClass<JSToggle>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
83 JSClass<JSToggle>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
84 JSClass<JSToggle>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
85 JSClass<JSToggle>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
86 JSClass<JSToggle>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
87 JSClass<JSToggle>::StaticMethod("borderRadius", &JSToggle::JsRadius);
88 JSClass<JSToggle>::StaticMethod("border", &JSToggle::JsBorder);
89 JSClass<JSToggle>::InheritAndBind<JSViewAbstract>(globalObj);
90 }
91
ParseToggleIsOnObject(const JSCallbackInfo & info,const JSRef<JSVal> & changeEventVal)92 void ParseToggleIsOnObject(const JSCallbackInfo& info, const JSRef<JSVal>& changeEventVal)
93 {
94 CHECK_NULL_VOID(changeEventVal->IsFunction());
95
96 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(changeEventVal));
97 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
98 auto onChangeEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
99 bool isOn) {
100 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
101 ACE_SCORING_EVENT("Toggle.onChangeEvent");
102 PipelineContext::SetCallBackNode(node);
103 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(isOn));
104 func->ExecuteJS(1, &newJSVal);
105 };
106 ToggleModel::GetInstance()->OnChangeEvent(std::move(onChangeEvent));
107 }
108
Create(const JSCallbackInfo & info)109 void JSToggle::Create(const JSCallbackInfo& info)
110 {
111 if (!info[0]->IsObject()) {
112 return;
113 }
114
115 auto paramObject = JSRef<JSObject>::Cast(info[0]);
116 auto type = paramObject->GetProperty("type");
117 int32_t toggleTypeInt = 1;
118 if (type->IsNumber()) {
119 toggleTypeInt = type->ToNumber<int32_t>();
120 }
121 if (toggleTypeInt < 0 || toggleTypeInt > 2) {
122 toggleTypeInt = 1;
123 }
124 toggleType_ = toggleTypeInt;
125 auto tempIsOn = paramObject->GetProperty("isOn");
126 bool isOn = false;
127 JSRef<JSVal> changeEventVal;
128 if (tempIsOn->IsObject()) {
129 JSRef<JSObject> isOnObj = JSRef<JSObject>::Cast(tempIsOn);
130 changeEventVal = isOnObj->GetProperty("changeEvent");
131 auto isOnProperty = isOnObj->GetProperty("value");
132 isOn = isOnProperty->IsBoolean() ? isOnProperty->ToBoolean() : false;
133 } else if (paramObject->HasProperty("$isOn")) {
134 changeEventVal = paramObject->GetProperty("$isOn");
135 isOn = tempIsOn->IsBoolean() ? tempIsOn->ToBoolean() : false;
136 } else {
137 isOn = tempIsOn->IsBoolean() ? tempIsOn->ToBoolean() : false;
138 }
139 TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "toggle create type %{public}d isOn %{public}d", toggleTypeInt, isOn);
140 ToggleModel::GetInstance()->Create(NG::ToggleType(toggleTypeInt), isOn);
141 if (!changeEventVal->IsUndefined() && changeEventVal->IsFunction()) {
142 ParseToggleIsOnObject(info, changeEventVal);
143 }
144 }
145
JsWidth(const JSCallbackInfo & info)146 void JSToggle::JsWidth(const JSCallbackInfo& info)
147 {
148 if (info.Length() < 1) {
149 return;
150 }
151 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
152 JSViewAbstract::JsWidth(info[0]);
153 return;
154 }
155 JsWidth(info[0]);
156 }
157
JsWidth(const JSRef<JSVal> & jsValue)158 void JSToggle::JsWidth(const JSRef<JSVal>& jsValue)
159 {
160 auto switchTheme = GetTheme<SwitchTheme>();
161 CHECK_NULL_VOID(switchTheme);
162 auto defaultWidth = switchTheme->GetWidth();
163 auto horizontalPadding = switchTheme->GetHotZoneHorizontalPadding();
164 auto width = defaultWidth - horizontalPadding * 2;
165 if (toggleType_ == 0) {
166 auto checkboxTheme = GetTheme<CheckboxTheme>();
167 CHECK_NULL_VOID(checkboxTheme);
168 defaultWidth = checkboxTheme->GetDefaultWidth();
169 horizontalPadding = checkboxTheme->GetHotZoneHorizontalPadding();
170 width = defaultWidth - horizontalPadding * 2;
171 }
172 CalcDimension value(width);
173 ParseJsDimensionVp(jsValue, value);
174 if (value.IsNegative()) {
175 value = width;
176 }
177 ToggleModel::GetInstance()->SetWidth(value);
178 }
179
JsHeight(const JSCallbackInfo & info)180 void JSToggle::JsHeight(const JSCallbackInfo& info)
181 {
182 if (info.Length() < 1) {
183 return;
184 }
185 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
186 JSViewAbstract::JsHeight(info[0]);
187 return;
188 }
189 JsHeight(info[0]);
190 }
191
JsHeight(const JSRef<JSVal> & jsValue)192 void JSToggle::JsHeight(const JSRef<JSVal>& jsValue)
193 {
194 auto switchTheme = GetTheme<SwitchTheme>();
195 CHECK_NULL_VOID(switchTheme);
196 auto defaultHeight = switchTheme->GetHeight();
197 auto verticalPadding = switchTheme->GetHotZoneVerticalPadding();
198 auto height = defaultHeight - verticalPadding * 2;
199 CalcDimension value(height);
200 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
201 if (!ParseJsDimensionVpNG(jsValue, value) || value.IsNegative()) {
202 value = height;
203 }
204 } else {
205 ParseJsDimensionVp(jsValue, value);
206 if (value.IsNegative()) {
207 value = height;
208 }
209 }
210 ToggleModel::GetInstance()->SetHeight(value);
211 }
212
JsResponseRegion(const JSCallbackInfo & info)213 void JSToggle::JsResponseRegion(const JSCallbackInfo& info)
214 {
215 if (!Container::IsCurrentUseNewPipeline()) {
216 JSViewAbstract::JsResponseRegion(info);
217 return;
218 }
219 std::vector<DimensionRect> result;
220 if (!JSViewAbstract::ParseJsResponseRegionArray(info[0], result)) {
221 return;
222 }
223 ToggleModel::GetInstance()->SetResponseRegion(result);
224 }
225
JsSize(const JSCallbackInfo & info)226 void JSToggle::JsSize(const JSCallbackInfo& info)
227 {
228 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
229 JSViewAbstract::JsSize(info);
230 return;
231 }
232 if (!info[0]->IsObject()) {
233 return;
234 }
235
236 JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
237 JsWidth(sizeObj->GetProperty("width"));
238 JsHeight(sizeObj->GetProperty("height"));
239 }
240
OnChange(const JSCallbackInfo & args)241 void JSToggle::OnChange(const JSCallbackInfo& args)
242 {
243 auto jsVal = args[0];
244 if (!jsVal->IsFunction()) {
245 return;
246 }
247 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(jsVal));
248 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
249 auto onChange = [execCtx = args.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](bool isOn) {
250 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
251 ACE_SCORING_EVENT("Toggle.onChange");
252 PipelineContext::SetCallBackNode(node);
253 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(isOn));
254 func->ExecuteJS(1, &newJSVal);
255 UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", "Toggle.onChange");
256 };
257 ToggleModel::GetInstance()->OnChange(std::move(onChange));
258 args.ReturnSelf();
259 }
260
SelectedColor(const JSCallbackInfo & info)261 void JSToggle::SelectedColor(const JSCallbackInfo& info)
262 {
263 if (info.Length() < 1) {
264 return;
265 }
266 Color color;
267 std::optional<Color> selectedColor;
268 if (ParseJsColor(info[0], color)) {
269 selectedColor = color;
270 }
271
272 ToggleModel::GetInstance()->SetSelectedColor(selectedColor);
273 }
274
SwitchPointColor(const JSCallbackInfo & info)275 void JSToggle::SwitchPointColor(const JSCallbackInfo& info)
276 {
277 if (info.Length() < 1) {
278 return;
279 }
280 Color color;
281 std::optional<Color> switchPointColor;
282 if (ParseJsColor(info[0], color)) {
283 switchPointColor = color;
284 }
285
286 ToggleModel::GetInstance()->SetSwitchPointColor(switchPointColor);
287 }
288
JsPadding(const JSCallbackInfo & info)289 void JSToggle::JsPadding(const JSCallbackInfo& info)
290 {
291 if (info.Length() < 1) {
292 return;
293 }
294 NG::PaddingPropertyF oldPadding = GetOldPadding(info);
295 NG::PaddingProperty newPadding = GetNewPadding(info);
296 ToggleModel::GetInstance()->SetPadding(oldPadding, newPadding);
297 }
298
GetOldPadding(const JSCallbackInfo & info)299 NG::PaddingPropertyF JSToggle::GetOldPadding(const JSCallbackInfo& info)
300 {
301 NG::PaddingPropertyF padding({ 0.0f, 0.0f, 0.0f, 0.0f });
302 if (info[0]->IsObject()) {
303 JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
304 if (jsObj->HasProperty("top") || jsObj->HasProperty("bottom")
305 || jsObj->HasProperty("left") || jsObj->HasProperty("right")) {
306 CalcDimension topDimen = CalcDimension(0.0, DimensionUnit::VP);
307 CalcDimension leftDimen = CalcDimension(0.0, DimensionUnit::VP);
308 CalcDimension rightDimen = CalcDimension(0.0, DimensionUnit::VP);
309 CalcDimension bottomDimen = CalcDimension(0.0, DimensionUnit::VP);
310 ParseJsDimensionVp(jsObj->GetProperty("top"), topDimen);
311 ParseJsDimensionVp(jsObj->GetProperty("left"), leftDimen);
312 ParseJsDimensionVp(jsObj->GetProperty("right"), rightDimen);
313 ParseJsDimensionVp(jsObj->GetProperty("bottom"), bottomDimen);
314 if (leftDimen == 0.0_vp) {
315 leftDimen = rightDimen;
316 }
317 if (topDimen == 0.0_vp) {
318 topDimen = bottomDimen;
319 }
320 if (leftDimen == 0.0_vp) {
321 leftDimen = topDimen;
322 }
323
324 padding.left = leftDimen.ConvertToPx();
325 padding.right = rightDimen.ConvertToPx();
326 padding.top = topDimen.ConvertToPx();
327 padding.bottom = bottomDimen.ConvertToPx();
328 return padding;
329 }
330 }
331
332 CalcDimension length;
333 if (!ParseJsDimensionVp(info[0], length)) {
334 return padding;
335 }
336
337 padding.left = length.ConvertToPx();
338 padding.right = length.ConvertToPx();
339 padding.top = length.ConvertToPx();
340 padding.bottom = length.ConvertToPx();
341 return padding;
342 }
343
GetNewPadding(const JSCallbackInfo & info)344 NG::PaddingProperty JSToggle::GetNewPadding(const JSCallbackInfo& info)
345 {
346 NG::PaddingProperty padding({ NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp),
347 NG::CalcLength(0.0_vp), std::nullopt, std::nullopt });
348 if (info[0]->IsObject()) {
349 JSRef<JSObject> paddingObj = JSRef<JSObject>::Cast(info[0]);
350 CommonCalcDimension commonCalcDimension;
351 ParseCommonMarginOrPaddingCorner(paddingObj, commonCalcDimension);
352 if (commonCalcDimension.left.has_value() || commonCalcDimension.right.has_value() ||
353 commonCalcDimension.top.has_value() || commonCalcDimension.bottom.has_value()) {
354 padding = GetPadding(commonCalcDimension.top, commonCalcDimension.bottom, commonCalcDimension.left,
355 commonCalcDimension.right);
356 return padding;
357 }
358 }
359 CalcDimension length;
360 if (!ParseJsDimensionVp(info[0], length)) {
361 length.Reset();
362 }
363
364 padding.SetEdges(NG::CalcLength(length.IsNonNegative() ? length : CalcDimension()));
365 return padding;
366 }
367
GetPadding(const std::optional<CalcDimension> & top,const std::optional<CalcDimension> & bottom,const std::optional<CalcDimension> & left,const std::optional<CalcDimension> & right)368 NG::PaddingProperty JSToggle::GetPadding(const std::optional<CalcDimension>& top,
369 const std::optional<CalcDimension>& bottom, const std::optional<CalcDimension>& left,
370 const std::optional<CalcDimension>& right)
371 {
372 NG::PaddingProperty padding({ NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp),
373 NG::CalcLength(0.0_vp), std::nullopt, std::nullopt });
374 if (left.has_value() && left.value().IsNonNegative()) {
375 padding.left = NG::CalcLength(left.value());
376 }
377 if (right.has_value() && right.value().IsNonNegative()) {
378 padding.right = NG::CalcLength(right.value());
379 }
380 if (top.has_value() && top.value().IsNonNegative()) {
381 padding.top = NG::CalcLength(top.value());
382 }
383 if (bottom.has_value() && bottom.value().IsNonNegative()) {
384 padding.bottom = NG::CalcLength(bottom.value());
385 }
386 return padding;
387 }
388
SetBackgroundColor(const JSCallbackInfo & info)389 void JSToggle::SetBackgroundColor(const JSCallbackInfo& info)
390 {
391 Color backgroundColor = Color::TRANSPARENT;
392 bool flag = ParseJsColor(info[0], backgroundColor);
393 if (!Container::IsCurrentUseNewPipeline()) {
394 JSViewAbstract::JsBackgroundColor(info);
395 return;
396 }
397 ToggleModel::GetInstance()->SetBackgroundColor(backgroundColor, flag);
398 }
399
JsHoverEffect(const JSCallbackInfo & info)400 void JSToggle::JsHoverEffect(const JSCallbackInfo& info)
401 {
402 if (info.Length() > 0 && info[0]->IsNumber()) {
403 ToggleModel::GetInstance()->SetHoverEffect(static_cast<HoverEffectType>(info[0]->ToNumber<int32_t>()));
404 }
405 }
406
Pop()407 void JSToggle::Pop()
408 {
409 if (ViewStackModel::GetInstance()->IsPrebuilding()) {
410 return ViewStackModel::GetInstance()->PushPrebuildCompCmd("[JSToggle][pop]", &JSToggle::Pop);
411 }
412 ToggleModel::GetInstance()->Pop();
413 }
414
SwitchStyle(const JSCallbackInfo & info)415 void JSToggle::SwitchStyle(const JSCallbackInfo& info)
416 {
417 if ((info.Length() < 1) || !info[0]->IsObject()) {
418 return;
419 }
420 JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
421
422 CalcDimension pointRadius;
423 if (jsObj->HasProperty("pointRadius") &&
424 ParseJsDimensionVpNG(jsObj->GetProperty("pointRadius"), pointRadius, false) && !pointRadius.IsNegative()) {
425 ToggleModel::GetInstance()->SetPointRadius(pointRadius);
426 } else {
427 ToggleModel::GetInstance()->ResetPointRadius();
428 }
429
430 Color unselectedColor;
431 if (jsObj->HasProperty("unselectedColor") &&
432 ParseJsColor(jsObj->GetProperty("unselectedColor"), unselectedColor)) {
433 ToggleModel::GetInstance()->SetUnselectedColor(unselectedColor);
434 } else {
435 auto theme = GetTheme<SwitchTheme>();
436 if (theme) {
437 unselectedColor = theme->GetInactiveColor();
438 }
439 ToggleModel::GetInstance()->SetUnselectedColor(unselectedColor);
440 }
441
442 Color pointColor;
443 if (jsObj->HasProperty("pointColor") && ParseJsColor(jsObj->GetProperty("pointColor"), pointColor)) {
444 ToggleModel::GetInstance()->SetSwitchPointColor(pointColor);
445 } else {
446 auto theme = GetTheme<SwitchTheme>();
447 if (theme) {
448 pointColor = theme->GetPointColor();
449 }
450 ToggleModel::GetInstance()->SetSwitchPointColor(pointColor);
451 }
452
453 CalcDimension trackRadius;
454 if (jsObj->HasProperty("trackBorderRadius") &&
455 ParseJsDimensionVpNG(jsObj->GetProperty("trackBorderRadius"), trackRadius, false) &&
456 !trackRadius.IsNegative()) {
457 ToggleModel::GetInstance()->SetTrackBorderRadius(trackRadius);
458 } else {
459 ToggleModel::GetInstance()->ResetTrackBorderRadius();
460 }
461 }
462
JsRadius(const JSCallbackInfo & info)463 void JSToggle::JsRadius(const JSCallbackInfo& info)
464 {
465 CalcDimension radius;
466 // when toggle equels button should follow button model.
467 if (static_cast<NG::ToggleType>(toggleType_) == NG::ToggleType::BUTTON) {
468 JSButton::JsRadius(info);
469 } else {
470 JSViewAbstract::JsBorderRadius(info);
471 }
472 }
473
JsBorder(const JSCallbackInfo & info)474 void JSToggle::JsBorder(const JSCallbackInfo& info)
475 {
476 JSViewAbstract::JsBorder(info);
477 if (!info[0]->IsObject()) {
478 return;
479 }
480 if (static_cast<NG::ToggleType>(toggleType_) == NG::ToggleType::BUTTON) {
481 JSRef<JSObject> object = JSRef<JSObject>::Cast(info[0]);
482 auto valueRadius = object->GetProperty("radius");
483 JSButton::JsRadius(valueRadius);
484 }
485 }
486 } // namespace OHOS::Ace::Framework
487