• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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