1 /*
2 * Copyright (c) 2022-2023 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_side_bar.h"
17
18 #include "base/geometry/dimension.h"
19 #include "base/image/pixel_map.h"
20 #include "base/log/ace_scoring_log.h"
21 #include "base/log/log.h"
22 #include "bridge/declarative_frontend/engine/js_ref_ptr.h"
23 #include "bridge/declarative_frontend/engine/js_types.h"
24 #include "bridge/declarative_frontend/jsview/js_utils.h"
25 #include "core/components_ng/base/view_stack_processor.h"
26 #include "core/components_ng/pattern/side_bar/side_bar_container_model_ng.h"
27 #include "frameworks/bridge/declarative_frontend/jsview/js_view_common_def.h"
28 #include "frameworks/bridge/declarative_frontend/jsview/models/side_bar_container_model_impl.h"
29
30 namespace OHOS::Ace {
31 std::unique_ptr<SideBarContainerModel> SideBarContainerModel::instance_ = nullptr;
32 std::mutex SideBarContainerModel::mutex_;
33
GetInstance()34 SideBarContainerModel* SideBarContainerModel::GetInstance()
35 {
36 if (!instance_) {
37 std::lock_guard<std::mutex> lock(mutex_);
38 if (!instance_) {
39 #ifdef NG_BUILD
40 instance_.reset(new NG::SideBarContainerModelNG());
41 #else
42 if (Container::IsCurrentUseNewPipeline()) {
43 instance_.reset(new NG::SideBarContainerModelNG());
44 } else {
45 instance_.reset(new Framework::SideBarContainerModelImpl());
46 }
47 #endif
48 }
49 }
50 return instance_.get();
51 }
52 } // namespace OHOS::Ace
53
54 namespace OHOS::Ace::Framework {
55 namespace {
56 constexpr Dimension DEFAULT_CONTROL_BUTTON_WIDTH = 32.0_vp;
57 constexpr Dimension DEFAULT_CONTROL_BUTTON_HEIGHT = 32.0_vp;
58 constexpr Dimension DEFAULT_CONTROL_BUTTON_TOP = 48.0_vp;
59 constexpr Dimension DEFAULT_DIVIDER_STROKE_WIDTH = 1.0_vp;
60 constexpr Dimension DEFAULT_DIVIDER_START_MARGIN = 0.0_vp;
61 constexpr Dimension DEFAULT_DIVIDER_END_MARGIN = 0.0_vp;
62 static Dimension DEFAULT_SIDE_BAR_WIDTH = 200.0_vp;
63 static Dimension DEFAULT_MIN_SIDE_BAR_WIDTH = 200.0_vp;
64 constexpr Dimension DEFAULT_MAX_SIDE_BAR_WIDTH = 280.0_vp;
65 constexpr Color DEFAULT_DIVIDER_COLOR = Color(0x08000000);
66 constexpr int32_t PLATFORM_VERSION_TEN = 10;
67
ParseAndSetWidth(const JSCallbackInfo & info,WidthType widthType)68 void ParseAndSetWidth(const JSCallbackInfo& info, WidthType widthType)
69 {
70 if (info.Length() < 1) {
71 LOGE("The arg is wrong, it is supposed to have atleast 1 arguments");
72 return;
73 }
74
75 CalcDimension value;
76 auto pipeline = PipelineBase::GetCurrentContext();
77 CHECK_NULL_VOID(pipeline);
78 if (pipeline->GetMinPlatformVersion() >= PLATFORM_VERSION_TEN) {
79 DEFAULT_SIDE_BAR_WIDTH = 240.0_vp;
80 DEFAULT_MIN_SIDE_BAR_WIDTH = 240.0_vp;
81 }
82
83 auto isValid = pipeline->GetMinPlatformVersion() >= PLATFORM_VERSION_TEN
84 ? JSViewAbstract::ParseJsDimensionVpNG(info[0], value)
85 : JSViewAbstract::ParseJsDimensionVp(info[0], value);
86
87 if (!isValid) {
88 switch (widthType) {
89 case WidthType::SIDEBAR_WIDTH:
90 value = DEFAULT_SIDE_BAR_WIDTH;
91 break;
92 case WidthType::MIN_SIDEBAR_WIDTH:
93 value = DEFAULT_MIN_SIDE_BAR_WIDTH;
94 break;
95 case WidthType::MAX_SIDEBAR_WIDTH:
96 value = DEFAULT_MAX_SIDE_BAR_WIDTH;
97 break;
98 default:
99 break;
100 }
101 }
102 SideBarContainerModel::GetInstance()->ParseAndSetWidth(widthType, value);
103 }
104 } // namespace
105
Create(const JSCallbackInfo & info)106 void JSSideBar::Create(const JSCallbackInfo& info)
107 {
108 SideBarContainerModel::GetInstance()->Create();
109 SideBarContainerType style = SideBarContainerType::EMBED;
110 if (!info[0]->IsNull()) {
111 if (info[0]->IsBoolean()) {
112 style = static_cast<SideBarContainerType>(info[0]->ToBoolean());
113 } else if (info[0]->IsNumber()) {
114 style = static_cast<SideBarContainerType>(info[0]->ToNumber<int>());
115 } else {
116 LOGE("JSSideBar::CreateForNG The SideBarContainerType arg is wrong");
117 return;
118 }
119 }
120 SideBarContainerModel::GetInstance()->SetSideBarContainerType(style);
121 }
122
SetShowControlButton(bool isShow)123 void JSSideBar::SetShowControlButton(bool isShow)
124 {
125 SideBarContainerModel::GetInstance()->SetShowControlButton(isShow);
126 }
127
JsSideBarPosition(const JSCallbackInfo & info)128 void JSSideBar::JsSideBarPosition(const JSCallbackInfo& info)
129 {
130 if (info.Length() < 1) {
131 LOGE("The arg is wrong, it is supposed to have atleast 1 arguments");
132 return;
133 }
134 SideBarPosition sideBarPosition = SideBarPosition::START;
135 if (info[0]->IsNumber()) {
136 sideBarPosition = static_cast<SideBarPosition>(info[0]->ToNumber<int>());
137 } else {
138 LOGE("The arg is wrong");
139 return;
140 }
141 SideBarContainerModel::GetInstance()->SetSideBarPosition(sideBarPosition);
142 }
143
JSBind(BindingTarget globalObj)144 void JSSideBar::JSBind(BindingTarget globalObj)
145 {
146 JSClass<JSSideBar>::Declare("SideBarContainer");
147 MethodOptions opt = MethodOptions::NONE;
148 JSClass<JSSideBar>::StaticMethod("create", &JSSideBar::Create, opt);
149 JSClass<JSSideBar>::StaticMethod("pop", &JSSideBar::Pop);
150 JSClass<JSSideBar>::StaticMethod("showSideBar", &JSSideBar::JsShowSideBar);
151 JSClass<JSSideBar>::StaticMethod("controlButton", &JSSideBar::JsControlButton);
152 JSClass<JSSideBar>::StaticMethod("showControlButton", &JSSideBar::SetShowControlButton);
153 JSClass<JSSideBar>::StaticMethod("onChange", &JSSideBar::OnChange);
154 JSClass<JSSideBar>::StaticMethod("sideBarWidth", &JSSideBar::JsSideBarWidth);
155 JSClass<JSSideBar>::StaticMethod("minSideBarWidth", &JSSideBar::JsMinSideBarWidth);
156 JSClass<JSSideBar>::StaticMethod("maxSideBarWidth", &JSSideBar::JsMaxSideBarWidth);
157 JSClass<JSSideBar>::StaticMethod("autoHide", &JSSideBar::JsAutoHide);
158 JSClass<JSSideBar>::StaticMethod("sideBarPosition", &JSSideBar::JsSideBarPosition);
159 JSClass<JSSideBar>::StaticMethod("divider", &JSSideBar::JsDivider);
160 JSClass<JSSideBar>::StaticMethod("minContentWidth", &JSSideBar::JsMinContentWidth);
161 JSClass<JSSideBar>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
162 JSClass<JSSideBar>::StaticMethod("width", SetWidth);
163 JSClass<JSSideBar>::StaticMethod("height", SetHeight);
164 JSClass<JSSideBar>::StaticMethod("size", SetSize);
165 JSClass<JSSideBar>::StaticMethod("width", &JSStack::SetWidth);
166 JSClass<JSSideBar>::StaticMethod("height", &JSStack::SetHeight);
167 JSClass<JSSideBar>::StaticMethod("size", &JSStack::SetSize);
168 JSClass<JSSideBar>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
169 JSClass<JSSideBar>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
170 JSClass<JSSideBar>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
171 JSClass<JSSideBar>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
172 JSClass<JSSideBar>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
173 JSClass<JSSideBar>::InheritAndBind<JSContainerBase>(globalObj);
174 }
175
OnChange(const JSCallbackInfo & info)176 void JSSideBar::OnChange(const JSCallbackInfo& info)
177 {
178 if (info.Length() < 1 || !info[0]->IsFunction()) {
179 LOGE("JSSideBar::OnChange info param is wrong.");
180 return;
181 }
182
183 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
184 auto onChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](bool isShow) {
185 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
186 ACE_SCORING_EVENT("SideBarContainer.onChange");
187 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(isShow));
188 func->ExecuteJS(1, &newJSVal);
189 };
190 SideBarContainerModel::GetInstance()->SetOnChange(std::move(onChange));
191 info.ReturnSelf();
192 }
193
JsSideBarWidth(const JSCallbackInfo & info)194 void JSSideBar::JsSideBarWidth(const JSCallbackInfo& info)
195 {
196 ParseAndSetWidth(info, WidthType::SIDEBAR_WIDTH);
197 }
198
JsMaxSideBarWidth(const JSCallbackInfo & info)199 void JSSideBar::JsMaxSideBarWidth(const JSCallbackInfo& info)
200 {
201 ParseAndSetWidth(info, WidthType::MAX_SIDEBAR_WIDTH);
202 }
203
JsMinSideBarWidth(const JSCallbackInfo & info)204 void JSSideBar::JsMinSideBarWidth(const JSCallbackInfo& info)
205 {
206 ParseAndSetWidth(info, WidthType::MIN_SIDEBAR_WIDTH);
207 }
208
ParseShowSideBarObject(const JSCallbackInfo & args,const JSRef<JSVal> & changeEventVal)209 void ParseShowSideBarObject(const JSCallbackInfo& args, const JSRef<JSVal>& changeEventVal)
210 {
211 CHECK_NULL_VOID(changeEventVal->IsFunction());
212
213 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(changeEventVal));
214 auto onChangeEvent = [execCtx = args.GetExecutionContext(), func = std::move(jsFunc)](bool isShow) {
215 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
216 ACE_SCORING_EVENT("SideBarContainer.onChangeEvent");
217 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(isShow));
218 func->ExecuteJS(1, &newJSVal);
219 };
220 SideBarContainerModel::GetInstance()->SetOnChangeEvent(std::move(onChangeEvent));
221 }
222
JsShowSideBar(const JSCallbackInfo & info)223 void JSSideBar::JsShowSideBar(const JSCallbackInfo& info)
224 {
225 if (info.Length() < 1 || info.Length() > 2) {
226 LOGE("The arg is wrong, it is supposed to have 1 or 2 arguments");
227 return;
228 }
229
230 bool isShow = true;
231 if (info.Length() > 0 && info[0]->IsBoolean()) {
232 isShow = info[0]->ToBoolean();
233 }
234
235 SideBarContainerModel::GetInstance()->SetShowSideBar(isShow);
236 if (info.Length() > 1 && info[1]->IsFunction()) {
237 ParseShowSideBarObject(info, info[1]);
238 }
239 }
240
SetControlButtonIcon(SideBarControlButtonType iconType,JSRef<JSVal> icon)241 void JSSideBar::SetControlButtonIcon(SideBarControlButtonType iconType, JSRef<JSVal> icon)
242 {
243 if (icon->IsUndefined() || icon->IsNull()) {
244 LOGE("SetControlButtonIcon icon parse failed, icon is null.");
245 return;
246 }
247 std::string iconPath;
248 auto isStrType = ParseJsMedia(icon, iconPath);
249 RefPtr<PixelMap> pixMap = nullptr;
250 #if defined(PIXEL_MAP_SUPPORTED)
251 if (!isStrType) {
252 pixMap = CreatePixelMapFromNapiValue(icon);
253 }
254 #endif
255 if (isStrType || pixMap != nullptr) {
256 switch (iconType) {
257 case SideBarControlButtonType::SHOWN:
258 SideBarContainerModel::GetInstance()->SetControlButtonShowIconInfo(iconPath, !isStrType, pixMap);
259 break;
260 case SideBarControlButtonType::HIDDEN:
261 SideBarContainerModel::GetInstance()->SetControlButtonHiddenIconInfo(iconPath, !isStrType, pixMap);
262 break;
263 case SideBarControlButtonType::SWITCHING:
264 SideBarContainerModel::GetInstance()->SetControlButtonSwitchingIconInfo(iconPath, !isStrType, pixMap);
265 break;
266 default:
267 break;
268 }
269 }
270 }
271
JsControlButton(const JSCallbackInfo & info)272 void JSSideBar::JsControlButton(const JSCallbackInfo& info)
273 {
274 if (info.Length() < 1) {
275 LOGE("JSSideBar::JsControlButtonForNG The arg is wrong, it is supposed to have at least 1 arguments");
276 return;
277 }
278
279 if (!info[0]->IsNull() && info[0]->IsObject()) {
280 JSRef<JSObject> value = JSRef<JSObject>::Cast(info[0]);
281 auto pipeline = PipelineBase::GetCurrentContext();
282 CHECK_NULL_VOID(pipeline);
283 if (pipeline->GetMinPlatformVersion() < PLATFORM_VERSION_TEN) {
284 ParseControlButtonOG(value);
285 } else {
286 ParseControlButtonNG(value);
287 }
288
289 JSRef<JSVal> icons = value->GetProperty("icons");
290 if (!icons->IsNull() && icons->IsObject()) {
291 JSRef<JSObject> iconsVal = JSRef<JSObject>::Cast(icons);
292 JSRef<JSVal> showIcon = iconsVal->GetProperty("shown");
293 JSRef<JSVal> switchingIcon = iconsVal->GetProperty("switching");
294 JSRef<JSVal> hiddenIcon = iconsVal->GetProperty("hidden");
295 SetControlButtonIcon(SideBarControlButtonType::SHOWN, showIcon);
296 SetControlButtonIcon(SideBarControlButtonType::HIDDEN, hiddenIcon);
297 SetControlButtonIcon(SideBarControlButtonType::SWITCHING, switchingIcon);
298 }
299 }
300 }
301
JsDivider(const JSCallbackInfo & info)302 void JSSideBar::JsDivider(const JSCallbackInfo& info)
303 {
304 if (info.Length() < 1) {
305 LOGE("Invalid params");
306 return;
307 }
308
309 if (info[0]->IsNull()) {
310 SideBarContainerModel::GetInstance()->SetDividerStrokeWidth(0.0_vp);
311 return;
312 }
313
314 if (info[0]->IsObject()) {
315 JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
316
317 Dimension strokeWidth = DEFAULT_DIVIDER_STROKE_WIDTH;
318 if (!ConvertFromJSValueNG(obj->GetProperty("strokeWidth"), strokeWidth) || (strokeWidth.Value() < 0.0f)) {
319 strokeWidth = DEFAULT_DIVIDER_STROKE_WIDTH;
320 }
321 SideBarContainerModel::GetInstance()->SetDividerStrokeWidth(strokeWidth);
322
323 Color color = DEFAULT_DIVIDER_COLOR;
324 if (!ConvertFromJSValue(obj->GetProperty("color"), color)) {
325 color = DEFAULT_DIVIDER_COLOR;
326 }
327 SideBarContainerModel::GetInstance()->SetDividerColor(color);
328
329 Dimension startMargin = DEFAULT_DIVIDER_START_MARGIN;
330 if (!ConvertFromJSValueNG(obj->GetProperty("startMargin"), startMargin) || (startMargin.Value() < 0.0f)) {
331 startMargin = DEFAULT_DIVIDER_START_MARGIN;
332 }
333 SideBarContainerModel::GetInstance()->SetDividerStartMargin(startMargin);
334
335 Dimension endMargin = DEFAULT_DIVIDER_END_MARGIN;
336 if (!ConvertFromJSValueNG(obj->GetProperty("endMargin"), endMargin) || (endMargin.Value() < 0.0f)) {
337 endMargin = DEFAULT_DIVIDER_END_MARGIN;
338 }
339 SideBarContainerModel::GetInstance()->SetDividerEndMargin(endMargin);
340 }
341 }
342
JsMinContentWidth(const JSCallbackInfo & info)343 void JSSideBar::JsMinContentWidth(const JSCallbackInfo& info)
344 {
345 if (info.Length() < 1) {
346 LOGW("JsMinContentWidth::Invalid params");
347 return;
348 }
349 if (info[0]->IsNull()) {
350 SideBarContainerModel::GetInstance()->SetMinContentWidth(-1.0_vp);
351 LOGW("JsMinContentWidth::info[0]->IsNull()");
352 return;
353 }
354 CalcDimension minContentWidth;
355 if (!JSViewAbstract::ParseJsDimensionVp(info[0], minContentWidth)) {
356 SideBarContainerModel::GetInstance()->SetMinContentWidth(-1.0_vp);
357 LOGW("JsMinContentWidth::ParseJsDimensionVp Fail!!!");
358 return;
359 }
360 SideBarContainerModel::GetInstance()->SetMinContentWidth(minContentWidth);
361 }
362
JsAutoHide(bool autoHide)363 void JSSideBar::JsAutoHide(bool autoHide)
364 {
365 SideBarContainerModel::GetInstance()->SetAutoHide(autoHide);
366 }
367
Pop()368 void JSSideBar::Pop()
369 {
370 SideBarContainerModel::GetInstance()->Pop();
371 }
372
373
ParseControlButtonOG(JSRef<JSObject> value)374 void JSSideBar::ParseControlButtonOG(JSRef<JSObject> value)
375 {
376 JSRef<JSVal> width = value->GetProperty("width");
377 JSRef<JSVal> height = value->GetProperty("height");
378 JSRef<JSVal> left = value->GetProperty("left");
379 JSRef<JSVal> top = value->GetProperty("top");
380
381 if (!width->IsNull() && width->IsNumber()) {
382 auto controlButtonWidth = CalcDimension(width->ToNumber<double>(), DimensionUnit::VP);
383 if (LessNotEqual(controlButtonWidth.Value(), 0.0)) {
384 controlButtonWidth = DEFAULT_CONTROL_BUTTON_WIDTH;
385 }
386 SideBarContainerModel::GetInstance()->SetControlButtonWidth(controlButtonWidth);
387 }
388
389 if (!height->IsNull() && height->IsNumber()) {
390 auto controlButtonHeight = CalcDimension(height->ToNumber<double>(), DimensionUnit::VP);
391 if (LessNotEqual(controlButtonHeight.Value(), 0.0)) {
392 controlButtonHeight = DEFAULT_CONTROL_BUTTON_HEIGHT;
393 }
394 SideBarContainerModel::GetInstance()->SetControlButtonHeight(controlButtonHeight);
395 }
396
397 if (!left->IsNull() && left->IsNumber()) {
398 SideBarContainerModel::GetInstance()->SetControlButtonLeft(
399 Dimension(left->ToNumber<double>(), DimensionUnit::VP));
400 }
401
402 if (!top->IsNull() && top->IsNumber()) {
403 SideBarContainerModel::GetInstance()->SetControlButtonTop(
404 Dimension(top->ToNumber<double>(), DimensionUnit::VP));
405 }
406 }
407
ParseControlButtonNG(JSRef<JSObject> value)408 void JSSideBar::ParseControlButtonNG(JSRef<JSObject> value)
409 {
410 JSRef<JSVal> width = value->GetProperty("width");
411 JSRef<JSVal> height = value->GetProperty("height");
412 JSRef<JSVal> left = value->GetProperty("left");
413 JSRef<JSVal> top = value->GetProperty("top");
414
415 auto controlButtonWidth = DEFAULT_CONTROL_BUTTON_WIDTH;
416 if (width->IsNumber() && GreatOrEqual(width->ToNumber<double>(), 0.0)) {
417 controlButtonWidth = CalcDimension(width->ToNumber<double>(), DimensionUnit::VP);
418 }
419 SideBarContainerModel::GetInstance()->SetControlButtonWidth(controlButtonWidth);
420
421 auto controlButtonHeight = DEFAULT_CONTROL_BUTTON_HEIGHT;
422 if (height->IsNumber() && GreatOrEqual(height->ToNumber<double>(), 0.0)) {
423 controlButtonHeight = CalcDimension(height->ToNumber<double>(), DimensionUnit::VP);
424 }
425 SideBarContainerModel::GetInstance()->SetControlButtonHeight(controlButtonHeight);
426
427 if (left->IsNumber() && GreatOrEqual(left->ToNumber<double>(), 0.0)) {
428 auto controlButtonLeft = CalcDimension(left->ToNumber<double>(), DimensionUnit::VP);
429 SideBarContainerModel::GetInstance()->SetControlButtonLeft(controlButtonLeft);
430 } else {
431 SideBarContainerModel::GetInstance()->ResetControlButtonLeft();
432 }
433
434 auto controlButtonTop = DEFAULT_CONTROL_BUTTON_TOP;
435 if (top->IsNumber() && GreatOrEqual(top->ToNumber<double>(), 0.0)) {
436 controlButtonTop = CalcDimension(top->ToNumber<double>(), DimensionUnit::VP);
437 }
438 SideBarContainerModel::GetInstance()->SetControlButtonTop(controlButtonTop);
439 }
440
441 } // namespace OHOS::Ace::Framework
442