• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "bridge/declarative_frontend/jsview/js_water_flow.h"
17 
18 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
19 #include "bridge/declarative_frontend/jsview/js_scrollable.h"
20 #include "bridge/declarative_frontend/jsview/js_scroller.h"
21 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
22 #include "bridge/declarative_frontend/jsview/models/water_flow_model_impl.h"
23 #include "bridge/declarative_frontend/view_stack_processor.h"
24 #include "core/common/container.h"
25 #include "core/components_ng/pattern/waterflow/water_flow_model_ng.h"
26 
27 namespace OHOS::Ace {
28 std::unique_ptr<WaterFlowModel> WaterFlowModel::instance_ = nullptr;
29 std::mutex WaterFlowModel::mutex_;
30 
GetInstance()31 WaterFlowModel* WaterFlowModel::GetInstance()
32 {
33     if (!instance_) {
34         std::lock_guard<std::mutex> lock(mutex_);
35         if (!instance_) {
36 #ifdef NG_BUILD
37             instance_.reset(new NG::WaterFlowModelNG());
38 #else
39             if (Container::IsCurrentUseNewPipeline()) {
40                 instance_.reset(new NG::WaterFlowModelNG());
41             } else {
42                 instance_.reset(new Framework::WaterFlowModelImpl());
43             }
44 #endif
45         }
46     }
47     return instance_.get();
48 }
49 } // namespace OHOS::Ace
50 namespace OHOS::Ace::Framework {
51 namespace {
52 const std::vector<FlexDirection> LAYOUT_DIRECTION = { FlexDirection::ROW, FlexDirection::COLUMN,
53     FlexDirection::ROW_REVERSE, FlexDirection::COLUMN_REVERSE };
54 } // namespace
55 
Create(const JSCallbackInfo & args)56 void JSWaterFlow::Create(const JSCallbackInfo& args)
57 {
58     if (args.Length() > 1) {
59         LOGW("Arg is wrong, it is supposed to have at most one argument");
60         return;
61     }
62 
63     WaterFlowModel::GetInstance()->Create();
64 
65     if (args.Length() == 1) {
66         if (!args[0]->IsObject()) {
67             LOGE("The arg must be object");
68             return;
69         }
70         JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
71         auto footerObject = obj->GetProperty("footer");
72         if (footerObject->IsFunction()) {
73             auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(footerObject));
74             auto footerAction = [builderFunc]() { builderFunc->Execute(); };
75             WaterFlowModel::GetInstance()->SetFooter(footerAction);
76         }
77         auto scroller = obj->GetProperty("scroller");
78         if (scroller->IsObject()) {
79             auto* jsScroller = JSRef<JSObject>::Cast(scroller)->Unwrap<JSScroller>();
80             CHECK_NULL_VOID(jsScroller);
81             jsScroller->SetInstanceId(Container::CurrentId());
82             auto positionController = WaterFlowModel::GetInstance()->CreateScrollController();
83             jsScroller->SetController(positionController);
84 
85             // Init scroll bar proxy.
86             auto proxy = jsScroller->GetScrollBarProxy();
87             if (!proxy) {
88                 proxy = WaterFlowModel::GetInstance()->CreateScrollBarProxy();
89                 jsScroller->SetScrollBarProxy(proxy);
90             }
91             WaterFlowModel::GetInstance()->SetScroller(positionController, proxy);
92         }
93     }
94 }
95 
JSBind(BindingTarget globalObj)96 void JSWaterFlow::JSBind(BindingTarget globalObj)
97 {
98     JSClass<JSWaterFlow>::Declare("WaterFlow");
99 
100     MethodOptions opt = MethodOptions::NONE;
101     JSClass<JSWaterFlow>::StaticMethod("create", &JSWaterFlow::Create, opt);
102     JSClass<JSWaterFlow>::StaticMethod("columnsGap", &JSWaterFlow::SetColumnsGap, opt);
103     JSClass<JSWaterFlow>::StaticMethod("rowsGap", &JSWaterFlow::SetRowsGap, opt);
104     JSClass<JSWaterFlow>::StaticMethod("layoutDirection", &JSWaterFlow::SetLayoutDirection, opt);
105     JSClass<JSWaterFlow>::StaticMethod("columnsTemplate", &JSWaterFlow::SetColumnsTemplate, opt);
106     JSClass<JSWaterFlow>::StaticMethod("itemConstraintSize", &JSWaterFlow::SetItemConstraintSize, opt);
107     JSClass<JSWaterFlow>::StaticMethod("rowsTemplate", &JSWaterFlow::SetRowsTemplate, opt);
108     JSClass<JSWaterFlow>::StaticMethod("nestedScroll", &JSWaterFlow::SetNestedScroll);
109     JSClass<JSWaterFlow>::StaticMethod("enableScrollInteraction", &JSWaterFlow::SetScrollEnabled);
110     JSClass<JSWaterFlow>::StaticMethod("onReachStart", &JSWaterFlow::ReachStartCallback);
111     JSClass<JSWaterFlow>::StaticMethod("onReachEnd", &JSWaterFlow::ReachEndCallback);
112     JSClass<JSWaterFlow>::StaticMethod("onScrollFrameBegin", &JSWaterFlow::ScrollFrameBeginCallback);
113     JSClass<JSWaterFlow>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
114     JSClass<JSWaterFlow>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
115     JSClass<JSWaterFlow>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
116     JSClass<JSWaterFlow>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
117     JSClass<JSWaterFlow>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
118     JSClass<JSWaterFlow>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
119     JSClass<JSWaterFlow>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
120     JSClass<JSWaterFlow>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
121     JSClass<JSWaterFlow>::StaticMethod("friction", &JSWaterFlow::SetFriction);
122     JSClass<JSWaterFlow>::StaticMethod("clip", &JSScrollable::JsClip);
123     JSClass<JSWaterFlow>::StaticMethod("cachedCount", &JSWaterFlow::SetCachedCount);
124     JSClass<JSWaterFlow>::StaticMethod("edgeEffect", &JSWaterFlow::SetEdgeEffect);
125 
126     JSClass<JSWaterFlow>::StaticMethod("onScroll", &JSWaterFlow::JsOnScroll);
127     JSClass<JSWaterFlow>::StaticMethod("onScrollStart", &JSWaterFlow::JsOnScrollStart);
128     JSClass<JSWaterFlow>::StaticMethod("onScrollStop", &JSWaterFlow::JsOnScrollStop);
129     JSClass<JSWaterFlow>::StaticMethod("onScrollIndex", &JSWaterFlow::JsOnScrollIndex);
130 
131     JSClass<JSWaterFlow>::StaticMethod("scrollBar", &JSWaterFlow::SetScrollBar, opt);
132     JSClass<JSWaterFlow>::StaticMethod("scrollBarWidth", &JSWaterFlow::SetScrollBarWidth, opt);
133     JSClass<JSWaterFlow>::StaticMethod("scrollBarColor", &JSWaterFlow::SetScrollBarColor, opt);
134 
135     JSClass<JSWaterFlow>::InheritAndBind<JSScrollableBase>(globalObj);
136 }
137 
SetColumnsGap(const JSCallbackInfo & info)138 void JSWaterFlow::SetColumnsGap(const JSCallbackInfo& info)
139 {
140     if (info.Length() < 1) {
141         return;
142     }
143     CalcDimension colGap;
144     if (!ParseJsDimensionVp(info[0], colGap) || colGap.Value() < 0) {
145         colGap.SetValue(0.0);
146     }
147     WaterFlowModel::GetInstance()->SetColumnsGap(colGap);
148 }
149 
SetRowsGap(const JSCallbackInfo & info)150 void JSWaterFlow::SetRowsGap(const JSCallbackInfo& info)
151 {
152     if (info.Length() < 1) {
153         return;
154     }
155     CalcDimension rowGap;
156     if (!ParseJsDimensionVp(info[0], rowGap) || rowGap.Value() < 0) {
157         rowGap.SetValue(0.0);
158     }
159     WaterFlowModel::GetInstance()->SetRowsGap(rowGap);
160 }
161 
SetLayoutDirection(const JSCallbackInfo & info)162 void JSWaterFlow::SetLayoutDirection(const JSCallbackInfo& info)
163 {
164     if (info.Length() < 1) {
165         return;
166     }
167     auto value = static_cast<int32_t>(FlexDirection::COLUMN);
168     auto jsValue = info[0];
169     if (!jsValue->IsUndefined()) {
170         ParseJsInteger<int32_t>(jsValue, value);
171     }
172     if (value >= 0 && value < static_cast<int32_t>(LAYOUT_DIRECTION.size())) {
173         WaterFlowModel::GetInstance()->SetLayoutDirection(LAYOUT_DIRECTION[value]);
174     } else {
175         WaterFlowModel::GetInstance()->SetLayoutDirection(FlexDirection::COLUMN);
176     }
177 }
178 
SetColumnsTemplate(const std::string & value)179 void JSWaterFlow::SetColumnsTemplate(const std::string& value)
180 {
181     WaterFlowModel::GetInstance()->SetColumnsTemplate(value);
182 }
183 
SetItemConstraintSize(const JSCallbackInfo & info)184 void JSWaterFlow::SetItemConstraintSize(const JSCallbackInfo& info)
185 {
186     if (info.Length() < 1 || !info[0]->IsObject()) {
187         return;
188     }
189 
190     JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
191 
192     JSRef<JSVal> minWidthValue = sizeObj->GetProperty("minWidth");
193     CalcDimension minWidth;
194     if (ParseJsDimensionVp(minWidthValue, minWidth)) {
195         WaterFlowModel::GetInstance()->SetItemMinWidth(minWidth);
196     }
197 
198     JSRef<JSVal> maxWidthValue = sizeObj->GetProperty("maxWidth");
199     CalcDimension maxWidth;
200     if (ParseJsDimensionVp(maxWidthValue, maxWidth)) {
201         WaterFlowModel::GetInstance()->SetItemMaxWidth(maxWidth);
202     }
203 
204     JSRef<JSVal> minHeightValue = sizeObj->GetProperty("minHeight");
205     CalcDimension minHeight;
206     if (ParseJsDimensionVp(minHeightValue, minHeight)) {
207         WaterFlowModel::GetInstance()->SetItemMinHeight(minHeight);
208     }
209 
210     JSRef<JSVal> maxHeightValue = sizeObj->GetProperty("maxHeight");
211     CalcDimension maxHeight;
212     if (ParseJsDimensionVp(maxHeightValue, maxHeight)) {
213         WaterFlowModel::GetInstance()->SetItemMaxHeight(maxHeight);
214     }
215 }
216 
SetRowsTemplate(const std::string & value)217 void JSWaterFlow::SetRowsTemplate(const std::string& value)
218 {
219     WaterFlowModel::GetInstance()->SetRowsTemplate(value);
220 }
221 
SetNestedScroll(const JSCallbackInfo & args)222 void JSWaterFlow::SetNestedScroll(const JSCallbackInfo& args)
223 {
224     NestedScrollOptions nestedOpt = {
225         .forward = NestedScrollMode::SELF_ONLY,
226         .backward = NestedScrollMode::SELF_ONLY,
227     };
228     if (args.Length() < 1 || !args[0]->IsObject()) {
229         WaterFlowModel::GetInstance()->SetNestedScroll(nestedOpt);
230         LOGW("Invalid params");
231         return;
232     }
233     JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
234     int32_t froward = 0;
235     JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollForward"), froward);
236     if (froward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
237         froward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
238         LOGW("ScrollFroward params invalid");
239         froward = 0;
240     }
241     int32_t backward = 0;
242     JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollBackward"), backward);
243     if (backward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
244         backward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
245         LOGW("ScrollFroward params invalid");
246         backward = 0;
247     }
248     nestedOpt.forward = static_cast<NestedScrollMode>(froward);
249     nestedOpt.backward = static_cast<NestedScrollMode>(backward);
250     WaterFlowModel::GetInstance()->SetNestedScroll(nestedOpt);
251     args.ReturnSelf();
252 }
253 
SetScrollEnabled(const JSCallbackInfo & args)254 void JSWaterFlow::SetScrollEnabled(const JSCallbackInfo& args)
255 {
256     WaterFlowModel::GetInstance()->SetScrollEnabled(args[0]->IsBoolean() ? args[0]->ToBoolean() : true);
257 }
258 
SetFriction(const JSCallbackInfo & info)259 void JSWaterFlow::SetFriction(const JSCallbackInfo& info)
260 {
261     double friction = -1.0;
262     if (!JSViewAbstract::ParseJsDouble(info[0], friction)) {
263         LOGW("Friction params invalid,can not convert to double");
264         friction = -1.0;
265     }
266     WaterFlowModel::GetInstance()->SetFriction(friction);
267 }
268 
ReachStartCallback(const JSCallbackInfo & args)269 void JSWaterFlow::ReachStartCallback(const JSCallbackInfo& args)
270 {
271     if (args[0]->IsFunction()) {
272         auto onReachStart = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
273             func->Call(JSRef<JSObject>());
274             return;
275         };
276         WaterFlowModel::GetInstance()->SetOnReachStart(std::move(onReachStart));
277     }
278     args.ReturnSelf();
279 }
280 
ReachEndCallback(const JSCallbackInfo & args)281 void JSWaterFlow::ReachEndCallback(const JSCallbackInfo& args)
282 {
283     if (args[0]->IsFunction()) {
284         auto onReachEnd = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
285             func->Call(JSRef<JSObject>());
286             return;
287         };
288         WaterFlowModel::GetInstance()->SetOnReachEnd(std::move(onReachEnd));
289     }
290     args.ReturnSelf();
291 }
292 
ScrollFrameBeginCallback(const JSCallbackInfo & args)293 void JSWaterFlow::ScrollFrameBeginCallback(const JSCallbackInfo& args)
294 {
295     if (args[0]->IsFunction()) {
296         auto onScrollBegin = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
297                                  const Dimension& offset, const ScrollState& state) -> ScrollFrameResult {
298             ScrollFrameResult scrollRes { .offset = offset };
299             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, scrollRes);
300             auto params = ConvertToJSValues(offset, state);
301             auto result = func->Call(JSRef<JSObject>(), params.size(), params.data());
302             if (result.IsEmpty()) {
303                 LOGE("Error calling onScrollFrameBegin, result is empty.");
304                 return scrollRes;
305             }
306 
307             if (!result->IsObject()) {
308                 LOGE("Error calling onScrollFrameBegin, result is not object.");
309                 return scrollRes;
310             }
311 
312             auto resObj = JSRef<JSObject>::Cast(result);
313             auto dxRemainValue = resObj->GetProperty("offsetRemain");
314             if (dxRemainValue->IsNumber()) {
315                 scrollRes.offset = Dimension(dxRemainValue->ToNumber<float>(), DimensionUnit::VP);
316             }
317             return scrollRes;
318         };
319         WaterFlowModel::GetInstance()->SetOnScrollFrameBegin(std::move(onScrollBegin));
320     }
321 }
322 
SetCachedCount(const JSCallbackInfo & info)323 void JSWaterFlow::SetCachedCount(const JSCallbackInfo& info)
324 {
325     int32_t cachedCount = 1;
326     auto jsValue = info[0];
327 
328     if (!jsValue->IsUndefined() && jsValue->IsNumber()) {
329         ParseJsInt32(jsValue, cachedCount);
330         if (cachedCount < 0) {
331             cachedCount = 1;
332         }
333     }
334 
335     WaterFlowModel::GetInstance()->SetCachedCount(cachedCount);
336 }
337 
SetEdgeEffect(const JSCallbackInfo & info)338 void JSWaterFlow::SetEdgeEffect(const JSCallbackInfo& info)
339 {
340     auto edgeEffect = JSScrollable::ParseEdgeEffect(info, WaterFlowModel::GetInstance()->GetEdgeEffect());
341     auto alwaysEnabled =
342         JSScrollable::ParseAlwaysEnable(info, WaterFlowModel::GetInstance()->GetAlwaysEnableEdgeEffect());
343     WaterFlowModel::GetInstance()->SetEdgeEffect(edgeEffect, alwaysEnabled);
344 }
345 
JsOnScroll(const JSCallbackInfo & args)346 void JSWaterFlow::JsOnScroll(const JSCallbackInfo& args)
347 {
348     if (args[0]->IsFunction()) {
349         auto onScroll = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
350                             const CalcDimension& scrollOffset, const ScrollState& scrollState) {
351             auto params = ConvertToJSValues(scrollOffset, scrollState);
352             func->Call(JSRef<JSObject>(), params.size(), params.data());
353             return;
354         };
355         WaterFlowModel::GetInstance()->SetOnScroll(std::move(onScroll));
356     }
357     args.ReturnSelf();
358 }
359 
JsOnScrollStart(const JSCallbackInfo & args)360 void JSWaterFlow::JsOnScrollStart(const JSCallbackInfo& args)
361 {
362     if (args[0]->IsFunction()) {
363         auto onScrollStart = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
364             func->Call(JSRef<JSObject>());
365             return;
366         };
367         WaterFlowModel::GetInstance()->SetOnScrollStart(std::move(onScrollStart));
368     }
369     args.ReturnSelf();
370 }
371 
JsOnScrollStop(const JSCallbackInfo & args)372 void JSWaterFlow::JsOnScrollStop(const JSCallbackInfo& args)
373 {
374     if (args[0]->IsFunction()) {
375         auto onScrollStop = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
376             func->Call(JSRef<JSObject>());
377             return;
378         };
379         WaterFlowModel::GetInstance()->SetOnScrollStop(std::move(onScrollStop));
380     }
381     args.ReturnSelf();
382 }
383 
JsOnScrollIndex(const JSCallbackInfo & args)384 void JSWaterFlow::JsOnScrollIndex(const JSCallbackInfo& args)
385 {
386     if (args[0]->IsFunction()) {
387         auto onScrollIndex = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
388                                  const int32_t first, const int32_t last) {
389             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
390             auto params = ConvertToJSValues(first, last);
391             func->Call(JSRef<JSObject>(), params.size(), params.data());
392             return;
393         };
394         WaterFlowModel::GetInstance()->SetOnScrollIndex(std::move(onScrollIndex));
395     }
396     args.ReturnSelf();
397 }
398 
SetScrollBar(const JSCallbackInfo & info)399 void JSWaterFlow::SetScrollBar(const JSCallbackInfo& info)
400 {
401     auto displayMode = JSScrollable::ParseDisplayMode(info, WaterFlowModel::GetDisplayMode());
402     WaterFlowModel::GetInstance()->SetScrollBarMode(displayMode);
403 }
404 
SetScrollBarColor(const std::string & color)405 void JSWaterFlow::SetScrollBarColor(const std::string& color)
406 {
407     auto scrollBarColor = JSScrollable::ParseBarColor(color);
408     if (!scrollBarColor.empty()) {
409         WaterFlowModel::GetInstance()->SetScrollBarColor(scrollBarColor);
410     }
411 }
412 
SetScrollBarWidth(const JSCallbackInfo & scrollWidth)413 void JSWaterFlow::SetScrollBarWidth(const JSCallbackInfo& scrollWidth)
414 {
415     auto scrollBarWidth = JSScrollable::ParseBarWidth(scrollWidth);
416     if (!scrollBarWidth.empty()) {
417         WaterFlowModel::GetInstance()->SetScrollBarWidth(scrollBarWidth);
418     }
419 }
420 } // namespace OHOS::Ace::Framework
421