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_rect.h"
17 
18 #include "base/log/ace_trace.h"
19 #include "bridge/declarative_frontend/jsview/models/rect_model_impl.h"
20 #include "core/components_ng/base/view_stack_model.h"
21 #include "core/components_ng/pattern/shape/rect_model.h"
22 #include "core/components_ng/pattern/shape/rect_model_ng.h"
23 
24 namespace OHOS::Ace {
25 
26 std::unique_ptr<RectModel> RectModel::instance_ = nullptr;
27 std::mutex RectModel::mutex_;
28 
GetInstance()29 RectModel* RectModel::GetInstance()
30 {
31     if (!instance_) {
32         std::lock_guard<std::mutex> lock(mutex_);
33         if (!instance_) {
34 #ifdef NG_BUILD
35             instance_.reset(new NG::RectModelNG());
36 #else
37             if (Container::IsCurrentUseNewPipeline()) {
38                 instance_.reset(new NG::RectModelNG());
39             } else {
40                 instance_.reset(new Framework::RectModelImpl());
41             }
42 #endif
43         }
44     }
45     return instance_.get();
46 }
47 
48 } // namespace OHOS::Ace
49 
50 namespace OHOS::Ace::Framework {
51 namespace {
52 constexpr uint32_t HAS_RADIUS_WIDTH = 1;
53 constexpr uint32_t HAS_RADIUS_HEIGHT = 1 << 1;
54 constexpr uint32_t HAS_RADIUS = 1 << 2;
55 } // namespace
56 
Create(const JSCallbackInfo & info)57 void JSRect::Create(const JSCallbackInfo& info)
58 {
59     RectModel::GetInstance()->Create();
60     JSShapeAbstract::SetSize(info);
61     if (info.Length() > 0 && info[0]->IsObject()) {
62         JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
63         auto propertyNames = obj->GetPropertyNames();
64         if (!propertyNames->IsArray()) {
65             return;
66         }
67         auto propertyFlag = 0U;
68         for (size_t i = 0; i < propertyNames->Length(); i++) {
69             JSRef<JSVal> value = propertyNames->GetValueAt(i);
70             if (!value->IsString()) {
71                 continue;
72             }
73             auto propertyName = value->ToString();
74             if (propertyName == "radiusWidth") {
75                 propertyFlag = propertyFlag | HAS_RADIUS_WIDTH;
76             } else if (propertyName == "radiusHeight") {
77                 propertyFlag = propertyFlag | HAS_RADIUS_HEIGHT;
78             } else if (propertyName == "radius") {
79                 propertyFlag = propertyFlag | HAS_RADIUS;
80             }
81         }
82         if ((propertyFlag & HAS_RADIUS_WIDTH) == HAS_RADIUS_WIDTH) {
83             JSRef<JSVal> radiusWidth = obj->GetProperty("radiusWidth");
84             SetRadiusWidth(radiusWidth);
85         }
86         if ((propertyFlag & HAS_RADIUS_HEIGHT) == HAS_RADIUS_HEIGHT) {
87             JSRef<JSVal> radiusHeight = obj->GetProperty("radiusHeight");
88             SetRadiusHeight(radiusHeight);
89         }
90         if ((propertyFlag & HAS_RADIUS) == HAS_RADIUS) {
91             JSRef<JSVal> radius = obj->GetProperty("radius");
92             if (radius->IsNumber() || radius->IsString()) {
93                 SetRadiusWithJsVal(nullptr, radius);
94             } else if (radius->IsArray()) {
95                 SetRadiusWithArrayValue(nullptr, radius);
96             }
97         }
98         info.SetReturnValue(info.This());
99     }
100 }
101 
JsRadiusWidth(const JSCallbackInfo & info)102 void JSRect::JsRadiusWidth(const JSCallbackInfo& info)
103 {
104     if (info.Length() < 1) {
105         return;
106     }
107     SetRadiusWidth(info[0]);
108 }
109 
JsRadiusHeight(const JSCallbackInfo & info)110 void JSRect::JsRadiusHeight(const JSCallbackInfo& info)
111 {
112     if (info.Length() < 1) {
113         return;
114     }
115     SetRadiusHeight(info[0]);
116 }
117 
SetRadiusWidth(const JSRef<JSVal> & jsVal)118 void JSRect::SetRadiusWidth(const JSRef<JSVal>& jsVal)
119 {
120     CalcDimension value(0.0f);
121     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
122         ParseJsDimensionVp(jsVal, value);
123     } else {
124         if (!ParseJsDimensionVpNG(jsVal, value)) {
125             value.SetValue(0.0f);
126         }
127     }
128     RectModel::GetInstance()->SetRadiusWidth(value);
129 }
130 
SetRadiusHeight(const JSRef<JSVal> & jsVal)131 void JSRect::SetRadiusHeight(const JSRef<JSVal>& jsVal)
132 {
133     CalcDimension value(0.0f);
134     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
135         ParseJsDimensionVp(jsVal, value);
136     } else {
137         if (!ParseJsDimensionVpNG(jsVal, value)) {
138             value.SetValue(0.0f);
139         }
140     }
141     RectModel::GetInstance()->SetRadiusHeight(value);
142 }
143 
SetRadius(const JSCallbackInfo & info)144 void JSRect::SetRadius(const JSCallbackInfo& info)
145 {
146     if (info.Length() < 1) {
147         return;
148     }
149     CalcDimension value(0.0f);
150     RectModel::GetInstance()->SetRadiusWidth(value);
151     RectModel::GetInstance()->SetRadiusHeight(value);
152     if (info[0]->IsArray()) {
153         SetRadiusWithArrayValue(nullptr, info[0]);
154         info.SetReturnValue(info.This());
155         return;
156     }
157     if (info[0]->IsNumber() || info[0]->IsString() || info[0]->IsObject()) {
158         SetRadiusWithJsVal(nullptr, info[0]);
159         info.SetReturnValue(info.This());
160     }
161 }
162 
SetRadiusWithJsVal(const RefPtr<ShapeRect> & shapeRect,const JSRef<JSVal> & jsVal)163 void JSRect::SetRadiusWithJsVal(const RefPtr<ShapeRect>& shapeRect, const JSRef<JSVal>& jsVal)
164 {
165     CalcDimension value(0.0f);
166     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
167         ParseJsDimensionVp(jsVal, value);
168     } else {
169         if (!ParseJsDimensionVpNG(jsVal, value)) {
170             value.SetValue(0.0f);
171         }
172     }
173     if (shapeRect) {
174         AnimationOption option = ViewStackModel::GetInstance()->GetImplicitAnimationOption();
175         shapeRect->SetRadiusWidth(value, option);
176         shapeRect->SetRadiusHeight(value, option);
177         return;
178     }
179     RectModel::GetInstance()->SetRadiusWidth(value);
180     RectModel::GetInstance()->SetRadiusHeight(value);
181 }
182 
SetRadiusWithArrayValue(const RefPtr<ShapeRect> & shapeRect,const JSRef<JSVal> & jsVal)183 void JSRect::SetRadiusWithArrayValue(const RefPtr<ShapeRect>& shapeRect, const JSRef<JSVal>& jsVal)
184 {
185     if (!jsVal->IsArray()) {
186         return;
187     }
188     JSRef<JSArray> array = JSRef<JSArray>::Cast(jsVal);
189     auto length = static_cast<int32_t>(array->Length());
190     if (length <= 0) {
191         return;
192     }
193     length = std::min(length, 4);
194     for (int32_t i = 0; i < length; i++) {
195         JSRef<JSVal> radiusItem = array->GetValueAt(i);
196         if (!radiusItem->IsArray()) {
197             break;
198         }
199         JSRef<JSArray> radiusArray = JSRef<JSArray>::Cast(radiusItem);
200         if (radiusArray->Length() != 2) {
201             break;
202         }
203         JSRef<JSVal> radiusX = radiusArray->GetValueAt(0);
204         JSRef<JSVal> radiusY = radiusArray->GetValueAt(1);
205         CalcDimension radiusXValue(0.0f);
206         CalcDimension radiusYValue(0.0f);
207         if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
208             if (ParseJsDimensionVp(radiusX, radiusXValue)) {
209                 ParseJsDimensionVp(radiusY, radiusYValue);
210             }
211         } else {
212             if (!ParseJsDimensionVpNG(radiusX, radiusXValue)) {
213                 radiusXValue.SetValue(0.0f);
214             }
215             if (!ParseJsDimensionVpNG(radiusY, radiusYValue)) {
216                 radiusYValue.SetValue(0.0f);
217             }
218         }
219         SetRadiusValue(shapeRect, radiusXValue, radiusYValue, i);
220     }
221 }
222 
SetRadiusValue(const RefPtr<ShapeRect> & shapeRect,const CalcDimension & radiusX,const CalcDimension & radiusY,int32_t index)223 void JSRect::SetRadiusValue(
224     const RefPtr<ShapeRect>& shapeRect, const CalcDimension& radiusX, const CalcDimension& radiusY, int32_t index)
225 {
226     if (shapeRect) {
227         RectModel::GetInstance()->SetShapeRectRadius(shapeRect, radiusX, radiusY, index);
228     } else {
229         RectModel::GetInstance()->SetRadiusValue(radiusX, radiusY, index);
230     }
231 }
232 
ObjectRadiusWidth(const JSCallbackInfo & info)233 void JSRect::ObjectRadiusWidth(const JSCallbackInfo& info)
234 {
235     info.ReturnSelf();
236     if (info.Length() < 1) {
237         return;
238     }
239     CalcDimension value;
240     if (!ParseJsDimensionVp(info[0], value)) {
241         return;
242     }
243     if (LessNotEqual(value.Value(), 0.0)) {
244         return;
245     }
246     auto rect = AceType::DynamicCast<ShapeRect>(basicShape_);
247     if (rect) {
248         rect->SetRadiusWidth(value);
249     }
250 }
251 
ObjectRadiusHeight(const JSCallbackInfo & info)252 void JSRect::ObjectRadiusHeight(const JSCallbackInfo& info)
253 {
254     info.ReturnSelf();
255     if (info.Length() < 1) {
256         return;
257     }
258     CalcDimension value;
259     if (!ParseJsDimensionVp(info[0], value)) {
260         return;
261     }
262     if (LessNotEqual(value.Value(), 0.0)) {
263         return;
264     }
265     auto rect = AceType::DynamicCast<ShapeRect>(basicShape_);
266     if (rect) {
267         rect->SetRadiusHeight(value);
268     }
269 }
270 
ObjectRadius(const JSCallbackInfo & info)271 void JSRect::ObjectRadius(const JSCallbackInfo& info)
272 {
273     info.ReturnSelf();
274     if (info.Length() < 1) {
275         return;
276     }
277     auto rect = AceType::DynamicCast<ShapeRect>(basicShape_);
278     if (!rect) {
279         return;
280     }
281     if (info[0]->IsNumber() || info[0]->IsString()) {
282         SetRadiusWithJsVal(rect, info[0]);
283     }
284     if (info[0]->IsArray()) {
285         SetRadiusWithArrayValue(rect, info[0]);
286     }
287 }
288 
ParseRectObjBelowApi12(const RefPtr<ShapeRect> & rect,const JSRef<JSObject> & obj)289 void JSRect::ParseRectObjBelowApi12(const RefPtr<ShapeRect>& rect, const JSRef<JSObject>& obj)
290 {
291     CalcDimension width;
292     CalcDimension height;
293     CalcDimension radiusWidth;
294     CalcDimension radiusHeight;
295     if (ParseJsDimensionVp(obj->GetProperty("width"), width) && width.IsValid()) {
296         rect->SetWidth(width);
297     }
298     if (ParseJsDimensionVp(obj->GetProperty("height"), height) && height.IsValid()) {
299         rect->SetHeight(height);
300     }
301     if (ParseJsDimensionVp(obj->GetProperty("radiusWidth"), radiusWidth) && radiusWidth.IsValid()) {
302         rect->SetRadiusWidth(radiusWidth);
303     }
304     if (ParseJsDimensionVp(obj->GetProperty("radiusHeight"), radiusHeight) && radiusHeight.IsValid()) {
305         rect->SetRadiusHeight(radiusHeight);
306     }
307 }
308 
ParseRectObjAboveApi12(const RefPtr<ShapeRect> & rect,const JSRef<JSObject> & obj)309 void JSRect::ParseRectObjAboveApi12(const RefPtr<ShapeRect>& rect, const JSRef<JSObject>& obj)
310 {
311     CalcDimension width;
312     CalcDimension height;
313     CalcDimension radiusWidth;
314     CalcDimension radiusHeight;
315     if (ParseJsDimensionVpNG(obj->GetProperty("width"), width) && width.IsValid()) {
316         rect->SetWidth(width);
317     }
318     if (ParseJsDimensionVpNG(obj->GetProperty("height"), height) && height.IsValid()) {
319         rect->SetHeight(height);
320     }
321     if (ParseJsDimensionVpNG(obj->GetProperty("radiusWidth"), radiusWidth) && radiusWidth.IsValid()) {
322         rect->SetRadiusWidth(radiusWidth);
323     }
324     if (ParseJsDimensionVpNG(obj->GetProperty("radiusHeight"), radiusHeight) && radiusHeight.IsValid()) {
325         rect->SetRadiusHeight(radiusHeight);
326     }
327 }
328 
ConstructorCallback(const JSCallbackInfo & info)329 void JSRect::ConstructorCallback(const JSCallbackInfo& info)
330 {
331     auto jsRect = AceType::MakeRefPtr<JSRect>();
332     auto rect = AceType::MakeRefPtr<ShapeRect>();
333     if (info.Length() > 0 && info[0]->IsObject()) {
334         JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
335         if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
336             ParseRectObjBelowApi12(rect, obj);
337         } else {
338             ParseRectObjAboveApi12(rect, obj);
339         }
340         JSRef<JSVal> radius = obj->GetProperty("radius");
341         if (radius->IsNumber() || radius->IsString()) {
342             SetRadiusWithJsVal(rect, radius);
343         } else if (radius->IsArray()) {
344             SetRadiusWithArrayValue(rect, radius);
345         }
346         info.SetReturnValue(info.This());
347     }
348     jsRect->SetBasicShape(rect);
349     jsRect->IncRefCount();
350     info.SetReturnValue(AceType::RawPtr(jsRect));
351 }
352 
DestructorCallback(JSRect * jsRect)353 void JSRect::DestructorCallback(JSRect* jsRect)
354 {
355     if (jsRect != nullptr) {
356         jsRect->DecRefCount();
357     }
358 }
359 
JSBind(BindingTarget globalObj)360 void JSRect::JSBind(BindingTarget globalObj)
361 {
362     JSClass<JSRect>::Declare("Rect");
363     JSClass<JSRect>::StaticMethod("create", &JSRect::Create);
364     JSClass<JSRect>::StaticMethod("radiusWidth", &JSRect::JsRadiusWidth);
365     JSClass<JSRect>::StaticMethod("radiusHeight", &JSRect::JsRadiusHeight);
366     JSClass<JSRect>::StaticMethod("radius", &JSRect::SetRadius);
367 
368     JSClass<JSRect>::CustomMethod("width", &JSShapeAbstract::ObjectWidth);
369     JSClass<JSRect>::CustomMethod("height", &JSShapeAbstract::ObjectHeight);
370     JSClass<JSRect>::CustomMethod("size", &JSShapeAbstract::ObjectSize);
371     JSClass<JSRect>::CustomMethod("offset", &JSShapeAbstract::ObjectOffset);
372     JSClass<JSRect>::CustomMethod("radiusWidth", &JSRect::ObjectRadiusWidth);
373     JSClass<JSRect>::CustomMethod("radiusHeight", &JSRect::ObjectRadiusHeight);
374     JSClass<JSRect>::CustomMethod("radius", &JSRect::ObjectRadius);
375     JSClass<JSRect>::CustomMethod("fill", &JSShapeAbstract::ObjectFill);
376     JSClass<JSRect>::CustomMethod("position", &JSShapeAbstract::ObjectPosition);
377 
378     JSClass<JSRect>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
379     JSClass<JSRect>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
380     JSClass<JSRect>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
381     JSClass<JSRect>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
382     JSClass<JSRect>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
383 
384     JSClass<JSRect>::InheritAndBind<JSShapeAbstract>(
385         globalObj, JSRect::ConstructorCallback, JSRect::DestructorCallback);
386 }
387 
388 } // namespace OHOS::Ace::Framework
389