1 /*
2 * Copyright (c) 2022-2023 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_water_flow.h"
17
18 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
19 #include "frameworks/bridge/declarative_frontend/jsview/js_interactable_view.h"
20 #include "frameworks/bridge/declarative_frontend/jsview/js_scroller.h"
21 #include "frameworks/bridge/declarative_frontend/jsview/models/water_flow_model_impl.h"
22 #include "frameworks/bridge/declarative_frontend/view_stack_processor.h"
23 #include "frameworks/core/components_ng/pattern/waterflow/water_flow_model_ng.h"
24
25 namespace OHOS::Ace {
26 std::unique_ptr<WaterFlowModel> WaterFlowModel::instance_ = nullptr;
27 std::mutex WaterFlowModel::mutex_;
28
GetInstance()29 WaterFlowModel* WaterFlowModel::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::WaterFlowModelNG());
36 #else
37 if (Container::IsCurrentUseNewPipeline()) {
38 instance_.reset(new NG::WaterFlowModelNG());
39 } else {
40 instance_.reset(new Framework::WaterFlowModelImpl());
41 }
42 #endif
43 }
44 }
45 return instance_.get();
46 }
47 } // namespace OHOS::Ace
48 namespace OHOS::Ace::Framework {
49 namespace {
50 const std::vector<FlexDirection> LAYOUT_DIRECTION = { FlexDirection::ROW, FlexDirection::COLUMN,
51 FlexDirection::ROW_REVERSE, FlexDirection::COLUMN_REVERSE };
52 } // namespace
53
Create(const JSCallbackInfo & args)54 void JSWaterFlow::Create(const JSCallbackInfo& args)
55 {
56 if (args.Length() > 1) {
57 LOGW("Arg is wrong, it is supposed to have at most one argument");
58 return;
59 }
60
61 WaterFlowModel::GetInstance()->Create();
62
63 if (args.Length() == 1) {
64 if (!args[0]->IsObject()) {
65 LOGE("The arg must be object");
66 return;
67 }
68 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
69 auto footerObject = obj->GetProperty("footer");
70 if (footerObject->IsFunction()) {
71 auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(footerObject));
72 auto footerAction = [builderFunc]() { builderFunc->Execute(); };
73 WaterFlowModel::GetInstance()->SetFooter(footerAction);
74 }
75 auto scroller = obj->GetProperty("scroller");
76 if (scroller->IsObject()) {
77 auto* jsScroller = JSRef<JSObject>::Cast(scroller)->Unwrap<JSScroller>();
78 CHECK_NULL_VOID(jsScroller);
79 auto positionController = WaterFlowModel::GetInstance()->CreateScrollController();
80 jsScroller->SetController(positionController);
81
82 // Init scroll bar proxy.
83 auto proxy = jsScroller->GetScrollBarProxy();
84 if (!proxy) {
85 proxy = WaterFlowModel::GetInstance()->CreateScrollBarProxy();
86 jsScroller->SetScrollBarProxy(proxy);
87 }
88 WaterFlowModel::GetInstance()->SetScroller(positionController, proxy);
89 }
90 }
91 }
92
JSBind(BindingTarget globalObj)93 void JSWaterFlow::JSBind(BindingTarget globalObj)
94 {
95 LOGD("JSWaterFlow:JSBind");
96 JSClass<JSWaterFlow>::Declare("WaterFlow");
97
98 MethodOptions opt = MethodOptions::NONE;
99 JSClass<JSWaterFlow>::StaticMethod("create", &JSWaterFlow::Create, opt);
100 JSClass<JSWaterFlow>::StaticMethod("columnsGap", &JSWaterFlow::SetColumnsGap, opt);
101 JSClass<JSWaterFlow>::StaticMethod("rowsGap", &JSWaterFlow::SetRowsGap, opt);
102 JSClass<JSWaterFlow>::StaticMethod("layoutDirection", &JSWaterFlow::SetLayoutDirection, opt);
103 JSClass<JSWaterFlow>::StaticMethod("columnsTemplate", &JSWaterFlow::SetColumnsTemplate, opt);
104 JSClass<JSWaterFlow>::StaticMethod("itemConstraintSize", &JSWaterFlow::SetItemConstraintSize, opt);
105 JSClass<JSWaterFlow>::StaticMethod("rowsTemplate", &JSWaterFlow::SetRowsTemplate, opt);
106 JSClass<JSWaterFlow>::StaticMethod("nestedScroll", &JSWaterFlow::SetNestedScroll);
107 JSClass<JSWaterFlow>::StaticMethod("enableScrollInteraction", &JSWaterFlow::SetScrollEnabled);
108 JSClass<JSWaterFlow>::StaticMethod("onReachStart", &JSWaterFlow::ReachStartCallback);
109 JSClass<JSWaterFlow>::StaticMethod("onReachEnd", &JSWaterFlow::ReachEndCallback);
110 JSClass<JSWaterFlow>::StaticMethod("onScrollFrameBegin", &JSWaterFlow::ScrollFrameBeginCallback);
111 JSClass<JSWaterFlow>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
112 JSClass<JSWaterFlow>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
113 JSClass<JSWaterFlow>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
114 JSClass<JSWaterFlow>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
115 JSClass<JSWaterFlow>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
116 JSClass<JSWaterFlow>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
117 JSClass<JSWaterFlow>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
118 JSClass<JSWaterFlow>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
119 JSClass<JSWaterFlow>::StaticMethod("friction", &JSWaterFlow::SetFriction);
120
121 JSClass<JSWaterFlow>::InheritAndBind<JSContainerBase>(globalObj);
122 }
123
SetColumnsGap(const JSCallbackInfo & info)124 void JSWaterFlow::SetColumnsGap(const JSCallbackInfo& info)
125 {
126 if (info.Length() < 1) {
127 LOGW("Arg is wrong, it is supposed to have at least 1 argument");
128 return;
129 }
130 CalcDimension colGap;
131 if (!ParseJsDimensionVp(info[0], colGap) || colGap.Value() < 0) {
132 colGap.SetValue(0.0);
133 }
134 WaterFlowModel::GetInstance()->SetColumnsGap(colGap);
135 }
136
SetRowsGap(const JSCallbackInfo & info)137 void JSWaterFlow::SetRowsGap(const JSCallbackInfo& info)
138 {
139 if (info.Length() < 1) {
140 LOGW("Arg is wrong, it is supposed to have at least 1 argument");
141 return;
142 }
143 CalcDimension rowGap;
144 if (!ParseJsDimensionVp(info[0], rowGap) || rowGap.Value() < 0) {
145 rowGap.SetValue(0.0);
146 }
147 WaterFlowModel::GetInstance()->SetRowsGap(rowGap);
148 }
149
SetLayoutDirection(const JSCallbackInfo & info)150 void JSWaterFlow::SetLayoutDirection(const JSCallbackInfo& info)
151 {
152 if (info.Length() < 1) {
153 LOGE("The arg is wrong, it is supposed to have at least 1 arguments");
154 return;
155 }
156 auto value = static_cast<int32_t>(FlexDirection::COLUMN);
157 auto jsValue = info[0];
158 if (!jsValue->IsUndefined()) {
159 ParseJsInteger<int32_t>(jsValue, value);
160 }
161 if (value >= 0 && value < static_cast<int32_t>(LAYOUT_DIRECTION.size())) {
162 WaterFlowModel::GetInstance()->SetLayoutDirection(LAYOUT_DIRECTION[value]);
163 } else {
164 WaterFlowModel::GetInstance()->SetLayoutDirection(FlexDirection::COLUMN);
165 }
166 }
167
SetColumnsTemplate(const std::string & value)168 void JSWaterFlow::SetColumnsTemplate(const std::string& value)
169 {
170 WaterFlowModel::GetInstance()->SetColumnsTemplate(value);
171 }
172
SetItemConstraintSize(const JSCallbackInfo & info)173 void JSWaterFlow::SetItemConstraintSize(const JSCallbackInfo& info)
174 {
175 if (info.Length() < 1 || !info[0]->IsObject()) {
176 LOGI("waterflow create error, info is invalid");
177 return;
178 }
179
180 JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
181
182 JSRef<JSVal> minWidthValue = sizeObj->GetProperty("minWidth");
183 CalcDimension minWidth;
184 if (ParseJsDimensionVp(minWidthValue, minWidth)) {
185 WaterFlowModel::GetInstance()->SetItemMinWidth(minWidth);
186 }
187
188 JSRef<JSVal> maxWidthValue = sizeObj->GetProperty("maxWidth");
189 CalcDimension maxWidth;
190 if (ParseJsDimensionVp(maxWidthValue, maxWidth)) {
191 WaterFlowModel::GetInstance()->SetItemMaxWidth(maxWidth);
192 }
193
194 JSRef<JSVal> minHeightValue = sizeObj->GetProperty("minHeight");
195 CalcDimension minHeight;
196 if (ParseJsDimensionVp(minHeightValue, minHeight)) {
197 WaterFlowModel::GetInstance()->SetItemMinHeight(minHeight);
198 }
199
200 JSRef<JSVal> maxHeightValue = sizeObj->GetProperty("maxHeight");
201 CalcDimension maxHeight;
202 if (ParseJsDimensionVp(maxHeightValue, maxHeight)) {
203 WaterFlowModel::GetInstance()->SetItemMaxHeight(maxHeight);
204 }
205 }
206
SetRowsTemplate(const std::string & value)207 void JSWaterFlow::SetRowsTemplate(const std::string& value)
208 {
209 WaterFlowModel::GetInstance()->SetRowsTemplate(value);
210 }
211
SetNestedScroll(const JSCallbackInfo & args)212 void JSWaterFlow::SetNestedScroll(const JSCallbackInfo& args)
213 {
214 NestedScrollOptions nestedOpt = {
215 .forward = NestedScrollMode::SELF_ONLY,
216 .backward = NestedScrollMode::SELF_ONLY,
217 };
218 if (args.Length() < 1 || !args[0]->IsObject()) {
219 WaterFlowModel::GetInstance()->SetNestedScroll(nestedOpt);
220 LOGW("Invalid params");
221 return;
222 }
223 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
224 int32_t froward = 0;
225 JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollForward"), froward);
226 if (froward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
227 froward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
228 LOGW("ScrollFroward params invalid");
229 froward = 0;
230 }
231 int32_t backward = 0;
232 JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollBackward"), backward);
233 if (backward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
234 backward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
235 LOGW("ScrollFroward params invalid");
236 backward = 0;
237 }
238 nestedOpt.forward = static_cast<NestedScrollMode>(froward);
239 nestedOpt.backward = static_cast<NestedScrollMode>(backward);
240 WaterFlowModel::GetInstance()->SetNestedScroll(nestedOpt);
241 args.ReturnSelf();
242 }
243
SetScrollEnabled(const JSCallbackInfo & args)244 void JSWaterFlow::SetScrollEnabled(const JSCallbackInfo& args)
245 {
246 WaterFlowModel::GetInstance()->SetScrollEnabled(args[0]->IsBoolean() ? args[0]->ToBoolean() : true);
247 }
248
SetFriction(const JSCallbackInfo & info)249 void JSWaterFlow::SetFriction(const JSCallbackInfo& info)
250 {
251 double friction = -1.0;
252 if (!JSViewAbstract::ParseJsDouble(info[0], friction)) {
253 LOGW("Friction params invalid,can not convert to double");
254 friction = -1.0;
255 }
256 WaterFlowModel::GetInstance()->SetFriction(friction);
257 }
258
ReachStartCallback(const JSCallbackInfo & args)259 void JSWaterFlow::ReachStartCallback(const JSCallbackInfo& args)
260 {
261 if (args[0]->IsFunction()) {
262 auto onReachStart = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
263 func->Call(JSRef<JSObject>());
264 return;
265 };
266 WaterFlowModel::GetInstance()->SetOnReachStart(std::move(onReachStart));
267 }
268 args.ReturnSelf();
269 }
270
ReachEndCallback(const JSCallbackInfo & args)271 void JSWaterFlow::ReachEndCallback(const JSCallbackInfo& args)
272 {
273 if (args[0]->IsFunction()) {
274 auto onReachEnd = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
275 func->Call(JSRef<JSObject>());
276 return;
277 };
278 WaterFlowModel::GetInstance()->SetOnReachEnd(std::move(onReachEnd));
279 }
280 args.ReturnSelf();
281 }
282
ScrollFrameBeginCallback(const JSCallbackInfo & args)283 void JSWaterFlow::ScrollFrameBeginCallback(const JSCallbackInfo& args)
284 {
285 if (args[0]->IsFunction()) {
286 auto onScrollBegin = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
287 const Dimension& offset, const ScrollState& state) -> ScrollFrameResult {
288 ScrollFrameResult scrollRes { .offset = offset };
289 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, scrollRes);
290 auto params = ConvertToJSValues(offset, state);
291 auto result = func->Call(JSRef<JSObject>(), params.size(), params.data());
292 if (result.IsEmpty()) {
293 LOGE("Error calling onScrollFrameBegin, result is empty.");
294 return scrollRes;
295 }
296
297 if (!result->IsObject()) {
298 LOGE("Error calling onScrollFrameBegin, result is not object.");
299 return scrollRes;
300 }
301
302 auto resObj = JSRef<JSObject>::Cast(result);
303 auto dxRemainValue = resObj->GetProperty("offsetRemain");
304 if (dxRemainValue->IsNumber()) {
305 scrollRes.offset = Dimension(dxRemainValue->ToNumber<float>(), DimensionUnit::VP);
306 }
307 return scrollRes;
308 };
309 WaterFlowModel::GetInstance()->SetOnScrollFrameBegin(std::move(onScrollBegin));
310 }
311 }
312 } // namespace OHOS::Ace::Framework
313