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