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/pattern/grid_row/grid_row_model_ng.h"
25 #include "core/components_v2/grid_layout/grid_container_util_class.h"
26
27 namespace OHOS::Ace {
28
29 std::unique_ptr<GridRowModel> GridRowModel::instance_;
30
GetInstance()31 GridRowModel* GridRowModel::GetInstance()
32 {
33 if (!instance_) {
34 #ifdef NG_BUILD
35 instance_.reset(new NG::GridRowModelNG());
36 #else
37 if (Container::IsCurrentUseNewPipeline()) {
38 instance_.reset(new NG::GridRowModelNG());
39 } else {
40 instance_.reset(new Framework::GridRowModelImpl());
41 }
42 #endif
43 }
44 return instance_.get();
45 }
46
47 } // namespace OHOS::Ace
48
49 namespace OHOS::Ace::Framework {
50 namespace {
51
52 constexpr size_t MAX_NUMBER_BREAKPOINT = 6;
53
InheritGridRowOption(const RefPtr<V2::GridContainerSize> & gridContainerSize,std::optional<int32_t> (& containerSizeArray)[MAX_NUMBER_BREAKPOINT])54 void InheritGridRowOption(const RefPtr<V2::GridContainerSize>& gridContainerSize,
55 std::optional<int32_t> (&containerSizeArray)[MAX_NUMBER_BREAKPOINT])
56 {
57 if (!containerSizeArray[0].has_value()) {
58 containerSizeArray[0] = V2::DEFAULT_COLUMN_NUMBER;
59 }
60 for (size_t i = 1; i < MAX_NUMBER_BREAKPOINT; i++) {
61 if (!containerSizeArray[i].has_value()) {
62 containerSizeArray[i] = containerSizeArray[i - 1].value();
63 }
64 }
65 gridContainerSize->xs = containerSizeArray[0].value();
66 gridContainerSize->sm = containerSizeArray[1].value();
67 gridContainerSize->md = containerSizeArray[2].value();
68 gridContainerSize->lg = containerSizeArray[3].value();
69 gridContainerSize->xl = containerSizeArray[4].value();
70 gridContainerSize->xxl = containerSizeArray[5].value();
71 }
72
InheritGridRowGutterOption(const RefPtr<V2::Gutter> & gutter,std::optional<Dimension> (& gutterSizeArray)[MAX_NUMBER_BREAKPOINT],bool isHorizontal)73 void InheritGridRowGutterOption(const RefPtr<V2::Gutter>& gutter,
74 std::optional<Dimension> (&gutterSizeArray)[MAX_NUMBER_BREAKPOINT], bool isHorizontal)
75 {
76 if (!gutterSizeArray[0].has_value()) {
77 gutterSizeArray[0] = Dimension(0);
78 }
79 for (size_t i = 1; i < MAX_NUMBER_BREAKPOINT; i++) {
80 if (!gutterSizeArray[i].has_value()) {
81 gutterSizeArray[i] = gutterSizeArray[i - 1].value();
82 }
83 }
84 if (isHorizontal) {
85 gutter->xXs = gutterSizeArray[0].value();
86 gutter->xSm = gutterSizeArray[1].value();
87 gutter->xMd = gutterSizeArray[2].value();
88 gutter->xLg = gutterSizeArray[3].value();
89 gutter->xXl = gutterSizeArray[4].value();
90 gutter->xXXl = gutterSizeArray[5].value();
91 return;
92 }
93 gutter->yXs = gutterSizeArray[0].value();
94 gutter->ySm = gutterSizeArray[1].value();
95 gutter->yMd = gutterSizeArray[2].value();
96 gutter->yLg = gutterSizeArray[3].value();
97 gutter->yXl = gutterSizeArray[4].value();
98 gutter->yXXl = gutterSizeArray[5].value();
99 }
100
ParseGutterObject(const JSRef<JSVal> & gutterObject,RefPtr<V2::Gutter> & gutter,bool isHorizontal)101 void ParseGutterObject(const JSRef<JSVal>& gutterObject, RefPtr<V2::Gutter>& gutter, bool isHorizontal)
102 {
103 Dimension dim;
104 if (JSContainerBase::ParseJsDimensionVp(gutterObject, dim)) {
105 isHorizontal ? gutter->SetXGutter(dim) : gutter->SetYGutter(dim);
106 return;
107 }
108 if (!gutterObject->IsObject()) {
109 return;
110 }
111 std::optional<Dimension> gutterOptions[MAX_NUMBER_BREAKPOINT];
112 auto gutterParam = JSRef<JSObject>::Cast(gutterObject);
113 auto xs = gutterParam->GetProperty("xs");
114 Dimension xsDimension;
115 if (JSContainerBase::ParseJsDimensionVp(xs, xsDimension)) {
116 gutterOptions[0] = xsDimension;
117 }
118 auto sm = gutterParam->GetProperty("sm");
119 Dimension smDimension;
120 if (JSContainerBase::ParseJsDimensionVp(sm, smDimension)) {
121 gutterOptions[1] = smDimension;
122 }
123 auto md = gutterParam->GetProperty("md");
124 Dimension mdDimension;
125 if (JSContainerBase::ParseJsDimensionVp(md, mdDimension)) {
126 gutterOptions[2] = mdDimension;
127 }
128 auto lg = gutterParam->GetProperty("lg");
129 Dimension lgDimension;
130 if (JSContainerBase::ParseJsDimensionVp(lg, lgDimension)) {
131 gutterOptions[3] = lgDimension;
132 }
133 auto xl = gutterParam->GetProperty("xl");
134 Dimension xlDimension;
135 if (JSContainerBase::ParseJsDimensionVp(xl, xlDimension)) {
136 gutterOptions[4] = xlDimension;
137 }
138 auto xxl = gutterParam->GetProperty("xxl");
139 Dimension xxlDimension;
140 if (JSContainerBase::ParseJsDimensionVp(xxl, xxlDimension)) {
141 gutterOptions[5] = xxlDimension;
142 }
143 InheritGridRowGutterOption(gutter, gutterOptions, isHorizontal);
144 }
145
ParserGutter(const JSRef<JSVal> & jsValue)146 RefPtr<V2::Gutter> ParserGutter(const JSRef<JSVal>& jsValue)
147 {
148 Dimension result;
149 if (JSContainerBase::ParseJsDimensionVp(jsValue, result)) {
150 auto gutter = AceType::MakeRefPtr<V2::Gutter>(result);
151 return gutter;
152 } else {
153 if (!jsValue->IsObject()) {
154 return AceType::MakeRefPtr<V2::Gutter>();
155 }
156 auto paramGutter = JSRef<JSObject>::Cast(jsValue);
157 auto xObject = paramGutter->GetProperty("x");
158 auto yObject = paramGutter->GetProperty("y");
159 auto gutter = AceType::MakeRefPtr<V2::Gutter>();
160 ParseGutterObject(xObject, gutter, true);
161 ParseGutterObject(yObject, gutter, false);
162 return gutter;
163 }
164 }
165
ParserColumns(const JSRef<JSVal> & jsValue)166 RefPtr<V2::GridContainerSize> ParserColumns(const JSRef<JSVal>& jsValue)
167 {
168 if (jsValue->IsNumber()) {
169 auto columnNumber = jsValue->ToNumber<int32_t>();
170 return columnNumber > 0 ? AceType::MakeRefPtr<V2::GridContainerSize>(columnNumber)
171 : AceType::MakeRefPtr<V2::GridContainerSize>();
172 } else if (jsValue->IsObject()) {
173 auto gridContainerSize = AceType::MakeRefPtr<V2::GridContainerSize>(12);
174 auto gridParam = JSRef<JSObject>::Cast(jsValue);
175 std::optional<int32_t> containerSizeArray[MAX_NUMBER_BREAKPOINT];
176 auto xs = gridParam->GetProperty("xs");
177 if (xs->IsNumber() && xs->ToNumber<int32_t>() > 0) {
178 containerSizeArray[0] = xs->ToNumber<int32_t>();
179 }
180 auto sm = gridParam->GetProperty("sm");
181 if (sm->IsNumber() && sm->ToNumber<int32_t>() > 0) {
182 containerSizeArray[1] = sm->ToNumber<int32_t>();
183 }
184 auto md = gridParam->GetProperty("md");
185 if (md->IsNumber() && md->ToNumber<int32_t>() > 0) {
186 containerSizeArray[2] = md->ToNumber<int32_t>();
187 }
188 auto lg = gridParam->GetProperty("lg");
189 if (lg->IsNumber() && lg->ToNumber<int32_t>() > 0) {
190 containerSizeArray[3] = lg->ToNumber<int32_t>();
191 }
192 auto xl = gridParam->GetProperty("xl");
193 if (xl->IsNumber() && xl->ToNumber<int32_t>() > 0) {
194 containerSizeArray[4] = xl->ToNumber<int32_t>();
195 }
196 auto xxl = gridParam->GetProperty("xxl");
197 if (xxl->IsNumber() && xxl->ToNumber<int32_t>() > 0) {
198 containerSizeArray[5] = xxl->ToNumber<int32_t>();
199 }
200 InheritGridRowOption(gridContainerSize, containerSizeArray);
201 return gridContainerSize;
202 } else {
203 LOGI("parse column error");
204 return AceType::MakeRefPtr<V2::GridContainerSize>();
205 }
206 }
207
ParserBreakpoints(const JSRef<JSVal> & jsValue)208 RefPtr<V2::BreakPoints> ParserBreakpoints(const JSRef<JSVal>& jsValue)
209 {
210 if (!jsValue->IsObject()) {
211 return AceType::MakeRefPtr<V2::BreakPoints>();
212 }
213 auto breakpoints = JSRef<JSObject>::Cast(jsValue);
214 auto value = breakpoints->GetProperty("value");
215 auto reference = breakpoints->GetProperty("reference");
216 auto breakpoint = AceType::MakeRefPtr<V2::BreakPoints>();
217 if (reference->IsNumber()) {
218 breakpoint->reference = static_cast<V2::BreakPointsReference>(reference->ToNumber<int32_t>());
219 }
220 if (value->IsArray()) {
221 JSRef<JSArray> array = JSRef<JSArray>::Cast(value);
222 breakpoint->breakpoints.clear();
223 if (array->Length() > MAX_NUMBER_BREAKPOINT - 1) {
224 LOGI("The maximum number of breakpoints is %{public}zu", MAX_NUMBER_BREAKPOINT);
225 return breakpoint;
226 }
227 double width = -1.0;
228 for (size_t i = 0; i < array->Length(); i++) {
229 JSRef<JSVal> threshold = array->GetValueAt(i);
230 if (threshold->IsString() || threshold->IsNumber()) {
231 Dimension valueDimension;
232 JSContainerBase::ParseJsDimensionVp(threshold, valueDimension);
233 if (GreatNotEqual(width, valueDimension.Value())) {
234 LOGI("Array data must be sorted in ascending order");
235 return breakpoint;
236 }
237 width = valueDimension.Value();
238 breakpoint->breakpoints.push_back(threshold->ToString());
239 }
240 }
241 }
242 return breakpoint;
243 }
244
ParserDirection(const JSRef<JSVal> & jsValue)245 V2::GridRowDirection ParserDirection(const JSRef<JSVal>& jsValue)
246 {
247 V2::GridRowDirection direction(V2::GridRowDirection::Row);
248 if (jsValue->IsNumber()) {
249 direction = static_cast<V2::GridRowDirection>(jsValue->ToNumber<int32_t>());
250 }
251 return direction;
252 }
253
254 } // namespace
255
Create(const JSCallbackInfo & info)256 void JSGridRow::Create(const JSCallbackInfo& info)
257 {
258 if (info.Length() > 0 && info[0]->IsObject()) {
259 auto gridRow = JSRef<JSObject>::Cast(info[0]);
260 auto columns = gridRow->GetProperty("columns");
261 auto gutter = gridRow->GetProperty("gutter");
262 auto breakpoints = gridRow->GetProperty("breakpoints");
263 auto direction = gridRow->GetProperty("direction");
264
265 auto parsedColumns = ParserColumns(columns);
266 auto parsedGutter = ParserGutter(gutter);
267 auto parsedBreakpoints = ParserBreakpoints(breakpoints);
268 auto parsedDirection = ParserDirection(direction);
269
270 GridRowModel::GetInstance()->Create(parsedColumns, parsedGutter, parsedBreakpoints, parsedDirection);
271 } else {
272 GridRowModel::GetInstance()->Create();
273 }
274 }
275
JsBreakpointEvent(const JSCallbackInfo & info)276 void JSGridRow::JsBreakpointEvent(const JSCallbackInfo& info)
277 {
278 if (info.Length() < 1) {
279 LOGW("No breakpoint event info.");
280 return;
281 }
282 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
283 auto onBreakpointChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](
284 const std::string& value) {
285 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
286 ACE_SCORING_EVENT("GridRow.onBreakpointChange");
287 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(value));
288 func->ExecuteJS(1, &newJSVal);
289 };
290 GridRowModel::GetInstance()->SetOnBreakPointChange(onBreakpointChange);
291 }
292
Height(const JSCallbackInfo & info)293 void JSGridRow::Height(const JSCallbackInfo& info)
294 {
295 if (info.Length() < 1) {
296 LOGI("The arg is wrong, it is supposed to have at least 1 argument");
297 return;
298 }
299 JSViewAbstract::JsHeight(info[0]);
300 GridRowModel::GetInstance()->SetHeight();
301 }
302
JSBind(BindingTarget globalObj)303 void JSGridRow::JSBind(BindingTarget globalObj)
304 {
305 JSClass<JSGridRow>::Declare("GridRow");
306 JSClass<JSGridRow>::StaticMethod("create", &JSGridRow::Create);
307 JSClass<JSGridRow>::StaticMethod("onBreakpointChange", &JSGridRow::JsBreakpointEvent);
308 JSClass<JSGridRow>::StaticMethod("height", &JSGridRow::Height);
309 JSClass<JSGridRow>::Inherit<JSContainerBase>();
310 JSClass<JSGridRow>::Bind<>(globalObj);
311 }
312
313 } // namespace OHOS::Ace::Framework
314