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