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