1 /*
2 * Copyright (c) 2021-2022 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 "frameworks/bridge/declarative_frontend/jsview/js_shape.h"
17
18 #include "base/geometry/ng/image_mesh.h"
19 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
20 #include "bridge/declarative_frontend/jsview/models/shape_model_impl.h"
21 #include "core/common/container.h"
22 #include "core/components_ng/pattern/shape/shape_abstract_model.h"
23 #include "core/components_ng/pattern/shape/shape_model_ng.h"
24 #include "frameworks/bridge/declarative_frontend/jsview/js_utils.h"
25
26 namespace OHOS::Ace {
27 namespace {
28 constexpr double DEFAULT_OPACITY = 1.0;
29 constexpr double STROKE_MITERLIMIT_DEFAULT = 4.0f;
30 } // namespace
GetInstance()31 ShapeModel* ShapeModel::GetInstance()
32 {
33 #ifdef NG_BUILD
34 static NG::ShapeModelNG instance;
35 return &instance;
36 #else
37 if (Container::IsCurrentUseNewPipeline()) {
38 static NG::ShapeModelNG instance;
39 return &instance;
40 } else {
41 static Framework::ShapeModelImpl instance;
42 return &instance;
43 }
44 #endif
45 }
46 } // namespace OHOS::Ace
47
48 namespace OHOS::Ace::Framework {
49
Create(const JSCallbackInfo& info)50 void JSShape::Create(const JSCallbackInfo& info)
51 {
52 ShapeModel::GetInstance()->Create();
53 JSInteractableView::SetFocusable(true);
54 InitBox(info);
55 }
56
InitBox(const JSCallbackInfo& info)57 void JSShape::InitBox(const JSCallbackInfo& info)
58 {
59 RefPtr<PixelMap> pixMap = nullptr;
60 if (info.Length() == 1 && info[0]->IsObject()) {
61 #if !defined(PREVIEW)
62 pixMap = CreatePixelMapFromNapiValue(info[0]);
63 #endif
64 }
65 ShapeModel::GetInstance()->InitBox(pixMap);
66 }
67
SetViewPort(const JSCallbackInfo& info)68 void JSShape::SetViewPort(const JSCallbackInfo& info)
69 {
70 if (info.Length() < 1) {
71 return;
72 }
73 if (info[0]->IsObject()) {
74 JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
75 JSRef<JSVal> leftValue = obj->GetProperty("x");
76 JSRef<JSVal> topValue = obj->GetProperty("y");
77 JSRef<JSVal> widthValue = obj->GetProperty("width");
78 JSRef<JSVal> heightValue = obj->GetProperty("height");
79 ShapeViewBox viewBox;
80 CalcDimension dimLeft;
81 ParseJsDimensionVp(leftValue, dimLeft);
82 CalcDimension dimTop;
83 ParseJsDimensionVp(topValue, dimTop);
84 CalcDimension dimWidth;
85 ParseJsDimensionVp(widthValue, dimWidth);
86 CalcDimension dimHeight;
87 ParseJsDimensionVp(heightValue, dimHeight);
88 ShapeModel::GetInstance()->SetViewPort(dimLeft, dimTop, dimWidth, dimHeight);
89 }
90 info.SetReturnValue(info.This());
91 }
92
JsWidth(const JSCallbackInfo& info)93 void JSShape::JsWidth(const JSCallbackInfo& info)
94 {
95 if (info.Length() < 1) {
96 return;
97 }
98
99 JsWidth(info[0]);
100 }
101
JsWidth(const JSRef<JSVal>& jsValue)102 void JSShape::JsWidth(const JSRef<JSVal>& jsValue)
103 {
104 JSViewAbstract::JsWidth(jsValue);
105 ShapeModel::GetInstance()->SetWidth();
106 }
107
JsHeight(const JSCallbackInfo& info)108 void JSShape::JsHeight(const JSCallbackInfo& info)
109 {
110 if (info.Length() < 1) {
111 return;
112 }
113
114 JsHeight(info[0]);
115 }
116
JsHeight(const JSRef<JSVal>& jsValue)117 void JSShape::JsHeight(const JSRef<JSVal>& jsValue)
118 {
119 JSViewAbstract::JsHeight(jsValue);
120 ShapeModel::GetInstance()->SetHeight();
121 }
122
JsSize(const JSCallbackInfo& info)123 void JSShape::JsSize(const JSCallbackInfo& info)
124 {
125 if (info.Length() < 1) {
126 return;
127 }
128
129 if (!info[0]->IsObject()) {
130 return;
131 }
132
133 JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
134 JsWidth(sizeObj->GetProperty("width"));
135 JsHeight(sizeObj->GetProperty("height"));
136 }
137
SetStrokeDashArray(const JSCallbackInfo& info)138 void JSShape::SetStrokeDashArray(const JSCallbackInfo& info)
139 {
140 std::vector<Dimension> dashArray;
141 if (info.Length() < 1 || !info[0]->IsArray()) {
142 ShapeModel::GetInstance()->SetStrokeDashArray(dashArray);
143 return;
144 }
145 JSRef<JSArray> array = JSRef<JSArray>::Cast(info[0]);
146 int32_t length = static_cast<int32_t>(array->Length());
147 if (length <= 0) {
148 ShapeModel::GetInstance()->SetStrokeDashArray(dashArray);
149 return;
150 }
151 for (int32_t i = 0; i < length; i++) {
152 JSRef<JSVal> value = array->GetValueAt(i);
153 CalcDimension dim;
154 bool paramIsValid = false;
155 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
156 paramIsValid = ParseJsDimensionVp(value, dim);
157 } else {
158 paramIsValid = ParseJsDimensionVpNG(value, dim);
159 }
160 if (paramIsValid) {
161 dashArray.emplace_back(dim);
162 } else {
163 dashArray.clear();
164 break;
165 }
166 }
167 // if odd,add twice
168 if (static_cast<uint32_t>(length) == dashArray.size() && (static_cast<uint32_t>(length) & 1)) {
169 for (int32_t i = 0; i < length; i++) {
170 dashArray.emplace_back(dashArray[i]);
171 }
172 }
173 ShapeModel::GetInstance()->SetStrokeDashArray(dashArray);
174 info.SetReturnValue(info.This());
175 }
176
SetStroke(const JSCallbackInfo& info)177 void JSShape::SetStroke(const JSCallbackInfo& info)
178 {
179 if (info.Length() < 1) {
180 return;
181 }
182 Color strokeColor = Color::TRANSPARENT;
183 ParseJsColor(info[0], strokeColor);
184 ShapeModel::GetInstance()->SetStroke(strokeColor);
185 }
186
SetFill(const JSCallbackInfo& info)187 void JSShape::SetFill(const JSCallbackInfo& info)
188 {
189 if (info.Length() < 1) {
190 return;
191 }
192 if (info[0]->IsString() && info[0]->ToString() == "none") {
193 ShapeModel::GetInstance()->SetFill(Color::TRANSPARENT);
194 } else {
195 Color fillColor;
196 if (ParseJsColor(info[0], fillColor)) {
197 ShapeModel::GetInstance()->SetFill(fillColor);
198 } else {
199 ShapeModel::GetInstance()->SetFill(Color::BLACK);
200 }
201 }
202 }
203
SetStrokeDashOffset(const JSCallbackInfo& info)204 void JSShape::SetStrokeDashOffset(const JSCallbackInfo& info)
205 {
206 if (info.Length() < 1) {
207 return;
208 }
209 CalcDimension offset(0.0f);
210 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
211 if (!ParseJsDimensionVp(info[0], offset)) {
212 return;
213 }
214 } else {
215 if (!ParseJsDimensionVpNG(info[0], offset)) {
216 // set to default value(0.0f)
217 offset.SetValue(0.0f);
218 }
219 }
220 ShapeModel::GetInstance()->SetStrokeDashOffset(offset);
221 }
222
SetStrokeLineCap(int lineCap)223 void JSShape::SetStrokeLineCap(int lineCap)
224 {
225 ShapeModel::GetInstance()->SetStrokeLineCap(lineCap);
226 }
227
SetStrokeLineJoin(int lineJoin)228 void JSShape::SetStrokeLineJoin(int lineJoin)
229 {
230 ShapeModel::GetInstance()->SetStrokeLineJoin(lineJoin);
231 }
232
SetStrokeMiterLimit(const JSCallbackInfo& info)233 void JSShape::SetStrokeMiterLimit(const JSCallbackInfo& info)
234 {
235 if (info.Length() < 1) {
236 return;
237 }
238 double miterLimit = STROKE_MITERLIMIT_DEFAULT;
239 ParseJsDouble(info[0], miterLimit);
240 ShapeModel::GetInstance()->SetStrokeMiterLimit(miterLimit);
241 }
242
SetStrokeOpacity(const JSCallbackInfo& info)243 void JSShape::SetStrokeOpacity(const JSCallbackInfo& info)
244 {
245 if (info.Length() < 1) {
246 return;
247 }
248 double strokeOpacity = DEFAULT_OPACITY;
249 ParseJsDouble(info[0], strokeOpacity);
250 ShapeModel::GetInstance()->SetStrokeOpacity(strokeOpacity);
251 }
252
SetFillOpacity(const JSCallbackInfo& info)253 void JSShape::SetFillOpacity(const JSCallbackInfo& info)
254 {
255 if (info.Length() < 1) {
256 return;
257 }
258 double fillOpacity = DEFAULT_OPACITY;
259 ParseJsDouble(info[0], fillOpacity);
260 ShapeModel::GetInstance()->SetFillOpacity(fillOpacity);
261 }
262
SetStrokeWidth(const JSCallbackInfo& info)263 void JSShape::SetStrokeWidth(const JSCallbackInfo& info)
264 {
265 if (info.Length() < 1) {
266 return;
267 }
268 // the default value is 1.0_vp
269 CalcDimension lineWidth = 1.0_vp;
270 if (info[0]->IsString()) {
271 const std::string& value = info[0]->ToString();
272 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
273 lineWidth = StringUtils::StringToDimensionWithUnit(value, DimensionUnit::VP, 1.0);
274 } else {
275 if (!StringUtils::StringToDimensionWithUnitNG(value, lineWidth, DimensionUnit::VP, 1.0)) {
276 // unit is invalid, use default value(1.0vp) instead.
277 lineWidth = 1.0_vp;
278 }
279 }
280 } else {
281 ParseJsDimensionVp(info[0], lineWidth);
282 }
283 if (lineWidth.IsNegative()) {
284 lineWidth = 1.0_vp;
285 }
286 ShapeModel::GetInstance()->SetStrokeWidth(lineWidth);
287 }
288
SetAntiAlias(bool antiAlias)289 void JSShape::SetAntiAlias(bool antiAlias)
290 {
291 ShapeModel::GetInstance()->SetAntiAlias(antiAlias);
292 }
293
SetBitmapMesh(const JSCallbackInfo& info)294 void JSShape::SetBitmapMesh(const JSCallbackInfo& info)
295 {
296 if (info.Length() != 3) {
297 return;
298 }
299 std::vector<double> mesh;
300 JSRef<JSVal> meshValue = info[0];
301
302 if (meshValue->IsObject()) {
303 JSRef<JSObject> meshObj = JSRef<JSObject>::Cast(meshValue);
304 JSRef<JSArray> array = meshObj->GetPropertyNames();
305 for (size_t i = 0; i < array->Length(); i++) {
306 JSRef<JSVal> value = array->GetValueAt(i);
307 if (value->IsString()) {
308 std::string valueStr;
309 if (ParseJsString(value, valueStr)) {
310 double vert;
311 if (ParseJsDouble(meshObj->GetProperty(valueStr.c_str()), vert)) {
312 mesh.push_back(vert);
313 }
314 }
315 }
316 }
317 }
318 uint32_t column = 0;
319 uint32_t row = 0;
320 JSRef<JSVal> columnValue = info[1];
321 JSRef<JSVal> rowValue = info[2];
322 if (!ParseJsInteger(columnValue, column)) {
323 return;
324 }
325 if (!ParseJsInteger(rowValue, row)) {
326 return;
327 }
328 ShapeModel::GetInstance()->SetBitmapMesh(mesh, static_cast<int32_t>(column), static_cast<int32_t>(row));
329 }
330
SetForegroundColor(const JSCallbackInfo& info)331 void JSShape::SetForegroundColor(const JSCallbackInfo& info)
332 {
333 if (info.Length() < 1) {
334 return;
335 }
336 Color foregroundColor;
337 ForegroundColorStrategy strategy;
338 if (ParseJsColorStrategy(info[0], strategy)) {
339 ShapeModel::GetInstance()->SetFill(Color::FOREGROUND);
340 ViewAbstractModel::GetInstance()->SetForegroundColorStrategy(strategy);
341 return;
342 }
343 if (!ParseJsColor(info[0], foregroundColor)) {
344 return;
345 }
346 ShapeModel::GetInstance()->SetFill(foregroundColor);
347 ViewAbstractModel::GetInstance()->SetForegroundColor(foregroundColor);
348 }
349
JSBind(BindingTarget globalObj)350 void JSShape::JSBind(BindingTarget globalObj)
351 {
352 JSClass<JSShape>::Declare("Shape");
353 JSClass<JSShape>::StaticMethod("create", &JSShape::Create);
354 JSClass<JSShape>::StaticMethod("viewPort", &JSShape::SetViewPort);
355
356 JSClass<JSShape>::StaticMethod("width", &JSShape::JsWidth);
357 JSClass<JSShape>::StaticMethod("height", &JSShape::JsHeight);
358 JSClass<JSShape>::StaticMethod("size", &JSShape::JsSize);
359
360 JSClass<JSShape>::StaticMethod("stroke", &JSShape::SetStroke);
361 JSClass<JSShape>::StaticMethod("fill", &JSShape::SetFill);
362 JSClass<JSShape>::StaticMethod("strokeDashOffset", &JSShape::SetStrokeDashOffset);
363 JSClass<JSShape>::StaticMethod("strokeDashArray", &JSShape::SetStrokeDashArray);
364 JSClass<JSShape>::StaticMethod("strokeLineCap", &JSShape::SetStrokeLineCap);
365 JSClass<JSShape>::StaticMethod("strokeLineJoin", &JSShape::SetStrokeLineJoin);
366 JSClass<JSShape>::StaticMethod("strokeMiterLimit", &JSShape::SetStrokeMiterLimit);
367 JSClass<JSShape>::StaticMethod("strokeOpacity", &JSShape::SetStrokeOpacity);
368 JSClass<JSShape>::StaticMethod("fillOpacity", &JSShape::SetFillOpacity);
369 JSClass<JSShape>::StaticMethod("strokeWidth", &JSShape::SetStrokeWidth);
370 JSClass<JSShape>::StaticMethod("antiAlias", &JSShape::SetAntiAlias);
371 JSClass<JSShape>::StaticMethod("mesh", &JSShape::SetBitmapMesh);
372 JSClass<JSShape>::StaticMethod("foregroundColor", &JSShape::SetForegroundColor);
373
374 JSClass<JSShape>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
375 JSClass<JSShape>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
376 JSClass<JSShape>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
377 JSClass<JSShape>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
378 JSClass<JSShape>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
379 JSClass<JSShape>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
380 JSClass<JSShape>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
381 JSClass<JSShape>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
382 JSClass<JSShape>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
383 JSClass<JSShape>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
384 JSClass<JSShape>::InheritAndBind<JSContainerBase>(globalObj);
385 }
386
387 } // namespace OHOS::Ace::Framework
388