1 /*
2 * Copyright (c) 2024 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/engine/jsi/nativeModule/arkts_native_theme_bridge.h"
17
18 #include "bridge/declarative_frontend/engine/jsi/nativeModule/arkts_native_common_bridge.h"
19 #include "bridge/declarative_frontend/engine/jsi/nativeModule/arkts_utils.h"
20 #include "core/components_ng/syntax/with_theme_node.h"
21 #include "core/components_ng/token_theme/token_theme_storage.h"
22
23 namespace OHOS::Ace::NG {
24 constexpr char DEFAULT_THEME_TAG[] = "ThemeTag";
25
Create(ArkUIRuntimeCallInfo * runtimeCallInfo)26 ArkUINativeModuleValue ThemeBridge::Create(ArkUIRuntimeCallInfo* runtimeCallInfo)
27 {
28 EcmaVM* vm = runtimeCallInfo->GetVM();
29 CHECK_NULL_RETURN(vm, panda::JSValueRef::Undefined(vm));
30 Local<JSValueRef> themeScopeIdArg = runtimeCallInfo->GetCallArgRef(0);
31 Local<JSValueRef> themeIdArg = runtimeCallInfo->GetCallArgRef(1);
32 Local<JSValueRef> colorsArg = runtimeCallInfo->GetCallArgRef(2); // 2: colorsArg index
33 Local<JSValueRef> darkColorsArg = runtimeCallInfo->GetCallArgRef(3); // 3: darkColorsArg index
34 Local<JSValueRef> colorModeArg = runtimeCallInfo->GetCallArgRef(4); // 4: colorModeArg index
35 Local<JSValueRef> onThemeScopeDestroyArg = runtimeCallInfo->GetCallArgRef(5); // 5: destroy callback arg index
36 Local<JSValueRef> darkSetStatus = runtimeCallInfo->GetCallArgRef(6); // 6: is set darkColors
37
38 // check all argument valid
39 if (!themeScopeIdArg->IsNumber() || !themeIdArg->IsNumber() || !colorsArg->IsArray(vm) ||
40 !darkSetStatus->IsBoolean() || !colorModeArg->IsNumber() || !onThemeScopeDestroyArg->IsFunction(vm)) {
41 return panda::JSValueRef::Undefined(vm);
42 }
43 ArkUI_Int32 themeScopeId = static_cast<ArkUI_Int32>(themeScopeIdArg->Int32Value(vm));
44 ArkUI_Int32 themeId = static_cast<ArkUI_Int32>(themeIdArg->Int32Value(vm));
45 std::vector<ArkUI_Uint32> lightColors;
46 std::vector<RefPtr<ResourceObject>> lightResObjs;
47 if (!HandleThemeColorsArg(vm, colorsArg, lightColors, lightResObjs, themeId, false)) {
48 TAG_LOGD(AceLogTag::ACE_THEME, "Handle Theme Colors to array failed");
49 return panda::JSValueRef::Undefined(vm);
50 }
51
52 ArkUI_Bool isDarkSet = static_cast<ArkUI_Bool>(darkSetStatus->BooleaValue(vm));
53 std::vector<ArkUI_Uint32> darkColors;
54 std::vector<RefPtr<ResourceObject>> darkResObjs;
55 if (!isDarkSet) {
56 darkColors = lightColors; // if darkColors is not set, use lightColors
57 darkResObjs = lightResObjs; // if darkColors is not set, use lightResObjs
58 } else if (!darkColorsArg->IsArray(vm) ||
59 !HandleThemeColorsArg(vm, darkColorsArg, darkColors, darkResObjs, themeId, true)) {
60 TAG_LOGD(AceLogTag::ACE_THEME, "Handle Theme darkColors to array failed");
61 return panda::JSValueRef::Undefined(vm);
62 }
63
64 ArkUI_Int32 colorMode = static_cast<ArkUI_Int32>(colorModeArg->Int32Value(vm));
65 auto obj = onThemeScopeDestroyArg->ToObject(vm);
66 auto containerId = Container::CurrentId();
67 panda::Local<panda::FunctionRef> func = obj;
68 std::function<void()> onThemeScopeDestroy = [vm, func = panda::CopyableGlobal(vm, func), containerId]() {
69 panda::LocalScope pandaScope(vm);
70 panda::TryCatch trycatch(vm);
71 ContainerScope scope(containerId);
72 func->Call(vm, func.ToLocal(), nullptr, 0);
73 };
74
75 // execute C-API
76 auto themeModifier = GetArkUINodeModifiers()->getThemeModifier();
77 auto theme = themeModifier->createTheme(themeId, lightColors.data(), darkColors.data(), colorMode,
78 static_cast<void*>(&lightResObjs), static_cast<void*>(&darkResObjs));
79 CHECK_NULL_RETURN(theme, panda::NativePointerRef::New(vm, nullptr));
80 ArkUINodeHandle node = themeModifier->getWithThemeNode(themeScopeId);
81 if (!node) {
82 node = CreateWithThemeNode(themeScopeId);
83 }
84 themeModifier->createThemeScope(node, theme);
85 themeModifier->setOnThemeScopeDestroy(node, reinterpret_cast<void*>(&onThemeScopeDestroy));
86
87 return panda::JSValueRef::Undefined(vm);
88 }
89
HandleThemeColorsArg(const EcmaVM * vm,const Local<JSValueRef> & colorsArg,std::vector<ArkUI_Uint32> & colors,std::vector<RefPtr<ResourceObject>> & resObjs,ArkUI_Int32 themeId,bool isDark)90 bool ThemeBridge::HandleThemeColorsArg(const EcmaVM* vm, const Local<JSValueRef>& colorsArg,
91 std::vector<ArkUI_Uint32>& colors, std::vector<RefPtr<ResourceObject>>& resObjs,
92 ArkUI_Int32 themeId, bool isDark)
93 {
94 auto basisTheme = TokenThemeStorage::GetInstance()->GetDefaultTheme();
95 if (!basisTheme) {
96 basisTheme = TokenThemeStorage::GetInstance()->ObtainSystemTheme();
97 }
98 if (!basisTheme) {
99 return false;
100 }
101 auto basisObjs = basisTheme->GetResObjs();
102 bool basisObjsAvaliable = basisObjs.size() == TokenColors::TOTAL_NUMBER;
103 for (size_t i = 0; i < TokenColors::TOTAL_NUMBER; i++) {
104 Color color;
105 auto colorParams = panda::ArrayRef::GetValueAt(vm, colorsArg, i);
106 RefPtr<ResourceObject> resObj;
107 bool isColorSetByUser = true;
108 NodeInfo nodeInfo = { DEFAULT_THEME_TAG, ColorMode::COLOR_MODE_UNDEFINED };
109 if (!ArkTSUtils::ParseJsColorAlpha(vm, colorParams, color, resObj, nodeInfo)) {
110 TAG_LOGD(AceLogTag::ACE_THEME, "Parse JS Color Alpha failed");
111 color = basisTheme->Colors()->GetByIndex(i);
112 isColorSetByUser = false;
113 resObj = basisObjsAvaliable ? basisObjs[i] : nullptr;
114 }
115 resObjs.push_back(resObj);
116 colors.push_back(static_cast<ArkUI_Uint32>(color.GetValue()));
117 TokenThemeStorage::GetInstance()->SetIsThemeColorSetByUser(themeId, isDark, i, isColorSetByUser);
118 }
119 return true;
120 }
121
CreateWithThemeNode(ArkUI_Int32 themeScopeId)122 ArkUINodeHandle ThemeBridge::CreateWithThemeNode(ArkUI_Int32 themeScopeId)
123 {
124 auto themeModifier = GetArkUINodeModifiers()->getThemeModifier();
125 auto node = themeModifier->createWithThemeNode(themeScopeId);
126 RefPtr<WithThemeNode> withThemeNode = AceType::Claim(reinterpret_cast<WithThemeNode*>(node));
127 withThemeNode->DecRefCount();
128 ViewStackProcessor::GetInstance()->Push(withThemeNode);
129 return node;
130 }
131
Pop(ArkUIRuntimeCallInfo * runtimeCallInfo)132 ArkUINativeModuleValue ThemeBridge::Pop(ArkUIRuntimeCallInfo* runtimeCallInfo)
133 {
134 EcmaVM* vm = runtimeCallInfo->GetVM();
135 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
136 ViewStackProcessor::GetInstance()->PopContainer();
137 return panda::JSValueRef::Undefined(vm);
138 }
139
SetDefaultTheme(ArkUIRuntimeCallInfo * runtimeCallInfo)140 ArkUINativeModuleValue ThemeBridge::SetDefaultTheme(ArkUIRuntimeCallInfo* runtimeCallInfo)
141 {
142 EcmaVM* vm = runtimeCallInfo->GetVM();
143 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
144 Local<JSValueRef> colorsArg = runtimeCallInfo->GetCallArgRef(0);
145 Local<JSValueRef> isDarkArg = runtimeCallInfo->GetCallArgRef(1);
146
147 // handle color mode argument
148 if (!isDarkArg->IsBoolean()) {
149 return panda::JSValueRef::Undefined(vm);
150 }
151 ArkUI_Bool isDark = static_cast<ArkUI_Bool>(isDarkArg->BooleaValue(vm));
152
153 // handle colors argument
154 if (!colorsArg->IsArray(vm)) {
155 return panda::JSValueRef::Undefined(vm);
156 }
157 std::vector<ArkUI_Uint32> colors;
158 std::vector<RefPtr<ResourceObject>> resObjs;
159 auto basisTheme = TokenThemeStorage::GetInstance()->ObtainSystemTheme();
160 for (size_t i = 0; i < TokenColors::TOTAL_NUMBER; i++) {
161 Color color;
162 auto colorParams = panda::ArrayRef::GetValueAt(vm, colorsArg, i);
163 bool isColorAvailable = false;
164 RefPtr<ResourceObject> resObj;
165 NodeInfo nodeInfo = { DEFAULT_THEME_TAG, ColorMode::COLOR_MODE_UNDEFINED };
166 if (!ArkTSUtils::ParseJsColorAlpha(vm, colorParams, color, resObj, nodeInfo)) {
167 TAG_LOGD(AceLogTag::ACE_THEME, "Parse JS Color Alpha failed");
168 if (basisTheme) {
169 color = basisTheme->Colors()->GetByIndex(i);
170 isColorAvailable = true;
171 }
172 } else {
173 isColorAvailable = true;
174 }
175 TokenThemeStorage::GetInstance()->SetIsThemeColorAvailable(isDark, i, isColorAvailable);
176 resObjs.emplace_back(resObj);
177 colors.push_back(static_cast<ArkUI_Uint32>(color.GetValue()));
178 }
179
180 // execute C-API
181 GetArkUINodeModifiers()->getThemeModifier()->setDefaultTheme(colors.data(), isDark, static_cast<void*>(&resObjs));
182 return panda::JSValueRef::Undefined(vm);
183 }
184
RemoveFromCache(ArkUIRuntimeCallInfo * runtimeCallInfo)185 ArkUINativeModuleValue ThemeBridge::RemoveFromCache(ArkUIRuntimeCallInfo* runtimeCallInfo)
186 {
187 EcmaVM* vm = runtimeCallInfo->GetVM();
188 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
189 Local<JSValueRef> themeIdArg = runtimeCallInfo->GetCallArgRef(0);
190
191 // handle theme id argument
192 if (!themeIdArg->IsNumber()) {
193 return panda::JSValueRef::Undefined(vm);
194 }
195 ArkUI_Int32 themeId = static_cast<ArkUI_Int32>(themeIdArg->Int32Value(vm));
196
197 // execute C-API
198 GetArkUINodeModifiers()->getThemeModifier()->removeFromCache(themeId);
199 return panda::JSValueRef::Undefined(vm);
200 }
201 } // namespace OHOS::Ace::NG