• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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