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