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_text.h"
17
18 #include <cstdint>
19 #include <sstream>
20 #include <string>
21 #include <vector>
22 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
23 #include "core/components_ng/pattern/text/text_layout_property.h"
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/ark_theme/theme_apply/js_theme_utils.h"
33 #include "bridge/declarative_frontend/engine/functions/js_click_function.h"
34 #include "bridge/declarative_frontend/engine/functions/js_drag_function.h"
35 #include "bridge/declarative_frontend/engine/functions/js_function.h"
36 #include "bridge/declarative_frontend/engine/jsi/js_ui_index.h"
37 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
38 #include "bridge/declarative_frontend/jsview/js_layout_manager.h"
39 #include "bridge/declarative_frontend/jsview/js_text.h"
40 #include "bridge/declarative_frontend/jsview/js_utils.h"
41 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
42 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
43 #include "bridge/declarative_frontend/jsview/models/text_model_impl.h"
44 #include "bridge/declarative_frontend/style_string/js_span_string.h"
45 #include "bridge/declarative_frontend/view_stack_processor.h"
46 #include "core/common/container.h"
47 #include "core/components/common/layout/constants.h"
48 #include "core/components/common/properties/text_style_parser.h"
49 #include "core/components_ng/pattern/text/text_model_ng.h"
50 #include "core/event/ace_event_handler.h"
51 #include "core/pipeline/pipeline_base.h"
52 #include "core/text/text_emoji_processor.h"
53
54 namespace OHOS::Ace {
55
56 std::unique_ptr<TextModel> TextModel::instance_ = nullptr;
57 std::mutex TextModel::mutex_;
58
GetInstance()59 TextModel* TextModel::GetInstance()
60 {
61 #ifdef NG_BUILD
62 static NG::TextModelNG instance;
63 return &instance;
64 #else
65 if (Container::IsCurrentUseNewPipeline()) {
66 static NG::TextModelNG instance;
67 return &instance;
68 } else {
69 static Framework::TextModelImpl instance;
70 return &instance;
71 }
72 #endif
73 }
74
75 } // namespace OHOS::Ace
76
77 namespace OHOS::Ace::Framework {
78 namespace {
79
80 const std::vector<TextCase> TEXT_CASES = { TextCase::NORMAL, TextCase::LOWERCASE, TextCase::UPPERCASE };
81 const std::vector<TextOverflow> TEXT_OVERFLOWS = { TextOverflow::NONE, TextOverflow::CLIP, TextOverflow::ELLIPSIS,
82 TextOverflow::MARQUEE };
83 const std::vector<FontStyle> FONT_STYLES = { FontStyle::NORMAL, FontStyle::ITALIC };
84 const std::vector<TextAlign> TEXT_ALIGNS = { TextAlign::START, TextAlign::CENTER, TextAlign::END, TextAlign::JUSTIFY,
85 TextAlign::LEFT, TextAlign::RIGHT };
86 const std::vector<TextHeightAdaptivePolicy> HEIGHT_ADAPTIVE_POLICY = { TextHeightAdaptivePolicy::MAX_LINES_FIRST,
87 TextHeightAdaptivePolicy::MIN_FONT_SIZE_FIRST, TextHeightAdaptivePolicy::LAYOUT_CONSTRAINT_FIRST };
88 const std::vector<LineBreakStrategy> LINE_BREAK_STRATEGY_TYPES = { LineBreakStrategy::GREEDY,
89 LineBreakStrategy::HIGH_QUALITY, LineBreakStrategy::BALANCED };
90 const std::vector<EllipsisMode> ELLIPSIS_MODALS = { EllipsisMode::HEAD, EllipsisMode::MIDDLE, EllipsisMode::TAIL };
91 const std::vector<TextSelectableMode> TEXT_SELECTABLE_MODE = { TextSelectableMode::SELECTABLE_UNFOCUSABLE,
92 TextSelectableMode::SELECTABLE_FOCUSABLE, TextSelectableMode::UNSELECTABLE };
93 constexpr TextDecorationStyle DEFAULT_TEXT_DECORATION_STYLE = TextDecorationStyle::SOLID;
94 const int32_t DEFAULT_VARIABLE_FONT_WEIGHT = 400;
95 }; // namespace
96
SetWidth(const JSCallbackInfo & info)97 void JSText::SetWidth(const JSCallbackInfo& info)
98 {
99 JSViewAbstract::JsWidth(info);
100 TextModel::GetInstance()->OnSetWidth();
101 }
102
SetHeight(const JSCallbackInfo & info)103 void JSText::SetHeight(const JSCallbackInfo& info)
104 {
105 JSViewAbstract::JsHeight(info);
106 TextModel::GetInstance()->OnSetHeight();
107 }
108
SetFont(const JSCallbackInfo & info)109 void JSText::SetFont(const JSCallbackInfo& info)
110 {
111 Font font;
112 auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
113 CHECK_NULL_VOID(pipelineContext);
114 auto theme = pipelineContext->GetTheme<TextTheme>();
115 CHECK_NULL_VOID(theme);
116 font.fontSize = theme->GetTextStyle().GetFontSize();
117 font.fontWeight = theme->GetTextStyle().GetFontWeight();
118 font.fontFamilies = theme->GetTextStyle().GetFontFamilies();
119 font.fontStyle = theme->GetTextStyle().GetFontStyle();
120 GetFontInfo(info, font);
121 TextModel::GetInstance()->SetFont(font);
122 if (info.Length() < 2) { // 2 : two args
123 return;
124 }
125 auto tmpInfo = info[1];
126 if (!tmpInfo->IsObject()) {
127 return;
128 }
129 auto paramObject = JSRef<JSObject>::Cast(tmpInfo);
130 auto enableVariableFontWeight = paramObject->GetProperty("enableVariableFontWeight");
131 if (enableVariableFontWeight->IsBoolean()) {
132 TextModel::GetInstance()->SetEnableVariableFontWeight(enableVariableFontWeight->ToBoolean());
133 } else {
134 TextModel::GetInstance()->SetEnableVariableFontWeight(false);
135 }
136 }
137
GetFontInfo(const JSCallbackInfo & info,Font & font)138 void JSText::GetFontInfo(const JSCallbackInfo& info, Font& font)
139 {
140 auto tmpInfo = info[0];
141 if (!tmpInfo->IsObject()) {
142 return;
143 }
144 auto paramObject = JSRef<JSObject>::Cast(tmpInfo);
145 auto fontSize = paramObject->GetProperty(static_cast<int32_t>(ArkUIIndex::SIZE));
146 CalcDimension size;
147 if (ParseJsDimensionFpNG(fontSize, size, false) && size.IsNonNegative()) {
148 font.fontSize = size;
149 }
150 std::string weight;
151 auto fontWeight = paramObject->GetProperty(static_cast<int32_t>(ArkUIIndex::WEIGHT));
152 if (!fontWeight->IsNull()) {
153 int32_t variableFontWeight = DEFAULT_VARIABLE_FONT_WEIGHT;
154 ParseJsInt32(fontWeight, variableFontWeight);
155 TextModel::GetInstance()->SetVariableFontWeight(variableFontWeight);
156 if (fontWeight->IsNumber()) {
157 weight = std::to_string(fontWeight->ToNumber<int32_t>());
158 } else {
159 JSContainerBase::ParseJsString(fontWeight, weight);
160 }
161 font.fontWeight = ConvertStrToFontWeight(weight);
162 }
163 auto fontFamily = paramObject->GetProperty(static_cast<int32_t>(ArkUIIndex::FAMILY));
164 if (!fontFamily->IsNull()) {
165 std::vector<std::string> fontFamilies;
166 if (JSContainerBase::ParseJsFontFamilies(fontFamily, fontFamilies)) {
167 font.fontFamilies = fontFamilies;
168 }
169 }
170 auto style = paramObject->GetProperty(static_cast<int32_t>(ArkUIIndex::STYLE));
171 if (!style->IsNull() || style->IsNumber()) {
172 font.fontStyle = static_cast<FontStyle>(style->ToNumber<int32_t>());
173 }
174 }
175
SetFontSize(const JSCallbackInfo & info)176 void JSText::SetFontSize(const JSCallbackInfo& info)
177 {
178 if (info.Length() < 1) {
179 return;
180 }
181 CalcDimension fontSize;
182 JSRef<JSVal> args = info[0];
183 if (!ParseJsDimensionFpNG(args, fontSize, false) || fontSize.IsNegative()) {
184 auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
185 CHECK_NULL_VOID(pipelineContext);
186 auto theme = pipelineContext->GetTheme<TextTheme>();
187 CHECK_NULL_VOID(theme);
188 fontSize = theme->GetTextStyle().GetFontSize();
189 TextModel::GetInstance()->SetFontSize(fontSize);
190 return;
191 }
192 TextModel::GetInstance()->SetFontSize(fontSize);
193 }
194
SetFontWeight(const JSCallbackInfo & info)195 void JSText::SetFontWeight(const JSCallbackInfo& info)
196 {
197 if (info.Length() < 1) {
198 return;
199 }
200 JSRef<JSVal> args = info[0];
201 std::string fontWeight;
202 int32_t variableFontWeight = DEFAULT_VARIABLE_FONT_WEIGHT;
203 ParseJsInt32(args, variableFontWeight);
204 TextModel::GetInstance()->SetVariableFontWeight(variableFontWeight);
205
206 if (args->IsNumber()) {
207 fontWeight = args->ToString();
208 } else {
209 ParseJsString(args, fontWeight);
210 }
211 TextModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(fontWeight));
212
213 if (info.Length() < 2) { // 2 : two args
214 return;
215 }
216 auto tmpInfo = info[1];
217 if (!tmpInfo->IsObject()) {
218 return;
219 }
220 auto paramObject = JSRef<JSObject>::Cast(tmpInfo);
221 auto enableVariableFontWeight = paramObject->GetProperty("enableVariableFontWeight");
222 if (enableVariableFontWeight->IsBoolean()) {
223 TextModel::GetInstance()->SetEnableVariableFontWeight(enableVariableFontWeight->ToBoolean());
224 } else {
225 TextModel::GetInstance()->SetEnableVariableFontWeight(false);
226 }
227 }
228
SetMinFontScale(const JSCallbackInfo & info)229 void JSText::SetMinFontScale(const JSCallbackInfo& info)
230 {
231 double minFontScale;
232 if (info.Length() < 1 || !ParseJsDouble(info[0], minFontScale)) {
233 return;
234 }
235 if (LessOrEqual(minFontScale, 0.0f)) {
236 TextModel::GetInstance()->SetMinFontScale(0.0f);
237 return;
238 }
239 if (GreatOrEqual(minFontScale, 1.0f)) {
240 TextModel::GetInstance()->SetMinFontScale(1.0f);
241 return;
242 }
243 TextModel::GetInstance()->SetMinFontScale(static_cast<float>(minFontScale));
244 }
245
SetMaxFontScale(const JSCallbackInfo & info)246 void JSText::SetMaxFontScale(const JSCallbackInfo& info)
247 {
248 double maxFontScale;
249 if (info.Length() < 1 || !ParseJsDouble(info[0], maxFontScale)) {
250 return;
251 }
252 if (LessOrEqual(maxFontScale, 1.0f)) {
253 TextModel::GetInstance()->SetMaxFontScale(1.0f);
254 return;
255 }
256 TextModel::GetInstance()->SetMaxFontScale(static_cast<float>(maxFontScale));
257 }
258
SetForegroundColor(const JSCallbackInfo & info)259 void JSText::SetForegroundColor(const JSCallbackInfo& info)
260 {
261 if (info.Length() < 1) {
262 return;
263 }
264 ForegroundColorStrategy strategy;
265 if (ParseJsColorStrategy(info[0], strategy)) {
266 TextModel::GetInstance()->SetTextColor(Color::FOREGROUND);
267 ViewAbstractModel::GetInstance()->SetForegroundColorStrategy(strategy);
268 return;
269 }
270 SetTextColor(info);
271 }
272
SetTextColor(const JSCallbackInfo & info)273 void JSText::SetTextColor(const JSCallbackInfo& info)
274 {
275 if (info.Length() < 1) {
276 return;
277 }
278 Color textColor;
279 JSRef<JSVal> args = info[0];
280 if (!ParseJsColor(args, textColor)) {
281 TextModel::GetInstance()->ResetTextColor();
282 return;
283 }
284 TextModel::GetInstance()->SetTextColor(textColor);
285 }
286
SetTextShadow(const JSCallbackInfo & info)287 void JSText::SetTextShadow(const JSCallbackInfo& info)
288 {
289 if (info.Length() < 1) {
290 return;
291 }
292 std::vector<Shadow> shadows;
293 JSRef<JSVal> args = info[0];
294 ParseTextShadowFromShadowObject(args, shadows);
295 TextModel::GetInstance()->SetTextShadow(shadows);
296 }
297
SetTextOverflow(const JSCallbackInfo & info)298 void JSText::SetTextOverflow(const JSCallbackInfo& info)
299 {
300 do {
301 auto tmpInfo = info[0];
302 if (!tmpInfo->IsObject()) {
303 break;
304 }
305 JSRef<JSObject> obj = JSRef<JSObject>::Cast(tmpInfo);
306 JSRef<JSVal> overflowValue = obj->GetProperty("overflow");
307 if (!overflowValue->IsNumber() && !overflowValue->IsUndefined()) {
308 break;
309 }
310 auto overflow = overflowValue->ToNumber<int32_t>();
311 if(overflowValue->IsUndefined()) {
312 overflow = 0;
313 } else if (overflow < 0 || overflow >= static_cast<int32_t>(TEXT_OVERFLOWS.size())) {
314 break;
315 }
316 TextModel::GetInstance()->SetTextOverflow(TEXT_OVERFLOWS[overflow]);
317 } while (false);
318
319 info.SetReturnValue(info.This());
320 }
321
SetWordBreak(const JSCallbackInfo & info)322 void JSText::SetWordBreak(const JSCallbackInfo& info)
323 {
324 JSRef<JSVal> args = info[0];
325 if (!args->IsNumber()) {
326 return;
327 }
328 uint32_t index = args->ToNumber<uint32_t>();
329 if (index < WORD_BREAK_TYPES.size()) {
330 TextModel::GetInstance()->SetWordBreak(WORD_BREAK_TYPES[index]);
331 }
332 }
333
SetEllipsisMode(const JSCallbackInfo & info)334 void JSText::SetEllipsisMode(const JSCallbackInfo& info)
335 {
336 JSRef<JSVal> args = info[0];
337 if (!args->IsNumber()) {
338 return;
339 }
340 uint32_t index = args->ToNumber<uint32_t>();
341 if (index < ELLIPSIS_MODALS.size()) {
342 TextModel::GetInstance()->SetEllipsisMode(ELLIPSIS_MODALS[index]);
343 }
344 }
345
SetLineBreakStrategy(const JSCallbackInfo & info)346 void JSText::SetLineBreakStrategy(const JSCallbackInfo& info)
347 {
348 if (info.Length() < 1) {
349 TextModel::GetInstance()->SetLineBreakStrategy(LineBreakStrategy::GREEDY);
350 return;
351 }
352 if (!info[0]->IsNumber()) {
353 TextModel::GetInstance()->SetLineBreakStrategy(LineBreakStrategy::GREEDY);
354 return;
355 }
356 auto index = info[0]->ToNumber<int32_t>();
357 if (index < 0 || index >= static_cast<int32_t>(LINE_BREAK_STRATEGY_TYPES.size())) {
358 TextModel::GetInstance()->SetLineBreakStrategy(LineBreakStrategy::GREEDY);
359 return;
360 }
361 TextModel::GetInstance()->SetLineBreakStrategy(LINE_BREAK_STRATEGY_TYPES[index]);
362 }
363
SetTextSelection(const JSCallbackInfo & info)364 void JSText::SetTextSelection(const JSCallbackInfo& info)
365 {
366 if (info.Length() < 1) {
367 return;
368 }
369 JSRef<JSVal> argsStartIndex = info[0];
370 JSRef<JSVal> argsEndIndex = info[1];
371 if (!argsStartIndex->IsNumber() || !argsEndIndex->IsNumber()) {
372 return;
373 }
374 auto startIndex = argsStartIndex->ToNumber<int32_t>();
375 auto endIndex = argsEndIndex->ToNumber<int32_t>();
376 TextModel::GetInstance()->SetTextSelection(startIndex, endIndex);
377 }
378
SetTextCaretColor(const JSCallbackInfo & info)379 void JSText::SetTextCaretColor(const JSCallbackInfo& info)
380 {
381 if (info.Length() < 1) {
382 return;
383 }
384 Color caretColor;
385 if (!ParseJsColor(info[0], caretColor)) {
386 auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
387 CHECK_NULL_VOID(pipelineContext);
388 auto theme = pipelineContext->GetTheme<TextTheme>();
389 CHECK_NULL_VOID(theme);
390 caretColor = theme->GetCaretColor();
391 }
392 TextModel::GetInstance()->SetTextCaretColor(caretColor);
393 }
394
SetSelectedBackgroundColor(const JSCallbackInfo & info)395 void JSText::SetSelectedBackgroundColor(const JSCallbackInfo& info)
396 {
397 if (info.Length() < 1) {
398 return;
399 }
400 Color selectedColor;
401 if (!ParseJsColor(info[0], selectedColor)) {
402 auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
403 CHECK_NULL_VOID(pipelineContext);
404 auto theme = pipelineContext->GetTheme<TextTheme>();
405 CHECK_NULL_VOID(theme);
406 selectedColor = theme->GetSelectedColor();
407 }
408 // Alpha = 255 means opaque
409 if (selectedColor.GetAlpha() == JSThemeUtils::DEFAULT_ALPHA) {
410 // Default setting of 20% opacity
411 selectedColor = selectedColor.ChangeOpacity(JSThemeUtils::DEFAULT_OPACITY);
412 }
413 TextModel::GetInstance()->SetSelectedBackgroundColor(selectedColor);
414 }
415
SetTextSelectableMode(const JSCallbackInfo & info)416 void JSText::SetTextSelectableMode(const JSCallbackInfo& info)
417 {
418 if (info.Length() < 1) {
419 TextModel::GetInstance()->SetTextSelectableMode(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
420 return;
421 }
422 if (!info[0]->IsNumber()) {
423 TextModel::GetInstance()->SetTextSelectableMode(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
424 return;
425 }
426 auto index = info[0]->ToNumber<int32_t>();
427 if (index < 0 || index >= static_cast<int32_t>(TEXT_SELECTABLE_MODE.size())) {
428 TextModel::GetInstance()->SetTextSelectableMode(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
429 return;
430 }
431 TextModel::GetInstance()->SetTextSelectableMode(TEXT_SELECTABLE_MODE[index]);
432 }
433
SetMaxLines(const JSCallbackInfo & info)434 void JSText::SetMaxLines(const JSCallbackInfo& info)
435 {
436 JSRef<JSVal> args = info[0];
437 auto value = Infinity<int32_t>();
438 if (args->ToString() != "Infinity") {
439 ParseJsInt32(args, value);
440 }
441 TextModel::GetInstance()->SetMaxLines(value);
442 }
443
SetTextIndent(const JSCallbackInfo & info)444 void JSText::SetTextIndent(const JSCallbackInfo& info)
445 {
446 CalcDimension value;
447 JSRef<JSVal> args = info[0];
448 if (!ParseJsDimensionFpNG(args, value)) {
449 value.Reset();
450 TextModel::GetInstance()->SetTextIndent(value);
451 return;
452 }
453 TextModel::GetInstance()->SetTextIndent(value);
454 }
455
SetFontStyle(int32_t value)456 void JSText::SetFontStyle(int32_t value)
457 {
458 if (value < 0 || value >= static_cast<int32_t>(FONT_STYLES.size())) {
459 if (!(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE))) {
460 return;
461 }
462 value = 0;
463 }
464 TextModel::GetInstance()->SetItalicFontStyle(FONT_STYLES[value]);
465 }
466
SetTextAlign(int32_t value)467 void JSText::SetTextAlign(int32_t value)
468 {
469 if (value < 0 || value >= static_cast<int32_t>(TEXT_ALIGNS.size())) {
470 if (!(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE))) {
471 return;
472 }
473 value = 0;
474 }
475 TextModel::GetInstance()->SetTextAlign(TEXT_ALIGNS[value]);
476 }
477
SetAlign(const JSCallbackInfo & info)478 void JSText::SetAlign(const JSCallbackInfo& info)
479 {
480 JSViewAbstract::JsAlign(info);
481 JSRef<JSVal> args = info[0];
482 if (!args->IsNumber()) {
483 return;
484 }
485 TextModel::GetInstance()->OnSetAlign();
486 }
487
SetLineHeight(const JSCallbackInfo & info)488 void JSText::SetLineHeight(const JSCallbackInfo& info)
489 {
490 CalcDimension value;
491 JSRef<JSVal> args = info[0];
492 if (!ParseJsDimensionFpNG(args, value)) {
493 value.Reset();
494 TextModel::GetInstance()->SetLineHeight(value);
495 return;
496 }
497 if (value.IsNegative()) {
498 value.Reset();
499 }
500 TextModel::GetInstance()->SetLineHeight(value);
501 }
502
SetLineSpacing(const JSCallbackInfo & info)503 void JSText::SetLineSpacing(const JSCallbackInfo& info)
504 {
505 CalcDimension value;
506 JSRef<JSVal> args = info[0];
507 if (!ParseLengthMetricsToPositiveDimension(args, value)) {
508 value.Reset();
509 }
510 if (value.IsNegative()) {
511 value.Reset();
512 }
513 TextModel::GetInstance()->SetLineSpacing(value);
514 }
515
SetFontFamily(const JSCallbackInfo & info)516 void JSText::SetFontFamily(const JSCallbackInfo& info)
517 {
518 std::vector<std::string> fontFamilies;
519 JSRef<JSVal> args = info[0];
520 ParseJsFontFamilies(args, fontFamilies);
521 TextModel::GetInstance()->SetFontFamily(fontFamilies);
522 }
523
SetMinFontSize(const JSCallbackInfo & info)524 void JSText::SetMinFontSize(const JSCallbackInfo& info)
525 {
526 if (info.Length() < 1) {
527 return;
528 }
529 auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
530 CHECK_NULL_VOID(pipelineContext);
531 auto theme = pipelineContext->GetTheme<TextTheme>();
532 CHECK_NULL_VOID(theme);
533 CalcDimension minFontSize = theme->GetTextStyle().GetAdaptMinFontSize();
534 JSRef<JSVal> args = info[0];
535 if (!ParseJsDimensionFpNG(args, minFontSize, false)) {
536 minFontSize = theme->GetTextStyle().GetAdaptMinFontSize();
537 TextModel::GetInstance()->SetAdaptMinFontSize(minFontSize);
538 return;
539 }
540 if (minFontSize.IsNegative()) {
541 minFontSize = theme->GetTextStyle().GetAdaptMinFontSize();
542 }
543 TextModel::GetInstance()->SetAdaptMinFontSize(minFontSize);
544 }
545
SetMaxFontSize(const JSCallbackInfo & info)546 void JSText::SetMaxFontSize(const JSCallbackInfo& info)
547 {
548 if (info.Length() < 1) {
549 return;
550 }
551 auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
552 CHECK_NULL_VOID(pipelineContext);
553 auto theme = pipelineContext->GetTheme<TextTheme>();
554 CHECK_NULL_VOID(theme);
555 CalcDimension maxFontSize = theme->GetTextStyle().GetAdaptMaxFontSize();
556 JSRef<JSVal> args = info[0];
557 if (!ParseJsDimensionFpNG(args, maxFontSize, false)) {
558 maxFontSize = theme->GetTextStyle().GetAdaptMaxFontSize();
559 TextModel::GetInstance()->SetAdaptMaxFontSize(maxFontSize);
560 return;
561 }
562 if (maxFontSize.IsNegative()) {
563 maxFontSize = theme->GetTextStyle().GetAdaptMaxFontSize();
564 }
565 TextModel::GetInstance()->SetAdaptMaxFontSize(maxFontSize);
566 }
567
SetLetterSpacing(const JSCallbackInfo & info)568 void JSText::SetLetterSpacing(const JSCallbackInfo& info)
569 {
570 CalcDimension value;
571 JSRef<JSVal> args = info[0];
572 if (!ParseJsDimensionFpNG(args, value, false)) {
573 value.Reset();
574 TextModel::GetInstance()->SetLetterSpacing(value);
575 return;
576 }
577 TextModel::GetInstance()->SetLetterSpacing(value);
578 }
579
SetTextCase(int32_t value)580 void JSText::SetTextCase(int32_t value)
581 {
582 if (value < 0 || value >= static_cast<int32_t>(TEXT_CASES.size())) {
583 if (!(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE))) {
584 return;
585 }
586 value = 0;
587 }
588 TextModel::GetInstance()->SetTextCase(TEXT_CASES[value]);
589 }
590
SetBaselineOffset(const JSCallbackInfo & info)591 void JSText::SetBaselineOffset(const JSCallbackInfo& info)
592 {
593 CalcDimension value;
594 JSRef<JSVal> args = info[0];
595 if (!ParseJsDimensionFpNG(args, value, false)) {
596 value.Reset();
597 TextModel::GetInstance()->SetBaselineOffset(value);
598 return;
599 }
600 TextModel::GetInstance()->SetBaselineOffset(value);
601 }
602
SetDecoration(const JSCallbackInfo & info)603 void JSText::SetDecoration(const JSCallbackInfo& info)
604 {
605 auto tmpInfo = info[0];
606 if (tmpInfo->IsUndefined()) {
607 TextModel::GetInstance()->SetTextDecoration(TextDecoration::NONE);
608 info.ReturnSelf();
609 return;
610 }
611 if (!tmpInfo->IsObject()) {
612 info.ReturnSelf();
613 return;
614 }
615 JSRef<JSObject> obj = JSRef<JSObject>::Cast(tmpInfo);
616 JSRef<JSVal> typeValue = obj->GetProperty("type");
617 JSRef<JSVal> colorValue = obj->GetProperty("color");
618 JSRef<JSVal> styleValue = obj->GetProperty("style");
619
620 TextDecoration textDecoration;
621 if (typeValue->IsNumber()) {
622 textDecoration = static_cast<TextDecoration>(typeValue->ToNumber<int32_t>());
623 } else {
624 auto theme = GetTheme<TextTheme>();
625 CHECK_NULL_VOID(theme);
626 textDecoration = theme->GetTextStyle().GetTextDecoration();
627 }
628 Color result;
629 if (!ParseJsColor(colorValue, result)) {
630 auto theme = GetTheme<TextTheme>();
631 CHECK_NULL_VOID(theme);
632 if (Container::CurrentColorMode() == ColorMode::DARK) {
633 result = theme->GetTextStyle().GetTextColor();
634 } else {
635 result = theme->GetTextStyle().GetTextDecorationColor();
636 }
637 }
638 std::optional<TextDecorationStyle> textDecorationStyle;
639 if (styleValue->IsNumber()) {
640 textDecorationStyle = static_cast<TextDecorationStyle>(styleValue->ToNumber<int32_t>());
641 } else {
642 textDecorationStyle = DEFAULT_TEXT_DECORATION_STYLE;
643 }
644 TextModel::GetInstance()->SetTextDecoration(textDecoration);
645 TextModel::GetInstance()->SetTextDecorationColor(result);
646 if (textDecorationStyle) {
647 TextModel::GetInstance()->SetTextDecorationStyle(textDecorationStyle.value());
648 }
649 info.ReturnSelf();
650 }
651
SetHeightAdaptivePolicy(int32_t value)652 void JSText::SetHeightAdaptivePolicy(int32_t value)
653 {
654 if (value < 0 || value >= static_cast<int32_t>(HEIGHT_ADAPTIVE_POLICY.size())) {
655 if (!(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE))) {
656 return;
657 }
658 value = 0;
659 }
660 TextModel::GetInstance()->SetHeightAdaptivePolicy(HEIGHT_ADAPTIVE_POLICY[value]);
661 }
662
JsOnClick(const JSCallbackInfo & info)663 void JSText::JsOnClick(const JSCallbackInfo& info)
664 {
665 JSRef<JSVal> args = info[0];
666 if (Container::IsCurrentUseNewPipeline()) {
667 if (args->IsUndefined() && IsDisableEventVersion()) {
668 TextModel::GetInstance()->ClearOnClick();
669 return;
670 }
671 if (!args->IsFunction()) {
672 return;
673 }
674 auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
675 auto jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(args));
676 auto onClick = [execCtx = info.GetExecutionContext(), func = jsOnClickFunc, node = frameNode]
677 (BaseEventInfo* info) {
678 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
679 auto* clickInfo = TypeInfoHelper::DynamicCast<GestureEvent>(info);
680 ACE_SCORING_EVENT("Text.onClick");
681 PipelineContext::SetCallBackNode(node);
682 func->Execute(*clickInfo);
683 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
684 std::u16string label = u"";
685 auto frameNode = node.Upgrade();
686 if (frameNode) {
687 auto pattern = frameNode->GetPattern();
688 CHECK_NULL_VOID(pattern);
689 auto layoutProperty = pattern->GetLayoutProperty<NG::TextLayoutProperty>();
690 CHECK_NULL_VOID(layoutProperty);
691 label = layoutProperty->GetContent().value_or(u"");
692 }
693 JSInteractableView::ReportClickEvent(node, label);
694 #endif
695 };
696 double distanceThreshold = std::numeric_limits<double>::infinity();
697 if (info.Length() > 1 && info[1]->IsNumber()) {
698 distanceThreshold = info[1]->ToNumber<double>();
699 distanceThreshold = Dimension(distanceThreshold, DimensionUnit::VP).ConvertToPx();
700 }
701 TextModel::GetInstance()->SetOnClick(std::move(onClick), distanceThreshold);
702
703 auto focusHub = NG::ViewStackProcessor::GetInstance()->GetOrCreateMainFrameNodeFocusHub();
704 CHECK_NULL_VOID(focusHub);
705 focusHub->SetFocusable(true, false);
706 } else {
707 JsOnClickWithoutNGBUILD(info);
708 }
709 }
710
JsOnClickWithoutNGBUILD(const JSCallbackInfo & info)711 void JSText::JsOnClickWithoutNGBUILD(const JSCallbackInfo& info)
712 {
713 #ifndef NG_BUILD
714 JSRef<JSVal> args = info[0];
715 if (args->IsFunction()) {
716 auto inspector = ViewStackProcessor::GetInstance()->GetInspectorComposedComponent();
717 auto impl = inspector ? inspector->GetInspectorFunctionImpl() : nullptr;
718 auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
719 RefPtr<JsClickFunction> jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(args));
720 auto onClickId = [execCtx = info.GetExecutionContext(), func = std::move(jsOnClickFunc), impl,
721 node = frameNode](const BaseEventInfo* info) {
722 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
723 const auto* clickInfo = TypeInfoHelper::DynamicCast<ClickInfo>(info);
724 auto newInfo = *clickInfo;
725 if (impl) {
726 impl->UpdateEventInfo(newInfo);
727 }
728 ACE_SCORING_EVENT("Text.onClick");
729 PipelineContext::SetCallBackNode(node);
730 func->Execute(newInfo);
731 };
732 double distanceThreshold = std::numeric_limits<double>::infinity();
733 if (info.Length() > 1 && info[1]->IsNumber()) {
734 distanceThreshold = info[1]->ToNumber<double>();
735 distanceThreshold = Dimension(distanceThreshold, DimensionUnit::VP).ConvertToPx();
736 }
737 TextModel::GetInstance()->SetOnClick(std::move(onClickId), distanceThreshold);
738 }
739 #endif
740 }
741
JsRemoteMessage(const JSCallbackInfo & info)742 void JSText::JsRemoteMessage(const JSCallbackInfo& info)
743 {
744 JSInteractableView::JsCommonRemoteMessage(info);
745 auto callback = JSInteractableView::GetRemoteMessageEventCallback(info);
746 TextModel::GetInstance()->SetRemoteMessage(std::move(callback));
747 }
748
Create(const JSCallbackInfo & info)749 void JSText::Create(const JSCallbackInfo& info)
750 {
751 std::u16string data;
752 if (info.Length() <= 0) {
753 TextModel::GetInstance()->Create(data);
754 return;
755 }
756
757 JSRef<JSVal> args = info[0];
758 if (args->IsObject() && JSRef<JSObject>::Cast(args)->Unwrap<JSSpanString>()) {
759 auto *spanString = JSRef<JSObject>::Cast(args)->Unwrap<JSSpanString>();
760 if (spanString == nullptr) {
761 return;
762 }
763 auto spanStringController = spanString->GetController();
764 if (spanStringController) {
765 TextModel::GetInstance()->Create(spanStringController);
766 } else {
767 TextModel::GetInstance()->Create(data);
768 }
769 } else {
770 ParseJsString(args, data);
771 UtfUtils::HandleInvalidUTF16(reinterpret_cast<uint16_t*>(data.data()), data.length(), 0);
772 TextModel::GetInstance()->Create(data);
773 }
774
775 if (info.Length() <= 1 || !info[1]->IsObject()) {
776 return;
777 }
778
779 JSTextController* jsController = nullptr;
780 auto paramObject = JSRef<JSObject>::Cast(info[1]);
781 auto controllerObj = paramObject->GetProperty("controller");
782 if (!controllerObj->IsUndefined() && !controllerObj->IsNull() && controllerObj->IsObject()) {
783 jsController = JSRef<JSObject>::Cast(controllerObj)->Unwrap<JSTextController>();
784 }
785
786 RefPtr<TextControllerBase> controller = TextModel::GetInstance()->GetTextController();
787 if (jsController) {
788 jsController->SetController(controller);
789 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_FIFTEEN)) {
790 auto styledString = jsController->GetStyledString();
791 if (styledString) {
792 controller->SetStyledString(styledString, false);
793 jsController->ClearStyledString();
794 }
795 }
796 }
797 }
798
SetCopyOption(const JSCallbackInfo & info)799 void JSText::SetCopyOption(const JSCallbackInfo& info)
800 {
801 if (info.Length() == 0) {
802 return;
803 }
804 auto copyOptions = CopyOptions::None;
805 auto tmpInfo = info[0];
806 if (tmpInfo->IsNumber()) {
807 auto emunNumber = tmpInfo->ToNumber<int>();
808 copyOptions = static_cast<CopyOptions>(emunNumber);
809 }
810 TextModel::GetInstance()->SetCopyOption(copyOptions);
811 }
812
SetOnCopy(const JSCallbackInfo & info)813 void JSText::SetOnCopy(const JSCallbackInfo& info)
814 {
815 JSRef<JSVal> args = info[0];
816 CHECK_NULL_VOID(args->IsFunction());
817 JsEventCallback<void(const std::u16string&)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(args));
818 TextModel::GetInstance()->SetOnCopy(std::move(callback));
819 }
820
JsOnDragStart(const JSCallbackInfo & info)821 void JSText::JsOnDragStart(const JSCallbackInfo& info)
822 {
823 JSRef<JSVal> args = info[0];
824 CHECK_NULL_VOID(args->IsFunction());
825 RefPtr<JsDragFunction> jsOnDragStartFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::Cast(args));
826 WeakPtr<NG::FrameNode> frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
827 auto onDragStart = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragStartFunc),
828 targetNode = frameNode](
829 const RefPtr<DragEvent>& info, const std::string& extraParams) -> NG::DragDropBaseInfo {
830 NG::DragDropBaseInfo itemInfo;
831 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, itemInfo);
832 PipelineContext::SetCallBackNode(targetNode);
833 auto ret = func->Execute(info, extraParams);
834 if (!ret->IsObject()) {
835 return itemInfo;
836 }
837 auto node = ParseDragNode(ret);
838 if (node) {
839 itemInfo.node = node;
840 return itemInfo;
841 }
842 auto builderObj = JSRef<JSObject>::Cast(ret);
843 #if defined(PIXEL_MAP_SUPPORTED)
844 auto pixmap = builderObj->GetProperty("pixelMap");
845 itemInfo.pixelMap = CreatePixelMapFromNapiValue(pixmap);
846 #endif
847 auto extraInfo = builderObj->GetProperty("extraInfo");
848 ParseJsString(extraInfo, itemInfo.extraInfo);
849 node = ParseDragNode(builderObj->GetProperty("builder"));
850 itemInfo.node = node;
851 return itemInfo;
852 };
853
854 TextModel::GetInstance()->SetOnDragStart(std::move(onDragStart));
855 }
856
JsFocusable(const JSCallbackInfo & info)857 void JSText::JsFocusable(const JSCallbackInfo& info)
858 {
859 auto tmpInfo = info[0];
860 if (!tmpInfo->IsBoolean()) {
861 return;
862 }
863 JSInteractableView::SetFocusable(tmpInfo->ToBoolean());
864 JSInteractableView::SetFocusNode(false);
865 }
866
JsDraggable(const JSCallbackInfo & info)867 void JSText::JsDraggable(const JSCallbackInfo& info)
868 {
869 auto tmpInfo = info[0];
870 if (!tmpInfo->IsBoolean()) {
871 return;
872 }
873 ViewAbstractModel::GetInstance()->SetDraggable(tmpInfo->ToBoolean());
874 }
875
JsEnableDataDetector(const JSCallbackInfo & info)876 void JSText::JsEnableDataDetector(const JSCallbackInfo& info)
877 {
878 if (info.Length() < 1) {
879 return;
880 }
881 auto tmpInfo = info[0];
882 if (!tmpInfo->IsBoolean()) {
883 TextModel::GetInstance()->SetTextDetectEnable(false);
884 return;
885 }
886 auto enable = tmpInfo->ToBoolean();
887 TextModel::GetInstance()->SetTextDetectEnable(enable);
888 }
889
JsDataDetectorConfig(const JSCallbackInfo & info)890 void JSText::JsDataDetectorConfig(const JSCallbackInfo& info)
891 {
892 if (info.Length() < 1) {
893 return;
894 }
895 JSRef<JSVal> args = info[0];
896 if (!args->IsObject()) {
897 return;
898 }
899
900 TextDetectConfig textDetectConfig;
901 if (!ParseDataDetectorConfig(info, textDetectConfig)) {
902 return;
903 }
904 TextModel::GetInstance()->SetTextDetectConfig(textDetectConfig);
905 }
906
BindSelectionMenu(const JSCallbackInfo & info)907 void JSText::BindSelectionMenu(const JSCallbackInfo& info)
908 {
909 // TextSpanType
910 NG::TextSpanType textSpanType = NG::TextSpanType::TEXT;
911 bool isValidTextSpanType = true;
912 JSRef<JSVal> argsSpanType = info[0];
913 if (argsSpanType->IsNumber()) {
914 auto spanTypeId = argsSpanType->ToNumber<int32_t>();
915 isValidTextSpanType = NG::TextSpanTypeMapper::GetTextSpanTypeFromJsType(spanTypeId, textSpanType);
916 }
917
918 // Builder
919 JSRef<JSVal> argsMenuObj = info[1];
920 if (!argsMenuObj->IsObject()) {
921 return;
922 }
923 JSRef<JSObject> menuObj = JSRef<JSObject>::Cast(argsMenuObj);
924 auto builder = menuObj->GetProperty("builder");
925 if (!builder->IsFunction()) {
926 return;
927 }
928 auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(builder));
929 CHECK_NULL_VOID(builderFunc);
930
931 // TextResponseType
932 uint32_t resquiredParameterCount = 3;
933 JSRef<JSVal> argsResponse = info[resquiredParameterCount - 1];
934 NG::TextResponseType responseType = NG::TextResponseType::LONG_PRESS;
935 if (argsResponse->IsNumber()) {
936 auto response = argsResponse->ToNumber<int32_t>();
937 responseType = static_cast<NG::TextResponseType>(response);
938 }
939
940 WeakPtr<NG::FrameNode> frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
941 std::function<void()> buildFunc = [execCtx = info.GetExecutionContext(), func = std::move(builderFunc),
942 node = frameNode]() {
943 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
944 ACE_SCORING_EVENT("BindSelectionMenu");
945 PipelineContext::SetCallBackNode(node);
946 func->Execute();
947 };
948
949 // SelectionMenuOptions
950 NG::SelectMenuParam menuParam;
951 menuParam.isValid = isValidTextSpanType;
952 if (info.Length() > static_cast<uint32_t>(resquiredParameterCount)) {
953 JSRef<JSVal> argsMenuOptions = info[resquiredParameterCount];
954 if (argsMenuOptions->IsObject()) {
955 ParseMenuParam(info, argsMenuOptions, menuParam);
956 }
957 }
958
959 TextModel::GetInstance()->BindSelectionMenu(textSpanType, responseType, buildFunc, menuParam);
960 }
961
SetOnTextSelectionChange(const JSCallbackInfo & info)962 void JSText::SetOnTextSelectionChange(const JSCallbackInfo& info)
963 {
964 JSRef<JSVal> args = info[0];
965 CHECK_NULL_VOID(args->IsFunction());
966 JsEventCallback<void(int32_t, int32_t)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(args));
967 TextModel::GetInstance()->SetOnTextSelectionChange(std::move(callback));
968 }
969
JsClip(const JSCallbackInfo & info)970 void JSText::JsClip(const JSCallbackInfo& info)
971 {
972 JSViewAbstract::JsClip(info);
973 JSRef<JSVal> args = info[0];
974 if (args->IsBoolean()) {
975 TextModel::GetInstance()->SetClipEdge(args->ToBoolean());
976 }
977 }
978
SetFontFeature(const JSCallbackInfo & info)979 void JSText::SetFontFeature(const JSCallbackInfo& info)
980 {
981 if (info.Length() < 1) {
982 return;
983 }
984
985 if (!info[0]->IsString() && !info[0]->IsObject()) {
986 return;
987 }
988 std::string fontFeatureSettings = info[0]->ToString();
989 TextModel::GetInstance()->SetFontFeature(ParseFontFeatureSettings(fontFeatureSettings));
990 }
991
JsResponseRegion(const JSCallbackInfo & info)992 void JSText::JsResponseRegion(const JSCallbackInfo& info)
993 {
994 JSViewAbstract::JsResponseRegion(info);
995 TextModel::GetInstance()->SetResponseRegion(true);
996 }
997
SetHalfLeading(const JSCallbackInfo & info)998 void JSText::SetHalfLeading(const JSCallbackInfo& info)
999 {
1000 if (info.Length() < 1) {
1001 return;
1002 }
1003 auto halfLeading = info[0];
1004 if (!halfLeading->IsBoolean()) {
1005 TextModel::GetInstance()->SetHalfLeading(false);
1006 return;
1007 }
1008 auto enable = halfLeading->ToBoolean();
1009 TextModel::GetInstance()->SetHalfLeading(enable);
1010 }
1011
SetEnableHapticFeedback(const JSCallbackInfo & info)1012 void JSText::SetEnableHapticFeedback(const JSCallbackInfo& info)
1013 {
1014 bool state = true;
1015 if (info.Length() > 0 && info[0]->IsBoolean()) {
1016 state = info[0]->ToBoolean();
1017 }
1018 TextModel::GetInstance()->SetEnableHapticFeedback(state);
1019 }
1020
JSBind(BindingTarget globalObj)1021 void JSText::JSBind(BindingTarget globalObj)
1022 {
1023 JSClass<JSText>::Declare("Text");
1024 MethodOptions opt = MethodOptions::NONE;
1025 JSClass<JSText>::StaticMethod("create", &JSText::Create, opt);
1026 JSClass<JSText>::StaticMethod("width", &JSText::SetWidth);
1027 JSClass<JSText>::StaticMethod("height", &JSText::SetHeight);
1028 JSClass<JSText>::StaticMethod("font", &JSText::SetFont, opt);
1029 JSClass<JSText>::StaticMethod("fontColor", &JSText::SetTextColor, opt);
1030 JSClass<JSText>::StaticMethod("textShadow", &JSText::SetTextShadow, opt);
1031 JSClass<JSText>::StaticMethod("fontSize", &JSText::SetFontSize, opt);
1032 JSClass<JSText>::StaticMethod("fontWeight", &JSText::SetFontWeight, opt);
1033 JSClass<JSText>::StaticMethod("minFontScale", &JSText::SetMinFontScale, opt);
1034 JSClass<JSText>::StaticMethod("maxFontScale", &JSText::SetMaxFontScale, opt);
1035 JSClass<JSText>::StaticMethod("wordBreak", &JSText::SetWordBreak, opt);
1036 JSClass<JSText>::StaticMethod("lineBreakStrategy", &JSText::SetLineBreakStrategy, opt);
1037 JSClass<JSText>::StaticMethod("ellipsisMode", &JSText::SetEllipsisMode, opt);
1038 JSClass<JSText>::StaticMethod("selection", &JSText::SetTextSelection, opt);
1039 JSClass<JSText>::StaticMethod("textSelectable", &JSText::SetTextSelectableMode, opt);
1040 JSClass<JSText>::StaticMethod("maxLines", &JSText::SetMaxLines, opt);
1041 JSClass<JSText>::StaticMethod("textIndent", &JSText::SetTextIndent);
1042 JSClass<JSText>::StaticMethod("textOverflow", &JSText::SetTextOverflow, opt);
1043 JSClass<JSText>::StaticMethod("fontStyle", &JSText::SetFontStyle, opt);
1044 JSClass<JSText>::StaticMethod("align", &JSText::SetAlign, opt);
1045 JSClass<JSText>::StaticMethod("textAlign", &JSText::SetTextAlign, opt);
1046 JSClass<JSText>::StaticMethod("lineHeight", &JSText::SetLineHeight, opt);
1047 JSClass<JSText>::StaticMethod("lineSpacing", &JSText::SetLineSpacing, opt);
1048 JSClass<JSText>::StaticMethod("fontFamily", &JSText::SetFontFamily, opt);
1049 JSClass<JSText>::StaticMethod("minFontSize", &JSText::SetMinFontSize, opt);
1050 JSClass<JSText>::StaticMethod("maxFontSize", &JSText::SetMaxFontSize, opt);
1051 JSClass<JSText>::StaticMethod("letterSpacing", &JSText::SetLetterSpacing, opt);
1052 JSClass<JSText>::StaticMethod("textCase", &JSText::SetTextCase, opt);
1053 JSClass<JSText>::StaticMethod("baselineOffset", &JSText::SetBaselineOffset, opt);
1054 JSClass<JSText>::StaticMethod("caretColor", &JSText::SetTextCaretColor);
1055 JSClass<JSText>::StaticMethod("selectedBackgroundColor", &JSText::SetSelectedBackgroundColor);
1056 JSClass<JSText>::StaticMethod("decoration", &JSText::SetDecoration);
1057 JSClass<JSText>::StaticMethod("heightAdaptivePolicy", &JSText::SetHeightAdaptivePolicy);
1058 JSClass<JSText>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
1059 JSClass<JSText>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
1060 JSClass<JSText>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
1061 JSClass<JSText>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
1062 JSClass<JSText>::StaticMethod("remoteMessage", &JSText::JsRemoteMessage);
1063 JSClass<JSText>::StaticMethod("copyOption", &JSText::SetCopyOption);
1064 JSClass<JSText>::StaticMethod("onClick", &JSText::JsOnClick);
1065 JSClass<JSText>::StaticMethod("onCopy", &JSText::SetOnCopy);
1066 JSClass<JSText>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
1067 JSClass<JSText>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
1068 JSClass<JSText>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
1069 JSClass<JSText>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
1070 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_FIFTEEN)) {
1071 JSClass<JSText>::StaticMethod("onDragStart", &JSText::JsOnDragStart);
1072 } else {
1073 JSClass<JSText>::StaticMethod("onDragStart", &JSViewAbstract::JsOnDragStart);
1074 }
1075 JSClass<JSText>::StaticMethod("focusable", &JSText::JsFocusable);
1076 JSClass<JSText>::StaticMethod("draggable", &JSText::JsDraggable);
1077 JSClass<JSText>::StaticMethod("enableDataDetector", &JSText::JsEnableDataDetector);
1078 JSClass<JSText>::StaticMethod("dataDetectorConfig", &JSText::JsDataDetectorConfig);
1079 JSClass<JSText>::StaticMethod("bindSelectionMenu", &JSText::BindSelectionMenu);
1080 JSClass<JSText>::StaticMethod("onTextSelectionChange", &JSText::SetOnTextSelectionChange);
1081 JSClass<JSText>::StaticMethod("clip", &JSText::JsClip);
1082 JSClass<JSText>::StaticMethod("fontFeature", &JSText::SetFontFeature);
1083 JSClass<JSText>::StaticMethod("foregroundColor", &JSText::SetForegroundColor);
1084 JSClass<JSText>::StaticMethod("marqueeOptions", &JSText::SetMarqueeOptions);
1085 JSClass<JSText>::StaticMethod("onMarqueeStateChange", &JSText::SetOnMarqueeStateChange);
1086 JSClass<JSText>::StaticMethod("editMenuOptions", &JSText::EditMenuOptions);
1087 JSClass<JSText>::StaticMethod("responseRegion", &JSText::JsResponseRegion);
1088 JSClass<JSText>::StaticMethod("halfLeading", &JSText::SetHalfLeading);
1089 JSClass<JSText>::StaticMethod("enableHapticFeedback", &JSText::SetEnableHapticFeedback);
1090 JSClass<JSText>::InheritAndBind<JSContainerBase>(globalObj);
1091 }
1092
CloseSelectionMenu()1093 void JSTextController::CloseSelectionMenu()
1094 {
1095 auto controller = controllerWeak_.Upgrade();
1096 CHECK_NULL_VOID(controller);
1097 controller->CloseSelectionMenu();
1098 }
1099
GetLayoutManager(const JSCallbackInfo & args)1100 void JSTextController::GetLayoutManager(const JSCallbackInfo& args)
1101 {
1102 JSRef<JSObject> obj = JSClass<JSLayoutManager>::NewInstance();
1103 auto jsLayoutManager = Referenced::Claim(obj->Unwrap<JSLayoutManager>());
1104 CHECK_NULL_VOID(jsLayoutManager);
1105 jsLayoutManager->IncRefCount();
1106 auto controller = controllerWeak_.Upgrade();
1107 CHECK_NULL_VOID(controller);
1108 auto layoutInfoInterface = controller->GetLayoutInfoInterface();
1109 jsLayoutManager->SetLayoutInfoInterface(layoutInfoInterface);
1110 args.SetReturnValue(obj);
1111 }
1112
SetStyledString(const JSCallbackInfo & info)1113 void JSTextController::SetStyledString(const JSCallbackInfo& info)
1114 {
1115 if (info.Length() != 1 || !info[0]->IsObject()) {
1116 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
1117 return;
1118 }
1119 auto* spanString = JSRef<JSObject>::Cast(info[0])->Unwrap<JSSpanString>();
1120 if (!spanString) {
1121 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
1122 return;
1123 }
1124 auto spanStringController = spanString->GetController();
1125 CHECK_NULL_VOID(spanStringController);
1126 auto controller = controllerWeak_.Upgrade();
1127 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_FIFTEEN) && !controller) {
1128 styledString_ = spanStringController;
1129 }
1130 CHECK_NULL_VOID(controller);
1131 controller->SetStyledString(spanStringController, true);
1132 auto thisObj = info.This();
1133 thisObj->SetPropertyObject("STYLED_STRING_IN_CONTROLLER", info[0]);
1134 }
1135
JSBind(BindingTarget globalObj)1136 void JSTextController::JSBind(BindingTarget globalObj)
1137 {
1138 JSClass<JSTextController>::Declare("TextController");
1139 JSClass<JSTextController>::Method("closeSelectionMenu", &JSTextController::CloseSelectionMenu);
1140 JSClass<JSTextController>::CustomMethod("setStyledString", &JSTextController::SetStyledString);
1141 JSClass<JSTextController>::CustomMethod("getLayoutManager", &JSTextController::GetLayoutManager);
1142 JSClass<JSTextController>::Bind(globalObj, JSTextController::Constructor, JSTextController::Destructor);
1143 }
1144
ParseMenuParam(const JSCallbackInfo & info,const JSRef<JSObject> & menuOptions,NG::SelectMenuParam & menuParam)1145 void JSText::ParseMenuParam(
1146 const JSCallbackInfo& info, const JSRef<JSObject>& menuOptions, NG::SelectMenuParam& menuParam)
1147 {
1148 WeakPtr<NG::FrameNode> frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
1149 auto onAppearValue = menuOptions->GetProperty("onAppear");
1150 if (onAppearValue->IsFunction()) {
1151 RefPtr<JsFunction> jsOnAppearFunc =
1152 AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onAppearValue));
1153 auto onAppear = [execCtx = info.GetExecutionContext(), func = std::move(jsOnAppearFunc), node = frameNode](
1154 int32_t start, int32_t end) {
1155 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
1156 ACE_SCORING_EVENT("onAppear");
1157
1158 JSRef<JSVal> params[2];
1159 params[0] = JSRef<JSVal>::Make(ToJSValue(start));
1160 params[1] = JSRef<JSVal>::Make(ToJSValue(end));
1161 PipelineContext::SetCallBackNode(node);
1162 func->ExecuteJS(2, params);
1163 };
1164 menuParam.onAppear = std::move(onAppear);
1165 }
1166
1167 auto onDisappearValue = menuOptions->GetProperty("onDisappear");
1168 if (onDisappearValue->IsFunction()) {
1169 RefPtr<JsFunction> jsOnDisAppearFunc =
1170 AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onDisappearValue));
1171 auto onDisappear = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDisAppearFunc),
1172 node = frameNode]() {
1173 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
1174 ACE_SCORING_EVENT("onDisappear");
1175 PipelineContext::SetCallBackNode(node);
1176 func->Execute();
1177 };
1178 menuParam.onDisappear = std::move(onDisappear);
1179 }
1180 menuParam.onMenuShow = ParseMenuCallback(frameNode, menuOptions, info, "onMenuShow");
1181 menuParam.onMenuHide = ParseMenuCallback(frameNode, menuOptions, info, "onMenuHide");
1182 menuParam.previewMenuOptions = ParsePreviewMenuOptions(menuOptions);
1183 }
1184
ParseMenuCallback(const WeakPtr<NG::FrameNode> & frameNode,const JSRef<JSObject> & menuOptions,const JSCallbackInfo & info,const std::string & name)1185 std::function<void(int32_t, int32_t)> JSText::ParseMenuCallback(const WeakPtr<NG::FrameNode>& frameNode,
1186 const JSRef<JSObject>& menuOptions, const JSCallbackInfo& info, const std::string& name)
1187 {
1188 auto onMenuCallbackValue = menuOptions->GetProperty(name.c_str());
1189 if (onMenuCallbackValue->IsFunction()) {
1190 RefPtr<JsFunction> jsOnMenuCallbackFunc =
1191 AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onMenuCallbackValue));
1192 auto onMenuCallback = [execCtx = info.GetExecutionContext(), func = std::move(jsOnMenuCallbackFunc),
1193 node = frameNode, eventName = name](int32_t start, int32_t end) {
1194 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
1195 ACE_SCORING_EVENT(eventName);
1196
1197 JSRef<JSVal> params[2];
1198 params[0] = JSRef<JSVal>::Make(ToJSValue(start));
1199 params[1] = JSRef<JSVal>::Make(ToJSValue(end));
1200 PipelineContext::SetCallBackNode(node);
1201 func->ExecuteJS(2, params);
1202 };
1203 return onMenuCallback;
1204 }
1205 return nullptr;
1206 }
1207
ParsePreviewMenuOptions(const JSRef<JSObject> & menuOptions)1208 NG::PreviewMenuOptions JSText::ParsePreviewMenuOptions(const JSRef<JSObject>& menuOptions)
1209 {
1210 NG::PreviewMenuOptions previewMenuOptions;
1211 auto jsPreviewMenuOp = menuOptions->GetProperty("previewMenuOptions");
1212 CHECK_EQUAL_RETURN(jsPreviewMenuOp->IsObject(), false, previewMenuOptions);
1213 auto jsPreviewMenuOpObj = JSRef<JSObject>::Cast(jsPreviewMenuOp);
1214 CHECK_EQUAL_RETURN(jsPreviewMenuOpObj->IsUndefined(), true, previewMenuOptions);
1215 JSRef<JSVal> jsHapticFeedbackMode = jsPreviewMenuOpObj->GetProperty("hapticFeedbackMode");
1216 CHECK_EQUAL_RETURN(jsHapticFeedbackMode->IsNumber(), false, previewMenuOptions);
1217 auto hapticFeedbackMode = static_cast<HapticFeedbackMode>(jsHapticFeedbackMode->ToNumber<int32_t>());
1218 if (hapticFeedbackMode >= HapticFeedbackMode::DISABLED && hapticFeedbackMode <= HapticFeedbackMode::AUTO) {
1219 previewMenuOptions.hapticFeedbackMode = hapticFeedbackMode;
1220 }
1221 return previewMenuOptions;
1222 }
1223
SetMarqueeOptions(const JSCallbackInfo & info)1224 void JSText::SetMarqueeOptions(const JSCallbackInfo& info)
1225 {
1226 if (info.Length() < 1) {
1227 return;
1228 }
1229
1230 auto args = info[0];
1231 NG::TextMarqueeOptions options;
1232
1233 if (!args->IsObject()) {
1234 TextModel::GetInstance()->SetMarqueeOptions(options);
1235 return;
1236 }
1237
1238 auto paramObject = JSRef<JSObject>::Cast(args);
1239 ParseMarqueeParam(paramObject, options);
1240 TextModel::GetInstance()->SetMarqueeOptions(options);
1241 }
1242
ParseMarqueeParam(const JSRef<JSObject> & paramObject,NG::TextMarqueeOptions & options)1243 void JSText::ParseMarqueeParam(const JSRef<JSObject>& paramObject, NG::TextMarqueeOptions& options)
1244 {
1245 auto getStart = paramObject->GetProperty("start");
1246 if (getStart->IsBoolean()) {
1247 options.UpdateTextMarqueeStart(getStart->ToBoolean());
1248 }
1249
1250 auto getLoop = paramObject->GetProperty("loop");
1251 if (getLoop->IsNumber()) {
1252 int32_t loop = static_cast<int32_t>(getLoop->ToNumber<double>());
1253 if (loop == std::numeric_limits<int32_t>::max() || loop <= 0) {
1254 loop = -1;
1255 }
1256 options.UpdateTextMarqueeLoop(loop);
1257 }
1258
1259 auto getStep = paramObject->GetProperty("step");
1260 if (getStep->IsNumber()) {
1261 auto step = getStep->ToNumber<double>();
1262 if (GreatNotEqual(step, 0.0)) {
1263 options.UpdateTextMarqueeStep(Dimension(step, DimensionUnit::VP).ConvertToPx());
1264 }
1265 }
1266
1267 auto delay = paramObject->GetProperty("delay");
1268 if (delay->IsNumber()) {
1269 auto delayDouble = delay->ToNumber<double>();
1270 auto delayValue = static_cast<int32_t>(delayDouble);
1271 if (delayValue < 0) {
1272 delayValue = 0;
1273 }
1274 options.UpdateTextMarqueeDelay(delayValue);
1275 }
1276
1277 auto getFromStart = paramObject->GetProperty("fromStart");
1278 if (getFromStart->IsBoolean()) {
1279 options.UpdateTextMarqueeDirection(
1280 getFromStart->ToBoolean() ? MarqueeDirection::DEFAULT : MarqueeDirection::DEFAULT_REVERSE);
1281 }
1282
1283 auto getFadeout = paramObject->GetProperty("fadeout");
1284 if (getFadeout->IsBoolean()) {
1285 options.UpdateTextMarqueeFadeout(getFadeout->ToBoolean());
1286 }
1287
1288 auto getStartPolicy = paramObject->GetProperty("marqueeStartPolicy");
1289 if (getStartPolicy->IsNumber()) {
1290 auto startPolicy = static_cast<MarqueeStartPolicy>(getStartPolicy->ToNumber<int32_t>());
1291 options.UpdateTextMarqueeStartPolicy(startPolicy);
1292 }
1293 }
1294
SetOnMarqueeStateChange(const JSCallbackInfo & info)1295 void JSText::SetOnMarqueeStateChange(const JSCallbackInfo& info)
1296 {
1297 if (!info[0]->IsFunction()) {
1298 return;
1299 }
1300
1301 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
1302 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
1303 auto onMarqueeStateChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
1304 int32_t value) {
1305 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
1306 ACE_SCORING_EVENT("Text.onMarqueeStateChange");
1307 PipelineContext::SetCallBackNode(node);
1308 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(value));
1309 func->ExecuteJS(1, &newJSVal);
1310 };
1311
1312 TextModel::GetInstance()->SetOnMarqueeStateChange(std::move(onMarqueeStateChange));
1313 }
1314
EditMenuOptions(const JSCallbackInfo & info)1315 void JSText::EditMenuOptions(const JSCallbackInfo& info)
1316 {
1317 NG::OnCreateMenuCallback onCreateMenuCallback;
1318 NG::OnMenuItemClickCallback onMenuItemClick;
1319 JSViewAbstract::ParseEditMenuOptions(info, onCreateMenuCallback, onMenuItemClick);
1320 TextModel::GetInstance()->SetSelectionMenuOptions(std::move(onCreateMenuCallback), std::move(onMenuItemClick));
1321 }
1322 } // namespace OHOS::Ace::Framework
1323