1 /*
2  * Copyright (c) 2021-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_sliding_panel.h"
17 
18 #include <algorithm>
19 #include <cstdint>
20 #include <iterator>
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_view_common_def.h"
25 #include "bridge/declarative_frontend/jsview/models/sliding_panel_model_impl.h"
26 #include "core/components_ng/base/view_abstract_model_ng.h"
27 #include "core/components_ng/pattern/panel/sliding_panel_model.h"
28 #include "core/components_ng/pattern/panel/sliding_panel_model_ng.h"
29 
30 namespace OHOS::Ace {
31 
32 std::unique_ptr<SlidingPanelModel> SlidingPanelModel::instance_ = nullptr;
33 std::mutex SlidingPanelModel::mutex_;
34 
GetInstance()35 SlidingPanelModel* SlidingPanelModel::GetInstance()
36 {
37     if (!instance_) {
38         std::lock_guard<std::mutex> lock(mutex_);
39         if (!instance_) {
40 #ifdef NG_BUILD
41             instance_.reset(new NG::SlidingPanelModelNG());
42 #else
43             if (Container::IsCurrentUseNewPipeline()) {
44                 instance_.reset(new NG::SlidingPanelModelNG());
45             } else {
46                 instance_.reset(new Framework::SlidingPanelModelImpl());
47             }
48 #endif
49         }
50     }
51     return instance_.get();
52 }
53 
54 } // namespace OHOS::Ace
55 namespace OHOS::Ace::Framework {
56 namespace {
57 
58 const std::vector<PanelMode> PANEL_MODES = { PanelMode::MINI, PanelMode::HALF, PanelMode::FULL, PanelMode::AUTO };
59 const std::vector<PanelType> PANEL_TYPES = { PanelType::MINI_BAR, PanelType::FOLDABLE_BAR, PanelType::TEMP_DISPLAY,
60     PanelType::CUSTOM };
61 const std::vector<VisibleType> PANEL_VISIBLE_TYPES = { VisibleType::GONE, VisibleType::VISIBLE,
62     VisibleType::INVISIBLE };
63 
64 const static bool DEFAULT_HASDRAGBAR = true;
65 const static bool DEFAULT_SHOWCLOSEICON = false;
66 const static PanelMode DEFAULT_PANELMODE = PanelMode::HALF;
67 const static PanelType DEFAULT_PANELTYPE = PanelType::FOLDABLE_BAR;
68 const std::string DEFAULT_BACKGROUND_MASK = "#08182431";
69 } // namespace
70 
Create(const JSCallbackInfo & info)71 void JSSlidingPanel::Create(const JSCallbackInfo& info)
72 {
73     if (info.Length() > 0 && info[0]->IsBoolean()) {
74         bool isShow = true;
75         isShow = info[0]->ToBoolean();
76         SlidingPanelModel::GetInstance()->Create(isShow);
77         return;
78     }
79 }
80 
JSBind(BindingTarget globalObj)81 void JSSlidingPanel::JSBind(BindingTarget globalObj)
82 {
83     JSClass<JSSlidingPanel>::Declare("Panel");
84     MethodOptions opt = MethodOptions::NONE;
85     JSClass<JSSlidingPanel>::StaticMethod("create", &JSSlidingPanel::Create, opt);
86     JSClass<JSSlidingPanel>::StaticMethod("pop", &JSSlidingPanel::Pop, opt);
87     JSClass<JSSlidingPanel>::StaticMethod("dragBar", &JSSlidingPanel::SetHasDragBar, opt);
88     JSClass<JSSlidingPanel>::StaticMethod("show", &JSSlidingPanel::SetShow, opt);
89     JSClass<JSSlidingPanel>::StaticMethod("showCloseIcon", &JSSlidingPanel::SetShowCloseIcon, opt);
90     JSClass<JSSlidingPanel>::StaticMethod("mode", &JSSlidingPanel::SetPanelMode, opt);
91     JSClass<JSSlidingPanel>::StaticMethod("type", &JSSlidingPanel::SetPanelType, opt);
92     JSClass<JSSlidingPanel>::StaticMethod("customHeight", &JSSlidingPanel::SetCustomHeight, opt);
93     JSClass<JSSlidingPanel>::StaticMethod("backgroundMask", &JSSlidingPanel::SetBackgroundMask, opt);
94     JSClass<JSSlidingPanel>::StaticMethod("fullHeight", &JSSlidingPanel::SetFullHeight, opt);
95     JSClass<JSSlidingPanel>::StaticMethod("halfHeight", &JSSlidingPanel::SetHalfHeight, opt);
96     JSClass<JSSlidingPanel>::StaticMethod("miniHeight", &JSSlidingPanel::SetMiniHeight, opt);
97     JSClass<JSSlidingPanel>::StaticMethod("backgroundColor", JsBackgroundColor);
98     JSClass<JSSlidingPanel>::StaticMethod("border", JsPanelBorder);
99     JSClass<JSSlidingPanel>::StaticMethod("borderWidth", JsPanelBorderWidth);
100     JSClass<JSSlidingPanel>::StaticMethod("borderColor", JsPanelBorderColor);
101     JSClass<JSSlidingPanel>::StaticMethod("borderStyle", JsPanelBorderStyle);
102     JSClass<JSSlidingPanel>::StaticMethod("borderRadius", JsPanelBorderRadius);
103 
104     JSClass<JSSlidingPanel>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
105     JSClass<JSSlidingPanel>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
106     JSClass<JSSlidingPanel>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
107     JSClass<JSSlidingPanel>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
108     JSClass<JSSlidingPanel>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
109     JSClass<JSSlidingPanel>::StaticMethod("onChange", &JSSlidingPanel::SetOnSizeChange);
110     JSClass<JSSlidingPanel>::StaticMethod("onHeightChange", &JSSlidingPanel::SetOnHeightChange);
111     JSClass<JSSlidingPanel>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
112     JSClass<JSSlidingPanel>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
113     JSClass<JSSlidingPanel>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
114     JSClass<JSSlidingPanel>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
115 
116     JSClass<JSSlidingPanel>::InheritAndBind<JSContainerBase>(globalObj);
117 }
118 
SetBackgroundMask(const JSCallbackInfo & info)119 void JSSlidingPanel::SetBackgroundMask(const JSCallbackInfo& info)
120 {
121     Color color;
122     if (info.Length() < 1) {
123         color = Color::FromString(DEFAULT_BACKGROUND_MASK);
124     } else if (!ParseJsColor(info[0], color)) {
125         color = Color::FromString(DEFAULT_BACKGROUND_MASK);
126     }
127 
128     SlidingPanelModel::GetInstance()->SetBackgroundMask(color);
129 }
130 
ParsePanelRadius(const JSRef<JSVal> & args,BorderRadius & borderRadius)131 void JSSlidingPanel::ParsePanelRadius(const JSRef<JSVal>& args, BorderRadius& borderRadius)
132 {
133     if (!args->IsObject() && !args->IsNumber() && !args->IsString()) {
134         return;
135     }
136 
137     CalcDimension radius;
138     if (ParseJsDimensionVp(args, radius)) {
139         borderRadius.radiusTopLeft = radius;
140         borderRadius.radiusTopRight = radius;
141         borderRadius.radiusBottomLeft = radius;
142         borderRadius.radiusBottomRight = radius;
143         return;
144     }
145     if (args->IsObject()) {
146         JSRef<JSObject> object = JSRef<JSObject>::Cast(args);
147         auto valueTopLeft = object->GetProperty("topLeft");
148         if (!valueTopLeft->IsUndefined()) {
149             ParseJsDimensionVp(valueTopLeft, borderRadius.radiusTopLeft);
150         }
151         auto valueTopRight = object->GetProperty("topRight");
152         if (!valueTopRight->IsUndefined()) {
153             ParseJsDimensionVp(valueTopRight, borderRadius.radiusTopRight);
154         }
155         auto valueBottomLeft = object->GetProperty("bottomLeft");
156         if (!valueBottomLeft->IsUndefined()) {
157             ParseJsDimensionVp(valueBottomLeft, borderRadius.radiusBottomLeft);
158         }
159         auto valueBottomRight = object->GetProperty("bottomRight");
160         if (!valueBottomRight->IsUndefined()) {
161             ParseJsDimensionVp(valueBottomRight, borderRadius.radiusBottomRight);
162         }
163         return;
164     }
165 }
166 
JsPanelBorderRadius(const JSCallbackInfo & info)167 void JSSlidingPanel::JsPanelBorderRadius(const JSCallbackInfo& info)
168 {
169     BorderRadius borderRadius;
170     ParsePanelRadius(info[0], borderRadius);
171 
172     ViewAbstractModel::GetInstance()->SetBorderRadius(borderRadius.radiusTopLeft, borderRadius.radiusTopRight,
173         borderRadius.radiusBottomLeft, borderRadius.radiusBottomRight);
174 }
175 
JsBackgroundColor(const JSCallbackInfo & info)176 void JSSlidingPanel::JsBackgroundColor(const JSCallbackInfo& info)
177 {
178     if (info.Length() < 1) {
179         return;
180     }
181     Color backgroundColor;
182     if (!ParseJsColor(info[0], backgroundColor)) {
183         return;
184     }
185 
186     SlidingPanelModel::GetInstance()->SetBackgroundColor(backgroundColor);
187 }
188 
JsPanelBorderColor(const JSCallbackInfo & info)189 void JSSlidingPanel::JsPanelBorderColor(const JSCallbackInfo& info)
190 {
191     if (info.Length() < 1) {
192         return;
193     }
194     Color borderColor;
195     if (!ParseJsColor(info[0], borderColor)) {
196         return;
197     }
198 
199     SlidingPanelModel::GetInstance()->SetBorderColor(borderColor);
200 }
201 
JsPanelBorderWidth(const JSCallbackInfo & info)202 void JSSlidingPanel::JsPanelBorderWidth(const JSCallbackInfo& info)
203 {
204     if (info.Length() < 1) {
205         return;
206     }
207     CalcDimension borderWidth;
208     if (!ParseJsDimensionVp(info[0], borderWidth)) {
209         return;
210     }
211     SlidingPanelModel::GetInstance()->SetBorderWidth(borderWidth);
212 }
213 
JsPanelBorderStyle(int32_t style)214 void JSSlidingPanel::JsPanelBorderStyle(int32_t style)
215 {
216     BorderStyle borderStyle = BorderStyle::SOLID;
217     if (style > 0 && style < 4) {
218         borderStyle = static_cast<BorderStyle>(style);
219     }
220     SlidingPanelModel::GetInstance()->SetBorderStyle(borderStyle);
221 }
222 
JsPanelBorder(const JSCallbackInfo & info)223 void JSSlidingPanel::JsPanelBorder(const JSCallbackInfo& info)
224 {
225     if (info.Length() < 1) {
226         return;
227     }
228     if (!info[0]->IsObject()) {
229         return;
230     }
231 
232     auto argsPtrItem = JSRef<JSObject>::Cast(info[0]);
233     CalcDimension width = CalcDimension(0.0, DimensionUnit::VP);
234     ParseJsDimensionVp(argsPtrItem->GetProperty("width"), width);
235     SlidingPanelModel::GetInstance()->SetBorderWidth(width);
236 
237     BorderRadius borderRadius;
238     ParsePanelRadius(argsPtrItem->GetProperty("radius"), borderRadius);
239     ViewAbstractModel::GetInstance()->SetBorderRadius(borderRadius.radiusTopLeft, borderRadius.radiusTopRight,
240         borderRadius.radiusBottomLeft, borderRadius.radiusBottomRight);
241 
242     auto styleJsValue = argsPtrItem->GetProperty("style");
243     auto borderStyle = BorderStyle::SOLID;
244     if (!styleJsValue->IsUndefined() && styleJsValue->IsNumber()) {
245         auto styleValue = styleJsValue->ToNumber<uint32_t>();
246         if (styleValue > 0 && styleValue < 4) {
247             borderStyle = static_cast<BorderStyle>(styleValue);
248         }
249     }
250     SlidingPanelModel::GetInstance()->SetBorderStyle(borderStyle);
251 
252     Color borderColor;
253     ParseJsColor(argsPtrItem->GetProperty("color"), borderColor);
254     SlidingPanelModel::GetInstance()->SetBorderColor(borderColor);
255 }
256 
SetOnSizeChange(const JSCallbackInfo & args)257 void JSSlidingPanel::SetOnSizeChange(const JSCallbackInfo& args)
258 {
259     if (!args[0]->IsFunction()) {
260         return;
261     }
262 
263     auto onSizeChangeNG = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
264                               const BaseEventInfo* info) {
265         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
266         auto eventInfo = TypeInfoHelper::DynamicCast<SlidingPanelSizeChangeEvent>(info);
267         if (!eventInfo) {
268             return;
269         }
270         auto params = ConvertToJSValues(eventInfo->GetWidth(), eventInfo->GetHeight(), eventInfo->GetMode());
271         ACE_SCORING_EVENT("SlidingPanel.OnSizeChange");
272         func->Call(JSRef<JSObject>(), params.size(), params.data());
273     };
274     SlidingPanelModel::GetInstance()->SetOnSizeChange(onSizeChangeNG);
275 
276     args.ReturnSelf();
277 }
278 
SetOnHeightChange(const JSCallbackInfo & args)279 void JSSlidingPanel::SetOnHeightChange(const JSCallbackInfo& args)
280 {
281     if (args.Length() < 1) {
282         return;
283     }
284     if (!args[0]->IsFunction()) {
285         return;
286     }
287 
288     auto onHeightChangeCallback = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(args[0]));
289     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
290     auto onHeightChange = [execCtx = args.GetExecutionContext(), func = std::move(onHeightChangeCallback),
291                               node = targetNode](int32_t height) {
292         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
293         ACE_SCORING_EVENT("OnHeightChange");
294         PipelineContext::SetCallBackNode(node);
295         JSRef<JSVal> param = JSRef<JSVal>::Make(ToJSValue(height));
296         func->ExecuteJS(1, &param);
297     };
298 
299     SlidingPanelModel::GetInstance()->SetOnHeightChange(std::move(onHeightChange));
300 
301     args.ReturnSelf();
302 }
303 
SetHasDragBar(const JSCallbackInfo & info)304 void JSSlidingPanel::SetHasDragBar(const JSCallbackInfo& info)
305 {
306     if (info.Length() < 1) {
307         return;
308     }
309     auto hasDragBar = DEFAULT_HASDRAGBAR;
310     if (info[0]->IsBoolean()) {
311         hasDragBar = info[0]->ToBoolean();
312     }
313     SlidingPanelModel::GetInstance()->SetHasDragBar(hasDragBar);
314 }
315 
SetShowCloseIcon(const JSCallbackInfo & info)316 void JSSlidingPanel::SetShowCloseIcon(const JSCallbackInfo& info)
317 {
318     if (info.Length() < 1) {
319         return;
320     }
321     auto showCloseIcon = DEFAULT_SHOWCLOSEICON;
322     if (info[0]->IsBoolean()) {
323         showCloseIcon = info[0]->ToBoolean();
324     }
325     SlidingPanelModel::GetInstance()->SetShowCloseIcon(showCloseIcon);
326 }
327 
SetShow(const JSCallbackInfo & info)328 void JSSlidingPanel::SetShow(const JSCallbackInfo& info)
329 {
330     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN) &&
331         (info[0]->IsUndefined() || info[0]->IsNull())) {
332         SlidingPanelModel::GetInstance()->SetIsShow(true);
333     } else {
334         SlidingPanelModel::GetInstance()->SetIsShow(info[0]->ToBoolean());
335     }
336 }
337 
ParseModeObject(const JSCallbackInfo & info,const JSRef<JSVal> & changeEventVal)338 void ParseModeObject(const JSCallbackInfo& info, const JSRef<JSVal>& changeEventVal)
339 {
340     CHECK_NULL_VOID(changeEventVal->IsFunction());
341 
342     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(changeEventVal));
343     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
344     auto onMode = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
345                       const BaseEventInfo* baseEventInfo) {
346         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
347         ACE_SCORING_EVENT("SlidingPanel.ModeChangeEvent");
348         auto eventInfo = TypeInfoHelper::DynamicCast<SlidingPanelSizeChangeEvent>(baseEventInfo);
349         if (!eventInfo) {
350             return;
351         }
352         PipelineContext::SetCallBackNode(node);
353         auto newJSVal = JSRef<JSVal>::Make(ToJSValue(static_cast<int32_t>(eventInfo->GetMode())));
354         func->ExecuteJS(1, &newJSVal);
355     };
356     SlidingPanelModel::GetInstance()->SetModeChangeEvent(std::move(onMode));
357 }
358 
SetPanelMode(const JSCallbackInfo & info)359 void JSSlidingPanel::SetPanelMode(const JSCallbackInfo& info)
360 {
361     if (info.Length() < 1 || info.Length() > 2) {
362         return;
363     }
364 
365     int32_t mode = static_cast<int32_t>(DEFAULT_PANELMODE);
366     if (info.Length() > 0 && info[0]->IsNumber()) {
367         const auto modeNumber = info[0]->ToNumber<int32_t>();
368         if (modeNumber >= 0 && modeNumber < static_cast<int32_t>(PANEL_MODES.size())) {
369             mode = modeNumber;
370         }
371     }
372 
373     if (info.Length() > 1 && info[1]->IsFunction()) {
374         ParseModeObject(info, info[1]);
375     }
376 
377     SlidingPanelModel::GetInstance()->SetPanelMode(PANEL_MODES[mode]);
378 }
379 
SetPanelType(const JSCallbackInfo & info)380 void JSSlidingPanel::SetPanelType(const JSCallbackInfo& info)
381 {
382     if (info.Length() < 1) {
383         return;
384     }
385     auto type = static_cast<int32_t>(DEFAULT_PANELTYPE);
386     if (info[0]->IsNumber()) {
387         const auto typeNumber = info[0]->ToNumber<int32_t>();
388         if (typeNumber >= 0 && typeNumber < static_cast<int32_t>(PANEL_TYPES.size())) {
389             type = typeNumber;
390         }
391     }
392     SlidingPanelModel::GetInstance()->SetPanelType(PANEL_TYPES[type]);
393 }
394 
SetCustomHeight(const JSCallbackInfo & info)395 void JSSlidingPanel::SetCustomHeight(const JSCallbackInfo& info)
396 {
397     if (info.Length() < 1) {
398         return;
399     }
400     CalcDimension customHeight;
401     if (info[0]->IsString() && info[0]->ToString().find("wrapContent") != std::string::npos) {
402         customHeight = CalcDimension(info[0]->ToString());
403     } else if (!ParseJsDimensionVp(info[0], customHeight)) {
404         customHeight = Dimension(0.0);
405     }
406 
407     SlidingPanelModel::GetInstance()->SetCustomHeight(customHeight);
408 }
409 
SetMiniHeight(const JSCallbackInfo & info)410 void JSSlidingPanel::SetMiniHeight(const JSCallbackInfo& info)
411 {
412     if (info.Length() < 1) {
413         return;
414     }
415     CalcDimension miniHeight;
416     if (!ParseJsDimensionVp(info[0], miniHeight)) {
417         return;
418     }
419 
420     SlidingPanelModel::GetInstance()->SetMiniHeight(miniHeight);
421 }
422 
SetHalfHeight(const JSCallbackInfo & info)423 void JSSlidingPanel::SetHalfHeight(const JSCallbackInfo& info)
424 {
425     if (info.Length() < 1) {
426         return;
427     }
428     CalcDimension halfHeight;
429     if (!ParseJsDimensionVp(info[0], halfHeight)) {
430         return;
431     }
432     SlidingPanelModel::GetInstance()->SetHalfHeight(halfHeight);
433 }
434 
SetFullHeight(const JSCallbackInfo & info)435 void JSSlidingPanel::SetFullHeight(const JSCallbackInfo& info)
436 {
437     if (info.Length() < 1) {
438         return;
439     }
440     CalcDimension fullHeight;
441     if (!ParseJsDimensionVp(info[0], fullHeight)) {
442         return;
443     }
444     SlidingPanelModel::GetInstance()->SetFullHeight(fullHeight);
445 }
446 
Pop()447 void JSSlidingPanel::Pop()
448 {
449     SlidingPanelModel::GetInstance()->Pop();
450 }
451 
452 } // namespace OHOS::Ace::Framework
453