• 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 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
24 
25 #include "base/geometry/dimension.h"
26 #include "base/log/ace_scoring_log.h"
27 #include "base/log/ace_trace.h"
28 #include "base/utils/utils.h"
29 #include "bridge/common/utils/utils.h"
30 #include "bridge/declarative_frontend/engine/functions/js_click_function.h"
31 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
32 #include "bridge/declarative_frontend/jsview/js_utils.h"
33 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
34 #include "bridge/declarative_frontend/jsview/models/span_model_impl.h"
35 #include "bridge/declarative_frontend/jsview/models/text_model_impl.h"
36 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
37 #ifndef NG_BUILD
38 #include "bridge/declarative_frontend/view_stack_processor.h"
39 #endif
40 #include "bridge/declarative_frontend/jsview/js_text.h"
41 #include "core/common/container.h"
42 #include "core/components_ng/pattern/text/span_model.h"
43 #include "core/components_ng/pattern/text/span_model_ng.h"
44 #include "core/components_ng/pattern/text/text_model.h"
45 
46 namespace OHOS::Ace {
47 
48 std::unique_ptr<SpanModel> SpanModel::instance_ = nullptr;
49 std::mutex SpanModel::mutex_;
50 
GetInstance()51 SpanModel* SpanModel::GetInstance()
52 {
53 #ifdef NG_BUILD
54     static NG::SpanModelNG instance;
55     return &instance;
56 #else
57     if (Container::IsCurrentUseNewPipeline()) {
58         static NG::SpanModelNG instance;
59         return &instance;
60     } else {
61         static Framework::SpanModelImpl instance;
62         return &instance;
63     }
64 #endif
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 constexpr TextDecorationStyle DEFAULT_TEXT_DECORATION_STYLE = TextDecorationStyle::SOLID;
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 
SetBaselineOffset(const JSCallbackInfo & info)156 void JSSpan::SetBaselineOffset(const JSCallbackInfo& info)
157 {
158     if (info.Length() < 1) {
159         return;
160     }
161     NG::CalcLength value;
162     if (ConvertFromJSValueNG(info[0], value) &&
163         value.GetDimensionContainsNegative().Unit() != DimensionUnit::PERCENT) {
164         SpanModel::GetInstance()->SetBaselineOffset(value.GetDimensionContainsNegative());
165         return;
166     }
167     value.Reset();
168     SpanModel::GetInstance()->SetBaselineOffset(value.GetDimensionContainsNegative());
169 }
170 
SetTextCase(int32_t value)171 void JSSpan::SetTextCase(int32_t value)
172 {
173     if (value >= 0 && value < static_cast<int32_t>(TEXT_CASES.size())) {
174         auto textCase = TEXT_CASES[value];
175         SpanModel::GetInstance()->SetTextCase(textCase);
176     }
177 }
178 
SetDecoration(const JSCallbackInfo & info)179 void JSSpan::SetDecoration(const JSCallbackInfo& info)
180 {
181     if (info[0]->IsUndefined()) {
182         SpanModel::GetInstance()->SetTextDecoration(TextDecoration::NONE);
183         return;
184     }
185     if (!info[0]->IsObject()) {
186         return;
187     }
188     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
189     JSRef<JSVal> typeValue = obj->GetProperty("type");
190     JSRef<JSVal> colorValue = obj->GetProperty("color");
191     JSRef<JSVal> styleValue = obj->GetProperty("style");
192 
193     std::optional<TextDecoration> textDecoration;
194     if (typeValue->IsNumber()) {
195         textDecoration = static_cast<TextDecoration>(typeValue->ToNumber<int32_t>());
196     } else {
197         auto theme = GetTheme<TextTheme>();
198         CHECK_NULL_VOID(theme);
199         textDecoration = theme->GetTextStyle().GetTextDecoration();
200     }
201     std::optional<TextDecorationStyle> textDecorationStyle;
202     if (styleValue->IsNumber()) {
203         textDecorationStyle = static_cast<TextDecorationStyle>(styleValue->ToNumber<int32_t>());
204     } else {
205         textDecorationStyle = DEFAULT_TEXT_DECORATION_STYLE;
206     }
207     std::optional<Color> colorVal;
208     Color result;
209     if (ParseJsColor(colorValue, result)) {
210         colorVal = result;
211     } else {
212         auto theme = GetTheme<TextTheme>();
213         CHECK_NULL_VOID(theme);
214         if (Container::CurrentColorMode() == ColorMode::DARK) {
215             colorVal = theme->GetTextStyle().GetTextColor();
216         } else {
217             colorVal = Color::BLACK;
218         }
219     }
220     SpanModel::GetInstance()->SetTextDecoration(textDecoration.value());
221     SpanModel::GetInstance()->SetTextDecorationColor(colorVal.value());
222     if (textDecorationStyle) {
223         SpanModel::GetInstance()->SetTextDecorationStyle(textDecorationStyle.value());
224     }
225 }
226 
JsOnClick(const JSCallbackInfo & info)227 void JSSpan::JsOnClick(const JSCallbackInfo& info)
228 {
229     if (Container::IsCurrentUseNewPipeline()) {
230         if (info[0]->IsUndefined() && IsDisableEventVersion()) {
231             SpanModel::GetInstance()->ClearOnClick();
232             return;
233         }
234         if (!info[0]->IsFunction()) {
235             return;
236         }
237         auto jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(info[0]));
238         auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
239         auto onClick = [execCtx = info.GetExecutionContext(), func = jsOnClickFunc, node = targetNode](
240                            BaseEventInfo* info) {
241             auto* clickInfo = TypeInfoHelper::DynamicCast<GestureEvent>(info);
242             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
243             ACE_SCORING_EVENT("onClick");
244             PipelineContext::SetCallBackNode(node);
245             func->Execute(*clickInfo);
246 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
247             JSInteractableView::ReportClickEvent(node);
248 #endif
249         };
250         SpanModel::GetInstance()->SetOnClick(std::move(onClick));
251         return;
252     }
253 #ifndef NG_BUILD
254     if (info[0]->IsFunction()) {
255         auto inspector = ViewStackProcessor::GetInstance()->GetInspectorComposedComponent();
256         CHECK_NULL_VOID(inspector);
257         auto impl = inspector->GetInspectorFunctionImpl();
258         RefPtr<JsClickFunction> jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(info[0]));
259         auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
260         auto clickFunc = [execCtx = info.GetExecutionContext(), func = std::move(jsOnClickFunc), impl,
261                              node = targetNode](const BaseEventInfo* info) {
262             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
263             const auto* clickInfo = TypeInfoHelper::DynamicCast<ClickInfo>(info);
264             auto newInfo = *clickInfo;
265             if (impl) {
266                 impl->UpdateEventInfo(newInfo);
267             }
268             ACE_SCORING_EVENT("Span.onClick");
269             PipelineContext::SetCallBackNode(node);
270             func->Execute(newInfo);
271         };
272         SpanModel::GetInstance()->SetOnClick(std::move(clickFunc));
273     }
274 #endif
275 }
276 
JsRemoteMessage(const JSCallbackInfo & info)277 void JSSpan::JsRemoteMessage(const JSCallbackInfo& info)
278 {
279 #ifndef NG_BUILD
280     RemoteCallback remoteCallback;
281     JSInteractableView::JsRemoteMessage(info, remoteCallback);
282     EventMarker remoteMessageEventId(std::move(remoteCallback));
283     auto* stack = ViewStackProcessor::GetInstance();
284     auto textSpanComponent = AceType::DynamicCast<TextSpanComponent>(stack->GetMainComponent());
285     textSpanComponent->SetRemoteMessageEventId(remoteMessageEventId);
286 #endif
287 }
288 
SetLineHeight(const JSCallbackInfo & info)289 void JSSpan::SetLineHeight(const JSCallbackInfo& info)
290 {
291     CalcDimension value;
292     if (!ParseJsDimensionFpNG(info[0], value)) {
293         value.Reset();
294         SpanModel::GetInstance()->SetLineHeight(value);
295         return;
296     }
297     if (value.IsNegative()) {
298         value.Reset();
299     }
300     SpanModel::GetInstance()->SetLineHeight(value);
301 }
302 
SetTextShadow(const JSCallbackInfo & info)303 void JSSpan::SetTextShadow(const JSCallbackInfo& info)
304 {
305     if (info.Length() < 1) {
306         return;
307     }
308     std::vector<Shadow> shadows;
309     ParseTextShadowFromShadowObject(info[0], shadows);
310     SpanModel::GetInstance()->SetTextShadow(shadows);
311 }
312 
313 
SetAccessibilityText(const JSCallbackInfo & info)314 void JSSpan::SetAccessibilityText(const JSCallbackInfo& info)
315 {
316     std::string text;
317     if ((info.Length() > 0) && info[0]->IsString()) {
318         text = info[0]->ToString();
319     }
320     SpanModel::GetInstance()->SetAccessibilityText(text);
321 }
322 
SetAccessibilityDescription(const JSCallbackInfo & info)323 void JSSpan::SetAccessibilityDescription(const JSCallbackInfo& info)
324 {
325     std::string description;
326     if ((info.Length() > 0) && info[0]->IsString()) {
327         description = info[0]->ToString();
328     }
329     SpanModel::GetInstance()->SetAccessibilityDescription(description);
330 }
331 
SetAccessibilityLevel(const JSCallbackInfo & info)332 void JSSpan::SetAccessibilityLevel(const JSCallbackInfo& info)
333 {
334     std::string level;
335     if ((info.Length() > 0) && info[0]->IsString()) {
336         level = info[0]->ToString();
337     }
338     SpanModel::GetInstance()->SetAccessibilityImportance(level);
339 }
340 
JSBind(BindingTarget globalObj)341 void JSSpan::JSBind(BindingTarget globalObj)
342 {
343     JSClass<JSSpan>::Declare("Span");
344     MethodOptions opt = MethodOptions::NONE;
345     JSClass<JSSpan>::StaticMethod("create", &JSSpan::Create, opt);
346     JSClass<JSSpan>::StaticMethod("font", &JSSpan::SetFont, opt);
347     JSClass<JSSpan>::StaticMethod("fontColor", &JSSpan::SetTextColor, opt);
348     JSClass<JSSpan>::StaticMethod("fontSize", &JSSpan::SetFontSize, opt);
349     JSClass<JSSpan>::StaticMethod("fontWeight", &JSSpan::SetFontWeight, opt);
350     JSClass<JSSpan>::StaticMethod("fontStyle", &JSSpan::SetFontStyle, opt);
351     JSClass<JSSpan>::StaticMethod("fontFamily", &JSSpan::SetFontFamily, opt);
352     JSClass<JSSpan>::StaticMethod("letterSpacing", &JSSpan::SetLetterSpacing, opt);
353     JSClass<JSSpan>::StaticMethod("baselineOffset", &JSSpan::SetBaselineOffset, opt);
354     JSClass<JSSpan>::StaticMethod("textCase", &JSSpan::SetTextCase, opt);
355     JSClass<JSSpan>::StaticMethod("textShadow", &JSSpan::SetTextShadow, opt);
356     JSClass<JSSpan>::StaticMethod("decoration", &JSSpan::SetDecoration);
357     JSClass<JSSpan>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
358     JSClass<JSSpan>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
359     JSClass<JSSpan>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
360     JSClass<JSSpan>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
361     JSClass<JSSpan>::StaticMethod("remoteMessage", &JSSpan::JsRemoteMessage);
362     JSClass<JSSpan>::StaticMethod("onClick", &JSSpan::JsOnClick);
363     JSClass<JSSpan>::StaticMethod("lineHeight", &JSSpan::SetLineHeight, opt);
364     JSClass<JSSpan>::StaticMethod("textBackgroundStyle", &JSContainerSpan::SetTextBackgroundStyle, opt);
365     JSClass<JSSpan>::StaticMethod("accessibilityText", &JSSpan::SetAccessibilityText, opt);
366     JSClass<JSSpan>::StaticMethod("accessibilityDescription", &JSSpan::SetAccessibilityDescription, opt);
367     JSClass<JSSpan>::StaticMethod("accessibilityLevel", &JSSpan::SetAccessibilityLevel, opt);
368     JSClass<JSSpan>::InheritAndBind<JSContainerBase>(globalObj);
369 }
370 
Create(const JSCallbackInfo & info)371 void JSSpan::Create(const JSCallbackInfo& info)
372 {
373     std::u16string label;
374     if (info.Length() > 0) {
375         ParseJsString(info[0], label);
376     }
377     SpanModel::GetInstance()->Create(label);
378 }
379 
380 } // namespace OHOS::Ace::Framework
381