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