1 /*
2 * Copyright (c) 2021-2022 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_view_abstract.h"
31 #include "bridge/declarative_frontend/jsview/models/span_model_impl.h"
32 #include "bridge/declarative_frontend/jsview/models/text_model_impl.h"
33 #ifndef NG_BUILD
34 #include "bridge/declarative_frontend/view_stack_processor.h"
35 #endif
36 #include "core/common/container.h"
37 #include "core/components_ng/pattern/text/span_model.h"
38 #include "core/components_ng/pattern/text/span_model_ng.h"
39 #include "core/components_ng/pattern/text/text_model.h"
40
41 namespace OHOS::Ace {
42
43 std::unique_ptr<SpanModel> SpanModel::instance_ = nullptr;
44
GetInstance()45 SpanModel* SpanModel::GetInstance()
46 {
47 if (!instance_) {
48 #ifdef NG_BUILD
49 instance_.reset(new NG::SpanModelNG());
50 #else
51 if (Container::IsCurrentUseNewPipeline()) {
52 instance_.reset(new NG::SpanModelNG());
53 } else {
54 instance_.reset(new Framework::SpanModelImpl());
55 }
56 #endif
57 }
58 return instance_.get();
59 }
60
61 } // namespace OHOS::Ace
62
63 namespace OHOS::Ace::Framework {
64 namespace {
65
66 const std::vector<FontStyle> FONT_STYLES = { FontStyle::NORMAL, FontStyle::ITALIC };
67 const std::vector<TextCase> TEXT_CASES = { TextCase::NORMAL, TextCase::LOWERCASE, TextCase::UPPERCASE };
68
69 } // namespace
70
SetFontSize(const JSCallbackInfo & info)71 void JSSpan::SetFontSize(const JSCallbackInfo& info)
72 {
73 if (info.Length() < 1) {
74 LOGE("The argv is wrong, it is supposed to have at least 1 argument");
75 return;
76 }
77 Dimension fontSize;
78 if (!ParseJsDimensionFp(info[0], fontSize)) {
79 return;
80 }
81
82 SpanModel::GetInstance()->SetFontSize(fontSize);
83 }
84
SetFontWeight(const std::string & value)85 void JSSpan::SetFontWeight(const std::string& value)
86 {
87 SpanModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(value));
88 }
89
SetTextColor(const JSCallbackInfo & info)90 void JSSpan::SetTextColor(const JSCallbackInfo& info)
91 {
92 if (info.Length() < 1) {
93 LOGE("The argv is wrong, it is supposed to have at least 1 argument");
94 return;
95 }
96 Color textColor;
97 if (!ParseJsColor(info[0], textColor)) {
98 return;
99 }
100 SpanModel::GetInstance()->SetTextColor(textColor);
101 }
102
SetFontStyle(int32_t value)103 void JSSpan::SetFontStyle(int32_t value)
104 {
105 if (value >= 0 && value < static_cast<int32_t>(FONT_STYLES.size())) {
106 auto style = FONT_STYLES[value];
107 SpanModel::GetInstance()->SetItalicFontStyle(style);
108 } else {
109 LOGE("Text fontStyle(%{public}d) illegal value", value);
110 }
111 }
112
SetFontFamily(const JSCallbackInfo & info)113 void JSSpan::SetFontFamily(const JSCallbackInfo& info)
114 {
115 if (info.Length() < 1) {
116 LOGE("The argv is wrong, it is supposed to have at least 1 argument");
117 return;
118 }
119 std::vector<std::string> fontFamilies;
120 if (!ParseJsFontFamilies(info[0], fontFamilies)) {
121 LOGE("Parse FontFamilies failed");
122 return;
123 }
124 SpanModel::GetInstance()->SetFontFamily(fontFamilies);
125 }
126
SetLetterSpacing(const JSCallbackInfo & info)127 void JSSpan::SetLetterSpacing(const JSCallbackInfo& info)
128 {
129 if (info.Length() < 1) {
130 LOGE("The argv is wrong, it is supposed to have at least 1 argument");
131 return;
132 }
133 Dimension value;
134 if (!ParseJsDimensionFp(info[0], value)) {
135 return;
136 }
137 SpanModel::GetInstance()->SetLetterSpacing(value);
138 }
139
SetTextCase(int32_t value)140 void JSSpan::SetTextCase(int32_t value)
141 {
142 if (value >= 0 && value < static_cast<int32_t>(TEXT_CASES.size())) {
143 auto textCase = TEXT_CASES[value];
144 SpanModel::GetInstance()->SetTextCase(textCase);
145 } else {
146 LOGE("Text textCase(%{public}d) illegal value", value);
147 }
148 }
149
SetDecoration(const JSCallbackInfo & info)150 void JSSpan::SetDecoration(const JSCallbackInfo& info)
151 {
152 if (!info[0]->IsObject()) {
153 LOGE("info[0] not is Object");
154 return;
155 }
156 JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
157 JSRef<JSVal> typeValue = obj->GetProperty("type");
158 JSRef<JSVal> colorValue = obj->GetProperty("color");
159
160 std::optional<TextDecoration> textDecoration;
161 if (typeValue->IsNumber()) {
162 textDecoration = static_cast<TextDecoration>(typeValue->ToNumber<int32_t>());
163 }
164 std::optional<Color> colorVal;
165 Color result;
166 if (ParseJsColor(colorValue, result)) {
167 colorVal = result;
168 } else {
169 // default color
170 colorVal = Color::BLACK;
171 }
172 if (textDecoration) {
173 SpanModel::GetInstance()->SetTextDecoration(textDecoration.value());
174 }
175 if (colorVal) {
176 SpanModel::GetInstance()->SetTextDecorationColor(colorVal.value());
177 }
178 }
179
JsOnClick(const JSCallbackInfo & info)180 void JSSpan::JsOnClick(const JSCallbackInfo& info)
181 {
182 if (Container::IsCurrentUseNewPipeline()) {
183 if (!info[0]->IsFunction()) {
184 LOGW("the info is not click function");
185 return;
186 }
187 auto jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(info[0]));
188 auto onClick = [execCtx = info.GetExecutionContext(), func = jsOnClickFunc](const BaseEventInfo* info) {
189 const auto* clickInfo = TypeInfoHelper::DynamicCast<GestureEvent>(info);
190 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
191 ACE_SCORING_EVENT("onClick");
192 func->Execute(*clickInfo);
193 };
194 SpanModel::GetInstance()->SetOnClick(std::move(onClick));
195 return;
196 }
197 #ifndef NG_BUILD
198 if (info[0]->IsFunction()) {
199 auto inspector = ViewStackProcessor::GetInstance()->GetInspectorComposedComponent();
200 CHECK_NULL_VOID(inspector);
201 auto impl = inspector->GetInspectorFunctionImpl();
202 RefPtr<JsClickFunction> jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(info[0]));
203 auto clickFunc = [execCtx = info.GetExecutionContext(), func = std::move(jsOnClickFunc), impl](
204 const BaseEventInfo* info) {
205 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
206 LOGD("About to call onclick method on js");
207 const auto* clickInfo = TypeInfoHelper::DynamicCast<ClickInfo>(info);
208 auto newInfo = *clickInfo;
209 if (impl) {
210 impl->UpdateEventInfo(newInfo);
211 }
212 ACE_SCORING_EVENT("Span.onClick");
213 func->Execute(newInfo);
214 };
215 SpanModel::GetInstance()->SetOnClick(std::move(clickFunc));
216 }
217 #endif
218 }
219
JsRemoteMessage(const JSCallbackInfo & info)220 void JSSpan::JsRemoteMessage(const JSCallbackInfo& info)
221 {
222 #ifndef NG_BUILD
223 RemoteCallback remoteCallback;
224 JSInteractableView::JsRemoteMessage(info, remoteCallback);
225 EventMarker remoteMessageEventId(std::move(remoteCallback));
226 auto* stack = ViewStackProcessor::GetInstance();
227 auto textSpanComponent = AceType::DynamicCast<TextSpanComponent>(stack->GetMainComponent());
228 textSpanComponent->SetRemoteMessageEventId(remoteMessageEventId);
229 #endif
230 }
231
JSBind(BindingTarget globalObj)232 void JSSpan::JSBind(BindingTarget globalObj)
233 {
234 JSClass<JSSpan>::Declare("Span");
235 MethodOptions opt = MethodOptions::NONE;
236 JSClass<JSSpan>::StaticMethod("create", &JSSpan::Create, opt);
237 JSClass<JSSpan>::StaticMethod("fontColor", &JSSpan::SetTextColor, opt);
238 JSClass<JSSpan>::StaticMethod("fontSize", &JSSpan::SetFontSize, opt);
239 JSClass<JSSpan>::StaticMethod("fontWeight", &JSSpan::SetFontWeight, opt);
240 JSClass<JSSpan>::StaticMethod("fontStyle", &JSSpan::SetFontStyle, opt);
241 JSClass<JSSpan>::StaticMethod("fontFamily", &JSSpan::SetFontFamily, opt);
242 JSClass<JSSpan>::StaticMethod("letterSpacing", &JSSpan::SetLetterSpacing, opt);
243 JSClass<JSSpan>::StaticMethod("textCase", &JSSpan::SetTextCase, opt);
244 JSClass<JSSpan>::StaticMethod("decoration", &JSSpan::SetDecoration);
245 JSClass<JSSpan>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
246 JSClass<JSSpan>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
247 JSClass<JSSpan>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
248 JSClass<JSSpan>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
249 JSClass<JSSpan>::StaticMethod("remoteMessage", &JSSpan::JsRemoteMessage);
250 JSClass<JSSpan>::StaticMethod("onClick", &JSSpan::JsOnClick);
251 JSClass<JSSpan>::Inherit<JSContainerBase>();
252 JSClass<JSSpan>::Inherit<JSViewAbstract>();
253 JSClass<JSSpan>::Bind<>(globalObj);
254 }
255
Create(const JSCallbackInfo & info)256 void JSSpan::Create(const JSCallbackInfo& info)
257 {
258 std::string label;
259 if (info.Length() > 0) {
260 ParseJsString(info[0], label);
261 }
262 SpanModel::GetInstance()->Create(label);
263 }
264
265 } // namespace OHOS::Ace::Framework
266