• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/base/view_stack_processor.h"
22 #include "core/components_ng/pattern/menu/menu_item/menu_item_model.h"
23 #include "core/components_ng/pattern/menu/menu_item/menu_item_model_ng.h"
24 #include "bridge/declarative_frontend/ark_theme/theme_apply/js_menu_item_theme.h"
25 #include "core/components_ng/pattern/symbol/symbol_source_info.h"
26 #include "core/common/resource/resource_parse_utils.h"
27 
28 namespace OHOS::Ace {
29 std::unique_ptr<MenuItemModel> MenuItemModel::instance_ = nullptr;
30 std::mutex MenuItemModel::mutex_;
31 
GetInstance()32 MenuItemModel* MenuItemModel::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::MenuItemModelNG());
39 #else
40             if (Container::IsCurrentUseNewPipeline()) {
41                 instance_.reset(new NG::MenuItemModelNG());
42             } else {
43                 instance_.reset(new Framework::MenuItemModelImpl());
44             }
45 #endif
46         }
47     }
48     return instance_.get();
49 }
50 } // namespace OHOS::Ace
51 
52 namespace OHOS::Ace::Framework {
ParseMenuItemOptionsResource(const JSCallbackInfo & info,const JSRef<JSObject> & menuItemObj,MenuItemProperties & menuItemProps)53 void JSMenuItem::ParseMenuItemOptionsResource(
54     const JSCallbackInfo& info, const JSRef<JSObject>& menuItemObj, MenuItemProperties& menuItemProps)
55 {
56     std::string startIconPath;
57     std::string contentStr;
58     std::string endIconPath;
59     std::string labelStr;
60     RefPtr<ResourceObject> contentStrObj;
61     RefPtr<ResourceObject> labelStrObj;
62     RefPtr<ResourceObject> startIconObj;
63     RefPtr<ResourceObject> endIconObj;
64     std::function<void(WeakPtr<NG::FrameNode>)> symbolApply;
65 
66     auto startIcon = menuItemObj->GetProperty("startIcon");
67     auto content = menuItemObj->GetProperty("content");
68     auto endIcon = menuItemObj->GetProperty("endIcon");
69     auto label = menuItemObj->GetProperty("labelInfo");
70     auto symbolStart = menuItemObj->GetProperty("symbolStartIcon");
71     auto symbolEnd = menuItemObj->GetProperty("symbolEndIcon");
72 
73     if (symbolStart->IsObject()) {
74         JSViewAbstract::SetSymbolOptionApply(info, symbolApply, symbolStart);
75         menuItemProps.startApply = symbolApply;
76     } else if (ParseJsMedia(startIcon, startIconPath, startIconObj)) {
77         std::string bundleName;
78         std::string moduleName;
79         GetJsMediaBundleInfo(startIcon, bundleName, moduleName);
80         ImageSourceInfo imageSourceInfo(startIconPath, bundleName, moduleName);
81         menuItemProps.startIcon = imageSourceInfo;
82     }
83     ParseJsString(content, contentStr, contentStrObj);
84     menuItemProps.content = contentStr;
85     if (symbolEnd->IsObject()) {
86         JSViewAbstract::SetSymbolOptionApply(info, symbolApply, symbolEnd);
87         menuItemProps.endApply = symbolApply;
88     } else if (ParseJsMedia(endIcon, endIconPath, endIconObj)) {
89         std::string bundleName;
90         std::string moduleName;
91         GetJsMediaBundleInfo(endIcon, bundleName, moduleName);
92         ImageSourceInfo imageSourceInfo(endIconPath, bundleName, moduleName);
93         menuItemProps.endIcon = imageSourceInfo;
94     }
95     if (ParseJsString(label, labelStr, labelStrObj)) {
96         menuItemProps.labelInfo = labelStr;
97     }
98     if (SystemProperties::ConfigChangePerform()) {
99         AddMenuItemOptionsResource(contentStrObj, labelStrObj, startIconObj, endIconObj, menuItemProps);
100     }
101 }
102 
AddMenuItemOptionsResource(const RefPtr<ResourceObject> & contentStrObj,const RefPtr<ResourceObject> & labelStrObj,const RefPtr<ResourceObject> & startIconObj,const RefPtr<ResourceObject> & endIconObj,MenuItemProperties & menuItemProps)103 void JSMenuItem::AddMenuItemOptionsResource(const RefPtr<ResourceObject>& contentStrObj,
104     const RefPtr<ResourceObject>& labelStrObj, const RefPtr<ResourceObject>& startIconObj,
105     const RefPtr<ResourceObject>& endIconObj, MenuItemProperties& menuItemProps)
106 {
107     if (contentStrObj) {
108         menuItemProps.AddResource("MenuItem.Content", contentStrObj, [](const RefPtr<ResourceObject>& resObj,
109             MenuItemProperties& props) {
110             std::string contentStr;
111             CHECK_NE_VOID(ResourceParseUtils::ParseResString(resObj, contentStr), true);
112             props.content = contentStr;
113         });
114     }
115     if (labelStrObj) {
116         menuItemProps.AddResource("MenuItem.Label", labelStrObj, [](const RefPtr<ResourceObject>& resObj,
117             MenuItemProperties& props) {
118             std::string labelInfoStr;
119             CHECK_NE_VOID(ResourceParseUtils::ParseResString(resObj, labelInfoStr), true);
120             props.labelInfo = labelInfoStr;
121         });
122     }
123     if (startIconObj) {
124         menuItemProps.AddResource("MenuItem.StartIcon", startIconObj, [](const RefPtr<ResourceObject>& resObj,
125             MenuItemProperties& props) {
126             std::string iconStr;
127             CHECK_NE_VOID(ResourceParseUtils::ParseResMedia(resObj, iconStr), true);
128             ImageSourceInfo imageSourceInfo(iconStr);
129             props.startIcon = imageSourceInfo;
130         });
131     }
132     if (endIconObj) {
133         menuItemProps.AddResource("MenuItem.EndIcon", endIconObj, [](const RefPtr<ResourceObject>& resObj,
134             MenuItemProperties& props) {
135             std::string iconStr;
136             CHECK_NE_VOID(ResourceParseUtils::ParseResMedia(resObj, iconStr), true);
137             ImageSourceInfo imageSourceInfo(iconStr);
138             props.endIcon = imageSourceInfo;
139         });
140     }
141 }
142 
Create(const JSCallbackInfo & info)143 void JSMenuItem::Create(const JSCallbackInfo& info)
144 {
145     if (info.Length() < 1 || (!info[0]->IsObject() && !info[0]->IsFunction())) {
146         MenuItemModel::GetInstance()->Create(nullptr);
147         return;
148     }
149     // custom menu item
150     if (info[0]->IsFunction()) {
151         auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(info[0]));
152         CHECK_NULL_VOID(builderFunc);
153         RefPtr<NG::UINode> customNode;
154         {
155             ViewStackModel::GetInstance()->NewScope();
156             builderFunc->Execute();
157             customNode = AceType::DynamicCast<NG::UINode>(ViewStackModel::GetInstance()->Finish());
158         }
159         CHECK_NULL_VOID(customNode);
160         MenuItemModel::GetInstance()->Create(customNode);
161     } else {
162         auto menuItemObj = JSRef<JSObject>::Cast(info[0]);
163         MenuItemProperties menuItemProps;
164         ParseMenuItemOptionsResource(info, menuItemObj, menuItemProps);
165         auto builder = menuItemObj->GetProperty("builder");
166         if (!builder.IsEmpty() && builder->IsFunction()) {
167             auto subBuilderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(builder));
168             CHECK_NULL_VOID(subBuilderFunc);
169             auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
170             auto subBuildFunc = [execCtx = info.GetExecutionContext(), func = std::move(subBuilderFunc),
171                                     node = targetNode]() {
172                 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
173                 ACE_SCORING_EVENT("MenuItem SubBuilder");
174                 PipelineContext::SetCallBackNode(node);
175                 func->ExecuteJS();
176             };
177             menuItemProps.buildFunc = std::move(subBuildFunc);
178         }
179         MenuItemModel::GetInstance()->Create(menuItemProps);
180     }
181     JSMenuItemTheme::ApplyTheme();
182 }
183 
JSBind(BindingTarget globalObj)184 void JSMenuItem::JSBind(BindingTarget globalObj)
185 {
186     JSClass<JSMenuItem>::Declare("MenuItem");
187     MethodOptions opt = MethodOptions::NONE;
188     JSClass<JSMenuItem>::StaticMethod("create", &JSMenuItem::Create, opt);
189 
190     JSClass<JSMenuItem>::StaticMethod("selected", &JSMenuItem::IsSelected, opt);
191     JSClass<JSMenuItem>::StaticMethod("selectIcon", &JSMenuItem::SelectIcon, opt);
192     JSClass<JSMenuItem>::StaticMethod("onChange", &JSMenuItem::OnChange, opt);
193     JSClass<JSMenuItem>::StaticMethod("contentFont", &JSMenuItem::ContentFont, opt);
194     JSClass<JSMenuItem>::StaticMethod("contentFontColor", &JSMenuItem::ContentFontColor, opt);
195     JSClass<JSMenuItem>::StaticMethod("labelFont", &JSMenuItem::LabelFont, opt);
196     JSClass<JSMenuItem>::StaticMethod("labelFontColor", &JSMenuItem::LabelFontColor, opt);
197     JSClass<JSMenuItem>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
198     JSClass<JSMenuItem>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
199     JSClass<JSMenuItem>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
200     JSClass<JSMenuItem>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
201     JSClass<JSMenuItem>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
202     JSClass<JSMenuItem>::InheritAndBind<JSContainerBase>(globalObj);
203 }
204 
ParseIsSelectedObject(const JSCallbackInfo & info,const JSRef<JSVal> & changeEventVal)205 void ParseIsSelectedObject(const JSCallbackInfo& info, const JSRef<JSVal>& changeEventVal)
206 {
207     CHECK_NULL_VOID(changeEventVal->IsFunction());
208 
209     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(changeEventVal));
210     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
211     auto onSelected = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
212                           bool selected) {
213         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
214         ACE_SCORING_EVENT("MenuItem.SelectedChangeEvent");
215         PipelineContext::SetCallBackNode(node);
216         auto newJSVal = JSRef<JSVal>::Make(ToJSValue(selected));
217         func->ExecuteJS(1, &newJSVal);
218     };
219     MenuItemModel::GetInstance()->SetSelectedChangeEvent(std::move(onSelected));
220 }
221 
IsSelected(const JSCallbackInfo & info)222 void JSMenuItem::IsSelected(const JSCallbackInfo& info)
223 {
224     if (info.Length() < 1 || info.Length() > 2) {
225         return;
226     }
227 
228     bool isSelected = false;
229     JSRef<JSVal> changeEventVal;
230     auto selectedVal = info[0];
231     if (selectedVal->IsObject()) {
232         JSRef<JSObject> obj = JSRef<JSObject>::Cast(selectedVal);
233         selectedVal = obj->GetProperty("value");
234         changeEventVal = obj->GetProperty("$value");
235     } else if (info.Length() > 1) {
236         changeEventVal = info[1];
237     }
238     if (selectedVal->IsBoolean()) {
239         isSelected = selectedVal->ToBoolean();
240     }
241 
242     MenuItemModel::GetInstance()->SetSelected(isSelected);
243     if (changeEventVal->IsFunction()) {
244         ParseIsSelectedObject(info, changeEventVal);
245     }
246 }
247 
SelectIcon(const JSCallbackInfo & info)248 void JSMenuItem::SelectIcon(const JSCallbackInfo& info)
249 {
250     bool isShow = false;
251     std::string icon;
252     RefPtr<ResourceObject> resObj;
253     std::function<void(WeakPtr<NG::FrameNode>)> symbolApply;
254     if (info[0]->IsBoolean()) {
255         isShow = info[0]->ToBoolean();
256     } else if (info[0]->IsString()) {
257         ParseJsString(info[0], icon, resObj);
258         isShow = true;
259     } else if (ParseJsMedia(info[0], icon)) {
260         isShow = true;
261     } else if (info[0]->IsObject()) {
262         isShow = true;
263         JSViewAbstract::SetSymbolOptionApply(info, symbolApply, info[0]);
264     }
265     MenuItemModel::GetInstance()->SetSelectIcon(isShow);
266     MenuItemModel::GetInstance()->SetSelectIconSrc(icon);
267     MenuItemModel::GetInstance()->SetSelectIconSymbol(std::move(symbolApply));
268     if (SystemProperties::ConfigChangePerform()) {
269         MenuItemModel::GetInstance()->CreateWithStringResourceObj(resObj, MenuItemStringType::SELECT_ICON);
270     }
271 }
272 
OnChange(const JSCallbackInfo & info)273 void JSMenuItem::OnChange(const JSCallbackInfo& info)
274 {
275     if (info.Length() < 1 || !info[0]->IsFunction()) {
276         return;
277     }
278     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
279     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
280     auto onChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](bool selected) {
281         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
282         ACE_SCORING_EVENT("MenuItem.onChange");
283         PipelineContext::SetCallBackNode(node);
284         JSRef<JSVal> params[1];
285         params[0] = JSRef<JSVal>::Make(ToJSValue(selected));
286         func->ExecuteJS(1, params);
287     };
288     MenuItemModel::GetInstance()->SetOnChange(std::move(onChange));
289     info.ReturnSelf();
290 }
291 
ContentFont(const JSCallbackInfo & info)292 void JSMenuItem::ContentFont(const JSCallbackInfo& info)
293 {
294     CalcDimension fontSize;
295     std::string weight;
296     RefPtr<ResourceObject> fontSizeResObj;
297     CHECK_NE_VOID(info[0]->IsObject(), true);
298     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
299     JSRef<JSVal> size = obj->GetProperty("size");
300     if (!size->IsNull()) {
301         ParseJsDimensionFp(size, fontSize, fontSizeResObj);
302         if (fontSize.Unit() == DimensionUnit::PERCENT) {
303             // set zero for abnormal value
304             fontSize = CalcDimension();
305         }
306     }
307 
308     auto jsWeight = obj->GetProperty("weight");
309     if (!jsWeight->IsNull()) {
310         if (jsWeight->IsNumber()) {
311             weight = std::to_string(jsWeight->ToNumber<int32_t>());
312         } else {
313             ParseJsString(jsWeight, weight);
314         }
315     }
316 
317     auto jsStyle = obj->GetProperty("style");
318     if (!jsStyle->IsNull()) {
319         if (jsStyle->IsNumber()) {
320             MenuItemModel::GetInstance()->SetFontStyle(static_cast<FontStyle>(jsStyle->ToNumber<int32_t>()));
321         } else {
322             std::string style;
323             ParseJsString(jsStyle, style);
324             MenuItemModel::GetInstance()->SetFontStyle(ConvertStrToFontStyle(style));
325         }
326     }
327 
328     auto jsFamily = obj->GetProperty("family");
329     if (!jsFamily->IsNull() && jsFamily->IsString()) {
330         RefPtr<ResourceObject> familyResObj;
331         std::vector<std::string> fontFamilies;
332         if (ParseJsFontFamilies(jsFamily, fontFamilies, familyResObj)) {
333             MenuItemModel::GetInstance()->SetFontFamily(fontFamilies);
334         }
335         if (SystemProperties::ConfigChangePerform()) {
336             MenuItemModel::GetInstance()->CreateWithFontFamilyResourceObj(
337                 familyResObj, MenuItemFontFamilyType::FONT_FAMILY);
338         }
339     }
340     MenuItemModel::GetInstance()->SetFontSize(fontSize);
341     if (SystemProperties::ConfigChangePerform()) {
342         MenuItemModel::GetInstance()->CreateWithDimensionFpResourceObj(
343             fontSizeResObj, MenuItemFontSizeType::FONT_SIZE);
344     }
345     MenuItemModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(weight));
346 }
347 
ContentFontColor(const JSCallbackInfo & info)348 void JSMenuItem::ContentFontColor(const JSCallbackInfo& info)
349 {
350     if (info.Length() < 1) {
351         return;
352     }
353     std::optional<Color> color = std::nullopt;
354     Color textColor;
355     RefPtr<ResourceObject> resObj;
356     if (ParseJsColor(info[0], textColor, resObj)) {
357         color = textColor;
358     }
359     MenuItemModel::GetInstance()->SetFontColor(color);
360     if (SystemProperties::ConfigChangePerform()) {
361         MenuItemModel::GetInstance()->CreateWithColorResourceObj(resObj, MenuItemFontColorType::FONT_COLOR);
362     }
363 }
364 
LabelFont(const JSCallbackInfo & info)365 void JSMenuItem::LabelFont(const JSCallbackInfo& info)
366 {
367     CalcDimension fontSize;
368     std::string weight;
369     RefPtr<ResourceObject> fontSizeResObj;
370     CHECK_NE_VOID(info[0]->IsObject(), true);
371     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
372     JSRef<JSVal> size = obj->GetProperty("size");
373     if (!size->IsNull()) {
374         ParseJsDimensionFp(size, fontSize, fontSizeResObj);
375         if (fontSize.Unit() == DimensionUnit::PERCENT) {
376             // set zero for abnormal value
377             fontSize = CalcDimension();
378         }
379     }
380 
381     auto jsWeight = obj->GetProperty("weight");
382     if (!jsWeight->IsNull()) {
383         if (jsWeight->IsNumber()) {
384             weight = std::to_string(jsWeight->ToNumber<int32_t>());
385         } else {
386             ParseJsString(jsWeight, weight);
387         }
388     }
389 
390     auto jsStyle = obj->GetProperty("style");
391     if (!jsStyle->IsNull()) {
392         if (jsStyle->IsNumber()) {
393             MenuItemModel::GetInstance()->SetLabelFontStyle(static_cast<FontStyle>(jsStyle->ToNumber<int32_t>()));
394         } else {
395             std::string style;
396             ParseJsString(jsStyle, style);
397             MenuItemModel::GetInstance()->SetLabelFontStyle(ConvertStrToFontStyle(style));
398         }
399     }
400 
401     auto jsFamily = obj->GetProperty("family");
402     if (!jsFamily->IsNull() && jsFamily->IsString()) {
403         RefPtr<ResourceObject> familyResObj;
404         std::vector<std::string> fontFamilies;
405         if (ParseJsFontFamilies(jsFamily, fontFamilies, familyResObj)) {
406             MenuItemModel::GetInstance()->SetLabelFontFamily(fontFamilies);
407         }
408         if (SystemProperties::ConfigChangePerform()) {
409             MenuItemModel::GetInstance()->CreateWithFontFamilyResourceObj(
410                 familyResObj, MenuItemFontFamilyType::LABEL_FONT_FAMILY);
411         }
412     }
413     MenuItemModel::GetInstance()->SetLabelFontSize(fontSize);
414     if (SystemProperties::ConfigChangePerform()) {
415         MenuItemModel::GetInstance()->CreateWithDimensionFpResourceObj(
416             fontSizeResObj, MenuItemFontSizeType::LABEL_FONT_SIZE);
417     }
418     MenuItemModel::GetInstance()->SetLabelFontWeight(ConvertStrToFontWeight(weight));
419 }
420 
LabelFontColor(const JSCallbackInfo & info)421 void JSMenuItem::LabelFontColor(const JSCallbackInfo& info)
422 {
423     if (info.Length() < 1) {
424         return;
425     }
426     std::optional<Color> color = std::nullopt;
427     Color textColor;
428     RefPtr<ResourceObject> resObj;
429     if (ParseJsColor(info[0], textColor, resObj)) {
430         color = textColor;
431     }
432     MenuItemModel::GetInstance()->SetLabelFontColor(color);
433     if (SystemProperties::ConfigChangePerform()) {
434         MenuItemModel::GetInstance()->CreateWithColorResourceObj(resObj, MenuItemFontColorType::LABEL_FONT_COLOR);
435     }
436 }
437 } // namespace OHOS::Ace::Framework
438