1 /*
2 * Copyright (c) 2021-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_span.h"
17
18 #include <optional>
19 #include <sstream>
20 #include <string>
21 #include <vector>
22
23 #include "base/geometry/dimension.h"
24 #include "base/log/ace_scoring_log.h"
25 #include "base/log/ace_trace.h"
26 #include "base/utils/utils.h"
27 #include "bridge/common/utils/utils.h"
28 #include "bridge/declarative_frontend/engine/functions/js_click_function.h"
29 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
30 #include "bridge/declarative_frontend/jsview/js_utils.h"
31 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
32 #include "bridge/declarative_frontend/jsview/models/span_model_impl.h"
33 #include "bridge/declarative_frontend/jsview/models/text_model_impl.h"
34 #ifndef NG_BUILD
35 #include "bridge/declarative_frontend/view_stack_processor.h"
36 #endif
37 #include "bridge/declarative_frontend/jsview/js_text.h"
38 #include "core/common/container.h"
39 #include "core/components_ng/pattern/text/span_model.h"
40 #include "core/components_ng/pattern/text/span_model_ng.h"
41 #include "core/components_ng/pattern/text/text_model.h"
42
43 namespace OHOS::Ace {
44
45 std::unique_ptr<SpanModel> SpanModel::instance_ = nullptr;
46 std::mutex SpanModel::mutex_;
47
GetInstance()48 SpanModel* SpanModel::GetInstance()
49 {
50 if (!instance_) {
51 std::lock_guard<std::mutex> lock(mutex_);
52 if (!instance_) {
53 #ifdef NG_BUILD
54 instance_.reset(new NG::SpanModelNG());
55 #else
56 if (Container::IsCurrentUseNewPipeline()) {
57 instance_.reset(new NG::SpanModelNG());
58 } else {
59 instance_.reset(new Framework::SpanModelImpl());
60 }
61 #endif
62 }
63 }
64 return instance_.get();
65 }
66
67 } // namespace OHOS::Ace
68
69 namespace OHOS::Ace::Framework {
70 namespace {
71
72 const std::vector<FontStyle> FONT_STYLES = { FontStyle::NORMAL, FontStyle::ITALIC };
73 const std::vector<TextCase> TEXT_CASES = { TextCase::NORMAL, TextCase::LOWERCASE, TextCase::UPPERCASE };
74
75 } // namespace
76
SetFont(const JSCallbackInfo & info)77 void JSSpan::SetFont(const JSCallbackInfo& info)
78 {
79 Font font;
80 JSText::GetFontInfo(info, font);
81 SpanModel::GetInstance()->SetFont(font);
82 }
83
SetFontSize(const JSCallbackInfo & info)84 void JSSpan::SetFontSize(const JSCallbackInfo& info)
85 {
86 if (info.Length() < 1) {
87 LOGE("The argv is wrong, it is supposed to have at least 1 argument");
88 return;
89 }
90 CalcDimension fontSize;
91 if (!ParseJsDimensionFp(info[0], fontSize)) {
92 return;
93 }
94
95 SpanModel::GetInstance()->SetFontSize(fontSize);
96 }
97
SetFontWeight(const std::string & value)98 void JSSpan::SetFontWeight(const std::string& value)
99 {
100 SpanModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(value));
101 }
102
SetTextColor(const JSCallbackInfo & info)103 void JSSpan::SetTextColor(const JSCallbackInfo& info)
104 {
105 if (info.Length() < 1) {
106 LOGE("The argv is wrong, it is supposed to have at least 1 argument");
107 return;
108 }
109 Color textColor;
110 if (!ParseJsColor(info[0], textColor)) {
111 auto pipelineContext = PipelineBase::GetCurrentContext();
112 CHECK_NULL_VOID_NOLOG(pipelineContext);
113 auto theme = pipelineContext->GetTheme<TextTheme>();
114 CHECK_NULL_VOID_NOLOG(theme);
115 textColor = theme->GetTextStyle().GetTextColor();
116 }
117 SpanModel::GetInstance()->SetTextColor(textColor);
118 }
119
SetFontStyle(int32_t value)120 void JSSpan::SetFontStyle(int32_t value)
121 {
122 if (value >= 0 && value < static_cast<int32_t>(FONT_STYLES.size())) {
123 auto style = FONT_STYLES[value];
124 SpanModel::GetInstance()->SetItalicFontStyle(style);
125 } else {
126 LOGE("Text fontStyle(%{public}d) illegal value", value);
127 }
128 }
129
SetFontFamily(const JSCallbackInfo & info)130 void JSSpan::SetFontFamily(const JSCallbackInfo& info)
131 {
132 if (info.Length() < 1) {
133 LOGE("The argv is wrong, it is supposed to have at least 1 argument");
134 return;
135 }
136 std::vector<std::string> fontFamilies;
137 if (!ParseJsFontFamilies(info[0], fontFamilies)) {
138 LOGE("Parse FontFamilies failed");
139 return;
140 }
141 SpanModel::GetInstance()->SetFontFamily(fontFamilies);
142 }
143
SetLetterSpacing(const JSCallbackInfo & info)144 void JSSpan::SetLetterSpacing(const JSCallbackInfo& info)
145 {
146 if (info.Length() < 1) {
147 LOGE("The argv is wrong, it is supposed to have at least 1 argument");
148 return;
149 }
150 CalcDimension value;
151 if (!ParseJsDimensionFp(info[0], value)) {
152 return;
153 }
154 SpanModel::GetInstance()->SetLetterSpacing(value);
155 }
156
SetTextCase(int32_t value)157 void JSSpan::SetTextCase(int32_t value)
158 {
159 if (value >= 0 && value < static_cast<int32_t>(TEXT_CASES.size())) {
160 auto textCase = TEXT_CASES[value];
161 SpanModel::GetInstance()->SetTextCase(textCase);
162 } else {
163 LOGE("Text textCase(%{public}d) illegal value", value);
164 }
165 }
166
SetDecoration(const JSCallbackInfo & info)167 void JSSpan::SetDecoration(const JSCallbackInfo& info)
168 {
169 if (!info[0]->IsObject()) {
170 LOGE("info[0] not is Object");
171 return;
172 }
173 JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
174 JSRef<JSVal> typeValue = obj->GetProperty("type");
175 JSRef<JSVal> colorValue = obj->GetProperty("color");
176
177 std::optional<TextDecoration> textDecoration;
178 if (typeValue->IsNumber()) {
179 textDecoration = static_cast<TextDecoration>(typeValue->ToNumber<int32_t>());
180 }
181 std::optional<Color> colorVal;
182 Color result;
183 if (ParseJsColor(colorValue, result)) {
184 colorVal = result;
185 } else {
186 // default color
187 colorVal = Color::BLACK;
188 }
189 if (textDecoration) {
190 SpanModel::GetInstance()->SetTextDecoration(textDecoration.value());
191 }
192 if (colorVal) {
193 SpanModel::GetInstance()->SetTextDecorationColor(colorVal.value());
194 }
195 }
196
JsOnClick(const JSCallbackInfo & info)197 void JSSpan::JsOnClick(const JSCallbackInfo& info)
198 {
199 if (Container::IsCurrentUseNewPipeline()) {
200 if (info[0]->IsUndefined() && IsDisableEventVersion()) {
201 LOGD("JsOnClick callback is undefined");
202 SpanModel::GetInstance()->ClearOnClick();
203 return;
204 }
205 if (!info[0]->IsFunction()) {
206 LOGW("the info is not click function");
207 return;
208 }
209 auto jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(info[0]));
210 auto onClick = [execCtx = info.GetExecutionContext(), func = jsOnClickFunc](const BaseEventInfo* info) {
211 const auto* clickInfo = TypeInfoHelper::DynamicCast<GestureEvent>(info);
212 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
213 ACE_SCORING_EVENT("onClick");
214 func->Execute(*clickInfo);
215 };
216 SpanModel::GetInstance()->SetOnClick(std::move(onClick));
217 return;
218 }
219 #ifndef NG_BUILD
220 if (info[0]->IsFunction()) {
221 auto inspector = ViewStackProcessor::GetInstance()->GetInspectorComposedComponent();
222 CHECK_NULL_VOID(inspector);
223 auto impl = inspector->GetInspectorFunctionImpl();
224 RefPtr<JsClickFunction> jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(info[0]));
225 auto clickFunc = [execCtx = info.GetExecutionContext(), func = std::move(jsOnClickFunc), impl](
226 const BaseEventInfo* info) {
227 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
228 LOGD("About to call onclick method on js");
229 const auto* clickInfo = TypeInfoHelper::DynamicCast<ClickInfo>(info);
230 auto newInfo = *clickInfo;
231 if (impl) {
232 impl->UpdateEventInfo(newInfo);
233 }
234 ACE_SCORING_EVENT("Span.onClick");
235 func->Execute(newInfo);
236 };
237 SpanModel::GetInstance()->SetOnClick(std::move(clickFunc));
238 }
239 #endif
240 }
241
JsRemoteMessage(const JSCallbackInfo & info)242 void JSSpan::JsRemoteMessage(const JSCallbackInfo& info)
243 {
244 #ifndef NG_BUILD
245 RemoteCallback remoteCallback;
246 JSInteractableView::JsRemoteMessage(info, remoteCallback);
247 EventMarker remoteMessageEventId(std::move(remoteCallback));
248 auto* stack = ViewStackProcessor::GetInstance();
249 auto textSpanComponent = AceType::DynamicCast<TextSpanComponent>(stack->GetMainComponent());
250 textSpanComponent->SetRemoteMessageEventId(remoteMessageEventId);
251 #endif
252 }
253
SetLineHeight(const JSCallbackInfo & info)254 void JSSpan::SetLineHeight(const JSCallbackInfo& info)
255 {
256 if (info.Length() < 1) {
257 LOGI("The argv is wrong, it is supposed to have at least 1 argument");
258 return;
259 }
260 CalcDimension value;
261 if (!ParseJsDimensionFp(info[0], value)) {
262 return;
263 }
264 SpanModel::GetInstance()->SetLineHeight(value);
265 }
266
JSBind(BindingTarget globalObj)267 void JSSpan::JSBind(BindingTarget globalObj)
268 {
269 JSClass<JSSpan>::Declare("Span");
270 MethodOptions opt = MethodOptions::NONE;
271 JSClass<JSSpan>::StaticMethod("create", &JSSpan::Create, opt);
272 JSClass<JSSpan>::StaticMethod("font", &JSSpan::SetFont, opt);
273 JSClass<JSSpan>::StaticMethod("fontColor", &JSSpan::SetTextColor, opt);
274 JSClass<JSSpan>::StaticMethod("fontSize", &JSSpan::SetFontSize, opt);
275 JSClass<JSSpan>::StaticMethod("fontWeight", &JSSpan::SetFontWeight, opt);
276 JSClass<JSSpan>::StaticMethod("fontStyle", &JSSpan::SetFontStyle, opt);
277 JSClass<JSSpan>::StaticMethod("fontFamily", &JSSpan::SetFontFamily, opt);
278 JSClass<JSSpan>::StaticMethod("letterSpacing", &JSSpan::SetLetterSpacing, opt);
279 JSClass<JSSpan>::StaticMethod("textCase", &JSSpan::SetTextCase, opt);
280 JSClass<JSSpan>::StaticMethod("decoration", &JSSpan::SetDecoration);
281 JSClass<JSSpan>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
282 JSClass<JSSpan>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
283 JSClass<JSSpan>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
284 JSClass<JSSpan>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
285 JSClass<JSSpan>::StaticMethod("remoteMessage", &JSSpan::JsRemoteMessage);
286 JSClass<JSSpan>::StaticMethod("onClick", &JSSpan::JsOnClick);
287 JSClass<JSSpan>::StaticMethod("lineHeight", &JSSpan::SetLineHeight, opt);
288 JSClass<JSSpan>::InheritAndBind<JSContainerBase>(globalObj);
289 }
290
Create(const JSCallbackInfo & info)291 void JSSpan::Create(const JSCallbackInfo& info)
292 {
293 std::string label;
294 if (info.Length() > 0) {
295 ParseJsString(info[0], label);
296 }
297 SpanModel::GetInstance()->Create(label);
298 }
299
300 } // namespace OHOS::Ace::Framework
301