1 /*
2  * Copyright (c) 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_grid_row.h"
17 
18 #include "base/geometry/dimension.h"
19 #include "base/log/ace_scoring_log.h"
20 #include "base/log/ace_trace.h"
21 #include "base/memory/referenced.h"
22 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
23 #include "bridge/declarative_frontend/jsview/models/grid_row_model_impl.h"
24 #include "core/components_ng/base/view_stack_processor.h"
25 #include "core/components_ng/pattern/grid_row/grid_row_model_ng.h"
26 #include "core/components_v2/grid_layout/grid_container_util_class.h"
27 
28 namespace OHOS::Ace {
29 
30 std::unique_ptr<GridRowModel> GridRowModel::instance_;
31 std::mutex GridRowModel::mutex_;
32 
GetInstance()33 GridRowModel* GridRowModel::GetInstance()
34 {
35     if (!instance_) {
36         std::lock_guard<std::mutex> lock(mutex_);
37         if (!instance_) {
38 #ifdef NG_BUILD
39             instance_.reset(new NG::GridRowModelNG());
40 #else
41             if (Container::IsCurrentUseNewPipeline()) {
42                 instance_.reset(new NG::GridRowModelNG());
43             } else {
44                 instance_.reset(new Framework::GridRowModelImpl());
45             }
46 #endif
47         }
48     }
49     return instance_.get();
50 }
51 
52 } // namespace OHOS::Ace
53 
54 namespace OHOS::Ace::Framework {
55 namespace {
56 
57 constexpr size_t MAX_NUMBER_BREAKPOINT = 6;
58 
InheritGridRowOption(const RefPtr<V2::GridContainerSize> & gridContainerSize,std::optional<int32_t> (& containerSizeArray)[MAX_NUMBER_BREAKPOINT])59 void InheritGridRowOption(const RefPtr<V2::GridContainerSize>& gridContainerSize,
60     std::optional<int32_t> (&containerSizeArray)[MAX_NUMBER_BREAKPOINT])
61 {
62     if (!containerSizeArray[0].has_value()) {
63         containerSizeArray[0] = V2::DEFAULT_COLUMN_NUMBER;
64     }
65     for (size_t i = 1; i < MAX_NUMBER_BREAKPOINT; i++) {
66         if (!containerSizeArray[i].has_value()) {
67             containerSizeArray[i] = containerSizeArray[i - 1].value();
68         }
69     }
70     gridContainerSize->xs = containerSizeArray[0].value();
71     gridContainerSize->sm = containerSizeArray[1].value();
72     gridContainerSize->md = containerSizeArray[2].value();
73     gridContainerSize->lg = containerSizeArray[3].value();
74     gridContainerSize->xl = containerSizeArray[4].value();
75     gridContainerSize->xxl = containerSizeArray[5].value();
76 }
77 
InheritGridRowGutterOption(const RefPtr<V2::Gutter> & gutter,std::optional<CalcDimension> (& gutterSizeArray)[MAX_NUMBER_BREAKPOINT],bool isHorizontal)78 void InheritGridRowGutterOption(const RefPtr<V2::Gutter>& gutter,
79     std::optional<CalcDimension> (&gutterSizeArray)[MAX_NUMBER_BREAKPOINT], bool isHorizontal)
80 {
81     if (!gutterSizeArray[0].has_value()) {
82         gutterSizeArray[0] = CalcDimension(0);
83     }
84     for (size_t i = 1; i < MAX_NUMBER_BREAKPOINT; i++) {
85         if (!gutterSizeArray[i].has_value()) {
86             gutterSizeArray[i] = gutterSizeArray[i - 1].value();
87         }
88     }
89     if (isHorizontal) {
90         gutter->xXs = gutterSizeArray[0].value();
91         gutter->xSm = gutterSizeArray[1].value();
92         gutter->xMd = gutterSizeArray[2].value();
93         gutter->xLg = gutterSizeArray[3].value();
94         gutter->xXl = gutterSizeArray[4].value();
95         gutter->xXXl = gutterSizeArray[5].value();
96         return;
97     }
98     gutter->yXs = gutterSizeArray[0].value();
99     gutter->ySm = gutterSizeArray[1].value();
100     gutter->yMd = gutterSizeArray[2].value();
101     gutter->yLg = gutterSizeArray[3].value();
102     gutter->yXl = gutterSizeArray[4].value();
103     gutter->yXXl = gutterSizeArray[5].value();
104 }
105 
ParseGutterObject(const JSRef<JSVal> & gutterObject,RefPtr<V2::Gutter> & gutter,bool isHorizontal)106 void ParseGutterObject(const JSRef<JSVal>& gutterObject, RefPtr<V2::Gutter>& gutter, bool isHorizontal)
107 {
108     CalcDimension dim;
109     if (JSContainerBase::ParseJsDimensionVp(gutterObject, dim)) {
110         isHorizontal ? gutter->SetXGutter(dim) : gutter->SetYGutter(dim);
111         return;
112     }
113     if (!gutterObject->IsObject()) {
114         return;
115     }
116     std::optional<CalcDimension> gutterOptions[MAX_NUMBER_BREAKPOINT];
117     auto gutterParam = JSRef<JSObject>::Cast(gutterObject);
118     auto xs = gutterParam->GetProperty("xs");
119     CalcDimension xsDimension;
120     if (JSContainerBase::ParseJsDimensionVp(xs, xsDimension)) {
121         gutterOptions[0] = xsDimension;
122     }
123     auto sm = gutterParam->GetProperty("sm");
124     CalcDimension smDimension;
125     if (JSContainerBase::ParseJsDimensionVp(sm, smDimension)) {
126         gutterOptions[1] = smDimension;
127     }
128     auto md = gutterParam->GetProperty("md");
129     CalcDimension mdDimension;
130     if (JSContainerBase::ParseJsDimensionVp(md, mdDimension)) {
131         gutterOptions[2] = mdDimension;
132     }
133     auto lg = gutterParam->GetProperty("lg");
134     CalcDimension lgDimension;
135     if (JSContainerBase::ParseJsDimensionVp(lg, lgDimension)) {
136         gutterOptions[3] = lgDimension;
137     }
138     auto xl = gutterParam->GetProperty("xl");
139     CalcDimension xlDimension;
140     if (JSContainerBase::ParseJsDimensionVp(xl, xlDimension)) {
141         gutterOptions[4] = xlDimension;
142     }
143     auto xxl = gutterParam->GetProperty("xxl");
144     CalcDimension xxlDimension;
145     if (JSContainerBase::ParseJsDimensionVp(xxl, xxlDimension)) {
146         gutterOptions[5] = xxlDimension;
147     }
148     InheritGridRowGutterOption(gutter, gutterOptions, isHorizontal);
149 }
150 
ParserGutter(const JSRef<JSVal> & jsValue)151 RefPtr<V2::Gutter> ParserGutter(const JSRef<JSVal>& jsValue)
152 {
153     CalcDimension result;
154     if (JSContainerBase::ParseJsDimensionVp(jsValue, result)) {
155         auto gutter = AceType::MakeRefPtr<V2::Gutter>(result);
156         return gutter;
157     } else {
158         if (!jsValue->IsObject()) {
159             return AceType::MakeRefPtr<V2::Gutter>();
160         }
161         auto paramGutter = JSRef<JSObject>::Cast(jsValue);
162         auto xObject = paramGutter->GetProperty("x");
163         auto yObject = paramGutter->GetProperty("y");
164         auto gutter = AceType::MakeRefPtr<V2::Gutter>();
165         ParseGutterObject(xObject, gutter, true);
166         ParseGutterObject(yObject, gutter, false);
167         return gutter;
168     }
169 }
170 
ParserColumns(const JSRef<JSVal> & jsValue)171 RefPtr<V2::GridContainerSize> ParserColumns(const JSRef<JSVal>& jsValue)
172 {
173     if (jsValue->IsNumber()) {
174         auto columnNumber = jsValue->ToNumber<int32_t>();
175         return columnNumber > 0 ? AceType::MakeRefPtr<V2::GridContainerSize>(columnNumber)
176                                 : AceType::MakeRefPtr<V2::GridContainerSize>();
177     } else if (jsValue->IsObject()) {
178         auto gridContainerSize = AceType::MakeRefPtr<V2::GridContainerSize>(12);
179         auto gridParam = JSRef<JSObject>::Cast(jsValue);
180         std::optional<int32_t> containerSizeArray[MAX_NUMBER_BREAKPOINT];
181         auto xs = gridParam->GetProperty("xs");
182         if (xs->IsNumber() && xs->ToNumber<int32_t>() > 0) {
183             containerSizeArray[0] = xs->ToNumber<int32_t>();
184         }
185         auto sm = gridParam->GetProperty("sm");
186         if (sm->IsNumber() && sm->ToNumber<int32_t>() > 0) {
187             containerSizeArray[1] = sm->ToNumber<int32_t>();
188         }
189         auto md = gridParam->GetProperty("md");
190         if (md->IsNumber() && md->ToNumber<int32_t>() > 0) {
191             containerSizeArray[2] = md->ToNumber<int32_t>();
192         }
193         auto lg = gridParam->GetProperty("lg");
194         if (lg->IsNumber() && lg->ToNumber<int32_t>() > 0) {
195             containerSizeArray[3] = lg->ToNumber<int32_t>();
196         }
197         auto xl = gridParam->GetProperty("xl");
198         if (xl->IsNumber() && xl->ToNumber<int32_t>() > 0) {
199             containerSizeArray[4] = xl->ToNumber<int32_t>();
200         }
201         auto xxl = gridParam->GetProperty("xxl");
202         if (xxl->IsNumber() && xxl->ToNumber<int32_t>() > 0) {
203             containerSizeArray[5] = xxl->ToNumber<int32_t>();
204         }
205         InheritGridRowOption(gridContainerSize, containerSizeArray);
206         return gridContainerSize;
207     } else {
208         return AceType::MakeRefPtr<V2::GridContainerSize>();
209     }
210 }
211 
ParserBreakpoints(const JSRef<JSVal> & jsValue)212 RefPtr<V2::BreakPoints> ParserBreakpoints(const JSRef<JSVal>& jsValue)
213 {
214     if (!jsValue->IsObject()) {
215         return AceType::MakeRefPtr<V2::BreakPoints>();
216     }
217     auto breakpoints = JSRef<JSObject>::Cast(jsValue);
218     auto value = breakpoints->GetProperty("value");
219     auto reference = breakpoints->GetProperty("reference");
220     auto breakpoint = AceType::MakeRefPtr<V2::BreakPoints>();
221     if (reference->IsNumber()) {
222         breakpoint->reference = static_cast<V2::BreakPointsReference>(reference->ToNumber<int32_t>());
223     }
224     if (value->IsArray()) {
225         JSRef<JSArray> array = JSRef<JSArray>::Cast(value);
226         breakpoint->breakpoints.clear();
227         if (array->Length() > MAX_NUMBER_BREAKPOINT - 1) {
228             return breakpoint;
229         }
230         double width = -1.0;
231         for (size_t i = 0; i < array->Length(); i++) {
232             JSRef<JSVal> threshold = array->GetValueAt(i);
233             if (threshold->IsString() || threshold->IsNumber()) {
234                 CalcDimension valueDimension;
235                 JSContainerBase::ParseJsDimensionVp(threshold, valueDimension);
236                 if (GreatNotEqual(width, valueDimension.Value())) {
237                     return breakpoint;
238                 }
239                 width = valueDimension.Value();
240                 breakpoint->breakpoints.push_back(threshold->ToString());
241             }
242         }
243     }
244     return breakpoint;
245 }
246 
ParserDirection(const JSRef<JSVal> & jsValue)247 V2::GridRowDirection ParserDirection(const JSRef<JSVal>& jsValue)
248 {
249     V2::GridRowDirection direction(V2::GridRowDirection::Row);
250     if (jsValue->IsNumber()) {
251         direction = static_cast<V2::GridRowDirection>(jsValue->ToNumber<int32_t>());
252     }
253     return direction;
254 }
255 
256 } // namespace
257 
Create(const JSCallbackInfo & info)258 void JSGridRow::Create(const JSCallbackInfo& info)
259 {
260     if (info[0]->IsObject()) {
261         auto gridRow = JSRef<JSObject>::Cast(info[0]);
262         auto columns = gridRow->GetProperty("columns");
263         auto gutter = gridRow->GetProperty("gutter");
264         auto breakpoints = gridRow->GetProperty("breakpoints");
265         auto direction = gridRow->GetProperty("direction");
266 
267         auto parsedColumns = ParserColumns(columns);
268         auto parsedGutter = ParserGutter(gutter);
269         auto parsedBreakpoints = ParserBreakpoints(breakpoints);
270         auto parsedDirection = ParserDirection(direction);
271         GridRowModel::GetInstance()->Create(parsedColumns, parsedGutter, parsedBreakpoints, parsedDirection);
272     } else {
273         GridRowModel::GetInstance()->Create();
274     }
275 }
276 
JsBreakpointEvent(const JSCallbackInfo & info)277 void JSGridRow::JsBreakpointEvent(const JSCallbackInfo& info)
278 {
279     if (!info[0]->IsFunction()) {
280         return;
281     }
282     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
283     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
284     auto onBreakpointChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
285                                   const std::string& value) {
286         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
287         ACE_SCORING_EVENT("GridRow.onBreakpointChange");
288         auto newJSVal = JSRef<JSVal>::Make(ToJSValue(value));
289         PipelineContext::SetCallBackNode(node);
290         func->ExecuteJS(1, &newJSVal);
291     };
292     GridRowModel::GetInstance()->SetOnBreakPointChange(onBreakpointChange);
293 }
294 
Height(const JSCallbackInfo & info)295 void JSGridRow::Height(const JSCallbackInfo& info)
296 {
297     JSViewAbstract::JsHeight(info[0]);
298     GridRowModel::GetInstance()->SetHeight();
299 }
300 
AlignItems(const JSCallbackInfo & info)301 void JSGridRow::AlignItems(const JSCallbackInfo& info)
302 {
303     if (info[0]->IsNumber()) {
304         auto value = info[0]->ToNumber<int32_t>();
305         ParseAlignItems(value);
306     } else if (info[0]->IsUndefined()) {
307         GridRowModel::GetInstance()->SetAlignItems(FlexAlign::FLEX_START);
308     }
309 }
310 
ParseAlignItems(int32_t alignItem)311 void JSGridRow::ParseAlignItems(int32_t alignItem)
312 {
313     if (alignItem == static_cast<int32_t>(FlexAlign::FLEX_START) ||
314         alignItem == static_cast<int32_t>(FlexAlign::FLEX_END) ||
315         alignItem == static_cast<int32_t>(FlexAlign::CENTER) || alignItem == static_cast<int32_t>(FlexAlign::STRETCH)) {
316         GridRowModel::GetInstance()->SetAlignItems(static_cast<FlexAlign>(alignItem));
317     } else if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
318         GridRowModel::GetInstance()->SetAlignItems(FlexAlign::FLEX_START);
319     }
320 }
321 
JSBind(BindingTarget globalObj)322 void JSGridRow::JSBind(BindingTarget globalObj)
323 {
324     JSClass<JSGridRow>::Declare("GridRow");
325     JSClass<JSGridRow>::StaticMethod("create", &JSGridRow::Create);
326     JSClass<JSGridRow>::StaticMethod("onBreakpointChange", &JSGridRow::JsBreakpointEvent);
327     JSClass<JSGridRow>::StaticMethod("height", &JSGridRow::Height);
328     JSClass<JSGridRow>::StaticMethod("alignItems", &JSGridRow::AlignItems);
329     JSClass<JSGridRow>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
330     JSClass<JSGridRow>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
331     JSClass<JSGridRow>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
332     JSClass<JSGridRow>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
333     JSClass<JSGridRow>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
334     JSClass<JSGridRow>::InheritAndBind<JSContainerBase>(globalObj);
335 }
336 
337 } // namespace OHOS::Ace::Framework
338