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