1 /*
2 * Copyright (c) 2021 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_abstract.h"
17
18 #include "base/utils/utils.h"
19 #include "bridge/declarative_frontend/jsview/models/shape_abstract_model_impl.h"
20 #include "core/common/container.h"
21 #include "core/components_ng/pattern/shape/shape_abstract_model.h"
22 #include "core/components_ng/pattern/shape/shape_abstract_model_ng.h"
23 #include "frameworks/bridge/declarative_frontend/view_stack_processor.h"
24
25 namespace OHOS::Ace {
26
27 std::unique_ptr<ShapeAbstractModel> ShapeAbstractModel::instance_ = nullptr;
28 std::mutex ShapeAbstractModel::mutex_;
29
GetInstance()30 ShapeAbstractModel* ShapeAbstractModel::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::ShapeAbstractModelNG());
37 #else
38 if (Container::IsCurrentUseNewPipeline()) {
39 instance_.reset(new NG::ShapeAbstractModelNG());
40 } else {
41 instance_.reset(new Framework::ShapeAbstractModelImpl());
42 }
43 #endif
44 }
45 }
46 return instance_.get();
47 }
48
49 } // namespace OHOS::Ace
50
51 namespace OHOS::Ace::Framework {
52 namespace {
53 constexpr double DEFAULT_OPACITY = 1.0;
54 constexpr double MIN_OPACITY = 0.0;
55 constexpr double STROKE_MITERLIMIT_DEFAULT = 4.0f;
56 } // namespace
57
SetStrokeDashArray(const JSCallbackInfo & info)58 void JSShapeAbstract::SetStrokeDashArray(const JSCallbackInfo& info)
59 {
60 if (info.Length() < 1 || !info[0]->IsArray()) {
61 LOGE("info is not array");
62 return;
63 }
64 JSRef<JSArray> array = JSRef<JSArray>::Cast(info[0]);
65 auto length = static_cast<int32_t>(array->Length());
66 std::vector<Dimension> dashArray;
67 for (int32_t i = 0; i < length; i++) {
68 JSRef<JSVal> value = array->GetValueAt(i);
69 CalcDimension dim;
70 bool paramIsValid = false;
71 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
72 paramIsValid = ParseJsDimensionVp(value, dim);
73 } else {
74 paramIsValid = ParseJsDimensionVpNG(value, dim);
75 }
76 if (paramIsValid) {
77 dashArray.emplace_back(dim);
78 } else {
79 LOGE("ParseJsDimension failed");
80 dashArray.clear();
81 break;
82 }
83 }
84 // if odd,add twice
85 if (static_cast<uint32_t>(length) == dashArray.size() && (static_cast<uint32_t>(length) & 1)) {
86 for (int32_t i = 0; i < length; i++) {
87 dashArray.emplace_back(dashArray[i]);
88 }
89 }
90 ShapeAbstractModel::GetInstance()->SetStrokeDashArray(dashArray);
91 }
92
SetStroke(const JSCallbackInfo & info)93 void JSShapeAbstract::SetStroke(const JSCallbackInfo& info)
94 {
95 if (info.Length() < 1) {
96 LOGE("The argv is wrong, it is supposed to have at least 1 argument");
97 return;
98 }
99 Color strokeColor;
100 if (!ParseJsColor(info[0], strokeColor)) {
101 ShapeAbstractModel::GetInstance()->SetStroke(Color::TRANSPARENT);
102 return;
103 }
104 ShapeAbstractModel::GetInstance()->SetStroke(strokeColor);
105 }
106
SetFill(const JSCallbackInfo & info)107 void JSShapeAbstract::SetFill(const JSCallbackInfo& info)
108 {
109 if (info.Length() < 1) {
110 LOGE("The arg is wrong, it is supposed to have atleast 1 arguments");
111 return;
112 }
113 if (info[0]->IsString() && info[0]->ToString() == "none") {
114 ShapeAbstractModel::GetInstance()->SetFill(Color::TRANSPARENT);
115 } else {
116 Color fillColor = Color::BLACK;
117 static const char shapeComponentName[] = "";
118 static const char attrsShapeAbstractFill[] = "fill";
119 CheckColor(info[0], fillColor, shapeComponentName, attrsShapeAbstractFill);
120 ShapeAbstractModel::GetInstance()->SetFill(fillColor);
121 }
122 }
123
SetStrokeDashOffset(const JSCallbackInfo & info)124 void JSShapeAbstract::SetStrokeDashOffset(const JSCallbackInfo& info)
125 {
126 if (info.Length() < 1) {
127 LOGE("The arg is wrong, it is supposed to have at least 1 argument");
128 return;
129 }
130 CalcDimension offset(0.0f);
131 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
132 if (!ParseJsDimensionVp(info[0], offset)) {
133 return;
134 }
135 } else {
136 if (!ParseJsDimensionVpNG(info[0], offset)) {
137 // set to default value(0.0f)
138 offset.SetValue(0.0f);
139 }
140 }
141 ShapeAbstractModel::GetInstance()->SetStrokeDashOffset(offset);
142 }
143
SetStrokeLineCap(int lineCap)144 void JSShapeAbstract::SetStrokeLineCap(int lineCap)
145 {
146 ShapeAbstractModel::GetInstance()->SetStrokeLineCap(lineCap);
147 }
148
SetStrokeLineJoin(int lineJoin)149 void JSShapeAbstract::SetStrokeLineJoin(int lineJoin)
150 {
151 ShapeAbstractModel::GetInstance()->SetStrokeLineJoin(lineJoin);
152 }
153
SetStrokeMiterLimit(const JSCallbackInfo & info)154 void JSShapeAbstract::SetStrokeMiterLimit(const JSCallbackInfo& info)
155 {
156 if (info.Length() < 1) {
157 LOGE("The arg is wrong, it is supposed to have at least 1 argument");
158 return;
159 }
160 double miterLimit = STROKE_MITERLIMIT_DEFAULT;
161 if (!ParseJsDouble(info[0], miterLimit)) {
162 LOGI("strokeMiterLimit error. now use default value");
163 }
164 ShapeAbstractModel::GetInstance()->SetStrokeMiterLimit(miterLimit);
165 }
166
SetStrokeOpacity(const JSCallbackInfo & info)167 void JSShapeAbstract::SetStrokeOpacity(const JSCallbackInfo& info)
168 {
169 if (info.Length() < 1) {
170 LOGE("The arg is wrong, it is supposed to have at least 1 argument");
171 return;
172 }
173 double strokeOpacity = DEFAULT_OPACITY;
174 ParseJsDouble(info[0], strokeOpacity);
175 if (GreatOrEqual(strokeOpacity, 1.0)) {
176 strokeOpacity = DEFAULT_OPACITY;
177 }
178 if (LessOrEqual(strokeOpacity, 0.0)) {
179 strokeOpacity = MIN_OPACITY;
180 }
181 ShapeAbstractModel::GetInstance()->SetStrokeOpacity(strokeOpacity);
182 }
183
184 // https://svgwg.org/svg2-draft/painting.html#FillOpacity
SetFillOpacity(const JSCallbackInfo & info)185 void JSShapeAbstract::SetFillOpacity(const JSCallbackInfo& info)
186 {
187 if (info.Length() < 1) {
188 LOGE("The arg is wrong, it is supposed to have at least 1 argument");
189 return;
190 }
191 double fillOpacity = DEFAULT_OPACITY;
192 ParseJsDouble(info[0], fillOpacity);
193 if (GreatOrEqual(fillOpacity, DEFAULT_OPACITY)) {
194 fillOpacity = DEFAULT_OPACITY;
195 }
196 if (LessOrEqual(fillOpacity, MIN_OPACITY)) {
197 fillOpacity = MIN_OPACITY;
198 }
199 ShapeAbstractModel::GetInstance()->SetFillOpacity(fillOpacity);
200 }
201
SetStrokeWidth(const JSCallbackInfo & info)202 void JSShapeAbstract::SetStrokeWidth(const JSCallbackInfo& info)
203 {
204 if (info.Length() < 1) {
205 LOGE("The arg is wrong, it is supposed to have at least 1 argument");
206 return;
207 }
208 // the default value is 1.0_vp
209 CalcDimension lineWidth = 1.0_vp;
210 if (info[0]->IsString()) {
211 const std::string& value = info[0]->ToString();
212 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
213 lineWidth = StringUtils::StringToDimensionWithUnit(value, DimensionUnit::VP, 1.0);
214 } else {
215 if (!StringUtils::StringToDimensionWithUnitNG(value, lineWidth, DimensionUnit::VP, 1.0)) {
216 // unit is invalid, use default value(1.0vp) instead.
217 lineWidth = 1.0_vp;
218 }
219 }
220 } else {
221 ParseJsDimensionVp(info[0], lineWidth);
222 }
223 if (lineWidth.IsNegative()) {
224 lineWidth = 1.0_vp;
225 }
226 ShapeAbstractModel::GetInstance()->SetStrokeWidth(lineWidth);
227 }
228
SetAntiAlias(bool antiAlias)229 void JSShapeAbstract::SetAntiAlias(bool antiAlias)
230 {
231 ShapeAbstractModel::GetInstance()->SetAntiAlias(antiAlias);
232 }
233
JsWidth(const JSCallbackInfo & info)234 void JSShapeAbstract::JsWidth(const JSCallbackInfo& info)
235 {
236 if (info.Length() < 1) {
237 LOGE("The arg is wrong, it is supposed to have at least 1 argument");
238 return;
239 }
240
241 SetWidth(info[0]);
242 }
243
SetWidth(const JSRef<JSVal> & jsValue)244 void JSShapeAbstract::SetWidth(const JSRef<JSVal>& jsValue)
245 {
246 CalcDimension value;
247 if (jsValue->IsUndefined()) {
248 ViewAbstractModel::GetInstance()->ClearWidthOrHeight(true);
249 LOGW("jsValue is undefined");
250 return;
251 }
252 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
253 if (!ParseJsDimensionVp(jsValue, value)) {
254 LOGW("ParseJsDimensionVp failed");
255 return;
256 }
257 } else {
258 if (!ParseJsDimensionVpNG(jsValue, value)) {
259 ViewAbstractModel::GetInstance()->ClearWidthOrHeight(true);
260 LOGW("ParseJsDimensionVp failed");
261 return;
262 }
263 }
264
265 if (LessNotEqual(value.Value(), 0.0)) {
266 LOGW("value is less than zero");
267 value.SetValue(0.0);
268 }
269 ShapeAbstractModel::GetInstance()->SetWidth(value);
270 }
271
JsHeight(const JSCallbackInfo & info)272 void JSShapeAbstract::JsHeight(const JSCallbackInfo& info)
273 {
274 if (info.Length() < 1) {
275 LOGE("The arg is wrong, it is supposed to have at least 1 argument");
276 return;
277 }
278
279 SetHeight(info[0]);
280 }
281
SetHeight(const JSRef<JSVal> & jsValue)282 void JSShapeAbstract::SetHeight(const JSRef<JSVal>& jsValue)
283 {
284 CalcDimension value;
285 if (jsValue->IsUndefined()) {
286 ViewAbstractModel::GetInstance()->ClearWidthOrHeight(false);
287 LOGW("jsValue is undefined");
288 return;
289 }
290 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
291 if (!ParseJsDimensionVp(jsValue, value)) {
292 LOGW("ParseJsDimensionVp failed");
293 return;
294 }
295 } else {
296 if (!ParseJsDimensionVpNG(jsValue, value)) {
297 ViewAbstractModel::GetInstance()->ClearWidthOrHeight(false);
298 LOGW("ParseJsDimensionVp failed");
299 return;
300 }
301 }
302
303 if (LessNotEqual(value.Value(), 0.0)) {
304 LOGW("value is less than zero");
305 value.SetValue(0.0);
306 }
307 ShapeAbstractModel::GetInstance()->SetHeight(value);
308 }
309
JsSize(const JSCallbackInfo & info)310 void JSShapeAbstract::JsSize(const JSCallbackInfo& info)
311 {
312 if (info.Length() < 1) {
313 LOGE("The arg is wrong, it is supposed to have atleast 1 arguments");
314 return;
315 }
316
317 if (!info[0]->IsObject()) {
318 LOGE("arg is not Object or String.");
319 return;
320 }
321
322 JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
323 SetWidth(sizeObj->GetProperty("width"));
324 SetHeight(sizeObj->GetProperty("height"));
325 }
326
ObjectWidth(const JSCallbackInfo & info)327 void JSShapeAbstract::ObjectWidth(const JSCallbackInfo& info)
328 {
329 info.ReturnSelf();
330 if (info.Length() < 1) {
331 LOGE("The arg is wrong, it is supposed to have at least 1 argument");
332 return;
333 }
334
335 ObjectWidth(info[0]);
336 }
337
ObjectWidth(const JSRef<JSVal> & jsValue)338 void JSShapeAbstract::ObjectWidth(const JSRef<JSVal>& jsValue)
339 {
340 CalcDimension value;
341 if (!ParseJsDimensionVp(jsValue, value)) {
342 return;
343 }
344 if (LessNotEqual(value.Value(), 0.0)) {
345 LOGE("Value is less than zero");
346 return;
347 }
348 if (basicShape_) {
349 basicShape_->SetWidth(value);
350 }
351 }
352
ObjectHeight(const JSCallbackInfo & info)353 void JSShapeAbstract::ObjectHeight(const JSCallbackInfo& info)
354 {
355 info.ReturnSelf();
356 if (info.Length() < 1) {
357 LOGE("The arg is wrong, it is supposed to have at least 1 argument");
358 return;
359 }
360
361 ObjectHeight(info[0]);
362 }
363
ObjectHeight(const JSRef<JSVal> & jsValue)364 void JSShapeAbstract::ObjectHeight(const JSRef<JSVal>& jsValue)
365 {
366 CalcDimension value;
367 if (!ParseJsDimensionVp(jsValue, value)) {
368 return;
369 }
370 if (LessNotEqual(value.Value(), 0.0)) {
371 LOGE("Value is less than zero");
372 return;
373 }
374 if (basicShape_) {
375 basicShape_->SetHeight(value);
376 }
377 }
378
ObjectSize(const JSCallbackInfo & info)379 void JSShapeAbstract::ObjectSize(const JSCallbackInfo& info)
380 {
381 if (info.Length() < 1) {
382 LOGE("The arg is wrong, it is supposed to have atleast 1 arguments");
383 return;
384 }
385
386 if (!info[0]->IsObject()) {
387 LOGE("arg is not Object or String.");
388 return;
389 }
390
391 JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
392 ObjectWidth(sizeObj->GetProperty("width"));
393 ObjectHeight(sizeObj->GetProperty("height"));
394 }
395
ObjectOffset(const JSCallbackInfo & info)396 void JSShapeAbstract::ObjectOffset(const JSCallbackInfo& info)
397 {
398 info.ReturnSelf();
399 if (info.Length() > 0 && info[0]->IsObject()) {
400 JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
401 JSRef<JSVal> xVal = sizeObj->GetProperty("x");
402 JSRef<JSVal> yVal = sizeObj->GetProperty("y");
403 CalcDimension x;
404 CalcDimension y;
405 if (basicShape_ && ParseJsDimensionVp(xVal, x) && ParseJsDimensionVp(yVal, y)) {
406 basicShape_->SetOffset(DimensionOffset(x, y));
407 }
408 }
409 }
410
ObjectFill(const JSCallbackInfo & info)411 void JSShapeAbstract::ObjectFill(const JSCallbackInfo& info)
412 {
413 info.ReturnSelf();
414 if (info.Length() < 1) {
415 LOGE("The arg is wrong, it is supposed to have at least 1 arguments");
416 return;
417 }
418
419 Color color;
420 if (ParseJsColor(info[0], color) && basicShape_) {
421 basicShape_->SetColor(color);
422 }
423 }
424
JSBind(BindingTarget globalObj)425 void JSShapeAbstract::JSBind(BindingTarget globalObj)
426 {
427 JSClass<JSShapeAbstract>::Declare("JSShapeAbstract");
428 MethodOptions opt = MethodOptions::NONE;
429 JSClass<JSShapeAbstract>::StaticMethod("stroke", &JSShapeAbstract::SetStroke, opt);
430 JSClass<JSShapeAbstract>::StaticMethod("fill", &JSShapeAbstract::SetFill, opt);
431 JSClass<JSShapeAbstract>::StaticMethod("foregroundColor", &JSShapeAbstract::SetForegroundColor, opt);
432 JSClass<JSShapeAbstract>::StaticMethod("strokeDashOffset", &JSShapeAbstract::SetStrokeDashOffset, opt);
433 JSClass<JSShapeAbstract>::StaticMethod("strokeDashArray", &JSShapeAbstract::SetStrokeDashArray);
434 JSClass<JSShapeAbstract>::StaticMethod("strokeLineCap", &JSShapeAbstract::SetStrokeLineCap, opt);
435 JSClass<JSShapeAbstract>::StaticMethod("strokeLineJoin", &JSShapeAbstract::SetStrokeLineJoin, opt);
436 JSClass<JSShapeAbstract>::StaticMethod("strokeMiterLimit", &JSShapeAbstract::SetStrokeMiterLimit, opt);
437 JSClass<JSShapeAbstract>::StaticMethod("strokeOpacity", &JSShapeAbstract::SetStrokeOpacity, opt);
438 JSClass<JSShapeAbstract>::StaticMethod("fillOpacity", &JSShapeAbstract::SetFillOpacity, opt);
439 JSClass<JSShapeAbstract>::StaticMethod("strokeWidth", &JSShapeAbstract::SetStrokeWidth, opt);
440 JSClass<JSShapeAbstract>::StaticMethod("antiAlias", &JSShapeAbstract::SetAntiAlias, opt);
441 JSClass<JSShapeAbstract>::StaticMethod("width", &JSShapeAbstract::JsWidth, opt);
442 JSClass<JSShapeAbstract>::StaticMethod("height", &JSShapeAbstract::JsHeight, opt);
443 JSClass<JSShapeAbstract>::StaticMethod("size", &JSShapeAbstract::JsSize, opt);
444 JSClass<JSShapeAbstract>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
445 JSClass<JSShapeAbstract>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
446 JSClass<JSShapeAbstract>::InheritAndBind<JSViewAbstract>(globalObj);
447 }
448
SetSize(const JSCallbackInfo & info)449 void JSShapeAbstract::SetSize(const JSCallbackInfo& info)
450 {
451 if (info.Length() > 0 && info[0]->IsObject()) {
452 JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
453 JSRef<JSVal> width = obj->GetProperty("width");
454 JSRef<JSVal> height = obj->GetProperty("height");
455 SetWidth(width);
456 SetHeight(height);
457 }
458 }
459
ObjectPosition(const JSCallbackInfo & info)460 void JSShapeAbstract::ObjectPosition(const JSCallbackInfo& info)
461 {
462 info.ReturnSelf();
463 if (info.Length() > 0 && info[0]->IsObject()) {
464 JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
465 JSRef<JSVal> xVal = sizeObj->GetProperty("x");
466 JSRef<JSVal> yVal = sizeObj->GetProperty("y");
467 CalcDimension x;
468 CalcDimension y;
469 DimensionOffset position(x, y);
470 CHECK_NULL_VOID(basicShape_);
471 if (ParseJsDimensionVp(xVal, x)) {
472 position.SetX(x);
473 }
474 if (ParseJsDimensionVp(yVal, y)) {
475 position.SetY(y);
476 }
477 basicShape_->SetPosition(position);
478 }
479 }
480
SetForegroundColor(const JSCallbackInfo & info)481 void JSShapeAbstract::SetForegroundColor(const JSCallbackInfo& info)
482 {
483 if (info.Length() < 1) {
484 LOGE("The argv is wrong, it is supposed to have at least 1 argument");
485 return;
486 }
487 Color foregroundColor;
488 ForegroundColorStrategy strategy;
489 if (ParseJsColorStrategy(info[0], strategy)) {
490 ShapeAbstractModel::GetInstance()->SetFill(Color::FOREGROUND);
491 ViewAbstractModel::GetInstance()->SetForegroundColorStrategy(strategy);
492 return;
493 }
494 if (!ParseJsColor(info[0], foregroundColor)) {
495 ShapeAbstractModel::GetInstance()->SetFill(Color::BLACK);
496 ViewAbstractModel::GetInstance()->SetForegroundColor(Color::BLACK);
497 return;
498 }
499 ShapeAbstractModel::GetInstance()->SetFill(foregroundColor);
500 ViewAbstractModel::GetInstance()->SetForegroundColor(foregroundColor);
501 }
502 } // namespace OHOS::Ace::Framework
503