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