• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_text.h"
17 
18 #include <sstream>
19 #include <string>
20 #include <vector>
21 
22 #include "base/geometry/dimension.h"
23 #include "base/log/ace_scoring_log.h"
24 #include "base/log/ace_trace.h"
25 #include "base/utils/utils.h"
26 #include "bridge/common/utils/utils.h"
27 #include "bridge/declarative_frontend/engine/functions/js_click_function.h"
28 #include "bridge/declarative_frontend/engine/functions/js_drag_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/text_model_impl.h"
32 #include "bridge/declarative_frontend/view_stack_processor.h"
33 #include "core/common/container.h"
34 #include "core/components/text/text_theme.h"
35 #include "core/components_ng/event/gesture_event_hub.h"
36 #include "core/components_ng/pattern/text/text_model.h"
37 #include "core/components_ng/pattern/text/text_model_ng.h"
38 #include "core/event/ace_event_handler.h"
39 
40 namespace OHOS::Ace {
41 
42 std::unique_ptr<TextModel> TextModel::instance_ = nullptr;
43 
GetInstance()44 TextModel* TextModel::GetInstance()
45 {
46     if (!instance_) {
47 #ifdef NG_BUILD
48         instance_.reset(new NG::TextModelNG());
49 #else
50         if (Container::IsCurrentUseNewPipeline()) {
51             instance_.reset(new NG::TextModelNG());
52         } else {
53             instance_.reset(new Framework::TextModelImpl());
54         }
55 #endif
56     }
57     return instance_.get();
58 }
59 
60 } // namespace OHOS::Ace
61 
62 namespace OHOS::Ace::Framework {
63 namespace {
64 
65 const std::vector<TextCase> TEXT_CASES = { TextCase::NORMAL, TextCase::LOWERCASE, TextCase::UPPERCASE };
66 const std::vector<TextOverflow> TEXT_OVERFLOWS = { TextOverflow::CLIP, TextOverflow::ELLIPSIS, TextOverflow::NONE };
67 const std::vector<FontStyle> FONT_STYLES = { FontStyle::NORMAL, FontStyle::ITALIC };
68 const std::vector<TextAlign> TEXT_ALIGNS = { TextAlign::START, TextAlign::CENTER, TextAlign::END, TextAlign::LEFT,
69     TextAlign::RIGHT, TextAlign::JUSTIFY };
70 
71 }; // namespace
72 
SetWidth(const JSCallbackInfo & info)73 void JSText::SetWidth(const JSCallbackInfo& info)
74 {
75     JSViewAbstract::JsWidth(info);
76     TextModel::GetInstance()->OnSetWidth();
77 }
78 
SetHeight(const JSCallbackInfo & info)79 void JSText::SetHeight(const JSCallbackInfo& info)
80 {
81     JSViewAbstract::JsHeight(info);
82     TextModel::GetInstance()->OnSetHeight();
83 }
84 
SetFontSize(const JSCallbackInfo & info)85 void JSText::SetFontSize(const JSCallbackInfo& info)
86 {
87     if (info.Length() < 1) {
88         LOGE("The argv is wrong, it is supposed to have at least 1 argument");
89         return;
90     }
91     Dimension fontSize;
92     if (!ParseJsDimensionFp(info[0], fontSize)) {
93         return;
94     }
95     if (fontSize.IsNegative() || fontSize.Unit() == DimensionUnit::PERCENT) {
96         auto pipelineContext = PipelineContext::GetCurrentContext();
97         CHECK_NULL_VOID_NOLOG(pipelineContext);
98         auto theme = pipelineContext->GetTheme<TextTheme>();
99         CHECK_NULL_VOID_NOLOG(theme);
100         TextModel::GetInstance()->SetFontSize(theme->GetTextStyle().GetFontSize());
101         return;
102     }
103     TextModel::GetInstance()->SetFontSize(fontSize);
104 }
105 
SetFontWeight(const std::string & value)106 void JSText::SetFontWeight(const std::string& value)
107 {
108     TextModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(value));
109 }
110 
SetTextColor(const JSCallbackInfo & info)111 void JSText::SetTextColor(const JSCallbackInfo& info)
112 {
113     if (info.Length() < 1) {
114         LOGE("The argv is wrong, it is supposed to have at least 1 argument");
115         return;
116     }
117     Color textColor;
118     if (!ParseJsColor(info[0], textColor)) {
119         return;
120     }
121     TextModel::GetInstance()->SetTextColor(textColor);
122 }
123 
SetTextOverflow(const JSCallbackInfo & info)124 void JSText::SetTextOverflow(const JSCallbackInfo& info)
125 {
126     do {
127         if (!info[0]->IsObject()) {
128             LOGE("info[0] not is Object");
129             break;
130         }
131         JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
132         JSRef<JSVal> overflowValue = obj->GetProperty("overflow");
133         if (!overflowValue->IsNumber()) {
134             LOGE("overflow value is not a number");
135             break;
136         }
137         auto overflow = overflowValue->ToNumber<int32_t>();
138         if (overflow < 0 || overflow >= static_cast<int32_t>(TEXT_OVERFLOWS.size())) {
139             LOGE("Text: textOverflow(%{public}d) illegal value", overflow);
140             break;
141         }
142         TextModel::GetInstance()->SetTextOverflow(TEXT_OVERFLOWS[overflow]);
143     } while (false);
144 
145     info.SetReturnValue(info.This());
146 }
147 
SetMaxLines(const JSCallbackInfo & info)148 void JSText::SetMaxLines(const JSCallbackInfo& info)
149 {
150     int32_t value;
151     if (info[0]->ToString() == "Infinity") {
152         value = Infinity<uint32_t>();
153     } else if (!info[0]->IsNumber()) {
154         return;
155     } else {
156         ParseJsInt32(info[0], value);
157     }
158     TextModel::GetInstance()->SetMaxLines(value);
159 }
160 
SetFontStyle(int32_t value)161 void JSText::SetFontStyle(int32_t value)
162 {
163     if (value < 0 || value >= static_cast<int32_t>(FONT_STYLES.size())) {
164         LOGE("Text fontStyle(%{public}d) illegal value", value);
165         return;
166     }
167     TextModel::GetInstance()->SetItalicFontStyle(FONT_STYLES[value]);
168 }
169 
SetTextAlign(int32_t value)170 void JSText::SetTextAlign(int32_t value)
171 {
172     if (value < 0 || value >= static_cast<int32_t>(TEXT_ALIGNS.size())) {
173         LOGE("Text: TextAlign(%d) expected positive number", value);
174         return;
175     }
176     TextModel::GetInstance()->SetTextAlign(TEXT_ALIGNS[value]);
177 }
178 
SetAlign(const JSCallbackInfo & info)179 void JSText::SetAlign(const JSCallbackInfo& info)
180 {
181     JSViewAbstract::JsAlign(info);
182     if (!info[0]->IsNumber()) {
183         return;
184     }
185     TextModel::GetInstance()->OnSetAlign();
186 }
187 
SetLineHeight(const JSCallbackInfo & info)188 void JSText::SetLineHeight(const JSCallbackInfo& info)
189 {
190     if (info.Length() < 1) {
191         LOGE("The argv is wrong, it is supposed to have at least 1 argument");
192         return;
193     }
194     Dimension value;
195     if (!ParseJsDimensionFp(info[0], value)) {
196         return;
197     }
198     TextModel::GetInstance()->SetLineHeight(value);
199 }
200 
SetFontFamily(const JSCallbackInfo & info)201 void JSText::SetFontFamily(const JSCallbackInfo& info)
202 {
203     if (info.Length() < 1) {
204         LOGE("The argv is wrong, it is supposed to have at least 1 argument");
205         return;
206     }
207     std::vector<std::string> fontFamilies;
208     if (!ParseJsFontFamilies(info[0], fontFamilies)) {
209         LOGE("Parse FontFamilies failed");
210         return;
211     }
212     TextModel::GetInstance()->SetFontFamily(fontFamilies);
213 }
214 
SetMinFontSize(const JSCallbackInfo & info)215 void JSText::SetMinFontSize(const JSCallbackInfo& info)
216 {
217     if (info.Length() < 1) {
218         LOGE("The argv is wrong, it is supposed to have at least 1 argument");
219         return;
220     }
221     Dimension fontSize;
222     if (!ParseJsDimensionFp(info[0], fontSize)) {
223         return;
224     }
225     TextModel::GetInstance()->SetAdaptMinFontSize(fontSize);
226 }
227 
SetMaxFontSize(const JSCallbackInfo & info)228 void JSText::SetMaxFontSize(const JSCallbackInfo& info)
229 {
230     if (info.Length() < 1) {
231         LOGE("The argv is wrong, it is supposed to have at least 1 argument");
232         return;
233     }
234     Dimension fontSize;
235     if (!ParseJsDimensionFp(info[0], fontSize)) {
236         return;
237     }
238     TextModel::GetInstance()->SetAdaptMaxFontSize(fontSize);
239 }
240 
SetLetterSpacing(const JSCallbackInfo & info)241 void JSText::SetLetterSpacing(const JSCallbackInfo& info)
242 {
243     if (info.Length() < 1) {
244         LOGE("The argv is wrong, it is supposed to have at least 1 argument");
245         return;
246     }
247     Dimension value;
248     if (!ParseJsDimensionFp(info[0], value)) {
249         return;
250     }
251     TextModel::GetInstance()->SetLetterSpacing(value);
252 }
253 
SetTextCase(int32_t value)254 void JSText::SetTextCase(int32_t value)
255 {
256     if (value < 0 || value >= static_cast<int32_t>(TEXT_CASES.size())) {
257         LOGE("Text textCase(%{public}d) illegal value", value);
258         return;
259     }
260     TextModel::GetInstance()->SetTextCase(TEXT_CASES[value]);
261 }
262 
SetBaselineOffset(const JSCallbackInfo & info)263 void JSText::SetBaselineOffset(const JSCallbackInfo& info)
264 {
265     if (info.Length() < 1) {
266         LOGE("The argv is wrong, it is supposed to have at least 1 argument");
267         return;
268     }
269     Dimension value;
270     if (!ParseJsDimensionFp(info[0], value)) {
271         return;
272     }
273     TextModel::GetInstance()->SetBaselineOffset(value);
274 }
275 
SetDecoration(const JSCallbackInfo & info)276 void JSText::SetDecoration(const JSCallbackInfo& info)
277 {
278     do {
279         if (!info[0]->IsObject()) {
280             LOGE("info[0] not is Object");
281             break;
282         }
283         JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
284         JSRef<JSVal> typeValue = obj->GetProperty("type");
285         JSRef<JSVal> colorValue = obj->GetProperty("color");
286 
287         std::optional<TextDecoration> textDecoration;
288         if (typeValue->IsNumber()) {
289             textDecoration = static_cast<TextDecoration>(typeValue->ToNumber<int32_t>());
290         }
291         std::optional<Color> colorVal;
292         Color result;
293         if (ParseJsColor(colorValue, result)) {
294             colorVal = result;
295         }
296 
297         if (textDecoration) {
298             TextModel::GetInstance()->SetTextDecoration(textDecoration.value());
299         }
300         if (colorVal) {
301             TextModel::GetInstance()->SetTextDecorationColor(colorVal.value());
302         }
303     } while (false);
304     info.SetReturnValue(info.This());
305 }
306 
JsOnClick(const JSCallbackInfo & info)307 void JSText::JsOnClick(const JSCallbackInfo& info)
308 {
309     if (Container::IsCurrentUseNewPipeline()) {
310         JSInteractableView::JsOnClick(info);
311     } else {
312 #ifndef NG_BUILD
313         if (info[0]->IsFunction()) {
314             auto inspector = ViewStackProcessor::GetInstance()->GetInspectorComposedComponent();
315             auto impl = inspector ? inspector->GetInspectorFunctionImpl() : nullptr;
316             RefPtr<JsClickFunction> jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(info[0]));
317             auto onClickId = [execCtx = info.GetExecutionContext(), func = std::move(jsOnClickFunc), impl](
318                                  const BaseEventInfo* info) {
319                 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
320                 LOGD("About to call onclick method on js");
321                 const auto* clickInfo = TypeInfoHelper::DynamicCast<ClickInfo>(info);
322                 auto newInfo = *clickInfo;
323                 if (impl) {
324                     impl->UpdateEventInfo(newInfo);
325                 }
326                 ACE_SCORING_EVENT("Text.onClick");
327                 func->Execute(newInfo);
328             };
329             TextModel::GetInstance()->SetOnClick(std::move(onClickId));
330         }
331 #endif
332     }
333 }
334 
JsRemoteMessage(const JSCallbackInfo & info)335 void JSText::JsRemoteMessage(const JSCallbackInfo& info)
336 {
337     JSInteractableView::JsCommonRemoteMessage(info);
338     auto callback = JSInteractableView::GetRemoteMessageEventCallback(info);
339     TextModel::GetInstance()->SetRemoteMessage(std::move(callback));
340 }
341 
Create(const JSCallbackInfo & info)342 void JSText::Create(const JSCallbackInfo& info)
343 {
344     std::string data;
345     if (info.Length() > 0) {
346         ParseJsString(info[0], data);
347     }
348 
349     TextModel::GetInstance()->Create(data);
350 }
351 
SetCopyOption(const JSCallbackInfo & info)352 void JSText::SetCopyOption(const JSCallbackInfo& info)
353 {
354     if (info.Length() == 0) {
355         return;
356     }
357     auto copyOptions = CopyOptions::None;
358     if (info[0]->IsNumber()) {
359         auto emunNumber = info[0]->ToNumber<int>();
360         copyOptions = static_cast<CopyOptions>(emunNumber);
361     }
362     TextModel::GetInstance()->SetCopyOption(copyOptions);
363 }
364 
JsOnDragStart(const JSCallbackInfo & info)365 void JSText::JsOnDragStart(const JSCallbackInfo& info)
366 {
367     CHECK_NULL_VOID(info[0]->IsFunction());
368     RefPtr<JsDragFunction> jsOnDragStartFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::Cast(info[0]));
369     auto onDragStart = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragStartFunc)](
370                            const RefPtr<DragEvent>& info, const std::string& extraParams) -> NG::DragDropBaseInfo {
371         NG::DragDropBaseInfo itemInfo;
372         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, itemInfo);
373 
374         auto ret = func->Execute(info, extraParams);
375         if (!ret->IsObject()) {
376             LOGE("builder param is not an object.");
377             return itemInfo;
378         }
379         auto node = ParseDragNode(ret);
380         if (node) {
381             LOGI("use custom builder param.");
382             itemInfo.node = node;
383             return itemInfo;
384         }
385 
386         auto builderObj = JSRef<JSObject>::Cast(ret);
387 #if defined(PIXEL_MAP_SUPPORTED)
388         auto pixmap = builderObj->GetProperty("pixelMap");
389         itemInfo.pixelMap = CreatePixelMapFromNapiValue(pixmap);
390 #endif
391         auto extraInfo = builderObj->GetProperty("extraInfo");
392         ParseJsString(extraInfo, itemInfo.extraInfo);
393         node = ParseDragNode(builderObj->GetProperty("builder"));
394         itemInfo.node = node;
395         return itemInfo;
396     };
397 
398     TextModel::GetInstance()->SetOnDragStart(std::move(onDragStart));
399 }
400 
JsOnDragEnter(const JSCallbackInfo & info)401 void JSText::JsOnDragEnter(const JSCallbackInfo& info)
402 {
403     CHECK_NULL_VOID(info[0]->IsFunction());
404     RefPtr<JsDragFunction> jsOnDragEnterFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::Cast(info[0]));
405     auto onDragEnterId = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragEnterFunc)](
406                              const RefPtr<DragEvent>& info, const std::string& extraParams) {
407         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
408         ACE_SCORING_EVENT("onDragEnter");
409         func->Execute(info, extraParams);
410     };
411     TextModel::GetInstance()->SetOnDragEnter(std::move(onDragEnterId));
412 }
413 
JsOnDragMove(const JSCallbackInfo & info)414 void JSText::JsOnDragMove(const JSCallbackInfo& info)
415 {
416     CHECK_NULL_VOID(info[0]->IsFunction());
417     RefPtr<JsDragFunction> jsOnDragMoveFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::Cast(info[0]));
418     auto onDragMoveId = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragMoveFunc)](
419                             const RefPtr<DragEvent>& info, const std::string& extraParams) {
420         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
421         ACE_SCORING_EVENT("onDragMove");
422         func->Execute(info, extraParams);
423     };
424     TextModel::GetInstance()->SetOnDragMove(std::move(onDragMoveId));
425 }
426 
JsOnDragLeave(const JSCallbackInfo & info)427 void JSText::JsOnDragLeave(const JSCallbackInfo& info)
428 {
429     CHECK_NULL_VOID(info[0]->IsFunction());
430     RefPtr<JsDragFunction> jsOnDragLeaveFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::Cast(info[0]));
431     auto onDragLeaveId = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragLeaveFunc)](
432                              const RefPtr<DragEvent>& info, const std::string& extraParams) {
433         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
434         ACE_SCORING_EVENT("onDragLeave");
435         func->Execute(info, extraParams);
436     };
437     TextModel::GetInstance()->SetOnDragLeave(std::move(onDragLeaveId));
438 }
439 
JsOnDrop(const JSCallbackInfo & info)440 void JSText::JsOnDrop(const JSCallbackInfo& info)
441 {
442     CHECK_NULL_VOID(info[0]->IsFunction());
443     RefPtr<JsDragFunction> jsOnDropFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::Cast(info[0]));
444     auto onDropId = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDropFunc)](
445                         const RefPtr<DragEvent>& info, const std::string& extraParams) {
446         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
447         ACE_SCORING_EVENT("onDrop");
448         func->Execute(info, extraParams);
449     };
450     TextModel::GetInstance()->SetOnDrop(std::move(onDropId));
451 }
452 
JsFocusable(const JSCallbackInfo & info)453 void JSText::JsFocusable(const JSCallbackInfo& info)
454 {
455     if (!info[0]->IsBoolean()) {
456         LOGE("The info is wrong, it is supposed to be an boolean");
457         return;
458     }
459     JSInteractableView::SetFocusable(info[0]->ToBoolean());
460     JSInteractableView::SetFocusNode(false);
461 }
462 
JSBind(BindingTarget globalObj)463 void JSText::JSBind(BindingTarget globalObj)
464 {
465     JSClass<JSText>::Declare("Text");
466     MethodOptions opt = MethodOptions::NONE;
467     JSClass<JSText>::StaticMethod("create", &JSText::Create, opt);
468     JSClass<JSText>::StaticMethod("width", &JSText::SetWidth);
469     JSClass<JSText>::StaticMethod("height", &JSText::SetHeight);
470     JSClass<JSText>::StaticMethod("fontColor", &JSText::SetTextColor, opt);
471     JSClass<JSText>::StaticMethod("fontSize", &JSText::SetFontSize, opt);
472     JSClass<JSText>::StaticMethod("fontWeight", &JSText::SetFontWeight, opt);
473     JSClass<JSText>::StaticMethod("maxLines", &JSText::SetMaxLines, opt);
474     JSClass<JSText>::StaticMethod("textOverflow", &JSText::SetTextOverflow, opt);
475     JSClass<JSText>::StaticMethod("fontStyle", &JSText::SetFontStyle, opt);
476     JSClass<JSText>::StaticMethod("align", &JSText::SetAlign, opt);
477     JSClass<JSText>::StaticMethod("textAlign", &JSText::SetTextAlign, opt);
478     JSClass<JSText>::StaticMethod("lineHeight", &JSText::SetLineHeight, opt);
479     JSClass<JSText>::StaticMethod("fontFamily", &JSText::SetFontFamily, opt);
480     JSClass<JSText>::StaticMethod("minFontSize", &JSText::SetMinFontSize, opt);
481     JSClass<JSText>::StaticMethod("maxFontSize", &JSText::SetMaxFontSize, opt);
482     JSClass<JSText>::StaticMethod("letterSpacing", &JSText::SetLetterSpacing, opt);
483     JSClass<JSText>::StaticMethod("textCase", &JSText::SetTextCase, opt);
484     JSClass<JSText>::StaticMethod("baselineOffset", &JSText::SetBaselineOffset, opt);
485     JSClass<JSText>::StaticMethod("decoration", &JSText::SetDecoration);
486     JSClass<JSText>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
487     JSClass<JSText>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
488     JSClass<JSText>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
489     JSClass<JSText>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
490     JSClass<JSText>::StaticMethod("remoteMessage", &JSText::JsRemoteMessage);
491     JSClass<JSText>::StaticMethod("copyOption", &JSText::SetCopyOption);
492     JSClass<JSText>::StaticMethod("onClick", &JSText::JsOnClick);
493     JSClass<JSText>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
494     JSClass<JSText>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
495     JSClass<JSText>::StaticMethod("onDragStart", &JSText::JsOnDragStart);
496     JSClass<JSText>::StaticMethod("onDragEnter", &JSText::JsOnDragEnter);
497     JSClass<JSText>::StaticMethod("onDragMove", &JSText::JsOnDragMove);
498     JSClass<JSText>::StaticMethod("onDragLeave", &JSText::JsOnDragLeave);
499     JSClass<JSText>::StaticMethod("onDrop", &JSText::JsOnDrop);
500     JSClass<JSText>::StaticMethod("focusable", &JSText::JsFocusable);
501     JSClass<JSText>::Inherit<JSContainerBase>();
502     JSClass<JSText>::Inherit<JSViewAbstract>();
503     JSClass<JSText>::Bind<>(globalObj);
504 }
505 
506 } // namespace OHOS::Ace::Framework
507