1 /*
2  * Copyright (c) 2021 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_tabs_controller.h"
17 
18 #include "bridge/common/utils/engine_helper.h"
19 #include "bridge/declarative_frontend/engine/bindings.h"
20 #include "bridge/declarative_frontend/engine/js_converter.h"
21 #include "bridge/declarative_frontend/engine/jsi/js_ui_index.h"
22 #include "bridge/declarative_frontend/jsview/js_utils.h"
23 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
24 
25 namespace OHOS::Ace::Framework {
26 namespace {
27 
28 #ifndef NG_BUILD
29 // dismiss unused warning in NG_BUILD
30 int32_t g_tabControllerId = 0;
31 #endif
32 
33 struct TabsControllerAsyncContext {
34     napi_env env = nullptr;
35     napi_deferred deferred = nullptr;
36 };
37 
CreateErrorValue(napi_env env,int32_t errCode,const std::string & errMsg="")38 napi_value CreateErrorValue(napi_env env, int32_t errCode, const std::string& errMsg = "")
39 {
40     napi_value code = nullptr;
41     std::string codeStr = std::to_string(errCode);
42     napi_create_string_utf8(env, codeStr.c_str(), codeStr.length(), &code);
43     napi_value msg = nullptr;
44     napi_create_string_utf8(env, errMsg.c_str(), errMsg.length(), &msg);
45     napi_value error = nullptr;
46     napi_create_error(env, code, msg, &error);
47     return error;
48 }
49 
HandleDeferred(const shared_ptr<TabsControllerAsyncContext> & asyncContext,int32_t errorCode,std::string message)50 void HandleDeferred(const shared_ptr<TabsControllerAsyncContext>& asyncContext, int32_t errorCode, std::string message)
51 {
52     auto env = asyncContext->env;
53     CHECK_NULL_VOID(env);
54     auto deferred = asyncContext->deferred;
55     CHECK_NULL_VOID(deferred);
56 
57     napi_handle_scope scope = nullptr;
58     auto status = napi_open_handle_scope(env, &scope);
59     if (status != napi_ok) {
60         return;
61     }
62 
63     napi_value result = nullptr;
64     if (errorCode == ERROR_CODE_NO_ERROR) {
65         napi_get_null(env, &result);
66         napi_resolve_deferred(env, deferred, result);
67     } else {
68         result = CreateErrorValue(env, errorCode, message);
69         napi_reject_deferred(env, deferred, result);
70     }
71     napi_close_handle_scope(env, scope);
72 }
73 
ReturnPromise(const JSCallbackInfo & info,napi_value result)74 void ReturnPromise(const JSCallbackInfo& info, napi_value result)
75 {
76     CHECK_NULL_VOID(result);
77     auto jsPromise = JsConverter::ConvertNapiValueToJsVal(result);
78     if (!jsPromise->IsObject()) {
79         return;
80     }
81     info.SetReturnValue(JSRef<JSObject>::Cast(jsPromise));
82 }
83 } // namespace
84 
JSTabsController()85 JSTabsController::JSTabsController()
86 {
87     controller_ = CreateController();
88     tabsController_ = MakeRefPtr<NG::TabsControllerNG>();
89 }
90 
JSBind(BindingTarget globalObj)91 void JSTabsController::JSBind(BindingTarget globalObj)
92 {
93     JSClass<JSTabsController>::Declare("TabsController");
94     JSClass<JSTabsController>::Method("changeIndex", &JSTabsController::ChangeIndex);
95     JSClass<JSTabsController>::CustomMethod("preloadItems", &JSTabsController::PreloadItems);
96     JSClass<JSTabsController>::CustomMethod("setTabBarTranslate", &JSTabsController::SetTabBarTranslate);
97     JSClass<JSTabsController>::CustomMethod("setTabBarOpacity", &JSTabsController::SetTabBarOpacity);
98     JSClass<JSTabsController>::Bind(globalObj, JSTabsController::Constructor, JSTabsController::Destructor);
99 }
100 
Constructor(const JSCallbackInfo & args)101 void JSTabsController::Constructor(const JSCallbackInfo& args)
102 {
103     auto jsCalendarController = Referenced::MakeRefPtr<JSTabsController>();
104     jsCalendarController->IncRefCount();
105     args.SetReturnValue(Referenced::RawPtr(jsCalendarController));
106 }
107 
Destructor(JSTabsController * controller)108 void JSTabsController::Destructor(JSTabsController* controller)
109 {
110     if (controller != nullptr) {
111         controller->DecRefCount();
112     }
113 }
114 
CreateController()115 RefPtr<TabController> JSTabsController::CreateController()
116 {
117 #ifdef NG_BUILD
118     return nullptr;
119 #else
120     return TabController::GetController(++g_tabControllerId);
121 #endif
122 }
123 
ChangeIndex(int32_t index)124 void JSTabsController::ChangeIndex(int32_t index)
125 {
126     ContainerScope scope(instanceId_);
127     if (tabsController_) {
128         const auto& updateCubicCurveCallback = tabsController_->GetUpdateCubicCurveCallback();
129         if (updateCubicCurveCallback != nullptr) {
130             updateCubicCurveCallback();
131         }
132         tabsController_->SwipeTo(index);
133     }
134 
135 #ifndef NG_BUILD
136     if (controller_) {
137         controller_->SetIndexByController(index, false);
138     }
139 #endif
140 }
141 
PreloadItems(const JSCallbackInfo & args)142 void JSTabsController::PreloadItems(const JSCallbackInfo& args)
143 {
144     ContainerScope scope(instanceId_);
145     auto engine = EngineHelper::GetCurrentEngine();
146     CHECK_NULL_VOID(engine);
147     NativeEngine* nativeEngine = engine->GetNativeEngine();
148     auto env = reinterpret_cast<napi_env>(nativeEngine);
149     auto asyncContext = std::make_shared<TabsControllerAsyncContext>();
150     asyncContext->env = env;
151     napi_value promise = nullptr;
152     napi_create_promise(env, &asyncContext->deferred, &promise);
153     if (!tabsController_) {
154         ReturnPromise(args, promise);
155         return;
156     }
157 
158     ScopeRAII scopeRaii(env);
159     std::set<int32_t> indexSet;
160     if (args.Length() > 0 && args[0]->IsArray()) {
161         auto indexArray = JSRef<JSArray>::Cast(args[0]);
162         size_t size = indexArray->Length();
163         for (size_t i = 0; i < size; i++) {
164             int32_t index = -1;
165             JSViewAbstract::ParseJsInt32(indexArray->GetValueAt(i), index);
166             indexSet.emplace(index);
167         }
168     }
169 
170     auto onPreloadFinish = [asyncContext](int32_t errorCode, std::string message) {
171         CHECK_NULL_VOID(asyncContext);
172         HandleDeferred(asyncContext, errorCode, message);
173     };
174     tabsController_->SetPreloadFinishCallback(onPreloadFinish);
175     tabsController_->PreloadItems(indexSet);
176     ReturnPromise(args, promise);
177 }
178 
SetTabBarTranslate(const JSCallbackInfo & args)179 void JSTabsController::SetTabBarTranslate(const JSCallbackInfo& args)
180 {
181     ContainerScope scope(instanceId_);
182     CHECK_NULL_VOID(tabsController_);
183     if (args.Length() <= 0) {
184         return;
185     }
186     auto translate = args[0];
187     if (translate->IsObject()) {
188         JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(translate);
189         if (jsObj->HasProperty(static_cast<int32_t>(ArkUIIndex::X)) ||
190             jsObj->HasProperty(static_cast<int32_t>(ArkUIIndex::Y)) ||
191             jsObj->HasProperty(static_cast<int32_t>(ArkUIIndex::Z))) {
192             CalcDimension translateX;
193             CalcDimension translateY;
194             CalcDimension translateZ;
195             JSViewAbstract::ParseJsDimensionVp(jsObj->GetProperty(static_cast<int32_t>(ArkUIIndex::X)), translateX);
196             JSViewAbstract::ParseJsDimensionVp(jsObj->GetProperty(static_cast<int32_t>(ArkUIIndex::Y)), translateY);
197             JSViewAbstract::ParseJsDimensionVp(jsObj->GetProperty(static_cast<int32_t>(ArkUIIndex::Z)), translateZ);
198             auto options = NG::TranslateOptions(translateX, translateY, translateZ);
199             tabsController_->SetTabBarTranslate(options);
200             return;
201         }
202     }
203     CalcDimension value;
204     if (JSViewAbstract::ParseJsDimensionVp(translate, value)) {
205         auto options = NG::TranslateOptions(value, value, value);
206         tabsController_->SetTabBarTranslate(options);
207     } else {
208         auto options = NG::TranslateOptions(0.0f, 0.0f, 0.0f);
209         tabsController_->SetTabBarTranslate(options);
210     }
211 }
212 
SetTabBarOpacity(const JSCallbackInfo & args)213 void JSTabsController::SetTabBarOpacity(const JSCallbackInfo& args)
214 {
215     ContainerScope scope(instanceId_);
216     CHECK_NULL_VOID(tabsController_);
217     if (args.Length() <= 0) {
218         return;
219     }
220     double opacity = 0.0;
221     if (JSViewAbstract::ParseJsDouble(args[0], opacity)) {
222         opacity = std::clamp(opacity, 0.0, 1.0);
223         tabsController_->SetTabBarOpacity(opacity);
224     } else {
225         tabsController_->SetTabBarOpacity(1.0f);
226     }
227 }
228 
229 } // namespace OHOS::Ace::Framework
230