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