• 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 "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