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