• 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 
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