1 /*
2  * Copyright (c) 2021 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/common/dom/input/dom_textfield_util.h"
17 
18 #include "frameworks/bridge/common/utils/utils.h"
19 
20 namespace OHOS::Ace::Framework {
21 namespace {
22 
23 const TextInputAction INPUT_TEXTINPUTACTION_VALUE_DEFAULT = TextInputAction::UNSPECIFIED;
24 const std::vector<std::string> INPUT_FONT_FAMILY_VALUE = {
25     "sans-serif",
26 };
27 
28 constexpr Dimension BOX_HOVER_RADIUS = 18.0_vp;
29 
30 } // namespace
31 
32 Radius DOMTextFieldUtil::defaultRadius_;
33 
InitDefaultValue(const RefPtr<BoxComponent> & boxComponent,const RefPtr<TextFieldComponent> & component,const RefPtr<TextFieldTheme> & theme)34 void DOMTextFieldUtil::InitDefaultValue(const RefPtr<BoxComponent>& boxComponent,
35     const RefPtr<TextFieldComponent>& component, const RefPtr<TextFieldTheme>& theme)
36 {
37     if (!boxComponent || !component || !theme) {
38         return;
39     }
40 
41     component->SetAction(INPUT_TEXTINPUTACTION_VALUE_DEFAULT);
42     component->SetCursorColor(theme->GetCursorColor());
43     component->SetCursorRadius(theme->GetCursorRadius());
44     component->SetPlaceholderColor(theme->GetPlaceholderColor());
45 
46     component->SetFocusBgColor(theme->GetFocusBgColor());
47     component->SetFocusPlaceholderColor(theme->GetFocusPlaceholderColor());
48     component->SetFocusTextColor(theme->GetFocusTextColor());
49     component->SetBgColor(theme->GetBgColor());
50     component->SetTextColor(theme->GetTextColor());
51     component->SetSelectedColor(theme->GetSelectedColor());
52     component->SetHoverColor(theme->GetHoverColor());
53     component->SetPressColor(theme->GetPressColor());
54     component->SetNeedFade(theme->NeedFade());
55     component->SetShowEllipsis(theme->ShowEllipsis());
56 
57     TextStyle textStyle = component->GetTextStyle();
58     textStyle.SetTextColor(theme->GetTextColor());
59     textStyle.SetFontSize(theme->GetFontSize());
60     textStyle.SetFontWeight(theme->GetFontWeight());
61     textStyle.SetFontFamilies(INPUT_FONT_FAMILY_VALUE);
62     component->SetTextStyle(textStyle);
63 
64     component->SetCountTextStyle(theme->GetCountTextStyle());
65     component->SetOverCountStyle(theme->GetOverCountStyle());
66     component->SetCountTextStyleOuter(theme->GetCountTextStyleOuter());
67     component->SetOverCountStyleOuter(theme->GetOverCountStyleOuter());
68 
69     component->SetErrorTextStyle(theme->GetErrorTextStyle());
70     component->SetErrorSpacing(theme->GetErrorSpacing());
71     component->SetErrorIsInner(theme->GetErrorIsInner());
72     component->SetErrorBorderWidth(theme->GetErrorBorderWidth());
73     component->SetErrorBorderColor(theme->GetErrorBorderColor());
74 
75     RefPtr<Decoration> decoration = AceType::MakeRefPtr<Decoration>();
76     decoration->SetPadding(theme->GetPadding());
77     decoration->SetBackgroundColor(theme->GetBgColor());
78     decoration->SetBorderRadius(theme->GetBorderRadius());
79     defaultRadius_ = theme->GetBorderRadius();
80     const auto& boxDecoration = boxComponent->GetBackDecoration();
81     if (boxDecoration) {
82         decoration->SetImage(boxDecoration->GetImage());
83         decoration->SetGradient(boxDecoration->GetGradient());
84     }
85     component->SetDecoration(decoration);
86 
87     component->SetIconSize(theme->GetIconSize());
88     component->SetIconHotZoneSize(theme->GetIconHotZoneSize());
89 
90     boxComponent->SetPadding(theme->GetPadding());
91     component->SetHeight(theme->GetHeight());
92 }
93 
SetDisableStyle(const RefPtr<TextFieldComponent> & component,const RefPtr<TextFieldTheme> & theme)94 void DOMTextFieldUtil::SetDisableStyle(const RefPtr<TextFieldComponent>& component, const RefPtr<TextFieldTheme>& theme)
95 {
96     if (!component || !theme) {
97         return;
98     }
99 
100     TextStyle textStyle = component->GetTextStyle();
101     textStyle.SetTextColor(theme->GetDisableTextColor());
102     component->SetTextStyle(textStyle);
103     component->SetPlaceholderColor(theme->GetDisableTextColor());
104 }
105 
InitController(const RefPtr<TextFieldComponent> & component)106 void DOMTextFieldUtil::InitController(const RefPtr<TextFieldComponent>& component)
107 {
108     if (component) {
109         component->SetTextEditController(AceType::MakeRefPtr<TextEditController>());
110         component->SetTextFieldController(AceType::MakeRefPtr<TextFieldController>());
111     }
112 }
113 
CreateComponentAndSetChildAttr(const RefPtr<BoxComponent> & boxComponent,const std::string & type,const std::map<std::string,std::string> & attrs,const DOMInput & node)114 RefPtr<TextFieldComponent> DOMTextFieldUtil::CreateComponentAndSetChildAttr(const RefPtr<BoxComponent>& boxComponent,
115     const std::string& type, const std::map<std::string, std::string>& attrs, const DOMInput& node)
116 {
117     // create component
118     RefPtr<TextFieldComponent> component = AceType::MakeRefPtr<TextFieldComponent>();
119     // set default value
120     RefPtr<TextFieldTheme> theme = node.GetTheme<TextFieldTheme>();
121     InitController(component);
122     InitDefaultValue(boxComponent, component, theme);
123 
124     SetChildAttr(component, boxComponent, type, attrs);
125 
126     if (!component->IsEnabled()) {
127         SetDisableStyle(component, theme);
128     }
129 
130     return component;
131 }
132 
SetChildAttr(const RefPtr<TextFieldComponent> & component,const RefPtr<BoxComponent> & boxComponent,const std::string & type,const std::map<std::string,std::string> & attrs)133 void DOMTextFieldUtil::SetChildAttr(const RefPtr<TextFieldComponent>& component,
134     const RefPtr<BoxComponent>& boxComponent, const std::string& type, const std::map<std::string, std::string>& attrs)
135 {
136     if (!component) {
137         return;
138     }
139     component->SetTextInputType(ConvertStrToTextInputType(type));
140     component->SetObscure(type == DOM_INPUT_TYPE_PASSWORD);
141     // this static map should be sorted by key.
142     static const LinearMapNode<void (*)(const RefPtr<TextFieldComponent>&, const std::string&)> attrOperators[] = {
143         { DOM_AUTO_FOCUS, [](const RefPtr<TextFieldComponent>& component,
144                               const std::string& value) { component->SetAutoFocus(StringToBool(value)); } },
145         { DOM_DISABLED, [](const RefPtr<TextFieldComponent>& component,
146                         const std::string& value) { component->SetEnabled(!StringToBool(value)); } },
147         { DOM_INPUT_ENTERKEYTYPE,
148             [](const RefPtr<TextFieldComponent>& component, const std::string& value) {
149                 component->SetAction(ConvertStrToTextInputAction(value));
150             } },
151         { DOM_ICON_SRC, [](const RefPtr<TextFieldComponent>& component,
152                         const std::string& value) { component->SetIconImage(value); } },
153         { DOM_HIDE_ICON_SRC, [](const RefPtr<TextFieldComponent>& component,
154                              const std::string& value) { component->SetHideIconImage(value); } },
155         { DOM_INPUT_MAXLENGTH,
156             [](const RefPtr<TextFieldComponent>& component, const std::string& value) {
157                 int32_t tmp = StringUtils::StringToInt(value);
158                 component->SetMaxLength(tmp >= 0 ? (uint32_t)(tmp) : std::numeric_limits<uint32_t>::max());
159             } },
160         { DOM_INPUT_OBSCURE, [](const RefPtr<TextFieldComponent>& component,
161                              const std::string& value) { component->SetObscure(StringToBool(value)); } },
162         { DOM_INPUT_PLACEHOLDER, [](const RefPtr<TextFieldComponent>& component,
163                                  const std::string& value) { component->SetPlaceholder(value); } },
164         { DOM_INPUT_SELECTED_END, [](const RefPtr<TextFieldComponent>& component,
165                                       const std::string& value) { component->SetSelectedEnd(StringToInt(value)); } },
166         { DOM_INPUT_SELECTED_START,
167             [](const RefPtr<TextFieldComponent>& component, const std::string& value) {
168                 component->SetSelectedStart(StringToInt(value));
169             } },
170         { DOM_INPUT_SHOW_COUNTER, [](const RefPtr<TextFieldComponent>& component,
171                                   const std::string& value) { component->SetShowCounter(StringToBool(value)); } },
172         { DOM_SHOW_ICON_SRC, [](const RefPtr<TextFieldComponent>& component,
173                              const std::string& value) { component->SetShowIconImage(value); } },
174         { DOM_INPUT_SHOW_PASSWORD_ICON,
175             [](const RefPtr<TextFieldComponent>& component, const std::string& value) {
176                 component->SetShowPasswordIcon(StringToBool(value));
177             } },
178         { DOM_INPUT_SOFT_KEYBOARD_ENABLED,
179             [](const RefPtr<TextFieldComponent>& component, const std::string& value) {
180                 component->SetSoftKeyboardEnabled(StringToBool(value));
181             } },
182         { DOM_INPUT_VALUE,
183             [](const RefPtr<TextFieldComponent>& component, const std::string& value) { component->SetValue(value); } },
184     };
185     for (const auto& [key, value] : attrs) {
186         auto operatorIter = BinarySearchFindIndex(attrOperators, ArraySize(attrOperators), key.c_str());
187         if (operatorIter != -1) {
188             attrOperators[operatorIter].value(component, value);
189         }
190     }
191 
192     if (boxComponent) {
193         boxComponent->SetDeliverMinToChild(true);
194         if (GreatOrEqual(boxComponent->GetHeightDimension().Value(), 0.0)) {
195             component->SetHeight(boxComponent->GetHeightDimension());
196         }
197         if (NearZero(boxComponent->GetHeightDimension().Value(), 0.0)) {
198             boxComponent->SetHeight(0.0, DimensionUnit::PX);
199         } else {
200             boxComponent->SetHeight(-1.0, DimensionUnit::PX);
201         }
202     }
203 }
204 
SetChildStyle(const RefPtr<BoxComponent> & boxComponent,const RefPtr<TextFieldComponent> & component,const std::map<std::string,std::string> & styles,const Border & boxBorder,const DOMInput & node)205 void DOMTextFieldUtil::SetChildStyle(const RefPtr<BoxComponent>& boxComponent,
206     const RefPtr<TextFieldComponent>& component, const std::map<std::string, std::string>& styles,
207     const Border& boxBorder, const DOMInput& node)
208 {
209     if (!component) {
210         return;
211     }
212     TextStyle textStyle = component->GetTextStyle();
213     // static linear map must be sorted by key.
214     static const LinearMapNode<void (*)(
215         const RefPtr<TextFieldComponent>&, TextStyle&, const std::string&, const DOMInput&)>
216         styleOperators[] = {
217             { DOM_TEXT_ALLOW_SCALE,
218                 [](const RefPtr<TextFieldComponent>& component, TextStyle& style, const std::string& value,
219                     const DOMInput& node) { style.SetAllowScale(StringToBool(value)); } },
220             { DOM_BACKGROUND_COLOR,
221                 [](const RefPtr<TextFieldComponent>& component, TextStyle& style, const std::string& value,
222                     const DOMInput& node) {
223                     component->SetBgColor(node.ParseColor(value));
224                     component->SetFocusBgColor(node.ParseColor(value));
225                 } },
226             { DOM_CARET_COLOR,
227                 [](const RefPtr<TextFieldComponent>& component, TextStyle& style, const std::string& value,
228                     const DOMInput& node) { component->SetCursorColor(node.ParseColor(value)); } },
229             { DOM_INPUT_COLOR,
230                 [](const RefPtr<TextFieldComponent>& component, TextStyle& style, const std::string& value,
231                     const DOMInput& node) {
232                     style.SetTextColor(node.ParseColor(value));
233                     component->SetFocusTextColor(node.ParseColor(value));
234                 } },
235             { DOM_INPUT_CURSOR_COLOR,
236                 [](const RefPtr<TextFieldComponent>& component, TextStyle& style, const std::string& value,
237                     const DOMInput& node) { component->SetCursorColor(node.ParseColor(value)); } },
238             { DOM_INPUT_FONT_FAMILY,
239                 [](const RefPtr<TextFieldComponent>& component, TextStyle& style, const std::string& value,
240                     const DOMInput& node) {
241                     std::vector<std::string> fontFamilies;
242                     std::stringstream sstr(value);
243                     std::string fontFamily;
244                     while (getline(sstr, fontFamily, ',')) {
245                         fontFamilies.emplace_back(fontFamily);
246                     }
247                     style.SetFontFamilies(fontFamilies);
248                 } },
249             { DOM_INPUT_FONT_SIZE,
250                 [](const RefPtr<TextFieldComponent>& component, TextStyle& style, const std::string& value,
251                     const DOMInput& node) { style.SetFontSize(node.ParseDimension(value)); } },
252             { DOM_INPUT_FONT_WEIGHT,
253                 [](const RefPtr<TextFieldComponent>& component, TextStyle& style, const std::string& value,
254                     const DOMInput& node) { style.SetFontWeight(ConvertStrToFontWeight(value)); } },
255             { DOM_PADDING,
256                 [](const RefPtr<TextFieldComponent>& component, TextStyle& style, const std::string& value,
257                     const DOMInput& node) {
258                     Edge padding;
259                     if (Edge::FromString(value, padding)) {
260                         component->GetDecoration()->SetPadding(padding);
261                     }
262                 } },
263             { DOM_PADDING_BOTTOM,
264                 [](const RefPtr<TextFieldComponent>& component, TextStyle& style, const std::string& value,
265                     const DOMInput& node) {
266                     auto padding = component->GetDecoration()->GetPadding();
267                     padding.SetBottom(node.ParseDimension(value));
268                     component->GetDecoration()->SetPadding(padding);
269                 } },
270             { DOM_PADDING_END,
271                 [](const RefPtr<TextFieldComponent>& component, TextStyle& style, const std::string& value,
272                     const DOMInput& node) {
273                     auto padding = component->GetDecoration()->GetPadding();
274                     component->GetTextDirection() == TextDirection::LTR ? padding.SetRight(node.ParseDimension(value))
275                                                                         : padding.SetLeft(node.ParseDimension(value));
276                     component->GetDecoration()->SetPadding(padding);
277                 } },
278             { DOM_PADDING_LEFT,
279                 [](const RefPtr<TextFieldComponent>& component, TextStyle& style, const std::string& value,
280                     const DOMInput& node) {
281                     auto padding = component->GetDecoration()->GetPadding();
282                     padding.SetLeft(node.ParseDimension(value));
283                     component->GetDecoration()->SetPadding(padding);
284                 } },
285             { DOM_PADDING_RIGHT,
286                 [](const RefPtr<TextFieldComponent>& component, TextStyle& style, const std::string& value,
287                     const DOMInput& node) {
288                     auto padding = component->GetDecoration()->GetPadding();
289                     padding.SetRight(node.ParseDimension(value));
290                     component->GetDecoration()->SetPadding(padding);
291                 } },
292             { DOM_PADDING_START,
293                 [](const RefPtr<TextFieldComponent>& component, TextStyle& style, const std::string& value,
294                     const DOMInput& node) {
295                     auto padding = component->GetDecoration()->GetPadding();
296                     component->GetTextDirection() == TextDirection::LTR ? padding.SetLeft(node.ParseDimension(value))
297                                                                         : padding.SetRight(node.ParseDimension(value));
298                     component->GetDecoration()->SetPadding(padding);
299                 } },
300             { DOM_PADDING_TOP,
301                 [](const RefPtr<TextFieldComponent>& component, TextStyle& style, const std::string& value,
302                     const DOMInput& node) {
303                     auto padding = component->GetDecoration()->GetPadding();
304                     padding.SetTop(node.ParseDimension(value));
305                     component->GetDecoration()->SetPadding(padding);
306                 } },
307             { DOM_INPUT_PLACEHOLDER_COLOR,
308                 [](const RefPtr<TextFieldComponent>& component, TextStyle& style, const std::string& value,
309                     const DOMInput& node) {
310                     component->SetPlaceholderColor(node.ParseColor(value));
311                     component->SetFocusPlaceholderColor(node.ParseColor(value));
312                 } },
313         };
314     bool hasBoxRadius = false;
315     for (const auto& [key, value] : styles) {
316         auto operatorIter = BinarySearchFindIndex(styleOperators, ArraySize(styleOperators), key.c_str());
317         if (operatorIter != -1) {
318             styleOperators[operatorIter].value(component, textStyle, value, node);
319         }
320         if (IsRadiusStyle(key)) {
321             hasBoxRadius = true;
322         }
323     }
324     component->SetTextStyle(textStyle);
325     UpdateDecorationStyle(boxComponent, component, boxBorder, hasBoxRadius);
326 }
327 
UpdateDecorationStyle(const RefPtr<BoxComponent> & boxComponent,const RefPtr<TextFieldComponent> & component,const Border & boxBorder,bool hasBoxRadius)328 void DOMTextFieldUtil::UpdateDecorationStyle(const RefPtr<BoxComponent>& boxComponent,
329     const RefPtr<TextFieldComponent>& component, const Border& boxBorder, bool hasBoxRadius)
330 {
331     RefPtr<Decoration> decoration = component->GetDecoration();
332     if (!decoration) {
333         decoration = AceType::MakeRefPtr<Decoration>();
334     }
335     if (hasBoxRadius) {
336         decoration->SetBorder(boxBorder);
337     } else {
338         Border border = decoration->GetBorder();
339         border.SetLeftEdge(boxBorder.Left());
340         border.SetRightEdge(boxBorder.Right());
341         border.SetTopEdge(boxBorder.Top());
342         border.SetBottomEdge(boxBorder.Bottom());
343         border.SetBorderRadius(defaultRadius_);
344         decoration->SetBorder(border);
345     }
346     component->SetOriginBorder(decoration->GetBorder());
347 
348     if (!boxComponent) {
349         return;
350     }
351     RefPtr<Decoration> boxDecoration = boxComponent->GetBackDecoration();
352     if (boxDecoration && (boxDecoration->GetImage() || boxDecoration->GetGradient().IsValid())) {
353         // clear box properties except background image and radius.
354         boxDecoration->SetBackgroundColor(Color::TRANSPARENT);
355         Border border;
356         if (!hasBoxRadius) {
357             border.SetBorderRadius(defaultRadius_);
358         } else {
359             border.SetTopLeftRadius(boxBorder.TopLeftRadius());
360             border.SetTopRightRadius(boxBorder.TopRightRadius());
361             border.SetBottomLeftRadius(boxBorder.BottomLeftRadius());
362             border.SetBottomRightRadius(boxBorder.BottomRightRadius());
363         }
364         boxDecoration->SetBorder(border);
365     } else {
366         RefPtr<Decoration> backDecoration = AceType::MakeRefPtr<Decoration>();
367         backDecoration->SetBorderRadius(Radius(BOX_HOVER_RADIUS));
368         boxComponent->SetBackDecoration(backDecoration);
369     }
370     boxComponent->SetPadding(Edge());
371 }
372 
AddChildEvent(const RefPtr<TextFieldComponent> & component,int32_t pageId,const std::string & nodeId,const std::vector<std::string> & events)373 void DOMTextFieldUtil::AddChildEvent(const RefPtr<TextFieldComponent>& component, int32_t pageId,
374     const std::string& nodeId, const std::vector<std::string>& events)
375 {
376     static const LinearMapNode<void (*)(const RefPtr<TextFieldComponent>&, const EventMarker&)> eventOperators[] = {
377         { DOM_CATCH_BUBBLE_CLICK,
378             [](const RefPtr<TextFieldComponent>& component, const EventMarker& event) {
379                 EventMarker eventMarker(event);
380                 eventMarker.SetCatchMode(true);
381                 component->SetOnTap(eventMarker);
382             } },
383         { DOM_CHANGE, [](const RefPtr<TextFieldComponent>& component,
384                       const EventMarker& event) { component->SetOnTextChange(event); } },
385         { DOM_CLICK,
386             [](const RefPtr<TextFieldComponent>& component, const EventMarker& event) {
387                 EventMarker eventMarker(event);
388                 eventMarker.SetCatchMode(false);
389                 component->SetOnTap(eventMarker);
390             } },
391         { DOM_INPUT_EVENT_ENTERKEYCLICK, [](const RefPtr<TextFieldComponent>& component,
392                                          const EventMarker& event) { component->SetOnFinishInput(event); } },
393         { DOM_LONG_PRESS, [](const RefPtr<TextFieldComponent>& component,
394                           const EventMarker& event) { component->SetOnLongPress(event); } },
395         { DOM_INPUT_EVENT_OPTION_SELECT, [](const RefPtr<TextFieldComponent>& component,
396                                          const EventMarker& event) { component->SetOnOptionsClick(event); } },
397         { DOM_INPUT_EVENT_SEARCH, [](const RefPtr<TextFieldComponent>& component,
398                                         const EventMarker& event) { component->SetOnSearch(event); } },
399         { DOM_INPUT_EVENT_SELECT_CHANGE, [](const RefPtr<TextFieldComponent>& component,
400                                              const EventMarker& event) { component->SetOnSelectChange(event); } },
401         { DOM_INPUT_EVENT_SHARE, [](const RefPtr<TextFieldComponent>& component,
402                                  const EventMarker& event) { component->SetOnShare(event); } },
403         { DOM_INPUT_EVENT_TRANSLATE, [](const RefPtr<TextFieldComponent>& component,
404                                 const EventMarker& event) { component->SetOnTranslate(event); } },
405     };
406     for (const auto& event : events) {
407         auto operatorIter = BinarySearchFindIndex(eventOperators, ArraySize(eventOperators), event.c_str());
408         if (operatorIter != -1) {
409             eventOperators[operatorIter].value(component, EventMarker(nodeId, event, pageId));
410         }
411     }
412 }
413 
IsRadiusStyle(const std::string & style)414 bool DOMTextFieldUtil::IsRadiusStyle(const std::string& style)
415 {
416     return (style == DOM_BORDER_TOP_LEFT_RADIUS || style == DOM_BORDER_TOP_RIGHT_RADIUS ||
417             style == DOM_BORDER_BOTTOM_LEFT_RADIUS || style == DOM_BORDER_BOTTOM_RIGHT_RADIUS ||
418             style == DOM_BORDER_RADIUS);
419 }
420 
421 } // namespace OHOS::Ace::Framework
422