• 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.h"
17 
18 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
19 #include "bridge/declarative_frontend/jsview/models/menu_model_impl.h"
20 #include "core/components_ng/layout/layout_property.h"
21 #include "core/components_ng/pattern/menu/menu_model.h"
22 #include "core/components_ng/pattern/menu/menu_model_ng.h"
23 #include "core/components_ng/property/measure_property.h"
24 #include "bridge/declarative_frontend/ark_theme/theme_apply/js_menu_theme.h"
25 
26 namespace OHOS::Ace {
27 std::unique_ptr<MenuModel> MenuModel::instance_ = nullptr;
28 std::mutex MenuModel::mutex_;
29 
GetInstance()30 MenuModel* MenuModel::GetInstance()
31 {
32     if (!instance_) {
33         std::lock_guard<std::mutex> lock(mutex_);
34         if (!instance_) {
35 #ifdef NG_BUILD
36             instance_.reset(new NG::MenuModelNG());
37 #else
38             if (Container::IsCurrentUseNewPipeline()) {
39                 instance_.reset(new NG::MenuModelNG());
40             } else {
41                 instance_.reset(new Framework::MenuModelImpl());
42             }
43 #endif
44         }
45     }
46     return instance_.get();
47 }
48 } // namespace OHOS::Ace
49 
50 namespace OHOS::Ace::Framework {
Create(const JSCallbackInfo &)51 void JSMenu::Create(const JSCallbackInfo& /* info */)
52 {
53     MenuModel::GetInstance()->Create();
54     JSMenuTheme::ApplyTheme();
55 }
56 
FontSize(const JSCallbackInfo & info)57 void JSMenu::FontSize(const JSCallbackInfo& info)
58 {
59     if (info.Length() < 1) {
60         return;
61     }
62     CalcDimension fontSize;
63     if (!ParseJsDimensionFp(info[0], fontSize)) {
64         return;
65     }
66     MenuModel::GetInstance()->SetFontSize(fontSize);
67 }
68 
Font(const JSCallbackInfo & info)69 void JSMenu::Font(const JSCallbackInfo& info)
70 {
71     CalcDimension fontSize;
72     std::string weight;
73     if (!info[0]->IsObject()) {
74         if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
75             MenuModel::GetInstance()->SetFontSize(CalcDimension());
76             MenuModel::GetInstance()->SetFontWeight(FontWeight::NORMAL);
77             MenuModel::GetInstance()->SetFontStyle(FontStyle::NORMAL);
78             MenuModel::GetInstance()->ResetFontFamily();
79         }
80         return;
81     } else {
82         JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
83         JSRef<JSVal> size = obj->GetProperty("size");
84         if (!size->IsNull()) {
85             ParseJsDimensionFp(size, fontSize);
86             if (fontSize.Unit() == DimensionUnit::PERCENT) {
87                 // set zero for abnormal value
88                 fontSize = CalcDimension();
89             }
90         }
91         auto jsWeight = obj->GetProperty("weight");
92         if (!jsWeight->IsNull()) {
93             if (jsWeight->IsNumber()) {
94                 weight = std::to_string(jsWeight->ToNumber<int32_t>());
95             } else {
96                 ParseJsString(jsWeight, weight);
97             }
98         }
99         auto jsStyle = obj->GetProperty("style");
100         if (!jsStyle->IsNull()) {
101             if (jsStyle->IsNumber()) {
102                 MenuModel::GetInstance()->SetFontStyle(static_cast<FontStyle>(jsStyle->ToNumber<int32_t>()));
103             } else {
104                 std::string style;
105                 ParseJsString(jsStyle, style);
106                 MenuModel::GetInstance()->SetFontStyle(ConvertStrToFontStyle(style));
107             }
108         }
109         auto jsFamily = obj->GetProperty("family");
110         if (!jsFamily->IsNull() && jsFamily->IsString()) {
111             auto familyVal = jsFamily->ToString();
112             auto fontFamilies = ConvertStrToFontFamilies(familyVal);
113             MenuModel::GetInstance()->SetFontFamily(fontFamilies);
114         }
115     }
116     MenuModel::GetInstance()->SetFontSize(fontSize);
117     MenuModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(weight));
118 }
119 
FontColor(const JSCallbackInfo & info)120 void JSMenu::FontColor(const JSCallbackInfo& info)
121 {
122     std::optional<Color> color = std::nullopt;
123     if (info.Length() < 1) {
124         return;
125     } else {
126         Color textColor;
127         if (ParseJsColor(info[0], textColor)) {
128             color = textColor;
129         }
130     }
131     MenuModel::GetInstance()->SetFontColor(color);
132 }
133 
SetWidth(const JSCallbackInfo & info)134 void JSMenu::SetWidth(const JSCallbackInfo& info)
135 {
136     if (info.Length() < 1) {
137         return;
138     }
139     CalcDimension width;
140     ParseJsDimensionVp(info[0], width);
141     MenuModel::GetInstance()->SetWidth(width);
142 }
143 
HandleDifferentRadius(const JSRef<JSVal> & args)144 void JSMenu::HandleDifferentRadius(const JSRef<JSVal>& args)
145 {
146     std::optional<CalcDimension> radiusTopLeft;
147     std::optional<CalcDimension> radiusTopRight;
148     std::optional<CalcDimension> radiusBottomLeft;
149     std::optional<CalcDimension> radiusBottomRight;
150     if (args->IsObject()) {
151         JSRef<JSObject> object = JSRef<JSObject>::Cast(args);
152         CalcDimension topLeft;
153         if (ParseJsDimensionVp(object->GetProperty("topLeft"), topLeft)) {
154             radiusTopLeft = topLeft;
155         }
156         CalcDimension topRight;
157         if (ParseJsDimensionVp(object->GetProperty("topRight"), topRight)) {
158             radiusTopRight = topRight;
159         }
160         CalcDimension bottomLeft;
161         if (ParseJsDimensionVp(object->GetProperty("bottomLeft"), bottomLeft)) {
162             radiusBottomLeft = bottomLeft;
163         }
164         CalcDimension bottomRight;
165         if (ParseJsDimensionVp(object->GetProperty("bottomRight"), bottomRight)) {
166             radiusBottomRight = bottomRight;
167         }
168         if (!radiusTopLeft.has_value() && !radiusTopRight.has_value() && !radiusBottomLeft.has_value() &&
169             !radiusBottomRight.has_value()) {
170             return;
171         }
172         MenuModel::GetInstance()->SetBorderRadius(radiusTopLeft, radiusTopRight, radiusBottomLeft, radiusBottomRight);
173     }
174 }
175 
SetRadius(const JSCallbackInfo & info)176 void JSMenu::SetRadius(const JSCallbackInfo& info)
177 {
178     if (info.Length() < 1) {
179         return;
180     }
181     CalcDimension radius;
182     if (info[0]->IsObject()) {
183         HandleDifferentRadius(info[0]);
184     } else {
185         if (!ParseJsDimensionVpNG(info[0], radius)) {
186             MenuModel::GetInstance()->ResetBorderRadius();
187             return;
188         }
189         if (LessNotEqual(radius.Value(), 0.0)) {
190             MenuModel::GetInstance()->ResetBorderRadius();
191             return;
192         }
193         MenuModel::GetInstance()->SetBorderRadius(radius);
194     }
195 }
196 
SetExpandingMode(const JSCallbackInfo & info)197 void JSMenu::SetExpandingMode(const JSCallbackInfo& info)
198 {
199     if (info.Length() < 1 || info[0]->IsNull() || !info[0]->IsNumber()) {
200         return;
201     }
202 
203     auto mode = static_cast<SubMenuExpandingMode>(info[0]->ToNumber<int32_t>());
204     auto expandingMode =
205         mode == SubMenuExpandingMode::EMBEDDED
206             ? NG::SubMenuExpandingMode::EMBEDDED
207             : mode == SubMenuExpandingMode::STACK
208                 ? NG::SubMenuExpandingMode::STACK
209                 : NG::SubMenuExpandingMode::SIDE;
210 
211     MenuModel::GetInstance()->SetExpandingMode(expandingMode);
212 }
213 
SetItemGroupDivider(const JSCallbackInfo & args)214 void JSMenu::SetItemGroupDivider(const JSCallbackInfo& args)
215 {
216     auto mode = DividerMode::FLOATING_ABOVE_MENU;
217     auto divider = V2::ItemDivider{
218         .strokeWidth = Dimension(0.0f, DimensionUnit::INVALID),
219         .color = Color::FOREGROUND,
220     };
221     if (args.Length() >= 1 && args[0]->IsObject()) {
222         JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
223         CalcDimension value;
224         if (!ParseLengthMetricsToPositiveDimension(obj->GetProperty("strokeWidth"), value)) {
225             value.Reset();
226             value.SetUnit(DimensionUnit::INVALID);
227         }
228         if (value.IsNegative() || value.Unit() < DimensionUnit::PX || value.Unit() > DimensionUnit::LPX) {
229             value.Reset();
230             value.SetUnit(DimensionUnit::INVALID);
231         }
232         divider.strokeWidth = value;
233         if (!ParseLengthMetricsToPositiveDimension(obj->GetProperty("startMargin"), value)) {
234             value.Reset();
235             value.SetUnit(DimensionUnit::INVALID);
236         }
237         if (value.IsNegative() || value.Unit() < DimensionUnit::PX || value.Unit() > DimensionUnit::LPX) {
238             value.Reset();
239             value.SetUnit(DimensionUnit::INVALID);
240         }
241         divider.startMargin = value;
242         if (!ParseLengthMetricsToPositiveDimension(obj->GetProperty("endMargin"), value)) {
243             value.Reset();
244             value.SetUnit(DimensionUnit::INVALID);
245         }
246         if (value.IsNegative() || value.Unit() < DimensionUnit::PX || value.Unit() > DimensionUnit::LPX) {
247             value.Reset();
248             value.SetUnit(DimensionUnit::INVALID);
249         }
250         divider.endMargin = value;
251         if (!ConvertFromJSValue(obj->GetProperty("color"), divider.color)) {
252             divider.color = Color::FOREGROUND;
253         }
254         auto modeVal = obj->GetProperty("mode");
255         if (modeVal->IsNumber() && modeVal->ToNumber<int32_t>() == 1) {
256             mode = DividerMode::EMBEDDED_IN_MENU;
257         }
258     }
259     MenuModel::GetInstance()->SetItemGroupDivider(divider, mode);
260     args.ReturnSelf();
261 }
262 
SetItemDivider(const JSCallbackInfo & args)263 void JSMenu::SetItemDivider(const JSCallbackInfo& args)
264 {
265     auto mode = DividerMode::FLOATING_ABOVE_MENU;
266     V2::ItemDivider divider;
267     if (args.Length() >= 1 && args[0]->IsObject()) {
268         JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
269         auto modeVal = obj->GetProperty("mode");
270         if (modeVal->IsNumber() && modeVal->ToNumber<int32_t>() == 1) {
271             mode = DividerMode::EMBEDDED_IN_MENU;
272         }
273         CalcDimension value;
274         if (!ParseLengthMetricsToPositiveDimension(obj->GetProperty("strokeWidth"), value)) {
275             value.Reset();
276             value.SetUnit(mode == DividerMode::EMBEDDED_IN_MENU ? DimensionUnit::INVALID : DimensionUnit::PX);
277         }
278         if (value.IsNegative()) {
279             value.Reset();
280             value.SetUnit(mode == DividerMode::EMBEDDED_IN_MENU ? DimensionUnit::INVALID : DimensionUnit::PX);
281         }
282         divider.strokeWidth = value;
283         if (!ParseLengthMetricsToPositiveDimension(obj->GetProperty("startMargin"), value)) {
284             value.Reset();
285             value.SetUnit(mode == DividerMode::EMBEDDED_IN_MENU ? DimensionUnit::INVALID : DimensionUnit::PX);
286         }
287         if (value.IsNegative()) {
288             value.Reset();
289             value.SetUnit(mode == DividerMode::EMBEDDED_IN_MENU ? DimensionUnit::INVALID : DimensionUnit::PX);
290         }
291         divider.startMargin = value;
292         if (!ParseLengthMetricsToPositiveDimension(obj->GetProperty("endMargin"), value)) {
293             value.Reset();
294             value.SetUnit(mode == DividerMode::EMBEDDED_IN_MENU ? DimensionUnit::INVALID : DimensionUnit::PX);
295         }
296         if (value.IsNegative()) {
297             value.Reset();
298             value.SetUnit(mode == DividerMode::EMBEDDED_IN_MENU ? DimensionUnit::INVALID : DimensionUnit::PX);
299         }
300         divider.endMargin = value;
301 
302         if (!ConvertFromJSValue(obj->GetProperty("color"), divider.color)) {
303             divider.color = Color::TRANSPARENT;
304         }
305     }
306     MenuModel::GetInstance()->SetItemDivider(divider, mode);
307     args.ReturnSelf();
308 }
309 
JSBind(BindingTarget globalObj)310 void JSMenu::JSBind(BindingTarget globalObj)
311 {
312     JSClass<JSMenu>::Declare("Menu");
313     MethodOptions opt = MethodOptions::NONE;
314     JSClass<JSMenu>::StaticMethod("create", &JSMenu::Create, opt);
315     JSClass<JSMenu>::StaticMethod("fontSize", &JSMenu::FontSize, opt);
316     JSClass<JSMenu>::StaticMethod("font", &JSMenu::Font, opt);
317     JSClass<JSMenu>::StaticMethod("fontColor", &JSMenu::FontColor, opt);
318     JSClass<JSMenu>::StaticMethod("width", &JSMenu::SetWidth, opt);
319     JSClass<JSMenu>::StaticMethod("radius", &JSMenu::SetRadius, opt);
320     JSClass<JSMenu>::StaticMethod("subMenuExpandingMode", &JSMenu::SetExpandingMode);
321     JSClass<JSMenu>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
322     JSClass<JSMenu>::StaticMethod("menuItemDivider", &JSMenu::SetItemDivider);
323     JSClass<JSMenu>::StaticMethod("menuItemGroupDivider", &JSMenu::SetItemGroupDivider);
324     JSClass<JSMenu>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
325     JSClass<JSMenu>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
326     JSClass<JSMenu>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
327     JSClass<JSMenu>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
328     JSClass<JSMenu>::InheritAndBind<JSViewAbstract>(globalObj);
329 }
330 } // namespace OHOS::Ace::Framework
331