1 /*
2 * Copyright (c) 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 "bridge/declarative_frontend/jsview/js_menu_item.h"
17
18 #include "base/log/ace_scoring_log.h"
19 #include "bridge/declarative_frontend/jsview/models/menu_item_model_impl.h"
20 #include "core/components_ng/base/view_stack_model.h"
21 #include "core/components_ng/pattern/menu/menu_item/menu_item_model.h"
22 #include "core/components_ng/pattern/menu/menu_item/menu_item_model_ng.h"
23
24 namespace OHOS::Ace {
25 std::unique_ptr<MenuItemModel> MenuItemModel::instance_ = nullptr;
26 std::mutex MenuItemModel::mutex_;
27
GetInstance()28 MenuItemModel* MenuItemModel::GetInstance()
29 {
30 if (!instance_) {
31 std::lock_guard<std::mutex> lock(mutex_);
32 if (!instance_) {
33 #ifdef NG_BUILD
34 instance_.reset(new NG::MenuItemModelNG());
35 #else
36 if (Container::IsCurrentUseNewPipeline()) {
37 instance_.reset(new NG::MenuItemModelNG());
38 } else {
39 instance_.reset(new Framework::MenuItemModelImpl());
40 }
41 #endif
42 }
43 }
44 return instance_.get();
45 }
46 } // namespace OHOS::Ace
47
48 namespace OHOS::Ace::Framework {
Create(const JSCallbackInfo & info)49 void JSMenuItem::Create(const JSCallbackInfo& info)
50 {
51 if (info.Length() < 1 || (!info[0]->IsObject() && !info[0]->IsFunction())) {
52 LOGW("JSMenuItem The arg is wrong");
53 return;
54 }
55 // custom menu item
56 if (info[0]->IsFunction()) {
57 auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(info[0]));
58 CHECK_NULL_VOID(builderFunc);
59 RefPtr<NG::UINode> customNode;
60 {
61 ViewStackModel::GetInstance()->NewScope();
62 builderFunc->Execute();
63 customNode = AceType::DynamicCast<NG::UINode>(ViewStackModel::GetInstance()->Finish());
64 }
65 CHECK_NULL_VOID(customNode);
66 MenuItemModel::GetInstance()->Create(customNode);
67 } else {
68 auto menuItemObj = JSRef<JSObject>::Cast(info[0]);
69
70 std::string startIconPath;
71 std::string contentStr;
72 std::string endIconPath;
73 std::string labelStr;
74 MenuItemProperties menuItemProps;
75
76 auto startIcon = menuItemObj->GetProperty("startIcon");
77 auto content = menuItemObj->GetProperty("content");
78 auto endIcon = menuItemObj->GetProperty("endIcon");
79 auto label = menuItemObj->GetProperty("labelInfo");
80
81 if (ParseJsMedia(startIcon, startIconPath)) {
82 menuItemProps.startIcon = startIconPath;
83 } else {
84 LOGI("startIcon is null");
85 }
86
87 if (!ParseJsString(content, contentStr)) {
88 LOGI("content is null");
89 }
90 menuItemProps.content = contentStr;
91
92 if (ParseJsMedia(endIcon, endIconPath)) {
93 menuItemProps.endIcon = endIconPath;
94 } else {
95 LOGI("endIcon is null");
96 }
97
98 if (ParseJsString(label, labelStr)) {
99 menuItemProps.labelInfo = labelStr;
100 } else {
101 LOGI("labelInfo is null");
102 }
103
104 auto builder = menuItemObj->GetProperty("builder");
105 if (!builder.IsEmpty() && builder->IsFunction()) {
106 auto subBuilderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(builder));
107 CHECK_NULL_VOID(subBuilderFunc);
108 auto subBuildFunc = [execCtx = info.GetExecutionContext(), func = std::move(subBuilderFunc)]() {
109 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
110 ACE_SCORING_EVENT("MenuItem SubBuilder");
111 func->ExecuteJS();
112 };
113 menuItemProps.buildFunc = std::move(subBuildFunc);
114 }
115 MenuItemModel::GetInstance()->Create(menuItemProps);
116 }
117 }
118
JSBind(BindingTarget globalObj)119 void JSMenuItem::JSBind(BindingTarget globalObj)
120 {
121 JSClass<JSMenuItem>::Declare("MenuItem");
122 MethodOptions opt = MethodOptions::NONE;
123 JSClass<JSMenuItem>::StaticMethod("create", &JSMenuItem::Create, opt);
124
125 JSClass<JSMenuItem>::StaticMethod("selected", &JSMenuItem::IsSelected, opt);
126 JSClass<JSMenuItem>::StaticMethod("selectIcon", &JSMenuItem::SelectIcon, opt);
127 JSClass<JSMenuItem>::StaticMethod("onChange", &JSMenuItem::OnChange, opt);
128 JSClass<JSMenuItem>::StaticMethod("contentFont", &JSMenuItem::ContentFont, opt);
129 JSClass<JSMenuItem>::StaticMethod("contentFontColor", &JSMenuItem::ContentFontColor, opt);
130 JSClass<JSMenuItem>::StaticMethod("labelFont", &JSMenuItem::LabelFont, opt);
131 JSClass<JSMenuItem>::StaticMethod("labelFontColor", &JSMenuItem::LabelFontColor, opt);
132 JSClass<JSMenuItem>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
133 JSClass<JSMenuItem>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
134 JSClass<JSMenuItem>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
135 JSClass<JSMenuItem>::InheritAndBind<JSViewAbstract>(globalObj);
136 }
137
ParseIsSelectedObject(const JSCallbackInfo & info,const JSRef<JSVal> & changeEventVal)138 void ParseIsSelectedObject(const JSCallbackInfo& info, const JSRef<JSVal>& changeEventVal)
139 {
140 CHECK_NULL_VOID(changeEventVal->IsFunction());
141
142 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(changeEventVal));
143 auto onSelected = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](bool selected) {
144 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
145 ACE_SCORING_EVENT("MenuItem.SelectedChangeEvent");
146 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(selected));
147 func->ExecuteJS(1, &newJSVal);
148 };
149 MenuItemModel::GetInstance()->SetSelectedChangeEvent(std::move(onSelected));
150 }
151
IsSelected(const JSCallbackInfo & info)152 void JSMenuItem::IsSelected(const JSCallbackInfo& info)
153 {
154 if (info.Length() < 1 || info.Length() > 2) {
155 LOGE("The arg is wrong, it is supposed to have 1 or 2 arguments");
156 return;
157 }
158
159 bool isSelected = false;
160 if (info.Length() > 0 && info[0]->IsBoolean()) {
161 isSelected = info[0]->ToBoolean();
162 }
163 MenuItemModel::GetInstance()->SetSelected(isSelected);
164 if (info.Length() > 1 && info[1]->IsFunction()) {
165 ParseIsSelectedObject(info, info[1]);
166 }
167 }
168
SelectIcon(const JSCallbackInfo & info)169 void JSMenuItem::SelectIcon(const JSCallbackInfo& info)
170 {
171 bool isShow = false;
172 std::string icon;
173 if (info.Length() < 1) {
174 LOGW("The arg is wrong, it is supposed to have at least 1 arguments");
175 } else {
176 if (info[0]->IsBoolean()) {
177 isShow = info[0]->ToBoolean();
178 } else if (info[0]->IsString()) {
179 icon = info[0]->ToString();
180 isShow = true;
181 } else if (ParseJsMedia(info[0], icon)) {
182 isShow = true;
183 } else {
184 LOGW("can not parse select icon.");
185 }
186 }
187 MenuItemModel::GetInstance()->SetSelectIcon(isShow);
188 MenuItemModel::GetInstance()->SetSelectIconSrc(icon);
189 }
190
OnChange(const JSCallbackInfo & info)191 void JSMenuItem::OnChange(const JSCallbackInfo& info)
192 {
193 if (info.Length() < 1 || !info[0]->IsFunction()) {
194 LOGE("The arg is wrong, it is supposed to have atleast 1 argument.");
195 return;
196 }
197 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
198 auto onChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](bool selected) {
199 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
200 ACE_SCORING_EVENT("MenuItem.onChange");
201 JSRef<JSVal> params[1];
202 params[0] = JSRef<JSVal>::Make(ToJSValue(selected));
203 func->ExecuteJS(1, params);
204 };
205 MenuItemModel::GetInstance()->SetOnChange(std::move(onChange));
206 info.ReturnSelf();
207 }
208
ContentFont(const JSCallbackInfo & info)209 void JSMenuItem::ContentFont(const JSCallbackInfo& info)
210 {
211 CalcDimension fontSize;
212 std::string weight;
213 if (info.Length() < 1 || !info[0]->IsObject()) {
214 LOGW("The argv is wrong, it is supposed to have at least 1 object argument");
215 } else {
216 JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
217 JSRef<JSVal> size = obj->GetProperty("size");
218 if (!size->IsNull()) {
219 ParseJsDimensionFp(size, fontSize);
220 if (fontSize.Unit() == DimensionUnit::PERCENT) {
221 // set zero for abnormal value
222 fontSize = CalcDimension();
223 }
224 }
225
226 auto jsWeight = obj->GetProperty("weight");
227 if (!jsWeight->IsNull()) {
228 if (jsWeight->IsNumber()) {
229 weight = std::to_string(jsWeight->ToNumber<int32_t>());
230 } else {
231 ParseJsString(jsWeight, weight);
232 }
233 }
234
235 auto jsStyle = obj->GetProperty("style");
236 if (!jsStyle->IsNull()) {
237 if (jsStyle->IsNumber()) {
238 MenuItemModel::GetInstance()->SetFontStyle(static_cast<FontStyle>(jsStyle->ToNumber<int32_t>()));
239 } else {
240 std::string style;
241 ParseJsString(jsStyle, style);
242 MenuItemModel::GetInstance()->SetFontStyle(ConvertStrToFontStyle(style));
243 }
244 }
245
246 auto jsFamily = obj->GetProperty("family");
247 if (!jsFamily->IsNull() && jsFamily->IsString()) {
248 auto familyVal = jsFamily->ToString();
249 auto fontFamilies = ConvertStrToFontFamilies(familyVal);
250 MenuItemModel::GetInstance()->SetFontFamily(fontFamilies);
251 }
252 }
253 MenuItemModel::GetInstance()->SetFontSize(fontSize);
254 MenuItemModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(weight));
255 }
256
ContentFontColor(const JSCallbackInfo & info)257 void JSMenuItem::ContentFontColor(const JSCallbackInfo& info)
258 {
259 std::optional<Color> color = std::nullopt;
260 if (info.Length() < 1) {
261 LOGW("The argv is wrong, it is supposed to have at least 1 argument");
262 } else {
263 Color textColor;
264 if (ParseJsColor(info[0], textColor)) {
265 color = textColor;
266 }
267 }
268 MenuItemModel::GetInstance()->SetFontColor(color);
269 }
270
LabelFont(const JSCallbackInfo & info)271 void JSMenuItem::LabelFont(const JSCallbackInfo& info)
272 {
273 CalcDimension fontSize;
274 std::string weight;
275 if (info.Length() < 1 || !info[0]->IsObject()) {
276 LOGW("The argv is wrong, it is supposed to have at least 1 object argument");
277 } else {
278 JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
279 JSRef<JSVal> size = obj->GetProperty("size");
280 if (!size->IsNull()) {
281 ParseJsDimensionFp(size, fontSize);
282 if (fontSize.Unit() == DimensionUnit::PERCENT) {
283 // set zero for abnormal value
284 fontSize = CalcDimension();
285 }
286 }
287
288 auto jsWeight = obj->GetProperty("weight");
289 if (!jsWeight->IsNull()) {
290 if (jsWeight->IsNumber()) {
291 weight = std::to_string(jsWeight->ToNumber<int32_t>());
292 } else {
293 ParseJsString(jsWeight, weight);
294 }
295 }
296
297 auto jsStyle = obj->GetProperty("style");
298 if (!jsStyle->IsNull()) {
299 if (jsStyle->IsNumber()) {
300 MenuItemModel::GetInstance()->SetLabelFontStyle(static_cast<FontStyle>(jsStyle->ToNumber<int32_t>()));
301 } else {
302 std::string style;
303 ParseJsString(jsStyle, style);
304 MenuItemModel::GetInstance()->SetLabelFontStyle(ConvertStrToFontStyle(style));
305 }
306 }
307
308 auto jsFamily = obj->GetProperty("family");
309 if (!jsFamily->IsNull() && jsFamily->IsString()) {
310 auto familyVal = jsFamily->ToString();
311 auto fontFamilies = ConvertStrToFontFamilies(familyVal);
312 MenuItemModel::GetInstance()->SetLabelFontFamily(fontFamilies);
313 }
314 }
315 MenuItemModel::GetInstance()->SetLabelFontSize(fontSize);
316 MenuItemModel::GetInstance()->SetLabelFontWeight(ConvertStrToFontWeight(weight));
317 }
318
LabelFontColor(const JSCallbackInfo & info)319 void JSMenuItem::LabelFontColor(const JSCallbackInfo& info)
320 {
321 std::optional<Color> color = std::nullopt;
322 if (info.Length() < 1) {
323 LOGW("The argv is wrong, it is supposed to have at least 1 argument");
324 } else {
325 Color textColor;
326 if (ParseJsColor(info[0], textColor)) {
327 color = textColor;
328 }
329 }
330 MenuItemModel::GetInstance()->SetLabelFontColor(color);
331 }
332 } // namespace OHOS::Ace::Framework
333