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 #include "frameworks/bridge/declarative_frontend/jsview/js_container_span.h"
18
19 #include <optional>
20 #include <sstream>
21 #include <string>
22 #include <vector>
23 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
24 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
25 #endif
26
27 #include "base/geometry/dimension.h"
28 #include "base/log/ace_scoring_log.h"
29 #include "base/log/ace_trace.h"
30 #include "base/utils/utils.h"
31 #include "bridge/common/utils/utils.h"
32 #include "bridge/declarative_frontend/engine/functions/js_click_function.h"
33 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
34 #include "bridge/declarative_frontend/jsview/js_utils.h"
35 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
36 #include "bridge/declarative_frontend/jsview/models/span_model_impl.h"
37 #include "bridge/declarative_frontend/jsview/models/text_model_impl.h"
38 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
39 #ifndef NG_BUILD
40 #include "bridge/declarative_frontend/view_stack_processor.h"
41 #endif
42 #include "bridge/declarative_frontend/jsview/js_text.h"
43 #include "core/common/container.h"
44 #include "core/components_ng/pattern/text/span_model.h"
45 #include "core/components_ng/pattern/text/span_model_ng.h"
46 #include "core/components_ng/pattern/text/text_model.h"
47
48 namespace OHOS::Ace {
49
50 std::unique_ptr<SpanModel> SpanModel::instance_ = nullptr;
51 std::mutex SpanModel::mutex_;
52
GetInstance()53 SpanModel* SpanModel::GetInstance()
54 {
55 if (!instance_) {
56 std::lock_guard<std::mutex> lock(mutex_);
57 if (!instance_) {
58 #ifdef NG_BUILD
59 instance_.reset(new NG::SpanModelNG());
60 #else
61 if (Container::IsCurrentUseNewPipeline()) {
62 instance_.reset(new NG::SpanModelNG());
63 } else {
64 instance_.reset(new Framework::SpanModelImpl());
65 }
66 #endif
67 }
68 }
69 return instance_.get();
70 }
71
72 } // namespace OHOS::Ace
73
74 namespace OHOS::Ace::Framework {
75 namespace {
76
77 const std::vector<FontStyle> FONT_STYLES = { FontStyle::NORMAL, FontStyle::ITALIC };
78 const std::vector<TextCase> TEXT_CASES = { TextCase::NORMAL, TextCase::LOWERCASE, TextCase::UPPERCASE };
79 constexpr TextDecorationStyle DEFAULT_TEXT_DECORATION_STYLE = TextDecorationStyle::SOLID;
80
81 } // namespace
82
SetFont(const JSCallbackInfo & info)83 void JSSpan::SetFont(const JSCallbackInfo& info)
84 {
85 Font font;
86 JSText::GetFontInfo(info, font);
87 SpanModel::GetInstance()->SetFont(font);
88 }
89
SetFontSize(const JSCallbackInfo & info)90 void JSSpan::SetFontSize(const JSCallbackInfo& info)
91 {
92 if (info.Length() < 1) {
93 return;
94 }
95 CalcDimension fontSize;
96 if (!ParseJsDimensionFpNG(info[0], fontSize, false) || fontSize.IsNegative()) {
97 auto pipelineContext = PipelineBase::GetCurrentContext();
98 CHECK_NULL_VOID(pipelineContext);
99 auto theme = pipelineContext->GetTheme<TextTheme>();
100 CHECK_NULL_VOID(theme);
101 fontSize = theme->GetTextStyle().GetFontSize();
102 SpanModel::GetInstance()->SetFontSize(fontSize);
103 return;
104 }
105
106 SpanModel::GetInstance()->SetFontSize(fontSize);
107 }
108
SetFontWeight(const std::string & value)109 void JSSpan::SetFontWeight(const std::string& value)
110 {
111 SpanModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(value));
112 }
113
SetTextColor(const JSCallbackInfo & info)114 void JSSpan::SetTextColor(const JSCallbackInfo& info)
115 {
116 Color textColor;
117 if (!ParseJsColor(info[0], textColor)) {
118 auto pipelineContext = PipelineBase::GetCurrentContext();
119 CHECK_NULL_VOID(pipelineContext);
120 auto theme = pipelineContext->GetTheme<TextTheme>();
121 CHECK_NULL_VOID(theme);
122 textColor = theme->GetTextStyle().GetTextColor();
123 }
124 SpanModel::GetInstance()->SetTextColor(textColor);
125 }
126
SetFontStyle(int32_t value)127 void JSSpan::SetFontStyle(int32_t value)
128 {
129 if (value >= 0 && value < static_cast<int32_t>(FONT_STYLES.size())) {
130 auto style = FONT_STYLES[value];
131 SpanModel::GetInstance()->SetItalicFontStyle(style);
132 }
133 }
134
SetFontFamily(const JSCallbackInfo & info)135 void JSSpan::SetFontFamily(const JSCallbackInfo& info)
136 {
137 if (info.Length() < 1) {
138 return;
139 }
140 std::vector<std::string> fontFamilies;
141 if (!ParseJsFontFamilies(info[0], fontFamilies)) {
142 return;
143 }
144 SpanModel::GetInstance()->SetFontFamily(fontFamilies);
145 }
146
SetLetterSpacing(const JSCallbackInfo & info)147 void JSSpan::SetLetterSpacing(const JSCallbackInfo& info)
148 {
149 if (info.Length() < 1) {
150 return;
151 }
152 CalcDimension value;
153 if (!ParseJsDimensionFpNG(info[0], value, false)) {
154 value.Reset();
155 SpanModel::GetInstance()->SetLetterSpacing(value);
156 return;
157 }
158 SpanModel::GetInstance()->SetLetterSpacing(value);
159 }
160
SetBaselineOffset(const JSCallbackInfo & info)161 void JSSpan::SetBaselineOffset(const JSCallbackInfo& info)
162 {
163 if (info.Length() < 1) {
164 return;
165 }
166 NG::CalcLength value;
167 if (ConvertFromJSValueNG(info[0], value) &&
168 value.GetDimensionContainsNegative().Unit() != DimensionUnit::PERCENT) {
169 SpanModel::GetInstance()->SetBaselineOffset(value.GetDimensionContainsNegative());
170 return;
171 }
172 value.Reset();
173 SpanModel::GetInstance()->SetBaselineOffset(value.GetDimensionContainsNegative());
174 }
175
SetTextCase(int32_t value)176 void JSSpan::SetTextCase(int32_t value)
177 {
178 if (value >= 0 && value < static_cast<int32_t>(TEXT_CASES.size())) {
179 auto textCase = TEXT_CASES[value];
180 SpanModel::GetInstance()->SetTextCase(textCase);
181 }
182 }
183
SetDecoration(const JSCallbackInfo & info)184 void JSSpan::SetDecoration(const JSCallbackInfo& info)
185 {
186 if (info[0]->IsUndefined()) {
187 SpanModel::GetInstance()->SetTextDecoration(TextDecoration::NONE);
188 return;
189 }
190 if (!info[0]->IsObject()) {
191 return;
192 }
193 JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
194 JSRef<JSVal> typeValue = obj->GetProperty("type");
195 JSRef<JSVal> colorValue = obj->GetProperty("color");
196 JSRef<JSVal> styleValue = obj->GetProperty("style");
197
198 std::optional<TextDecoration> textDecoration;
199 if (typeValue->IsNumber()) {
200 textDecoration = static_cast<TextDecoration>(typeValue->ToNumber<int32_t>());
201 } else {
202 auto theme = GetTheme<TextTheme>();
203 CHECK_NULL_VOID(theme);
204 textDecoration = theme->GetTextStyle().GetTextDecoration();
205 }
206 std::optional<TextDecorationStyle> textDecorationStyle;
207 if (styleValue->IsNumber()) {
208 textDecorationStyle = static_cast<TextDecorationStyle>(styleValue->ToNumber<int32_t>());
209 } else {
210 textDecorationStyle = DEFAULT_TEXT_DECORATION_STYLE;
211 }
212 std::optional<Color> colorVal;
213 Color result;
214 if (ParseJsColor(colorValue, result)) {
215 colorVal = result;
216 } else {
217 auto theme = GetTheme<TextTheme>();
218 CHECK_NULL_VOID(theme);
219 if (SystemProperties::GetColorMode() == ColorMode::DARK) {
220 colorVal = theme->GetTextStyle().GetTextColor();
221 } else {
222 colorVal = Color::BLACK;
223 }
224 }
225 SpanModel::GetInstance()->SetTextDecoration(textDecoration.value());
226 SpanModel::GetInstance()->SetTextDecorationColor(colorVal.value());
227 if (textDecorationStyle) {
228 SpanModel::GetInstance()->SetTextDecorationStyle(textDecorationStyle.value());
229 }
230 }
231
JsOnClick(const JSCallbackInfo & info)232 void JSSpan::JsOnClick(const JSCallbackInfo& info)
233 {
234 if (Container::IsCurrentUseNewPipeline()) {
235 if (info[0]->IsUndefined() && IsDisableEventVersion()) {
236 SpanModel::GetInstance()->ClearOnClick();
237 return;
238 }
239 if (!info[0]->IsFunction()) {
240 return;
241 }
242 auto jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(info[0]));
243 auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
244 auto onClick = [execCtx = info.GetExecutionContext(), func = jsOnClickFunc, node = targetNode](
245 BaseEventInfo* info) {
246 auto* clickInfo = TypeInfoHelper::DynamicCast<GestureEvent>(info);
247 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
248 ACE_SCORING_EVENT("onClick");
249 PipelineContext::SetCallBackNode(node);
250 func->Execute(*clickInfo);
251 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
252 JSInteractableView::ReportClickEvent(node);
253 #endif
254 };
255 SpanModel::GetInstance()->SetOnClick(std::move(onClick));
256 return;
257 }
258 #ifndef NG_BUILD
259 if (info[0]->IsFunction()) {
260 auto inspector = ViewStackProcessor::GetInstance()->GetInspectorComposedComponent();
261 CHECK_NULL_VOID(inspector);
262 auto impl = inspector->GetInspectorFunctionImpl();
263 RefPtr<JsClickFunction> jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(info[0]));
264 auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
265 auto clickFunc = [execCtx = info.GetExecutionContext(), func = std::move(jsOnClickFunc), impl,
266 node = targetNode](const BaseEventInfo* info) {
267 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
268 const auto* clickInfo = TypeInfoHelper::DynamicCast<ClickInfo>(info);
269 auto newInfo = *clickInfo;
270 if (impl) {
271 impl->UpdateEventInfo(newInfo);
272 }
273 ACE_SCORING_EVENT("Span.onClick");
274 PipelineContext::SetCallBackNode(node);
275 func->Execute(newInfo);
276 };
277 SpanModel::GetInstance()->SetOnClick(std::move(clickFunc));
278 }
279 #endif
280 }
281
JsRemoteMessage(const JSCallbackInfo & info)282 void JSSpan::JsRemoteMessage(const JSCallbackInfo& info)
283 {
284 #ifndef NG_BUILD
285 RemoteCallback remoteCallback;
286 JSInteractableView::JsRemoteMessage(info, remoteCallback);
287 EventMarker remoteMessageEventId(std::move(remoteCallback));
288 auto* stack = ViewStackProcessor::GetInstance();
289 auto textSpanComponent = AceType::DynamicCast<TextSpanComponent>(stack->GetMainComponent());
290 textSpanComponent->SetRemoteMessageEventId(remoteMessageEventId);
291 #endif
292 }
293
SetLineHeight(const JSCallbackInfo & info)294 void JSSpan::SetLineHeight(const JSCallbackInfo& info)
295 {
296 CalcDimension value;
297 if (!ParseJsDimensionFpNG(info[0], value)) {
298 value.Reset();
299 SpanModel::GetInstance()->SetLineHeight(value);
300 return;
301 }
302 if (value.IsNegative()) {
303 value.Reset();
304 }
305 SpanModel::GetInstance()->SetLineHeight(value);
306 }
307
SetTextShadow(const JSCallbackInfo & info)308 void JSSpan::SetTextShadow(const JSCallbackInfo& info)
309 {
310 if (info.Length() < 1) {
311 return;
312 }
313 std::vector<Shadow> shadows;
314 ParseTextShadowFromShadowObject(info[0], shadows);
315 SpanModel::GetInstance()->SetTextShadow(shadows);
316 }
317
SetAccessibilityText(const JSCallbackInfo & info)318 void JSSpan::SetAccessibilityText(const JSCallbackInfo& info)
319 {
320 std::string text;
321 if ((info.Length() > 0) && info[0]->IsString()) {
322 text = info[0]->ToString();
323 }
324 SpanModel::GetInstance()->SetAccessibilityText(text);
325 }
326
SetAccessibilityDescription(const JSCallbackInfo & info)327 void JSSpan::SetAccessibilityDescription(const JSCallbackInfo& info)
328 {
329 std::string description;
330 if ((info.Length() > 0) && info[0]->IsString()) {
331 description = info[0]->ToString();
332 }
333 SpanModel::GetInstance()->SetAccessibilityDescription(description);
334 }
335
SetAccessibilityLevel(const JSCallbackInfo & info)336 void JSSpan::SetAccessibilityLevel(const JSCallbackInfo& info)
337 {
338 std::string level;
339 if ((info.Length() > 0) && info[0]->IsString()) {
340 level = info[0]->ToString();
341 }
342 SpanModel::GetInstance()->SetAccessibilityImportance(level);
343 }
344
JSBind(BindingTarget globalObj)345 void JSSpan::JSBind(BindingTarget globalObj)
346 {
347 JSClass<JSSpan>::Declare("Span");
348 MethodOptions opt = MethodOptions::NONE;
349 JSClass<JSSpan>::StaticMethod("create", &JSSpan::Create, opt);
350 JSClass<JSSpan>::StaticMethod("font", &JSSpan::SetFont, opt);
351 JSClass<JSSpan>::StaticMethod("fontColor", &JSSpan::SetTextColor, opt);
352 JSClass<JSSpan>::StaticMethod("fontSize", &JSSpan::SetFontSize, opt);
353 JSClass<JSSpan>::StaticMethod("fontWeight", &JSSpan::SetFontWeight, opt);
354 JSClass<JSSpan>::StaticMethod("fontStyle", &JSSpan::SetFontStyle, opt);
355 JSClass<JSSpan>::StaticMethod("fontFamily", &JSSpan::SetFontFamily, opt);
356 JSClass<JSSpan>::StaticMethod("letterSpacing", &JSSpan::SetLetterSpacing, opt);
357 JSClass<JSSpan>::StaticMethod("baselineOffset", &JSSpan::SetBaselineOffset, opt);
358 JSClass<JSSpan>::StaticMethod("textCase", &JSSpan::SetTextCase, opt);
359 JSClass<JSSpan>::StaticMethod("textShadow", &JSSpan::SetTextShadow, opt);
360 JSClass<JSSpan>::StaticMethod("decoration", &JSSpan::SetDecoration);
361 JSClass<JSSpan>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
362 JSClass<JSSpan>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
363 JSClass<JSSpan>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
364 JSClass<JSSpan>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
365 JSClass<JSSpan>::StaticMethod("remoteMessage", &JSSpan::JsRemoteMessage);
366 JSClass<JSSpan>::StaticMethod("onClick", &JSSpan::JsOnClick);
367 JSClass<JSSpan>::StaticMethod("lineHeight", &JSSpan::SetLineHeight, opt);
368 JSClass<JSSpan>::StaticMethod("textBackgroundStyle", &JSContainerSpan::SetTextBackgroundStyle, opt);
369 JSClass<JSSpan>::StaticMethod("accessibilityText", &JSSpan::SetAccessibilityText, opt);
370 JSClass<JSSpan>::StaticMethod("accessibilityDescription", &JSSpan::SetAccessibilityDescription, opt);
371 JSClass<JSSpan>::StaticMethod("accessibilityLevel", &JSSpan::SetAccessibilityLevel, opt);
372 JSClass<JSSpan>::InheritAndBind<JSContainerBase>(globalObj);
373 }
374
Create(const JSCallbackInfo & info)375 void JSSpan::Create(const JSCallbackInfo& info)
376 {
377 std::string label;
378 if (info.Length() > 0) {
379 ParseJsString(info[0], label);
380 }
381 SpanModel::GetInstance()->Create(label);
382 }
383
384 } // namespace OHOS::Ace::Framework
385