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>::StaticMethod("border", &JSToggle::JsBorder);
91 JSClass<JSToggle>::InheritAndBind<JSViewAbstract>(globalObj);
92 }
93
ParseToggleIsOnObject(const JSCallbackInfo& info, const JSRef<JSVal>& changeEventVal)94 void ParseToggleIsOnObject(const JSCallbackInfo& info, const JSRef<JSVal>& changeEventVal)
95 {
96 CHECK_NULL_VOID(changeEventVal->IsFunction());
97
98 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(changeEventVal));
99 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
100 auto onChangeEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
101 bool isOn) {
102 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
103 ACE_SCORING_EVENT("Toggle.onChangeEvent");
104 PipelineContext::SetCallBackNode(node);
105 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(isOn));
106 func->ExecuteJS(1, &newJSVal);
107 };
108 ToggleModel::GetInstance()->OnChangeEvent(std::move(onChangeEvent));
109 }
110
Create(const JSCallbackInfo& info)111 void JSToggle::Create(const JSCallbackInfo& info)
112 {
113 if (!info[0]->IsObject()) {
114 return;
115 }
116
117 auto paramObject = JSRef<JSObject>::Cast(info[0]);
118 auto type = paramObject->GetProperty("type");
119 int32_t toggleTypeInt = 1;
120 if (type->IsNumber()) {
121 toggleTypeInt = type->ToNumber<int32_t>();
122 }
123 if (toggleTypeInt < 0 || toggleTypeInt > 2) {
124 toggleTypeInt = 1;
125 }
126 toggleType_ = toggleTypeInt;
127 auto tempIsOn = paramObject->GetProperty("isOn");
128 bool isOn = false;
129 JSRef<JSVal> changeEventVal;
130 if (tempIsOn->IsObject()) {
131 JSRef<JSObject> isOnObj = JSRef<JSObject>::Cast(tempIsOn);
132 changeEventVal = isOnObj->GetProperty("changeEvent");
133 auto isOnProperty = isOnObj->GetProperty("value");
134 isOn = isOnProperty->IsBoolean() ? isOnProperty->ToBoolean() : false;
135 } else {
136 isOn = tempIsOn->IsBoolean() ? tempIsOn->ToBoolean() : false;
137 }
138 TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "toggle create type %{public}d isOn %{public}d", toggleTypeInt, isOn);
139 ToggleModel::GetInstance()->Create(NG::ToggleType(toggleTypeInt), isOn);
140 if (!changeEventVal->IsUndefined() && changeEventVal->IsFunction()) {
141 ParseToggleIsOnObject(info, changeEventVal);
142 }
143 JSToggleTheme::ApplyTheme(NG::ToggleType(toggleType_));
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 pipeline = PipelineBase::GetCurrentContext();
195 CHECK_NULL_VOID(pipeline);
196 auto switchTheme = pipeline->GetTheme<SwitchTheme>();
197 CHECK_NULL_VOID(switchTheme);
198 auto defaultHeight = switchTheme->GetHeight();
199 auto verticalPadding = switchTheme->GetHotZoneVerticalPadding();
200 auto height = defaultHeight - verticalPadding * 2;
201 CalcDimension value(height);
202 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
203 if (!ParseJsDimensionVpNG(jsValue, value) || value.IsNegative()) {
204 value = height;
205 }
206 } else {
207 ParseJsDimensionVp(jsValue, value);
208 if (value.IsNegative()) {
209 value = height;
210 }
211 }
212 ToggleModel::GetInstance()->SetHeight(value);
213 }
214
JsResponseRegion(const JSCallbackInfo& info)215 void JSToggle::JsResponseRegion(const JSCallbackInfo& info)
216 {
217 if (!Container::IsCurrentUseNewPipeline()) {
218 JSViewAbstract::JsResponseRegion(info);
219 return;
220 }
221 std::vector<DimensionRect> result;
222 if (!JSViewAbstract::ParseJsResponseRegionArray(info[0], result)) {
223 return;
224 }
225 ToggleModel::GetInstance()->SetResponseRegion(result);
226 }
227
JsSize(const JSCallbackInfo& info)228 void JSToggle::JsSize(const JSCallbackInfo& info)
229 {
230 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
231 JSViewAbstract::JsSize(info);
232 return;
233 }
234 if (!info[0]->IsObject()) {
235 return;
236 }
237
238 JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
239 JsWidth(sizeObj->GetProperty("width"));
240 JsHeight(sizeObj->GetProperty("height"));
241 }
242
OnChange(const JSCallbackInfo& args)243 void JSToggle::OnChange(const JSCallbackInfo& args)
244 {
245 auto jsVal = args[0];
246 if (!jsVal->IsFunction()) {
247 return;
248 }
249 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(jsVal));
250 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
251 auto onChange = [execCtx = args.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](bool isOn) {
252 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
253 ACE_SCORING_EVENT("Toggle.onChange");
254 PipelineContext::SetCallBackNode(node);
255 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(isOn));
256 func->ExecuteJS(1, &newJSVal);
257 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
258 UiSessionManager::GetInstance().ReportComponentChangeEvent("event", "Toggle.onChange");
259 #endif
260 };
261 ToggleModel::GetInstance()->OnChange(std::move(onChange));
262 args.ReturnSelf();
263 }
264
SelectedColor(const JSCallbackInfo& info)265 void JSToggle::SelectedColor(const JSCallbackInfo& info)
266 {
267 if (info.Length() < 1) {
268 return;
269 }
270 Color color;
271 std::optional<Color> selectedColor;
272 if (ParseJsColor(info[0], color)) {
273 selectedColor = color;
274 }
275
276 ToggleModel::GetInstance()->SetSelectedColor(selectedColor);
277 }
278
SwitchPointColor(const JSCallbackInfo& info)279 void JSToggle::SwitchPointColor(const JSCallbackInfo& info)
280 {
281 if (info.Length() < 1) {
282 return;
283 }
284 Color color;
285 if (!ParseJsColor(info[0], color)) {
286 auto theme = GetTheme<SwitchTheme>();
287 if (theme) {
288 color = theme->GetPointColor();
289 }
290 }
291
292 ToggleModel::GetInstance()->SetSwitchPointColor(color);
293 }
294
JsPadding(const JSCallbackInfo& info)295 void JSToggle::JsPadding(const JSCallbackInfo& info)
296 {
297 if (info.Length() < 1) {
298 return;
299 }
300 NG::PaddingPropertyF oldPadding = GetOldPadding(info);
301 NG::PaddingProperty newPadding = GetNewPadding(info);
302 ToggleModel::GetInstance()->SetPadding(oldPadding, newPadding);
303 }
304
GetOldPadding(const JSCallbackInfo& info)305 NG::PaddingPropertyF JSToggle::GetOldPadding(const JSCallbackInfo& info)
306 {
307 NG::PaddingPropertyF padding({ 0.0f, 0.0f, 0.0f, 0.0f });
308 if (info[0]->IsObject()) {
309 JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
310 if (jsObj->HasProperty("top") || jsObj->HasProperty("bottom")
311 || jsObj->HasProperty("left") || jsObj->HasProperty("right")) {
312 CalcDimension topDimen = CalcDimension(0.0, DimensionUnit::VP);
313 CalcDimension leftDimen = CalcDimension(0.0, DimensionUnit::VP);
314 CalcDimension rightDimen = CalcDimension(0.0, DimensionUnit::VP);
315 CalcDimension bottomDimen = CalcDimension(0.0, DimensionUnit::VP);
316 ParseJsDimensionVp(jsObj->GetProperty("top"), topDimen);
317 ParseJsDimensionVp(jsObj->GetProperty("left"), leftDimen);
318 ParseJsDimensionVp(jsObj->GetProperty("right"), rightDimen);
319 ParseJsDimensionVp(jsObj->GetProperty("bottom"), bottomDimen);
320 if (leftDimen == 0.0_vp) {
321 leftDimen = rightDimen;
322 }
323 if (topDimen == 0.0_vp) {
324 topDimen = bottomDimen;
325 }
326 if (leftDimen == 0.0_vp) {
327 leftDimen = topDimen;
328 }
329
330 padding.left = leftDimen.ConvertToPx();
331 padding.right = rightDimen.ConvertToPx();
332 padding.top = topDimen.ConvertToPx();
333 padding.bottom = bottomDimen.ConvertToPx();
334 return padding;
335 }
336 }
337
338 CalcDimension length;
339 if (!ParseJsDimensionVp(info[0], length)) {
340 return padding;
341 }
342
343 padding.left = length.ConvertToPx();
344 padding.right = length.ConvertToPx();
345 padding.top = length.ConvertToPx();
346 padding.bottom = length.ConvertToPx();
347 return padding;
348 }
349
GetNewPadding(const JSCallbackInfo& info)350 NG::PaddingProperty JSToggle::GetNewPadding(const JSCallbackInfo& info)
351 {
352 NG::PaddingProperty padding({
353 NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp)
354 });
355 if (info[0]->IsObject()) {
356 JSRef<JSObject> paddingObj = JSRef<JSObject>::Cast(info[0]);
357 CommonCalcDimension commonCalcDimension;
358 ParseCommonMarginOrPaddingCorner(paddingObj, commonCalcDimension);
359 if (commonCalcDimension.left.has_value() || commonCalcDimension.right.has_value() ||
360 commonCalcDimension.top.has_value() || commonCalcDimension.bottom.has_value()) {
361 padding = GetPadding(commonCalcDimension.top, commonCalcDimension.bottom, commonCalcDimension.left,
362 commonCalcDimension.right);
363 return padding;
364 }
365 }
366 CalcDimension length;
367 if (!ParseJsDimensionVp(info[0], length)) {
368 length.Reset();
369 }
370
371 padding.SetEdges(NG::CalcLength(length.IsNonNegative() ? length : CalcDimension()));
372 return padding;
373 }
374
GetPadding(const std::optional<CalcDimension>& top, const std::optional<CalcDimension>& bottom, const std::optional<CalcDimension>& left, const std::optional<CalcDimension>& right)375 NG::PaddingProperty JSToggle::GetPadding(const std::optional<CalcDimension>& top,
376 const std::optional<CalcDimension>& bottom, const std::optional<CalcDimension>& left,
377 const std::optional<CalcDimension>& right)
378 {
379 NG::PaddingProperty padding({
380 NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp), NG::CalcLength(0.0_vp)
381 });
382 if (left.has_value() && left.value().IsNonNegative()) {
383 padding.left = NG::CalcLength(left.value());
384 }
385 if (right.has_value() && right.value().IsNonNegative()) {
386 padding.right = NG::CalcLength(right.value());
387 }
388 if (top.has_value() && top.value().IsNonNegative()) {
389 padding.top = NG::CalcLength(top.value());
390 }
391 if (bottom.has_value() && bottom.value().IsNonNegative()) {
392 padding.bottom = NG::CalcLength(bottom.value());
393 }
394 return padding;
395 }
396
SetBackgroundColor(const JSCallbackInfo& info)397 void JSToggle::SetBackgroundColor(const JSCallbackInfo& info)
398 {
399 Color backgroundColor = Color::TRANSPARENT;
400 bool flag = ParseJsColor(info[0], backgroundColor);
401 if (!Container::IsCurrentUseNewPipeline()) {
402 JSViewAbstract::JsBackgroundColor(info);
403 return;
404 }
405 ToggleModel::GetInstance()->SetBackgroundColor(backgroundColor, flag);
406 }
407
JsHoverEffect(const JSCallbackInfo& info)408 void JSToggle::JsHoverEffect(const JSCallbackInfo& info)
409 {
410 if (info.Length() > 0 && info[0]->IsNumber()) {
411 ToggleModel::GetInstance()->SetHoverEffect(static_cast<HoverEffectType>(info[0]->ToNumber<int32_t>()));
412 }
413 }
414
Pop()415 void JSToggle::Pop()
416 {
417 ToggleModel::GetInstance()->Pop();
418 }
419
SwitchStyle(const JSCallbackInfo& info)420 void JSToggle::SwitchStyle(const JSCallbackInfo& info)
421 {
422 if ((info.Length() < 1) || !info[0]->IsObject()) {
423 return;
424 }
425 JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
426
427 CalcDimension pointRadius;
428 if (jsObj->HasProperty("pointRadius") &&
429 ParseJsDimensionVpNG(jsObj->GetProperty("pointRadius"), pointRadius, false) && !pointRadius.IsNegative()) {
430 ToggleModel::GetInstance()->SetPointRadius(pointRadius);
431 } else {
432 ToggleModel::GetInstance()->ResetPointRadius();
433 }
434
435 Color unselectedColor;
436 if (jsObj->HasProperty("unselectedColor") &&
437 ParseJsColor(jsObj->GetProperty("unselectedColor"), unselectedColor)) {
438 ToggleModel::GetInstance()->SetUnselectedColor(unselectedColor);
439 } else {
440 auto theme = GetTheme<SwitchTheme>();
441 if (theme) {
442 unselectedColor = theme->GetInactiveColor();
443 }
444 ToggleModel::GetInstance()->SetUnselectedColor(unselectedColor);
445 }
446
447 Color pointColor;
448 if (jsObj->HasProperty("pointColor") && ParseJsColor(jsObj->GetProperty("pointColor"), pointColor)) {
449 ToggleModel::GetInstance()->SetSwitchPointColor(pointColor);
450 } else {
451 auto theme = GetTheme<SwitchTheme>();
452 if (theme) {
453 pointColor = theme->GetPointColor();
454 }
455 ToggleModel::GetInstance()->SetSwitchPointColor(pointColor);
456 }
457
458 CalcDimension trackRadius;
459 if (jsObj->HasProperty("trackBorderRadius") &&
460 ParseJsDimensionVpNG(jsObj->GetProperty("trackBorderRadius"), trackRadius, false) &&
461 !trackRadius.IsNegative()) {
462 ToggleModel::GetInstance()->SetTrackBorderRadius(trackRadius);
463 } else {
464 ToggleModel::GetInstance()->ResetTrackBorderRadius();
465 }
466 }
467
JsRadius(const JSCallbackInfo& info)468 void JSToggle::JsRadius(const JSCallbackInfo& info)
469 {
470 CalcDimension radius;
471 // when toggle equels button should follow button model.
472 if (static_cast<NG::ToggleType>(toggleType_) == NG::ToggleType::BUTTON) {
473 JSButton::JsRadius(info);
474 } else {
475 JSViewAbstract::JsBorderRadius(info);
476 }
477 }
478
JsBorder(const JSCallbackInfo& info)479 void JSToggle::JsBorder(const JSCallbackInfo& info)
480 {
481 LOGE("JSToggle::JsBorder");
482 JSViewAbstract::JsBorder(info);
483 if (!info[0]->IsObject()) {
484 return;
485 }
486 JSRef<JSObject> object = JSRef<JSObject>::Cast(info[0]);
487 CalcDimension borderRadius;
488 auto valueRadius = object->GetProperty("radius");
489 if (static_cast<NG::ToggleType>(toggleType_) == NG::ToggleType::BUTTON) {
490 JSButton::JsRadius(valueRadius);
491 }
492 }
493 } // namespace OHOS::Ace::Framework
494