1 /*
2 * Copyright (c) 2021-2022 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/action_sheet/js_action_sheet.h"
17
18 #include <string>
19 #include <vector>
20
21 #include "base/log/ace_scoring_log.h"
22 #include "bridge/common/utils/engine_helper.h"
23 #include "bridge/declarative_frontend/engine/functions/js_function.h"
24 #include "bridge/declarative_frontend/jsview/models/action_sheet_model_impl.h"
25 #include "core/common/container.h"
26 #include "core/components_ng/pattern/action_sheet/action_sheet_model_ng.h"
27
28 namespace OHOS::Ace {
29 std::unique_ptr<ActionSheetModel> ActionSheetModel::instance_ = nullptr;
30 std::mutex ActionSheetModel::mutex_;
31
GetInstance()32 ActionSheetModel* ActionSheetModel::GetInstance()
33 {
34 if (!instance_) {
35 std::lock_guard<std::mutex> lock(mutex_);
36 if (!instance_) {
37 #ifdef NG_BUILD
38 instance_.reset(new NG::ActionSheetModelNG());
39 #else
40 if (Container::IsCurrentUseNewPipeline()) {
41 instance_.reset(new NG::ActionSheetModelNG());
42 } else {
43 instance_.reset(new Framework::ActionSheetModelImpl());
44 }
45 #endif
46 }
47 }
48 return instance_.get();
49 }
50 } // namespace OHOS::Ace
51
52 namespace OHOS::Ace::Framework {
53 namespace {
54 const DimensionOffset ACTION_SHEET_OFFSET_DEFAULT = DimensionOffset(0.0_vp, -40.0_vp);
55 const DimensionOffset ACTION_SHEET_OFFSET_DEFAULT_TOP = DimensionOffset(0.0_vp, 40.0_vp);
56 const std::vector<DialogAlignment> DIALOG_ALIGNMENT = { DialogAlignment::TOP, DialogAlignment::CENTER,
57 DialogAlignment::BOTTOM, DialogAlignment::DEFAULT, DialogAlignment::TOP_START, DialogAlignment::TOP_END,
58 DialogAlignment::CENTER_START, DialogAlignment::CENTER_END, DialogAlignment::BOTTOM_START,
59 DialogAlignment::BOTTOM_END };
60 } // namespace
61
SetParseStyle(ButtonInfo & buttonInfo,const int32_t styleValue)62 static void SetParseStyle(ButtonInfo& buttonInfo, const int32_t styleValue)
63 {
64 if (styleValue >= static_cast<int32_t>(DialogButtonStyle::DEFAULT) &&
65 styleValue <= static_cast<int32_t>(DialogButtonStyle::HIGHTLIGHT)) {
66 buttonInfo.dlgButtonStyle = static_cast<DialogButtonStyle>(styleValue);
67 }
68 }
69
ParseSheetInfo(const JSCallbackInfo & args,JSRef<JSVal> val)70 ActionSheetInfo ParseSheetInfo(const JSCallbackInfo& args, JSRef<JSVal> val)
71 {
72 ActionSheetInfo sheetInfo;
73 if (!val->IsObject()) {
74 LOGW("param is not an object.");
75 return sheetInfo;
76 }
77
78 auto obj = JSRef<JSObject>::Cast(val);
79 auto titleVal = obj->GetProperty("title");
80 std::string title;
81 if (JSActionSheet::ParseJsString(titleVal, title)) {
82 sheetInfo.title = title;
83 }
84
85 auto iconVal = obj->GetProperty("icon");
86 std::string icon;
87 if (JSActionSheet::ParseJsMedia(iconVal, icon)) {
88 sheetInfo.icon = icon;
89 }
90
91 auto actionValue = obj->GetProperty("action");
92 if (actionValue->IsFunction()) {
93 auto actionFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(actionValue));
94 auto eventFunc = [execCtx = args.GetExecutionContext(), func = std::move(actionFunc)](const GestureEvent&) {
95 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
96 ACE_SCORING_EVENT("SheetInfo.action");
97 func->ExecuteJS();
98 };
99 ActionSheetModel::GetInstance()->SetAction(eventFunc, sheetInfo);
100 }
101 return sheetInfo;
102 }
103
Show(const JSCallbackInfo & args)104 void JSActionSheet::Show(const JSCallbackInfo& args)
105 {
106 LOGD("show ActionSheet");
107 auto scopedDelegate = EngineHelper::GetCurrentDelegate();
108 if (!scopedDelegate) {
109 // this case usually means there is no foreground container, need to figure out the reason.
110 LOGE("scopedDelegate is null, please check");
111 return;
112 }
113 if (!args[0]->IsObject()) {
114 LOGE("args is not an object, can't show ActionSheet.");
115 return;
116 }
117
118 DialogProperties properties {
119 .type = DialogType::ACTION_SHEET, .alignment = DialogAlignment::BOTTOM, .offset = ACTION_SHEET_OFFSET_DEFAULT
120 };
121 auto obj = JSRef<JSObject>::Cast(args[0]);
122 // Parse title.
123 auto titleValue = obj->GetProperty("title");
124 std::string title;
125 if (ParseJsString(titleValue, title)) {
126 properties.title = title;
127 }
128
129 // Parse subtitle.
130 auto subtitleValue = obj->GetProperty("subtitle");
131 std::string subtitle;
132 if (ParseJsString(subtitleValue, subtitle)) {
133 properties.subtitle = subtitle;
134 }
135
136 // Parses message.
137 auto messageValue = obj->GetProperty("message");
138 std::string message;
139 if (ParseJsString(messageValue, message)) {
140 properties.content = message;
141 }
142
143 // Parse auto autoCancel.
144 auto autoCancelValue = obj->GetProperty("autoCancel");
145 if (autoCancelValue->IsBoolean()) {
146 properties.autoCancel = autoCancelValue->ToBoolean();
147 }
148
149 // Parse cancel.
150 auto cancelValue = obj->GetProperty("cancel");
151 if (cancelValue->IsFunction()) {
152 auto cancelFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(cancelValue));
153 auto eventFunc = [execCtx = args.GetExecutionContext(), func = std::move(cancelFunc)]() {
154 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
155 ACE_SCORING_EVENT("ActionSheet.cancel");
156 func->Execute();
157 };
158 ActionSheetModel::GetInstance()->SetCancel(eventFunc, properties);
159 }
160
161 // Parse confirm.
162 auto confirmVal = obj->GetProperty("confirm");
163 if (confirmVal->IsObject()) {
164 JSRef<JSObject> confirmObj = JSRef<JSObject>::Cast(confirmVal);
165 JSRef<JSVal> value = confirmObj->GetProperty("value");
166 std::string buttonValue;
167 if (ParseJsString(value, buttonValue)) {
168 ButtonInfo buttonInfo = { .text = buttonValue };
169 JSRef<JSVal> actionValue = confirmObj->GetProperty("action");
170 // parse confirm action
171 if (actionValue->IsFunction()) {
172 auto actionFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(actionValue));
173 auto gestureEvent = [execCtx = args.GetExecutionContext(),
174 func = std::move(actionFunc)](GestureEvent&) {
175 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
176 ACE_SCORING_EVENT("ActionSheet.confirm.action");
177 LOGD("actionSheet confirm triggered");
178 func->ExecuteJS();
179 };
180 actionFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(actionValue));
181 auto eventFunc = [execCtx = args.GetExecutionContext(), func = std::move(actionFunc)]() {
182 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
183 ACE_SCORING_EVENT("ActionSheet.confirm.action");
184 func->Execute();
185 };
186 ActionSheetModel::GetInstance()->SetConfirm(gestureEvent, eventFunc, buttonInfo, properties);
187 }
188
189 // Parse enabled
190 auto enabledValue = confirmObj->GetProperty("enabled");
191 if (enabledValue->IsBoolean()) {
192 buttonInfo.enabled = enabledValue->ToBoolean();
193 }
194
195 // Parse defaultFocus
196 auto defaultFocusValue = confirmObj->GetProperty("defaultFocus");
197 if (defaultFocusValue->IsBoolean()) {
198 buttonInfo.defaultFocus = defaultFocusValue->ToBoolean();
199 }
200
201 // Parse style
202 auto style = confirmObj->GetProperty("style");
203 if (style->IsNumber()) {
204 auto styleValue = style->ToNumber<int32_t>();
205 SetParseStyle(buttonInfo, styleValue);
206 }
207
208 if (buttonInfo.IsValid()) {
209 properties.buttons.emplace_back(buttonInfo);
210 }
211 }
212 }
213
214 // Parse sheets
215 auto sheetsVal = obj->GetProperty("sheets");
216 if (sheetsVal->IsArray()) {
217 std::vector<ActionSheetInfo> sheetsInfo;
218 auto sheetsArr = JSRef<JSArray>::Cast(sheetsVal);
219 for (size_t index = 0; index < sheetsArr->Length(); ++index) {
220 sheetsInfo.emplace_back(ParseSheetInfo(args, sheetsArr->GetValueAt(index)));
221 }
222 properties.sheetsInfo = std::move(sheetsInfo);
223 }
224
225 // Parse alignment
226 auto alignmentValue = obj->GetProperty("alignment");
227 if (alignmentValue->IsNumber()) {
228 auto alignment = alignmentValue->ToNumber<int32_t>();
229 if (alignment >= 0 && alignment <= static_cast<int32_t>(DIALOG_ALIGNMENT.size())) {
230 properties.alignment = DIALOG_ALIGNMENT[alignment];
231 }
232 if (alignment == static_cast<int32_t>(DialogAlignment::TOP) ||
233 alignment == static_cast<int32_t>(DialogAlignment::TOP_START) ||
234 alignment == static_cast<int32_t>(DialogAlignment::TOP_END)) {
235 properties.offset = ACTION_SHEET_OFFSET_DEFAULT_TOP;
236 }
237 }
238
239 // Parse offset
240 auto offsetValue = obj->GetProperty("offset");
241 if (offsetValue->IsObject()) {
242 auto offsetObj = JSRef<JSObject>::Cast(offsetValue);
243 CalcDimension dx;
244 auto dxValue = offsetObj->GetProperty("dx");
245 ParseJsDimensionVp(dxValue, dx);
246 CalcDimension dy;
247 auto dyValue = offsetObj->GetProperty("dy");
248 ParseJsDimensionVp(dyValue, dy);
249 properties.offset = DimensionOffset(dx, dy);
250 }
251
252 // Parse maskRect.
253 auto maskRectValue = obj->GetProperty("maskRect");
254 DimensionRect maskRect;
255 if (JSViewAbstract::ParseJsDimensionRect(maskRectValue, maskRect)) {
256 properties.maskRect = maskRect;
257 }
258
259 // Parses gridCount.
260 auto gridCountValue = obj->GetProperty("gridCount");
261 if (gridCountValue->IsNumber()) {
262 properties.gridCount = gridCountValue->ToNumber<int32_t>();
263 }
264
265 ActionSheetModel::GetInstance()->ShowActionSheet(properties);
266 args.SetReturnValue(args.This());
267 }
268
JSBind(BindingTarget globalObj)269 void JSActionSheet::JSBind(BindingTarget globalObj)
270 {
271 JSClass<JSActionSheet>::Declare("ActionSheet");
272 JSClass<JSActionSheet>::StaticMethod("show", &JSActionSheet::Show);
273 JSClass<JSActionSheet>::InheritAndBind<JSViewAbstract>(globalObj);
274 }
275 } // namespace OHOS::Ace::Framework
276