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_flex_impl.h"
17 
18 #include "core/components_ng/pattern/flex/flex_model.h"
19 #include "frameworks/bridge/declarative_frontend/engine/js_ref_ptr.h"
20 #include "frameworks/bridge/declarative_frontend/jsview/js_view_common_def.h"
21 #include "frameworks/bridge/declarative_frontend/jsview/models/flex_model_impl.h"
22 #include "frameworks/bridge/declarative_frontend/view_stack_processor.h"
23 #include "frameworks/core/components_ng/pattern/flex/flex_model_ng.h"
24 
25 namespace OHOS::Ace::Framework {
26 
27 constexpr int32_t REVERSE_OFFSET_ZOOM = 2;
Create(const JSCallbackInfo & info)28 void JSFlexImpl::Create(const JSCallbackInfo& info)
29 {
30     if (info.Length() < 1) {
31         FlexModel::GetInstance()->CreateFlexRow();
32         return;
33     }
34     if (!info[0]->IsObject()) {
35         FlexModel::GetInstance()->CreateFlexRow();
36         return;
37     }
38     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
39     JSRef<JSVal> wrapVal = obj->GetProperty("wrap");
40     if (wrapVal->IsNumber()) {
41         auto wrapNum = wrapVal->ToNumber<int32_t>();
42         if (wrapNum == 0) {
43             CreateFlexComponent(info);
44         } else {
45             CreateWrapComponent(info, wrapNum);
46         }
47     } else {
48         CreateFlexComponent(info);
49     }
50 }
51 
CreateFlexComponent(const JSCallbackInfo & info)52 void JSFlexImpl::CreateFlexComponent(const JSCallbackInfo& info)
53 {
54     if (info.Length() < 1) {
55         FlexModel::GetInstance()->CreateFlexRow();
56         return;
57     }
58     if (!info[0]->IsObject()) {
59         FlexModel::GetInstance()->CreateFlexRow();
60         return;
61     }
62     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
63     JSRef<JSVal> directionVal = obj->GetProperty("direction");
64     JSRef<JSVal> justifyVal = obj->GetProperty("justifyContent");
65     JSRef<JSVal> alignItemVal = obj->GetProperty("alignItems");
66     FlexModel::GetInstance()->CreateFlexRow();
67     if (directionVal->IsNumber()) {
68         auto direction = directionVal->ToNumber<int32_t>();
69         if (direction >= 0 && direction <= DIRECTION_MAX_VALUE) {
70             FlexModel::GetInstance()->SetDirection(static_cast<FlexDirection>(direction));
71         }
72     }
73     if (justifyVal->IsNumber()) {
74         auto mainAlign = justifyVal->ToNumber<int32_t>();
75         if (mainAlign >= 0 && mainAlign <= MAIN_ALIGN_MAX_VALUE) {
76             FlexModel::GetInstance()->SetMainAxisAlign(static_cast<FlexAlign>(mainAlign));
77         }
78     } else if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
79         FlexModel::GetInstance()->SetMainAxisAlign(FlexAlign::FLEX_START);
80     }
81     if (alignItemVal->IsNumber()) {
82         auto crossAlign = alignItemVal->ToNumber<int32_t>();
83         if (crossAlign >= 0 && crossAlign <= CROSS_ALIGN_MAX_VALUE) {
84             FlexModel::GetInstance()->SetCrossAxisAlign(static_cast<FlexAlign>(crossAlign));
85         }
86     } else if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
87         FlexModel::GetInstance()->SetCrossAxisAlign(FlexAlign::FLEX_START);
88     }
89     if (obj->HasProperty("space")) {
90         JSRef<JSVal> spaceVal = obj->GetProperty("space");
91         if (spaceVal->IsUndefined()) {
92             return;
93         }
94         CalcDimension value;
95         JSRef<JSObject> spaceObj = JSRef<JSObject>::Cast(obj->GetProperty("space"));
96         JSRef<JSVal> mainSpaceVal = spaceObj->GetProperty("main");
97         if (!ParseLengthMetricsToPositiveDimension(mainSpaceVal, value) || value.IsNegative()) {
98             value.Reset();
99         }
100         FlexModel::GetInstance()->SetMainSpace(value);
101     }
102 }
103 
CreateWrapComponent(const JSCallbackInfo & info,int32_t wrapVal)104 void JSFlexImpl::CreateWrapComponent(const JSCallbackInfo& info, int32_t wrapVal)
105 {
106     if (info.Length() < 1 || !info[0]->IsObject()) {
107         return;
108     }
109     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
110     WrapComponent(obj, wrapVal);
111     if (obj->HasProperty("space")) {
112         JSRef<JSVal> spaceVal = obj->GetProperty("space");
113         if (spaceVal->IsUndefined()) {
114             return;
115         }
116         CalcDimension mainValue;
117         CalcDimension crossValue;
118         JSRef<JSObject> spaceObj = JSRef<JSObject>::Cast(obj->GetProperty("space"));
119         JSRef<JSVal> mainSpaceVal = spaceObj->GetProperty("main");
120         JSRef<JSVal> crossSpaceVal = spaceObj->GetProperty("cross");
121         if (!ParseLengthMetricsToPositiveDimension(mainSpaceVal, mainValue) || mainValue.IsNegative()) {
122             mainValue.Reset();
123         }
124         if (!ParseLengthMetricsToPositiveDimension(crossSpaceVal, crossValue) || crossValue.IsNegative()) {
125             crossValue.Reset();
126         }
127         FlexModel::GetInstance()->SetMainSpace(mainValue);
128         FlexModel::GetInstance()->SetCrossSpace(crossValue);
129     }
130 }
131 
WrapComponent(const JSRef<JSObject> & obj,int32_t wrapVal)132 void JSFlexImpl::WrapComponent(const JSRef<JSObject>& obj, int32_t wrapVal)
133 {
134     JSRef<JSVal> directionVal = obj->GetProperty("direction");
135     JSRef<JSVal> justifyVal = obj->GetProperty("justifyContent");
136     JSRef<JSVal> alignItemVal = obj->GetProperty("alignItems");
137     JSRef<JSVal> alignContentVal = obj->GetProperty("alignContent");
138     FlexModel::GetInstance()->CreateWrap();
139     if (directionVal->IsNumber()) {
140         auto direction = directionVal->ToNumber<int32_t>();
141         if (direction >= 0 && direction <= DIRECTION_MAX_VALUE) {
142             FlexModel::GetInstance()->SetDirection(static_cast<FlexDirection>(direction));
143             // WrapReverse means wrapVal = 2. Wrap means wrapVal = 1.
144             if (direction <= 1) {
145                 direction += REVERSE_OFFSET_ZOOM * (wrapVal - 1);
146             } else {
147                 direction -= REVERSE_OFFSET_ZOOM * (wrapVal - 1);
148             }
149             FlexModel::GetInstance()->SetWrapDirection(static_cast<WrapDirection>(direction));
150         }
151     } else {
152         // No direction set case: wrapVal == 2 means FlexWrap.WrapReverse.
153         WrapDirection wrapDirection = wrapVal == 2 ? WrapDirection::HORIZONTAL_REVERSE : WrapDirection::HORIZONTAL;
154         FlexModel::GetInstance()->SetWrapDirection(wrapDirection);
155     }
156     if (justifyVal->IsNumber()) {
157         auto mainAlign = justifyVal->ToNumber<int32_t>();
158         if (mainAlign >= 0 && mainAlign <= MAIN_ALIGN_MAX_VALUE) {
159             FlexModel::GetInstance()->SetWrapMainAlignment(WRAP_TABLE[mainAlign]);
160         }
161     }
162     if (alignItemVal->IsNumber()) {
163         auto crossAlign = alignItemVal->ToNumber<int32_t>();
164         if (crossAlign >= 0 && crossAlign <= CROSS_ALIGN_MAX_VALUE) {
165             FlexModel::GetInstance()->SetWrapCrossAlignment(WRAP_TABLE[crossAlign]);
166         }
167     }
168     if (alignContentVal->IsNumber()) {
169         auto alignContent = alignContentVal->ToNumber<int32_t>();
170         if (alignContent >= 0 && alignContent <= MAIN_ALIGN_MAX_VALUE) {
171             FlexModel::GetInstance()->SetWrapAlignment(WRAP_TABLE[alignContent]);
172         }
173     }
174 }
175 
JsFlexWidth(const JSCallbackInfo & info)176 void JSFlexImpl::JsFlexWidth(const JSCallbackInfo& info)
177 {
178     if (info.Length() < 1) {
179         LOGE("The arg is wrong, it is supposed to have at least 1 arguments");
180         return;
181     }
182 
183     JsFlexWidth(info[0]);
184 }
185 
JsFlexWidth(const JSRef<JSVal> & jsValue)186 void JSFlexImpl::JsFlexWidth(const JSRef<JSVal>& jsValue)
187 {
188     JSViewAbstract::JsWidth(jsValue);
189     FlexModel::GetInstance()->SetFlexWidth();
190 }
191 
JsFlexHeight(const JSCallbackInfo & info)192 void JSFlexImpl::JsFlexHeight(const JSCallbackInfo& info)
193 {
194     if (info.Length() < 1) {
195         LOGE("The arg is wrong, it is supposed to have at least 1 arguments");
196         return;
197     }
198 
199     JsFlexHeight(info[0]);
200 }
201 
JsFlexHeight(const JSRef<JSVal> & jsValue)202 void JSFlexImpl::JsFlexHeight(const JSRef<JSVal>& jsValue)
203 {
204     JSViewAbstract::JsHeight(jsValue);
205     FlexModel::GetInstance()->SetFlexHeight();
206 }
207 
JsFlexSize(const JSCallbackInfo & info)208 void JSFlexImpl::JsFlexSize(const JSCallbackInfo& info)
209 {
210     if (info.Length() < 1) {
211         LOGE("The arg is wrong, it is supposed to have atleast 1 arguments");
212         return;
213     }
214 
215     if (!info[0]->IsObject()) {
216         LOGE("arg is not Object or String.");
217         return;
218     }
219 
220     JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
221     JsFlexWidth(sizeObj->GetProperty("width"));
222     JsFlexHeight(sizeObj->GetProperty("height"));
223 }
224 
JSBind(BindingTarget globalObj)225 void JSFlexImpl::JSBind(BindingTarget globalObj)
226 {
227     JSClass<JSFlexImpl>::Declare("Flex");
228     MethodOptions opt = MethodOptions::NONE;
229     JSClass<JSFlexImpl>::StaticMethod("create", &JSFlexImpl::Create, opt);
230 
231     JSClass<JSFlexImpl>::StaticMethod("width", &JSFlexImpl::JsFlexWidth);
232     JSClass<JSFlexImpl>::StaticMethod("height", &JSFlexImpl::JsFlexHeight);
233     JSClass<JSFlexImpl>::StaticMethod("size", &JSFlexImpl::JsFlexSize);
234 
235     JSClass<JSFlexImpl>::StaticMethod("fillParent", &JSFlex::SetFillParent, opt);
236     JSClass<JSFlexImpl>::StaticMethod("wrapContent", &JSFlex::SetWrapContent, opt);
237     JSClass<JSFlexImpl>::StaticMethod("justifyContent", &JSFlex::SetJustifyContent, opt);
238     JSClass<JSFlexImpl>::StaticMethod("alignItems", &JSFlex::SetAlignItems, opt);
239     JSClass<JSFlexImpl>::StaticMethod("alignContent", &JSFlex::SetAlignContent, opt);
240     JSClass<JSFlexImpl>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
241     JSClass<JSFlexImpl>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
242     JSClass<JSFlexImpl>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
243     JSClass<JSFlexImpl>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
244     JSClass<JSFlexImpl>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
245     JSClass<JSFlexImpl>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
246     JSClass<JSFlexImpl>::StaticMethod("onPan", &JSInteractableView::JsOnPan);
247     JSClass<JSFlexImpl>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
248     JSClass<JSFlexImpl>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
249     JSClass<JSFlexImpl>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
250     JSClass<JSFlexImpl>::StaticMethod("pointLight", &JSViewAbstract::JsPointLight, opt);
251     JSClass<JSFlexImpl>::InheritAndBind<JSContainerBase>(globalObj);
252 }
253 
254 } // namespace OHOS::Ace::Framework
255