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 "base/log/event_report.h"
19 #include "bridge/common/utils/engine_helper.h"
20 #include "bridge/declarative_frontend/engine/bindings.h"
21 #include "bridge/declarative_frontend/engine/js_converter.h"
22 #include "bridge/declarative_frontend/engine/jsi/js_ui_index.h"
23 #include "bridge/declarative_frontend/jsview/js_utils.h"
24 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
25
26 namespace OHOS::Ace::Framework {
27 namespace {
28
29 #ifndef NG_BUILD
30 // dismiss unused warning in NG_BUILD
31 int32_t g_tabControllerId = 0;
32 #endif
33
34 struct TabsControllerAsyncContext {
35 napi_env env = nullptr;
36 napi_deferred deferred = nullptr;
37 };
38
CreateErrorValue(napi_env env,int32_t errCode,const std::string & errMsg="")39 napi_value CreateErrorValue(napi_env env, int32_t errCode, const std::string& errMsg = "")
40 {
41 napi_value code = nullptr;
42 std::string codeStr = std::to_string(errCode);
43 napi_create_string_utf8(env, codeStr.c_str(), codeStr.length(), &code);
44 napi_value msg = nullptr;
45 napi_create_string_utf8(env, errMsg.c_str(), errMsg.length(), &msg);
46 napi_value error = nullptr;
47 napi_create_error(env, code, msg, &error);
48 return error;
49 }
50
HandleDeferred(const shared_ptr<TabsControllerAsyncContext> & asyncContext,int32_t errorCode,std::string message)51 void HandleDeferred(const shared_ptr<TabsControllerAsyncContext>& asyncContext, int32_t errorCode, std::string message)
52 {
53 auto env = asyncContext->env;
54 CHECK_NULL_VOID(env);
55 auto deferred = asyncContext->deferred;
56 CHECK_NULL_VOID(deferred);
57
58 napi_handle_scope scope = nullptr;
59 auto status = napi_open_handle_scope(env, &scope);
60 if (status != napi_ok) {
61 return;
62 }
63
64 napi_value result = nullptr;
65 if (errorCode == ERROR_CODE_NO_ERROR) {
66 napi_get_null(env, &result);
67 napi_resolve_deferred(env, deferred, result);
68 } else {
69 result = CreateErrorValue(env, errorCode, message);
70 napi_reject_deferred(env, deferred, result);
71 }
72 napi_close_handle_scope(env, scope);
73 }
74
ReturnPromise(const JSCallbackInfo & info,napi_value result)75 void ReturnPromise(const JSCallbackInfo& info, napi_value result)
76 {
77 CHECK_NULL_VOID(result);
78 auto jsPromise = JsConverter::ConvertNapiValueToJsVal(result);
79 if (!jsPromise->IsObject()) {
80 return;
81 }
82 info.SetReturnValue(JSRef<JSObject>::Cast(jsPromise));
83 }
84 } // namespace
85
JSTabsController()86 JSTabsController::JSTabsController()
87 {
88 controller_ = CreateController();
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 auto tabsController = tabsControllerWeak_.Upgrade();
128 if (tabsController) {
129 const auto& updateCubicCurveCallback = tabsController->GetUpdateCubicCurveCallback();
130 if (updateCubicCurveCallback != nullptr) {
131 updateCubicCurveCallback();
132 }
133 TAG_LOGI(AceLogTag::ACE_TABS, "changeIndex %{public}d", index);
134 tabsController->SwipeTo(index);
135 } else {
136 EventReport::ReportScrollableErrorEvent(
137 "Tabs", ScrollableErrorType::CONTROLLER_NOT_BIND, "changeIndex: Tabs controller not bind.");
138 }
139
140 #ifndef NG_BUILD
141 if (controller_) {
142 controller_->SetIndexByController(index, false);
143 }
144 #endif
145 }
146
PreloadItems(const JSCallbackInfo & args)147 void JSTabsController::PreloadItems(const JSCallbackInfo& args)
148 {
149 ContainerScope scope(instanceId_);
150 auto engine = EngineHelper::GetCurrentEngine();
151 CHECK_NULL_VOID(engine);
152 NativeEngine* nativeEngine = engine->GetNativeEngine();
153 auto env = reinterpret_cast<napi_env>(nativeEngine);
154 auto asyncContext = std::make_shared<TabsControllerAsyncContext>();
155 asyncContext->env = env;
156 napi_value promise = nullptr;
157 napi_create_promise(env, &asyncContext->deferred, &promise);
158 auto tabsController = tabsControllerWeak_.Upgrade();
159 if (!tabsController) {
160 EventReport::ReportScrollableErrorEvent(
161 "Tabs", ScrollableErrorType::CONTROLLER_NOT_BIND, "preloadItems: Tabs controller not bind.");
162 ReturnPromise(args, promise);
163 return;
164 }
165
166 ScopeRAII scopeRaii(env);
167 std::set<int32_t> indexSet;
168 if (args.Length() > 0 && args[0]->IsArray()) {
169 auto indexArray = JSRef<JSArray>::Cast(args[0]);
170 size_t size = indexArray->Length();
171 for (size_t i = 0; i < size; i++) {
172 int32_t index = -1;
173 JSViewAbstract::ParseJsInt32(indexArray->GetValueAt(i), index);
174 indexSet.emplace(index);
175 }
176 }
177
178 auto onPreloadFinish = [asyncContext](int32_t errorCode, std::string message) {
179 CHECK_NULL_VOID(asyncContext);
180 HandleDeferred(asyncContext, errorCode, message);
181 };
182 tabsController->SetPreloadFinishCallback(onPreloadFinish);
183 tabsController->PreloadItems(indexSet);
184 ReturnPromise(args, promise);
185 }
186
SetTabBarTranslate(const JSCallbackInfo & args)187 void JSTabsController::SetTabBarTranslate(const JSCallbackInfo& args)
188 {
189 ContainerScope scope(instanceId_);
190 auto tabsController = tabsControllerWeak_.Upgrade();
191 if (!tabsController) {
192 EventReport::ReportScrollableErrorEvent(
193 "Tabs", ScrollableErrorType::CONTROLLER_NOT_BIND, "setTabBarTranslate: Tabs controller not bind.");
194 return;
195 }
196
197 if (args.Length() <= 0) {
198 return;
199 }
200 auto translate = args[0];
201 if (translate->IsObject()) {
202 JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(translate);
203 if (jsObj->HasProperty(static_cast<int32_t>(ArkUIIndex::X)) ||
204 jsObj->HasProperty(static_cast<int32_t>(ArkUIIndex::Y)) ||
205 jsObj->HasProperty(static_cast<int32_t>(ArkUIIndex::Z))) {
206 CalcDimension translateX;
207 CalcDimension translateY;
208 CalcDimension translateZ;
209 JSViewAbstract::ParseJsDimensionVp(jsObj->GetProperty(static_cast<int32_t>(ArkUIIndex::X)), translateX);
210 JSViewAbstract::ParseJsDimensionVp(jsObj->GetProperty(static_cast<int32_t>(ArkUIIndex::Y)), translateY);
211 JSViewAbstract::ParseJsDimensionVp(jsObj->GetProperty(static_cast<int32_t>(ArkUIIndex::Z)), translateZ);
212 auto options = NG::TranslateOptions(translateX, translateY, translateZ);
213 tabsController->SetTabBarTranslate(options);
214 return;
215 }
216 }
217 CalcDimension value;
218 if (JSViewAbstract::ParseJsDimensionVp(translate, value)) {
219 auto options = NG::TranslateOptions(value, value, value);
220 tabsController->SetTabBarTranslate(options);
221 } else {
222 auto options = NG::TranslateOptions(0.0f, 0.0f, 0.0f);
223 tabsController->SetTabBarTranslate(options);
224 }
225 }
226
SetTabBarOpacity(const JSCallbackInfo & args)227 void JSTabsController::SetTabBarOpacity(const JSCallbackInfo& args)
228 {
229 ContainerScope scope(instanceId_);
230 auto tabsController = tabsControllerWeak_.Upgrade();
231 if (!tabsController) {
232 EventReport::ReportScrollableErrorEvent(
233 "Tabs", ScrollableErrorType::CONTROLLER_NOT_BIND, "setTabBarOpacity: Tabs controller not bind.");
234 return;
235 }
236
237 if (args.Length() <= 0) {
238 return;
239 }
240 double opacity = 0.0;
241 if (JSViewAbstract::ParseJsDouble(args[0], opacity)) {
242 opacity = std::clamp(opacity, 0.0, 1.0);
243 tabsController->SetTabBarOpacity(opacity);
244 } else {
245 tabsController->SetTabBarOpacity(1.0f);
246 }
247 }
248
249 } // namespace OHOS::Ace::Framework
250