• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-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_textfield.h"
17 
18 #include <algorithm>
19 #include <cstdint>
20 #include <vector>
21 
22 #include "base/geometry/dimension.h"
23 #include "base/log/ace_scoring_log.h"
24 #include "base/utils/utils.h"
25 #include "bridge/common/utils/utils.h"
26 #include "bridge/declarative_frontend/engine/functions/js_click_function.h"
27 #include "bridge/declarative_frontend/engine/functions/js_clipboard_function.h"
28 #include "bridge/declarative_frontend/engine/functions/js_function.h"
29 #include "bridge/declarative_frontend/jsview/js_container_base.h"
30 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
31 #include "bridge/declarative_frontend/jsview/js_textarea.h"
32 #include "bridge/declarative_frontend/jsview/js_textinput.h"
33 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
34 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
35 #include "bridge/declarative_frontend/jsview/models/text_field_model_impl.h"
36 #include "core/common/container.h"
37 #include "core/common/ime/text_input_action.h"
38 #include "core/common/ime/text_input_type.h"
39 #include "core/components/common/layout/constants.h"
40 #include "core/components/text_field/textfield_theme.h"
41 #include "core/components_ng/base/view_abstract.h"
42 #include "core/components_ng/pattern/text_field/text_field_model.h"
43 #include "core/components_ng/pattern/text_field/text_field_model_ng.h"
44 #include "core/pipeline/pipeline_base.h"
45 
46 namespace OHOS::Ace {
47 
48 std::unique_ptr<TextFieldModel> TextFieldModel::instance_ = nullptr;
49 std::mutex TextFieldModel::mutex_;
50 
GetInstance()51 TextFieldModel* TextFieldModel::GetInstance()
52 {
53     if (!instance_) {
54         std::lock_guard<std::mutex> lock(mutex_);
55         if (!instance_) {
56 #ifdef NG_BUILD
57             instance_.reset(new NG::TextFieldModelNG());
58 #else
59             if (Container::IsCurrentUseNewPipeline()) {
60                 instance_.reset(new NG::TextFieldModelNG());
61             } else {
62                 instance_.reset(new Framework::TextFieldModelImpl());
63             }
64 #endif
65         }
66     }
67     return instance_.get();
68 }
69 
70 } // namespace OHOS::Ace
71 
72 namespace OHOS::Ace::Framework {
73 
74 namespace {
75 
76 const std::vector<TextAlign> TEXT_ALIGNS = { TextAlign::START, TextAlign::CENTER, TextAlign::END };
77 const std::vector<FontStyle> FONT_STYLES = { FontStyle::NORMAL, FontStyle::ITALIC };
78 const std::vector<std::string> INPUT_FONT_FAMILY_VALUE = { "sans-serif" };
79 const uint32_t MAX_LINES = 3;
80 } // namespace
81 
ParseTextFieldTextObject(const JSCallbackInfo & info,const JSRef<JSVal> & changeEventVal)82 void ParseTextFieldTextObject(const JSCallbackInfo& info, const JSRef<JSVal>& changeEventVal)
83 {
84     CHECK_NULL_VOID(changeEventVal->IsFunction());
85 
86     JsEventCallback<void(const std::string&)> onChangeEvent(
87         info.GetExecutionContext(), JSRef<JSFunc>::Cast(changeEventVal));
88     TextFieldModel::GetInstance()->SetOnChangeEvent(std::move(onChangeEvent));
89 }
90 
CreateTextInput(const JSCallbackInfo & info)91 void JSTextField::CreateTextInput(const JSCallbackInfo& info)
92 {
93     std::optional<std::string> placeholderSrc;
94     std::optional<std::string> value;
95     JSTextInputController* jsController = nullptr;
96     JSRef<JSVal> changeEventVal = JSRef<JSVal>::Make();
97     if (info[0]->IsObject()) {
98         auto paramObject = JSRef<JSObject>::Cast(info[0]);
99         std::string placeholder;
100         if (ParseJsString(paramObject->GetProperty("placeholder"), placeholder)) {
101             placeholderSrc = placeholder;
102         }
103         std::string text;
104         JSRef<JSVal> textValue = paramObject->GetProperty("text");
105         if (textValue->IsObject()) {
106             JSRef<JSObject> valueObj = JSRef<JSObject>::Cast(textValue);
107             changeEventVal = valueObj->GetProperty("changeEvent");
108             if (changeEventVal->IsFunction()) {
109                 textValue = valueObj->GetProperty("value");
110             }
111             if (ParseJsString(textValue, text)) {
112                 value = text;
113             }
114         } else if (paramObject->HasProperty("text")) {
115             if (ParseJsString(textValue, text)) {
116                 value = text;
117             }
118             if (textValue->IsUndefined()) {
119                 value = "";
120             }
121         }
122         auto controllerObj = paramObject->GetProperty("controller");
123         if (!controllerObj->IsUndefined() && !controllerObj->IsNull()) {
124             jsController = JSRef<JSObject>::Cast(controllerObj)->Unwrap<JSTextInputController>();
125         }
126     }
127 
128     auto controller = TextFieldModel::GetInstance()->CreateTextInput(placeholderSrc, value);
129     if (jsController) {
130         jsController->SetController(controller);
131     }
132     if (!changeEventVal->IsUndefined() && changeEventVal->IsFunction()) {
133         ParseTextFieldTextObject(info, changeEventVal);
134     }
135 
136     TextFieldModel::GetInstance()->SetFocusableAndFocusNode();
137 }
138 
CreateTextArea(const JSCallbackInfo & info)139 void JSTextField::CreateTextArea(const JSCallbackInfo& info)
140 {
141     std::optional<std::string> placeholderSrc;
142     std::optional<std::string> value;
143     JSTextAreaController* jsController = nullptr;
144     JSRef<JSVal> changeEventVal = JSRef<JSVal>::Make();
145     if (info[0]->IsObject()) {
146         auto paramObject = JSRef<JSObject>::Cast(info[0]);
147         std::string placeholder;
148         if (ParseJsString(paramObject->GetProperty("placeholder"), placeholder)) {
149             placeholderSrc = placeholder;
150         }
151         std::string text;
152         JSRef<JSVal> textValue = paramObject->GetProperty("text");
153         if (textValue->IsObject()) {
154             JSRef<JSObject> valueObj = JSRef<JSObject>::Cast(textValue);
155             changeEventVal = valueObj->GetProperty("changeEvent");
156             if (changeEventVal->IsFunction()) {
157                 textValue = valueObj->GetProperty("value");
158             }
159             if (ParseJsString(textValue, text)) {
160                 value = text;
161             }
162         } else if (paramObject->HasProperty("text")) {
163             if (ParseJsString(textValue, text)) {
164                 value = text;
165             }
166             if (textValue->IsUndefined()) {
167                 value = "";
168             }
169         }
170         auto controllerObj = paramObject->GetProperty("controller");
171         if (!controllerObj->IsUndefined() && !controllerObj->IsNull()) {
172             jsController = JSRef<JSObject>::Cast(controllerObj)->Unwrap<JSTextAreaController>();
173         }
174     }
175     auto controller = TextFieldModel::GetInstance()->CreateTextArea(placeholderSrc, value);
176     if (jsController) {
177         jsController->SetController(controller);
178     }
179     if (!changeEventVal->IsUndefined() && changeEventVal->IsFunction()) {
180         ParseTextFieldTextObject(info, changeEventVal);
181     }
182 
183     TextFieldModel::GetInstance()->SetFocusableAndFocusNode();
184 }
185 
SetType(const JSCallbackInfo & info)186 void JSTextField::SetType(const JSCallbackInfo& info)
187 {
188     if (info.Length() < 1) {
189         LOGI("SetType create error, info is non-valid");
190         return;
191     }
192     if (!info[0]->IsNumber()) {
193         LOGI("The inputType is not number");
194         return;
195     }
196     TextInputType textInputType = static_cast<TextInputType>(info[0]->ToNumber<int32_t>());
197     TextFieldModel::GetInstance()->SetType(textInputType);
198 }
199 
SetPlaceholderColor(const JSCallbackInfo & info)200 void JSTextField::SetPlaceholderColor(const JSCallbackInfo& info)
201 {
202     if (info.Length() < 1) {
203         LOGI("The arg(SetPlaceholderColor) is wrong, it is supposed to have atleast 1 argument");
204         return;
205     }
206 
207     auto theme = GetTheme<TextFieldTheme>();
208     CHECK_NULL_VOID(theme);
209     Color color = theme->GetPlaceholderColor();
210     CheckColor(info[0], color, V2::TEXTINPUT_ETS_TAG, "PlaceholderColor");
211     TextFieldModel::GetInstance()->SetPlaceholderColor(color);
212 }
213 
SetPlaceholderFont(const JSCallbackInfo & info)214 void JSTextField::SetPlaceholderFont(const JSCallbackInfo& info)
215 {
216     if (info.Length() < 1 || !info[0]->IsObject()) {
217         LOGI("PlaceholderFont create error, info is non-valid");
218         return;
219     }
220     Font font;
221     auto paramObject = JSRef<JSObject>::Cast(info[0]);
222     auto fontSize = paramObject->GetProperty("size");
223     if (fontSize->IsNull() || fontSize->IsUndefined()) {
224         font.fontSize = Dimension(-1);
225     } else {
226         CalcDimension size;
227         if (fontSize->IsString()) {
228             auto result = StringUtils::StringToDimensionWithThemeValue(fontSize->ToString(), true, Dimension(-1));
229             font.fontSize = result;
230         } else if (!ParseJsDimensionFp(fontSize, size) || size.Unit() == DimensionUnit::PERCENT) {
231             font.fontSize = Dimension(-1);
232             LOGW("Parse to dimension FP failed.");
233         } else {
234             font.fontSize = size;
235         }
236     }
237 
238     std::string weight;
239     auto fontWeight = paramObject->GetProperty("weight");
240     if (!fontWeight->IsNull()) {
241         if (fontWeight->IsNumber()) {
242             weight = std::to_string(fontWeight->ToNumber<int32_t>());
243         } else {
244             ParseJsString(fontWeight, weight);
245         }
246         font.fontWeight = ConvertStrToFontWeight(weight);
247     }
248 
249     auto fontFamily = paramObject->GetProperty("family");
250     if (!fontFamily->IsNull()) {
251         std::vector<std::string> fontFamilies;
252         if (ParseJsFontFamilies(fontFamily, fontFamilies)) {
253             font.fontFamilies = fontFamilies;
254         }
255     }
256 
257     auto style = paramObject->GetProperty("style");
258     if (!style->IsNull()) {
259         font.fontStyle = static_cast<FontStyle>(style->ToNumber<int32_t>());
260     }
261     TextFieldModel::GetInstance()->SetPlaceholderFont(font);
262 }
263 
SetEnterKeyType(const JSCallbackInfo & info)264 void JSTextField::SetEnterKeyType(const JSCallbackInfo& info)
265 {
266     if (info.Length() < 1) {
267         return;
268     }
269     if (!info[0]->IsNumber()) {
270         LOGI("Info(SetEnterKeyType) is not number");
271         return;
272     }
273     TextInputAction textInputAction = static_cast<TextInputAction>(info[0]->ToNumber<int32_t>());
274     TextFieldModel::GetInstance()->SetEnterKeyType(textInputAction);
275 }
276 
SetTextAlign(int32_t value)277 void JSTextField::SetTextAlign(int32_t value)
278 {
279     if (value >= 0 && value < static_cast<int32_t>(TEXT_ALIGNS.size())) {
280         TextFieldModel::GetInstance()->SetTextAlign(TEXT_ALIGNS[value]);
281     } else {
282         LOGI("the value is error");
283     }
284 }
285 
SetInputStyle(const JSCallbackInfo & info)286 void JSTextField::SetInputStyle(const JSCallbackInfo& info)
287 {
288     if (info.Length() < 1) {
289         LOGI("The arg(SetInputStyle) is wrong, it is supposed to have at least 1 argument");
290         return;
291     }
292     auto styleString = info[0]->ToString();
293     if (styleString == "Inline") {
294         TextFieldModel::GetInstance()->SetInputStyle(InputStyle::INLINE);
295     } else {
296         TextFieldModel::GetInstance()->SetInputStyle(InputStyle::DEFAULT);
297     }
298 }
299 
SetCaretColor(const JSCallbackInfo & info)300 void JSTextField::SetCaretColor(const JSCallbackInfo& info)
301 {
302     if (info.Length() < 1) {
303         LOGI("The arg(SetCareColor) is wrong, it is supposed to have atleast 1 argument");
304         return;
305     }
306 
307     Color color;
308     if (!ParseJsColor(info[0], color)) {
309         LOGI("info[0] is null");
310         return;
311     }
312 
313     TextFieldModel::GetInstance()->SetCaretColor(color);
314 }
315 
SetCaretStyle(const JSCallbackInfo & info)316 void JSTextField::SetCaretStyle(const JSCallbackInfo& info)
317 {
318     if (info.Length() < 1 || !info[0]->IsObject()) {
319         LOGW("CaretStyle create error, info is non-valid");
320         return;
321     }
322     CaretStyle caretStyle;
323     auto paramObject = JSRef<JSObject>::Cast(info[0]);
324     auto caretWidth = paramObject->GetProperty("width");
325 
326     auto pipeline = PipelineBase::GetCurrentContext();
327     CHECK_NULL_VOID(pipeline);
328     auto theme = pipeline->GetThemeManager()->GetTheme<TextFieldTheme>();
329     CHECK_NULL_VOID_NOLOG(theme);
330     if (caretWidth->IsNull() || caretWidth->IsUndefined()) {
331         caretStyle.caretWidth = theme->GetCursorWidth();
332     } else {
333         CalcDimension width;
334         if (!ParseJsDimensionVpNG(caretWidth, width, false)) {
335             width = theme->GetCursorWidth();
336         }
337         if (LessNotEqual(width.Value(), 0.0)) {
338             width = theme->GetCursorWidth();
339         }
340         caretStyle.caretWidth = width;
341     }
342     TextFieldModel::GetInstance()->SetCaretStyle(caretStyle);
343 }
344 
SetCaretPosition(const JSCallbackInfo & info)345 void JSTextField::SetCaretPosition(const JSCallbackInfo& info)
346 {
347     if (info.Length() < 1) {
348         LOGW("The arg(SetCaretPosition) is wrong, it is supposed to have at least 1 arguments");
349         return;
350     }
351 
352     int32_t caretPosition = 0;
353     if (!ParseJsInt32(info[0], caretPosition)) {
354         return;
355     }
356     if (caretPosition < 0) {
357         return;
358     }
359     TextFieldModel::GetInstance()->SetCaretPosition(caretPosition);
360 }
361 
SetSelectedBackgroundColor(const JSCallbackInfo & info)362 void JSTextField::SetSelectedBackgroundColor(const JSCallbackInfo& info)
363 {
364     if (info.Length() < 1) {
365         LOGW("The arg(SetSelectedBackgroundColor) is wrong, it is supposed to have atleast 1 argument");
366         return;
367     }
368 
369     Color selectedColor;
370     if (!ParseJsColor(info[0], selectedColor)) {
371         auto pipeline = PipelineBase::GetCurrentContext();
372         CHECK_NULL_VOID(pipeline);
373         auto theme = pipeline->GetThemeManager()->GetTheme<TextFieldTheme>();
374         CHECK_NULL_VOID_NOLOG(theme);
375         selectedColor = theme->GetSelectedColor();
376     }
377     TextFieldModel::GetInstance()->SetSelectedBackgroundColor(selectedColor);
378 }
379 
SetMaxLength(const JSCallbackInfo & info)380 void JSTextField::SetMaxLength(const JSCallbackInfo& info)
381 {
382     if (info.Length() < 1) {
383         LOGI("The arg(SetMaxLength) is wrong, it is supposed to have atleast 1 argument");
384         return;
385     }
386     int32_t maxLength = 0;
387     if (info[0]->IsUndefined()) {
388         TextFieldModel::GetInstance()->ResetMaxLength();
389         return;
390     } else if (!info[0]->IsNumber()) {
391         LOGI("Max length should be number");
392         TextFieldModel::GetInstance()->ResetMaxLength();
393         return;
394     }
395     maxLength = info[0]->ToNumber<int32_t>();
396     if (GreatOrEqual(maxLength, 0)) {
397         TextFieldModel::GetInstance()->SetMaxLength(maxLength);
398     } else {
399         TextFieldModel::GetInstance()->ResetMaxLength();
400     }
401 }
402 
SetFontSize(const JSCallbackInfo & info)403 void JSTextField::SetFontSize(const JSCallbackInfo& info)
404 {
405     if (info.Length() < 1) {
406         LOGI("JSTextField::SetFontSize The argv is wrong, it is supposed to have at least 1 argument");
407         return;
408     }
409     CalcDimension fontSize;
410     if (!ParseJsDimensionNG(info[0], fontSize, DimensionUnit::FP, false)) {
411         LOGI("Parse to dimension FP failed!");
412         auto theme = GetTheme<TextFieldTheme>();
413         CHECK_NULL_VOID(theme);
414         fontSize = theme->GetFontSize();
415     }
416     TextFieldModel::GetInstance()->SetFontSize(fontSize);
417 }
418 
SetFontWeight(const std::string & value)419 void JSTextField::SetFontWeight(const std::string& value)
420 {
421     TextFieldModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(value));
422 }
423 
SetTextColor(const JSCallbackInfo & info)424 void JSTextField::SetTextColor(const JSCallbackInfo& info)
425 {
426     if (info.Length() < 1) {
427         LOGI("The argv is wrong, it is supposed to have at least 1 argument");
428         return;
429     }
430     Color textColor;
431     if (!ParseJsColor(info[0], textColor)) {
432         auto theme = GetTheme<TextFieldTheme>();
433         CHECK_NULL_VOID_NOLOG(theme);
434         textColor = theme->GetTextColor();
435     }
436     TextFieldModel::GetInstance()->SetTextColor(textColor);
437 }
438 
SetForegroundColor(const JSCallbackInfo & info)439 void JSTextField::SetForegroundColor(const JSCallbackInfo& info)
440 {
441     if (info.Length() < 1) {
442         LOGE("The argv is wrong, it is supposed to have at least 1 argument");
443         return;
444     }
445     ForegroundColorStrategy strategy;
446     if (ParseJsColorStrategy(info[0], strategy)) {
447         ViewAbstractModel::GetInstance()->SetForegroundColorStrategy(strategy);
448         TextFieldModel::GetInstance()->SetForegroundColor(Color::FOREGROUND);
449         return;
450     }
451     Color foregroundColor;
452     if (!ParseJsColor(info[0], foregroundColor)) {
453         return;
454     }
455     ViewAbstractModel::GetInstance()->SetForegroundColor(foregroundColor);
456     TextFieldModel::GetInstance()->SetForegroundColor(foregroundColor);
457 }
458 
SetFontStyle(int32_t value)459 void JSTextField::SetFontStyle(int32_t value)
460 {
461     if (value >= 0 && value < static_cast<int32_t>(FONT_STYLES.size())) {
462         TextFieldModel::GetInstance()->SetFontStyle(FONT_STYLES[value]);
463     } else {
464         LOGI("TextInput fontStyle(%{public}d) illegal value", value);
465     }
466 }
467 
SetFontFamily(const JSCallbackInfo & info)468 void JSTextField::SetFontFamily(const JSCallbackInfo& info)
469 {
470     if (info.Length() < 1) {
471         LOGI("The argv is wrong, it is supposed to have at least 1 argument");
472         return;
473     }
474     std::vector<std::string> fontFamilies;
475     if (!ParseJsFontFamilies(info[0], fontFamilies)) {
476         LOGI("Parse FontFamilies failed");
477         return;
478     }
479     TextFieldModel::GetInstance()->SetFontFamily(fontFamilies);
480 }
481 
SetInputFilter(const JSCallbackInfo & info)482 void JSTextField::SetInputFilter(const JSCallbackInfo& info)
483 {
484     if (info.Length() < 1) {
485         LOGI("The argv is wrong, it is supposed to have at least 1 argument");
486         return;
487     }
488     std::string inputFilter;
489     if (info[0]->IsUndefined()) {
490         TextFieldModel::GetInstance()->SetInputFilter(inputFilter, nullptr);
491         return;
492     }
493     if (!ParseJsString(info[0], inputFilter)) {
494         LOGI("Parse inputFilter failed");
495         return;
496     }
497     if (info.Length() > 1 && info[1]->IsFunction()) {
498         auto jsFunc = AceType::MakeRefPtr<JsClipboardFunction>(JSRef<JSFunc>::Cast(info[1]));
499         auto resultId = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](const std::string& info) {
500             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
501             func->Execute(info);
502         };
503         TextFieldModel::GetInstance()->SetInputFilter(inputFilter, resultId);
504         return;
505     }
506     TextFieldModel::GetInstance()->SetInputFilter(inputFilter, nullptr);
507 }
508 
SetShowPasswordIcon(const JSCallbackInfo & info)509 void JSTextField::SetShowPasswordIcon(const JSCallbackInfo& info)
510 {
511     if (!info[0]->IsBoolean()) {
512         LOGI("The info is wrong, it is supposed to be an boolean");
513         return;
514     }
515 
516     bool isShowPasswordIcon = info[0]->ToBoolean();
517     TextFieldModel::GetInstance()->SetShowPasswordIcon(isShowPasswordIcon);
518 }
519 
SetBackgroundColor(const JSCallbackInfo & info)520 void JSTextField::SetBackgroundColor(const JSCallbackInfo& info)
521 {
522     if (info.Length() < 1) {
523         LOGI("The argv is wrong, it is supposed to have at least 1 argument");
524         return;
525     }
526     Color backgroundColor;
527     bool tmp = !ParseJsColor(info[0], backgroundColor);
528     TextFieldModel::GetInstance()->SetBackgroundColor(backgroundColor, tmp);
529 }
530 
JsHeight(const JSCallbackInfo & info)531 void JSTextField::JsHeight(const JSCallbackInfo& info)
532 {
533     JSViewAbstract::JsHeight(info);
534     if (info.Length() < 1) {
535         LOGI("The arg is wrong, it is supposed to have at least 1 arguments");
536         return;
537     }
538     CalcDimension value;
539     if (!ParseJsDimensionVp(info[0], value)) {
540         LOGI("Parse to dimension VP failed!");
541         return;
542     }
543     if (LessNotEqual(value.Value(), 0.0)) {
544         LOGI("dimension value: %{public}f is invalid!", value.Value());
545         ViewAbstractModel::GetInstance()->ClearWidthOrHeight(false);
546         return;
547     }
548     TextFieldModel::GetInstance()->SetHeight(value);
549 }
550 
JsWidth(const JSCallbackInfo & info)551 void JSTextField::JsWidth(const JSCallbackInfo& info)
552 {
553     if (info.Length() < 1) {
554         LOGW("The arg is wrong, it is supposed to have atleast 1 arguments");
555         return;
556     }
557     if (info[0]->IsString() && info[0]->ToString() == "auto") {
558         ViewAbstractModel::GetInstance()->ClearWidthOrHeight(true);
559         TextFieldModel::GetInstance()->SetWidthAuto(true);
560         return;
561     }
562 
563     TextFieldModel::GetInstance()->SetWidthAuto(false);
564     CalcDimension value;
565     if (!ParseJsDimensionVp(info[0], value)) {
566         LOGW("Parse width fail");
567         return;
568     }
569     if (LessNotEqual(value.Value(), 0.0)) {
570         return;
571     }
572     ViewAbstractModel::GetInstance()->SetWidth(value);
573 }
574 
CheckIsIllegalString(const std::string & value)575 bool CheckIsIllegalString(const std::string& value)
576 {
577     if (value.empty()) {
578         return true;
579     }
580     errno = 0;
581     char* pEnd = nullptr;
582     std::strtod(value.c_str(), &pEnd);
583     return (pEnd == value.c_str() || errno == ERANGE);
584 }
585 
JsPadding(const JSCallbackInfo & info)586 void JSTextField::JsPadding(const JSCallbackInfo& info)
587 {
588     if (info[0]->IsUndefined() || (info[0]->IsString() && CheckIsIllegalString(info[0]->ToString()))) {
589         return;
590     };
591     CalcDimension length;
592     ParseJsDimensionVp(info[0], length);
593     if (length.IsNegative()) {
594         return;
595     }
596     bool tmp = !info[0]->IsString() && !info[0]->IsNumber() && !info[0]->IsObject();
597 
598     NG::PaddingProperty newPadding = GetNewPadding(info);
599     Edge oldPadding = Edge(GetOldPadding(info));
600     TextFieldModel::GetInstance()->SetPadding(newPadding, oldPadding, tmp);
601 }
602 
GetOldPadding(const JSCallbackInfo & info)603 Edge JSTextField::GetOldPadding(const JSCallbackInfo& info)
604 {
605     Edge padding;
606     if (info[0]->IsNumber() || info[0]->IsString()) {
607         CalcDimension edgeValue;
608         if (ParseJsDimensionVp(info[0], edgeValue)) {
609             padding = Edge(edgeValue);
610         }
611     }
612     if (info[0]->IsObject()) {
613         JSRef<JSObject> object = JSRef<JSObject>::Cast(info[0]);
614         CalcDimension left = CalcDimension(0.0, DimensionUnit::VP);
615         CalcDimension top = CalcDimension(0.0, DimensionUnit::VP);
616         CalcDimension right = CalcDimension(0.0, DimensionUnit::VP);
617         CalcDimension bottom = CalcDimension(0.0, DimensionUnit::VP);
618         ParseJsDimensionVp(object->GetProperty("left"), left);
619         ParseJsDimensionVp(object->GetProperty("top"), top);
620         ParseJsDimensionVp(object->GetProperty("right"), right);
621         ParseJsDimensionVp(object->GetProperty("bottom"), bottom);
622         padding = Edge(left, top, right, bottom);
623     }
624     return padding;
625 }
626 
GetNewPadding(const JSCallbackInfo & info)627 NG::PaddingProperty JSTextField::GetNewPadding(const JSCallbackInfo& info)
628 {
629     NG::PaddingProperty padding;
630     if (info[0]->IsObject()) {
631         std::optional<CalcDimension> left;
632         std::optional<CalcDimension> right;
633         std::optional<CalcDimension> top;
634         std::optional<CalcDimension> bottom;
635         JSRef<JSObject> paddingObj = JSRef<JSObject>::Cast(info[0]);
636 
637         CalcDimension leftDimen;
638         if (ParseJsDimensionVp(paddingObj->GetProperty("left"), leftDimen)) {
639             left = leftDimen;
640         }
641         CalcDimension rightDimen;
642         if (ParseJsDimensionVp(paddingObj->GetProperty("right"), rightDimen)) {
643             right = rightDimen;
644         }
645         CalcDimension topDimen;
646         if (ParseJsDimensionVp(paddingObj->GetProperty("top"), topDimen)) {
647             top = topDimen;
648         }
649         CalcDimension bottomDimen;
650         if (ParseJsDimensionVp(paddingObj->GetProperty("bottom"), bottomDimen)) {
651             bottom = bottomDimen;
652         }
653         if (left.has_value() || right.has_value() || top.has_value() || bottom.has_value()) {
654             padding = SetPaddings(top, bottom, left, right);
655             return padding;
656         }
657     }
658 
659     CalcDimension length;
660     if (!ParseJsDimensionVp(info[0], length)) {
661         // use default value.
662         length.Reset();
663     }
664     padding.SetEdges(NG::CalcLength(length.IsNonNegative() ? length : CalcDimension()));
665     return padding;
666 }
667 
SetPaddings(const std::optional<CalcDimension> & top,const std::optional<CalcDimension> & bottom,const std::optional<CalcDimension> & left,const std::optional<CalcDimension> & right)668 NG::PaddingProperty JSTextField::SetPaddings(const std::optional<CalcDimension>& top,
669     const std::optional<CalcDimension>& bottom, const std::optional<CalcDimension>& left,
670     const std::optional<CalcDimension>& right)
671 {
672     NG::PaddingProperty paddings;
673     if (top.has_value()) {
674         if (top.value().Unit() == DimensionUnit::CALC) {
675             paddings.top =
676                 NG::CalcLength(top.value().IsNonNegative() ? top.value().CalcValue() : CalcDimension().CalcValue());
677         } else {
678             paddings.top = NG::CalcLength(top.value().IsNonNegative() ? top.value() : CalcDimension());
679         }
680     }
681     if (bottom.has_value()) {
682         if (bottom.value().Unit() == DimensionUnit::CALC) {
683             paddings.bottom = NG::CalcLength(
684                 bottom.value().IsNonNegative() ? bottom.value().CalcValue() : CalcDimension().CalcValue());
685         } else {
686             paddings.bottom = NG::CalcLength(bottom.value().IsNonNegative() ? bottom.value() : CalcDimension());
687         }
688     }
689     if (left.has_value()) {
690         if (left.value().Unit() == DimensionUnit::CALC) {
691             paddings.left =
692                 NG::CalcLength(left.value().IsNonNegative() ? left.value().CalcValue() : CalcDimension().CalcValue());
693         } else {
694             paddings.left = NG::CalcLength(left.value().IsNonNegative() ? left.value() : CalcDimension());
695         }
696     }
697     if (right.has_value()) {
698         if (right.value().Unit() == DimensionUnit::CALC) {
699             paddings.right =
700                 NG::CalcLength(right.value().IsNonNegative() ? right.value().CalcValue() : CalcDimension().CalcValue());
701         } else {
702             paddings.right = NG::CalcLength(right.value().IsNonNegative() ? right.value() : CalcDimension());
703         }
704     }
705 
706     return paddings;
707 }
708 
JsBorder(const JSCallbackInfo & info)709 void JSTextField::JsBorder(const JSCallbackInfo& info)
710 {
711     if (!info[0]->IsObject()) {
712         LOGI("args is not a object. %s", info[0]->ToString().c_str());
713         return;
714     }
715     JSRef<JSObject> object = JSRef<JSObject>::Cast(info[0]);
716     auto valueWidth = object->GetProperty("width");
717     if (!valueWidth->IsUndefined()) {
718         ParseBorderWidth(valueWidth);
719     }
720     auto valueColor = object->GetProperty("color");
721     if (!valueColor->IsUndefined()) {
722         ParseBorderColor(valueColor);
723     }
724     auto valueRadius = object->GetProperty("radius");
725     if (!valueRadius->IsUndefined()) {
726         ParseBorderRadius(valueRadius);
727     }
728     auto valueStyle = object->GetProperty("style");
729     if (!valueStyle->IsUndefined()) {
730         ParseBorderStyle(valueStyle);
731     }
732     JSViewAbstract::JsBorder(info);
733     TextFieldModel::GetInstance()->SetBackBorder();
734     info.ReturnSelf();
735 }
736 
JsBorderWidth(const JSCallbackInfo & info)737 void JSTextField::JsBorderWidth(const JSCallbackInfo& info)
738 {
739     if (!info[0]->IsObject() && !info[0]->IsString() && !info[0]->IsNumber()) {
740         LOGI("args need a string or number or object");
741         return;
742     }
743     JSViewAbstract::JsBorderWidth(info);
744     TextFieldModel::GetInstance()->SetBackBorder();
745 }
746 
JsBorderColor(const JSCallbackInfo & info)747 void JSTextField::JsBorderColor(const JSCallbackInfo& info)
748 {
749     if (!info[0]->IsObject() && !info[0]->IsString() && !info[0]->IsNumber()) {
750         LOGI("args need a string or number or object");
751         return;
752     }
753     JSViewAbstract::JsBorderColor(info);
754     TextFieldModel::GetInstance()->SetBackBorder();
755 }
756 
JsBorderStyle(const JSCallbackInfo & info)757 void JSTextField::JsBorderStyle(const JSCallbackInfo& info)
758 {
759     if (!info[0]->IsObject() && !info[0]->IsNumber()) {
760         LOGI("args need a string or number or object");
761         return;
762     }
763     JSViewAbstract::JsBorderStyle(info);
764     TextFieldModel::GetInstance()->SetBackBorder();
765 }
766 
JsBorderRadius(const JSCallbackInfo & info)767 void JSTextField::JsBorderRadius(const JSCallbackInfo& info)
768 {
769     if (!info[0]->IsObject() && !info[0]->IsString() && !info[0]->IsNumber()) {
770         LOGI("args need a string or number or object");
771         return;
772     }
773     JSViewAbstract::JsBorderRadius(info);
774     TextFieldModel::GetInstance()->SetBackBorder();
775 }
776 
JsHoverEffect(const JSCallbackInfo & info)777 void JSTextField::JsHoverEffect(const JSCallbackInfo& info)
778 {
779     if (!info[0]->IsNumber()) {
780         LOGI("info[0] is not a number");
781         return;
782     }
783     TextFieldModel::GetInstance()->SetHoverEffect(static_cast<HoverEffectType>(info[0]->ToNumber<int32_t>()));
784 }
785 
SetOnEditChanged(const JSCallbackInfo & info)786 void JSTextField::SetOnEditChanged(const JSCallbackInfo& info)
787 {
788     CHECK_NULL_VOID(info[0]->IsFunction());
789     JsEventCallback<void(bool)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
790     TextFieldModel::GetInstance()->SetOnEditChanged(std::move(callback));
791 }
792 
SetOnSubmit(const JSCallbackInfo & info)793 void JSTextField::SetOnSubmit(const JSCallbackInfo& info)
794 {
795     CHECK_NULL_VOID(info[0]->IsFunction());
796     JsEventCallback<void(int32_t)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
797     TextFieldModel::GetInstance()->SetOnSubmit(std::move(callback));
798 }
799 
SetOnChange(const JSCallbackInfo & info)800 void JSTextField::SetOnChange(const JSCallbackInfo& info)
801 {
802     CHECK_NULL_VOID(info[0]->IsFunction());
803     JsEventCallback<void(const std::string&)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
804     TextFieldModel::GetInstance()->SetOnChange(std::move(callback));
805 }
806 
SetOnTextSelectionChange(const JSCallbackInfo & info)807 void JSTextField::SetOnTextSelectionChange(const JSCallbackInfo& info)
808 {
809     CHECK_NULL_VOID(info[0]->IsFunction());
810     JsEventCallback<void(int32_t, int32_t)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
811     TextFieldModel::GetInstance()->SetOnTextSelectionChange(std::move(callback));
812 }
813 
SetOnContentScroll(const JSCallbackInfo & info)814 void JSTextField::SetOnContentScroll(const JSCallbackInfo& info)
815 {
816     CHECK_NULL_VOID(info[0]->IsFunction());
817     JsEventCallback<void(float, float)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
818     TextFieldModel::GetInstance()->SetOnContentScroll(std::move(callback));
819 }
820 
SetOnCopy(const JSCallbackInfo & info)821 void JSTextField::SetOnCopy(const JSCallbackInfo& info)
822 {
823     CHECK_NULL_VOID(info[0]->IsFunction());
824     JsEventCallback<void(const std::string&)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
825     TextFieldModel::GetInstance()->SetOnCopy(std::move(callback));
826 }
827 
SetOnCut(const JSCallbackInfo & info)828 void JSTextField::SetOnCut(const JSCallbackInfo& info)
829 {
830     CHECK_NULL_VOID(info[0]->IsFunction());
831     JsEventCallback<void(const std::string&)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
832     TextFieldModel::GetInstance()->SetOnCut(std::move(callback));
833 }
834 
SetOnPaste(const JSCallbackInfo & info)835 void JSTextField::SetOnPaste(const JSCallbackInfo& info)
836 {
837     CHECK_NULL_VOID(info[0]->IsFunction());
838     JsEventCallback<void(const std::string&)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
839     TextFieldModel::GetInstance()->SetOnPaste(std::move(callback));
840 }
841 
SetOnClick(const JSCallbackInfo & info)842 void JSTextField::SetOnClick(const JSCallbackInfo& info)
843 {
844     if (Container::IsCurrentUseNewPipeline()) {
845         JSInteractableView::JsOnClick(info);
846         return;
847     }
848     JsEventCallback<void(const ClickInfo&)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
849     TextFieldModel::GetInstance()->SetOnClick(std::move(callback));
850     info.ReturnSelf();
851 }
852 
SetCopyOption(const JSCallbackInfo & info)853 void JSTextField::SetCopyOption(const JSCallbackInfo& info)
854 {
855     if (info.Length() == 0) {
856         return;
857     }
858     if (info[0]->IsUndefined()) {
859         TextFieldModel::GetInstance()->SetCopyOption(CopyOptions::Local);
860         return;
861     }
862     auto copyOptions = CopyOptions::None;
863     if (info[0]->IsNumber()) {
864         auto emunNumber = info[0]->ToNumber<int>();
865         copyOptions = static_cast<CopyOptions>(emunNumber);
866     }
867     TextFieldModel::GetInstance()->SetCopyOption(copyOptions);
868 }
869 
JsMenuOptionsExtension(const JSCallbackInfo & info)870 void JSTextField::JsMenuOptionsExtension(const JSCallbackInfo& info)
871 {
872     if (info[0]->IsArray()) {
873         std::vector<NG::MenuOptionsParam> menuOptionsItems;
874         JSViewAbstract::ParseMenuOptions(info, JSRef<JSArray>::Cast(info[0]), menuOptionsItems);
875         TextFieldModel::GetInstance()->SetMenuOptionItems(std::move(menuOptionsItems));
876     }
877 }
878 
SetShowUnderline(const JSCallbackInfo & info)879 void JSTextField::SetShowUnderline(const JSCallbackInfo& info)
880 {
881     if (!info[0]->IsBoolean()) {
882         LOGI("The info is wrong, it is supposed to be an boolean");
883         TextFieldModel::GetInstance()->SetShowUnderline(false);
884         return;
885     }
886     TextFieldModel::GetInstance()->SetShowUnderline(info[0]->ToBoolean());
887 }
888 
SetPasswordIcon(const JSCallbackInfo & info)889 void JSTextField::SetPasswordIcon(const JSCallbackInfo& info)
890 {
891     if (!Container::IsCurrentUseNewPipeline()) {
892         return;
893     }
894     if (!info[0]->IsObject()) {
895         return;
896     }
897     JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
898     JSRef<JSVal> showVal = jsObj->GetProperty("onIconSrc");
899     JSRef<JSVal> hideVal = jsObj->GetProperty("offIconSrc");
900     PasswordIcon passwordIcon;
901     if (showVal->IsString()) {
902         passwordIcon.showResult = showVal->ToString();
903     }
904     if (hideVal->IsString()) {
905         passwordIcon.hideResult = hideVal->ToString();
906     }
907     if (showVal->IsObject()) {
908         JSRef<JSVal> bundleName = JSRef<JSObject>::Cast(showVal)->GetProperty("bundleName");
909         JSRef<JSVal> moduleName = JSRef<JSObject>::Cast(showVal)->GetProperty("moduleName");
910         if (bundleName->IsString()) {
911             passwordIcon.showBundleName = bundleName->ToString();
912         }
913         if (moduleName->IsString()) {
914             passwordIcon.showModuleName = moduleName->ToString();
915         }
916         ParseJsMedia(JSRef<JSObject>::Cast(showVal), passwordIcon.showResult);
917     }
918     if (hideVal->IsObject()) {
919         JSRef<JSVal> bundleName = JSRef<JSObject>::Cast(hideVal)->GetProperty("bundleName");
920         JSRef<JSVal> moduleName = JSRef<JSObject>::Cast(hideVal)->GetProperty("moduleName");
921         if (bundleName->IsString()) {
922             passwordIcon.hideBundleName = bundleName->ToString();
923         }
924         if (moduleName->IsString()) {
925             passwordIcon.hideModuleName = moduleName->ToString();
926         }
927         ParseJsMedia(JSRef<JSObject>::Cast(hideVal), passwordIcon.hideResult);
928     }
929     if (!showVal->IsString() && !showVal->IsObject()) {
930         passwordIcon.showResult = "";
931     }
932     if (!hideVal->IsString() && !hideVal->IsObject()) {
933         passwordIcon.hideResult = "";
934     }
935     TextFieldModel::GetInstance()->SetPasswordIcon(passwordIcon);
936 }
937 
UpdateDecoration(const RefPtr<BoxComponent> & boxComponent,const RefPtr<TextFieldComponent> & component,const Border & boxBorder,const OHOS::Ace::RefPtr<OHOS::Ace::TextFieldTheme> & textFieldTheme)938 void JSTextField::UpdateDecoration(const RefPtr<BoxComponent>& boxComponent,
939     const RefPtr<TextFieldComponent>& component, const Border& boxBorder,
940     const OHOS::Ace::RefPtr<OHOS::Ace::TextFieldTheme>& textFieldTheme)
941 {
942     if (!textFieldTheme) {
943         LOGI("UpdateDecoration: textFieldTheme is null.");
944         return;
945     }
946 
947     RefPtr<Decoration> decoration = component->GetDecoration();
948     RefPtr<Decoration> boxDecoration = boxComponent->GetBackDecoration();
949     if (!decoration) {
950         decoration = AceType::MakeRefPtr<Decoration>();
951     }
952     if (boxDecoration) {
953         Border border = decoration->GetBorder();
954         border.SetLeftEdge(boxBorder.Left());
955         border.SetRightEdge(boxBorder.Right());
956         border.SetTopEdge(boxBorder.Top());
957         border.SetBottomEdge(boxBorder.Bottom());
958         border.SetBorderRadius(textFieldTheme->GetBorderRadius());
959         decoration->SetBorder(border);
960         component->SetOriginBorder(decoration->GetBorder());
961 
962         if (boxDecoration->GetImage() || boxDecoration->GetGradient().IsValid()) {
963             // clear box properties except background image and radius.
964             boxDecoration->SetBackgroundColor(Color::TRANSPARENT);
965             Border border;
966             border.SetBorderRadius(textFieldTheme->GetBorderRadius());
967             boxDecoration->SetBorder(border);
968         }
969     } else {
970         boxDecoration = AceType::MakeRefPtr<Decoration>();
971         boxDecoration->SetBorderRadius(textFieldTheme->GetBorderRadius());
972         boxComponent->SetBackDecoration(boxDecoration);
973     }
974 }
975 
SetShowUnit(const JSCallbackInfo & info)976 void JSTextField::SetShowUnit(const JSCallbackInfo& info)
977 {
978     if (!info[0]->IsFunction()) {
979         LOGI("fail to bind SetShowUnit event due to info is not object");
980         return;
981     }
982 
983     auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(info[0]));
984     auto unitFunc = [builderFunc]() { builderFunc->Execute(); };
985     TextFieldModel::GetInstance()->SetShowUnit(std::move(unitFunc));
986 }
987 
SetShowError(const JSCallbackInfo & info)988 void JSTextField::SetShowError(const JSCallbackInfo& info)
989 {
990     if (Container::IsCurrentUseNewPipeline()) {
991         if (!info[0]->IsUndefined() && !info[0]->IsString()) {
992             LOGI("args need a string or undefined");
993             TextFieldModel::GetInstance()->SetShowError("", false);
994             return;
995         }
996         TextFieldModel::GetInstance()->SetShowError(
997             info[0]->IsString() ? info[0]->ToString() : "", info[0]->IsUndefined() ? false : true);
998     }
999 }
1000 
SetShowCounter(const JSCallbackInfo & info)1001 void JSTextField::SetShowCounter(const JSCallbackInfo& info)
1002 {
1003     if (!info[0]->IsBoolean()) {
1004         LOGI("The info is wrong, it is supposed to be an boolean");
1005         TextFieldModel::GetInstance()->SetShowCounter(false);
1006         return;
1007     }
1008 
1009     TextFieldModel::GetInstance()->SetShowCounter(info[0]->ToBoolean());
1010 }
1011 
SetBarState(const JSCallbackInfo & info)1012 void JSTextField::SetBarState(const JSCallbackInfo& info)
1013 {
1014     if (info.Length() < 1 || !info[0]->IsNumber()) {
1015         LOGI("SetBarState create error, info is not number or non-valid");
1016         TextFieldModel::GetInstance()->SetBarState(DisplayMode::AUTO);
1017         return;
1018     }
1019     DisplayMode displayMode = static_cast<DisplayMode>(info[0]->ToNumber<int32_t>());
1020     TextFieldModel::GetInstance()->SetBarState(displayMode);
1021 }
1022 
SetMaxLines(const JSCallbackInfo & info)1023 void JSTextField::SetMaxLines(const JSCallbackInfo& info)
1024 {
1025     if (info.Length() < 1 || !info[0]->IsNumber()) {
1026         LOGI("SetMaxLines create error, info is not number or non-valid");
1027         TextFieldModel::GetInstance()->SetMaxViewLines(MAX_LINES);
1028         return;
1029     }
1030     if (info[0]->ToNumber<int32_t>() <= 0) {
1031         TextFieldModel::GetInstance()->SetMaxViewLines(MAX_LINES);
1032         return;
1033     }
1034     TextFieldModel::GetInstance()->SetMaxViewLines(info[0]->ToNumber<uint32_t>());
1035 }
1036 
SetEnableKeyboardOnFocus(const JSCallbackInfo & info)1037 void JSTextField::SetEnableKeyboardOnFocus(const JSCallbackInfo& info)
1038 {
1039     if (info.Length() < 1) {
1040         LOGW("EnableKeyboardOnFocus should have at least 1 param");
1041         return;
1042     }
1043     if (info[0]->IsUndefined() || !info[0]->IsBoolean()) {
1044         LOGI("The info of SetEnableKeyboardOnFocus is not correct, using default");
1045         TextFieldModel::GetInstance()->RequestKeyboardOnFocus(true);
1046         return;
1047     }
1048     TextFieldModel::GetInstance()->RequestKeyboardOnFocus(info[0]->ToBoolean());
1049 }
1050 
SetSelectionMenuHidden(const JSCallbackInfo & info)1051 void JSTextField::SetSelectionMenuHidden(const JSCallbackInfo& info)
1052 {
1053     if (info.Length() < 1) {
1054         LOGW("SelectionMenuHidden should have at least 1 param");
1055         return;
1056     }
1057     if (info[0]->IsUndefined() || !info[0]->IsBoolean()) {
1058         LOGI("The info of SetSelectionMenuHidden is not correct, using default");
1059         TextFieldModel::GetInstance()->SetSelectionMenuHidden(false);
1060         return;
1061     }
1062     TextFieldModel::GetInstance()->SetSelectionMenuHidden(info[0]->ToBoolean());
1063 }
1064 
ParseJsCustomKeyboardBuilder(const JSCallbackInfo & info,int32_t index,std::function<void ()> & buildFunc)1065 bool JSTextField::ParseJsCustomKeyboardBuilder(
1066     const JSCallbackInfo& info, int32_t index, std::function<void()>& buildFunc)
1067 {
1068     if (info.Length() <= index) {
1069         return false;
1070     }
1071     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[index]);
1072     auto builder = obj->GetProperty("builder");
1073     if (!builder->IsFunction()) {
1074         return false;
1075     }
1076     auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(builder));
1077     CHECK_NULL_RETURN(builderFunc, false);
1078     buildFunc = [execCtx = info.GetExecutionContext(), func = std::move(builderFunc)]() {
1079         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
1080         ACE_SCORING_EVENT("CustomKeyboard");
1081         func->Execute();
1082     };
1083     return true;
1084 }
1085 
SetCustomKeyboard(const JSCallbackInfo & info)1086 void JSTextField::SetCustomKeyboard(const JSCallbackInfo& info)
1087 {
1088     if (info.Length() > 0 && (info[0]->IsUndefined() || info[0]->IsNull())) {
1089         TextFieldModel::GetInstance()->SetCustomKeyboard(nullptr);
1090         return;
1091     }
1092     if (info.Length() < 1 || !info[0]->IsObject()) {
1093         return;
1094     }
1095     std::function<void()> buildFunc;
1096     if (ParseJsCustomKeyboardBuilder(info, 0, buildFunc)) {
1097         TextFieldModel::GetInstance()->SetCustomKeyboard(std::move(buildFunc));
1098     }
1099 }
1100 } // namespace OHOS::Ace::Framework
1101