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 divider = V2::ItemDivider{
217 .strokeWidth = Dimension(0.0f, DimensionUnit::INVALID),
218 .color = Color::FOREGROUND,
219 };
220 if (args.Length() >= 1 && args[0]->IsObject()) {
221 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
222 CalcDimension value;
223 if (!ParseLengthMetricsToPositiveDimension(obj->GetProperty("strokeWidth"), value)) {
224 value.Reset();
225 value.SetUnit(DimensionUnit::INVALID);
226 }
227 if (value.IsNegative() || value.Unit() < DimensionUnit::PX || value.Unit() > DimensionUnit::LPX) {
228 value.Reset();
229 value.SetUnit(DimensionUnit::INVALID);
230 }
231 divider.strokeWidth = value;
232 if (!ParseLengthMetricsToPositiveDimension(obj->GetProperty("startMargin"), value)) {
233 value.Reset();
234 value.SetUnit(DimensionUnit::INVALID);
235 }
236 if (value.IsNegative() || value.Unit() < DimensionUnit::PX || value.Unit() > DimensionUnit::LPX) {
237 value.Reset();
238 value.SetUnit(DimensionUnit::INVALID);
239 }
240 divider.startMargin = value;
241 if (!ParseLengthMetricsToPositiveDimension(obj->GetProperty("endMargin"), value)) {
242 value.Reset();
243 value.SetUnit(DimensionUnit::INVALID);
244 }
245 if (value.IsNegative() || value.Unit() < DimensionUnit::PX || value.Unit() > DimensionUnit::LPX) {
246 value.Reset();
247 value.SetUnit(DimensionUnit::INVALID);
248 }
249 divider.endMargin = value;
250 if (!ConvertFromJSValue(obj->GetProperty("color"), divider.color)) {
251 divider.color = Color::FOREGROUND;
252 }
253 }
254 MenuModel::GetInstance()->SetItemGroupDivider(divider);
255 args.ReturnSelf();
256 }
257
SetItemDivider(const JSCallbackInfo & args)258 void JSMenu::SetItemDivider(const JSCallbackInfo& args)
259 {
260 V2::ItemDivider divider;
261 if (args.Length() >= 1 && args[0]->IsObject()) {
262 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
263 CalcDimension value;
264 if (!ParseLengthMetricsToPositiveDimension(obj->GetProperty("strokeWidth"), value)) {
265 value.Reset();
266 }
267 if (value.IsNegative()) {
268 value.Reset();
269 }
270 divider.strokeWidth = value;
271 if (!ParseLengthMetricsToPositiveDimension(obj->GetProperty("startMargin"), value)) {
272 value.Reset();
273 }
274 if (value.IsNegative()) {
275 value.Reset();
276 }
277 divider.startMargin = value;
278 if (!ParseLengthMetricsToPositiveDimension(obj->GetProperty("endMargin"), value)) {
279 value.Reset();
280 }
281 if (value.IsNegative()) {
282 value.Reset();
283 }
284 divider.endMargin = value;
285
286 if (!ConvertFromJSValue(obj->GetProperty("color"), divider.color)) {
287 divider.color = Color::TRANSPARENT;
288 }
289 }
290 MenuModel::GetInstance()->SetItemDivider(divider);
291 args.ReturnSelf();
292 }
293
JSBind(BindingTarget globalObj)294 void JSMenu::JSBind(BindingTarget globalObj)
295 {
296 JSClass<JSMenu>::Declare("Menu");
297 MethodOptions opt = MethodOptions::NONE;
298 JSClass<JSMenu>::StaticMethod("create", &JSMenu::Create, opt);
299 JSClass<JSMenu>::StaticMethod("fontSize", &JSMenu::FontSize, opt);
300 JSClass<JSMenu>::StaticMethod("font", &JSMenu::Font, opt);
301 JSClass<JSMenu>::StaticMethod("fontColor", &JSMenu::FontColor, opt);
302 JSClass<JSMenu>::StaticMethod("width", &JSMenu::SetWidth, opt);
303 JSClass<JSMenu>::StaticMethod("radius", &JSMenu::SetRadius, opt);
304 JSClass<JSMenu>::StaticMethod("subMenuExpandingMode", &JSMenu::SetExpandingMode);
305 JSClass<JSMenu>::StaticMethod("menuItemDivider", &JSMenu::SetItemDivider);
306 JSClass<JSMenu>::StaticMethod("menuItemGroupDivider", &JSMenu::SetItemGroupDivider);
307 JSClass<JSMenu>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
308 JSClass<JSMenu>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
309 JSClass<JSMenu>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
310 JSClass<JSMenu>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
311 JSClass<JSMenu>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
312 JSClass<JSMenu>::InheritAndBind<JSViewAbstract>(globalObj);
313 }
314 } // namespace OHOS::Ace::Framework
315