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