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