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/dom_textarea.h"
17
18 #include <iostream>
19
20 #include "frameworks/bridge/common/dom/input/dom_textfield_util.h"
21 #include "frameworks/bridge/common/utils/utils.h"
22
23 namespace OHOS::Ace::Framework {
24 namespace {
25
26 constexpr uint32_t TEXTAREA_MAXLENGTH_VALUE_DEFAULT = std::numeric_limits<uint32_t>::max();
27 constexpr Dimension BOX_HOVER_RADIUS = 18.0_vp;
28
29 } // namespace
30
DOMTextarea(NodeId nodeId,const std::string & nodeName)31 DOMTextarea::DOMTextarea(NodeId nodeId, const std::string& nodeName) : DOMNode(nodeId, nodeName)
32 {
33 textAreaChild_ = AceType::MakeRefPtr<TextFieldComponent>();
34 textAreaChild_->SetTextInputType(TextInputType::MULTILINE);
35 textAreaChild_->SetTextEditController(AceType::MakeRefPtr<TextEditController>());
36 textAreaChild_->SetTextFieldController(AceType::MakeRefPtr<TextFieldController>());
37 }
38
ResetInitializedStyle()39 void DOMTextarea::ResetInitializedStyle()
40 {
41 InitializeStyle();
42 }
43
InitializeStyle()44 void DOMTextarea::InitializeStyle()
45 {
46 auto boxComponent = GetBoxComponent();
47 auto component = textAreaChild_;
48 auto theme = GetTheme<TextFieldTheme>();
49 if (!boxComponent || !component || !theme) {
50 return;
51 }
52
53 component->SetTextMaxLines(TEXTAREA_MAXLENGTH_VALUE_DEFAULT);
54 component->SetCursorColor(theme->GetCursorColor());
55 component->SetPlaceholderColor(theme->GetPlaceholderColor());
56 component->SetFocusBgColor(theme->GetFocusBgColor());
57 component->SetFocusPlaceholderColor(theme->GetFocusPlaceholderColor());
58 component->SetFocusTextColor(theme->GetFocusTextColor());
59 component->SetBgColor(theme->GetBgColor());
60 component->SetTextColor(theme->GetTextColor());
61 component->SetSelectedColor(theme->GetSelectedColor());
62 component->SetHoverColor(theme->GetHoverColor());
63 component->SetPressColor(theme->GetPressColor());
64 textStyle_.SetTextColor(theme->GetTextColor());
65 textStyle_.SetFontSize(theme->GetFontSize());
66 textStyle_.SetFontWeight(theme->GetFontWeight());
67 std::vector<std::string> textareaFontFamilyValueDefault = {
68 "sans-serif",
69 };
70 textStyle_.SetFontFamilies(textareaFontFamilyValueDefault);
71 component->SetTextStyle(textStyle_);
72 component->SetCountTextStyle(theme->GetCountTextStyle());
73 component->SetOverCountStyle(theme->GetOverCountStyle());
74 component->SetCountTextStyleOuter(theme->GetCountTextStyleOuter());
75 component->SetOverCountStyleOuter(theme->GetOverCountStyleOuter());
76
77 component->SetErrorBorderWidth(theme->GetErrorBorderWidth());
78 component->SetErrorBorderColor(theme->GetErrorBorderColor());
79
80 RefPtr<Decoration> backDecoration = AceType::MakeRefPtr<Decoration>();
81 backDecoration->SetPadding(theme->GetPadding());
82 backDecoration->SetBackgroundColor(theme->GetBgColor());
83 defaultRadius_ = theme->GetBorderRadius();
84 backDecoration->SetBorderRadius(defaultRadius_);
85 if (boxComponent->GetBackDecoration()) {
86 backDecoration->SetImage(boxComponent->GetBackDecoration()->GetImage());
87 backDecoration->SetGradient(boxComponent->GetBackDecoration()->GetGradient());
88 }
89 component->SetDecoration(backDecoration);
90 component->SetIconSize(theme->GetIconSize());
91 component->SetIconHotZoneSize(theme->GetIconHotZoneSize());
92
93 boxComponent->SetBackDecoration(backDecoration);
94 boxComponent->SetPadding(theme->GetPadding());
95 }
96
SetSpecializedAttr(const std::pair<std::string,std::string> & attr)97 bool DOMTextarea::SetSpecializedAttr(const std::pair<std::string, std::string>& attr)
98 {
99 static const DOMTextareaMap textAreaAttrMap = {
100 { DOM_AUTO_FOCUS, [](const std::string& val,
101 DOMTextarea& textarea) { textarea.textAreaChild_->SetAutoFocus(StringToBool(val)); } },
102 { DOM_TEXTAREA_VALUE,
103 [](const std::string& val, DOMTextarea& textarea) { textarea.textAreaChild_->SetValue(val); } },
104 { DOM_DISABLED, [](const std::string& val,
105 DOMTextarea& textarea) { textarea.textAreaChild_->SetEnabled(!StringToBool(val)); } },
106 { DOM_INPUT_ENTERKEYTYPE,
107 [](const std::string& val, DOMTextarea& textarea) {
108 textarea.textAreaChild_->SetAction(ConvertStrToTextInputAction(val));
109 } },
110 { DOM_TEXTAREA_PLACEHOLDER,
111 [](const std::string& val, DOMTextarea& textarea) { textarea.textAreaChild_->SetPlaceholder(val); } },
112 { DOM_TEXTAREA_MAXLENGTH,
113 [](const std::string& val, DOMTextarea& textarea) {
114 int32_t tmp = StringUtils::StringToInt(val);
115 textarea.textAreaChild_->SetMaxLength(
116 tmp >= 0 ? (uint32_t)(tmp) : std::numeric_limits<uint32_t>::max());
117 } },
118 { DOM_TEXTAREA_MAXLINES,
119 [](const std::string& val, DOMTextarea& textarea) {
120 textarea.textAreaChild_->SetTextMaxLines(std::max(StringToInt(val), 1));
121 } },
122 { DOM_TEXTAREA_OBSCURE,
123 [](const std::string& val, DOMTextarea& textarea) {
124 textarea.textAreaChild_->SetObscure(StringToBool(val));
125 } },
126 { DOM_TEXTAREA_OVERFLOWX,
127 [](const std::string& val, DOMTextarea& textarea) {
128 textarea.textAreaChild_->SetOverflowX(ConvertStrToTextFieldOverflowX(val));
129 } },
130 { DOM_TEXTAREA_EXTEND,
131 [](const std::string& val, DOMTextarea& textarea) {
132 textarea.textAreaChild_->SetExtend(StringToBool(val));
133 } },
134 { DOM_TEXTAREA_SHOW_COUNTER,
135 [](const std::string& val, DOMTextarea& textarea) {
136 textarea.textAreaChild_->SetShowCounter(StringToBool(val));
137 } },
138 { DOM_ICON_SRC,
139 [](const std::string& val, DOMTextarea& textarea) { textarea.textAreaChild_->SetIconImage(val); } },
140 { DOM_INPUT_SELECTED_START,
141 [](const std::string& val, DOMTextarea& textarea) {
142 textarea.textAreaChild_->SetSelectedStart(StringToInt(val));
143 } },
144 { DOM_INPUT_SELECTED_END,
145 [](const std::string& val, DOMTextarea& textarea) {
146 textarea.textAreaChild_->SetSelectedEnd(StringToInt(val)); } },
147 { DOM_INPUT_SOFT_KEYBOARD_ENABLED,
148 [](const std::string& val, DOMTextarea& textarea) {
149 textarea.textAreaChild_->SetSoftKeyboardEnabled(StringToBool(val));
150 } },
151 };
152 auto textareaAttrIter = textAreaAttrMap.find(attr.first);
153 if (textareaAttrIter != textAreaAttrMap.end()) {
154 textareaAttrIter->second(attr.second, *this);
155 return true;
156 }
157 return false;
158 }
159
SetSpecializedStyle(const std::pair<std::string,std::string> & style)160 bool DOMTextarea::SetSpecializedStyle(const std::pair<std::string, std::string>& style)
161 {
162 if (!textAreaChild_) {
163 return false;
164 }
165 static const DOMTextareaMap textAreaStyleMap = {
166 { DOM_BACKGROUND_COLOR,
167 [](const std::string& val, DOMTextarea& textarea) {
168 textarea.textAreaChild_->SetBgColor(textarea.ParseColor(val));
169 textarea.textAreaChild_->SetFocusBgColor(textarea.ParseColor(val));
170 } },
171 { DOM_TEXTAREA_COLOR,
172 [](const std::string& val, DOMTextarea& textarea) {
173 textarea.textStyle_.SetTextColor(textarea.ParseColor(val));
174 textarea.textAreaChild_->SetFocusTextColor(textarea.ParseColor(val));
175 } },
176 { DOM_TEXTAREA_FONT_SIZE,
177 [](const std::string& val, DOMTextarea& textarea) {
178 textarea.textStyle_.SetFontSize(textarea.ParseDimension(val));
179 } },
180 { DOM_TEXTAREA_FONT_WEIGHT,
181 [](const std::string& val, DOMTextarea& textarea) {
182 textarea.textStyle_.SetFontWeight(ConvertStrToFontWeight(val));
183 } },
184 { DOM_TEXTAREA_PLACEHOLDER_COLOR,
185 [](const std::string& val, DOMTextarea& textarea) {
186 textarea.textAreaChild_->SetPlaceholderColor(textarea.ParseColor(val));
187 textarea.textAreaChild_->SetFocusPlaceholderColor(textarea.ParseColor(val));
188 } },
189 { DOM_TEXTAREA_FONT_FAMILY,
190 [](const std::string& val, DOMTextarea& textarea) {
191 textarea.textStyle_.SetFontFamilies(textarea.ParseFontFamilies(val));
192 } },
193 { DOM_TEXT_ALLOW_SCALE, [](const std::string& val,
194 DOMTextarea& textarea) { textarea.textStyle_.SetAllowScale(StringToBool(val)); } },
195 { DOM_TEXTAREA_CURSOR_COLOR,
196 [](const std::string& val, DOMTextarea& textarea) {
197 textarea.textAreaChild_->SetCursorColor(textarea.ParseColor(val));
198 } },
199 { DOM_CARET_COLOR,
200 [](const std::string& val, DOMTextarea& textarea) {
201 textarea.textAreaChild_->SetCursorColor(textarea.ParseColor(val));
202 } },
203 { DOM_PADDING,
204 [](const std::string& val, DOMTextarea& textarea) {
205 Edge padding;
206 if (Edge::FromString(val, padding)) {
207 textarea.textAreaChild_->GetDecoration()->SetPadding(padding);
208 }
209 } },
210 { DOM_PADDING_LEFT,
211 [](const std::string& val, DOMTextarea& textarea) {
212 auto padding = textarea.textAreaChild_->GetDecoration()->GetPadding();
213 padding.SetLeft(textarea.ParseDimension(val));
214 textarea.textAreaChild_->GetDecoration()->SetPadding(padding);
215 } },
216 { DOM_PADDING_RIGHT,
217 [](const std::string& val, DOMTextarea& textarea) {
218 auto padding = textarea.textAreaChild_->GetDecoration()->GetPadding();
219 padding.SetRight(textarea.ParseDimension(val));
220 textarea.textAreaChild_->GetDecoration()->SetPadding(padding);
221 } },
222 { DOM_PADDING_TOP,
223 [](const std::string& val, DOMTextarea& textarea) {
224 auto padding = textarea.textAreaChild_->GetDecoration()->GetPadding();
225 padding.SetTop(textarea.ParseDimension(val));
226 textarea.textAreaChild_->GetDecoration()->SetPadding(padding);
227 } },
228 { DOM_PADDING_BOTTOM,
229 [](const std::string& val, DOMTextarea& textarea) {
230 auto padding = textarea.textAreaChild_->GetDecoration()->GetPadding();
231 padding.SetBottom(textarea.ParseDimension(val));
232 textarea.textAreaChild_->GetDecoration()->SetPadding(padding);
233 } },
234 { DOM_PADDING_START,
235 [](const std::string& val, DOMTextarea& textarea) {
236 auto padding = textarea.textAreaChild_->GetDecoration()->GetPadding();
237 textarea.IsRightToLeft() ? padding.SetRight(textarea.ParseDimension(val))
238 : padding.SetLeft(textarea.ParseDimension(val));
239 textarea.textAreaChild_->GetDecoration()->SetPadding(padding);
240 } },
241 { DOM_PADDING_END,
242 [](const std::string& val, DOMTextarea& textarea) {
243 auto padding = textarea.textAreaChild_->GetDecoration()->GetPadding();
244 textarea.IsRightToLeft() ? padding.SetLeft(textarea.ParseDimension(val))
245 : padding.SetRight(textarea.ParseDimension(val));
246 textarea.textAreaChild_->GetDecoration()->SetPadding(padding);
247 } },
248 };
249 auto textareaStyleIter = textAreaStyleMap.find(style.first);
250 if (textareaStyleIter != textAreaStyleMap.end()) {
251 textareaStyleIter->second(style.second, *this);
252 return true;
253 }
254 hasBoxRadius_ = DOMTextFieldUtil::IsRadiusStyle(style.first);
255 return false;
256 }
257
AddSpecializedEvent(int32_t pageId,const std::string & event)258 bool DOMTextarea::AddSpecializedEvent(int32_t pageId, const std::string& event)
259 {
260 static const LinearMapNode<void (*)(const RefPtr<TextFieldComponent>&, const EventMarker&)> eventOperators[] = {
261 { DOM_CATCH_BUBBLE_CLICK,
262 [](const RefPtr<TextFieldComponent>& component, const EventMarker& event) {
263 EventMarker eventMarker(event);
264 eventMarker.SetCatchMode(true);
265 component->SetOnTap(eventMarker);
266 } },
267 { DOM_CHANGE, [](const RefPtr<TextFieldComponent>& component,
268 const EventMarker& event) { component->SetOnTextChange(event); } },
269 { DOM_CLICK,
270 [](const RefPtr<TextFieldComponent>& component, const EventMarker& event) {
271 EventMarker eventMarker(event);
272 eventMarker.SetCatchMode(false);
273 component->SetOnTap(eventMarker);
274 } },
275 { DOM_LONG_PRESS, [](const RefPtr<TextFieldComponent>& component,
276 const EventMarker& event) { component->SetOnLongPress(event); } },
277 { DOM_INPUT_EVENT_OPTION_SELECT, [](const RefPtr<TextFieldComponent>& component,
278 const EventMarker& event) { component->SetOnOptionsClick(event); } },
279 { DOM_INPUT_EVENT_SEARCH, [](const RefPtr<TextFieldComponent>& component,
280 const EventMarker& event) { component->SetOnSearch(event); } },
281 { DOM_INPUT_EVENT_SELECT_CHANGE, [](const RefPtr<TextFieldComponent>& component,
282 const EventMarker& event) { component->SetOnSelectChange(event); } },
283 { DOM_INPUT_EVENT_SHARE, [](const RefPtr<TextFieldComponent>& component,
284 const EventMarker& event) { component->SetOnShare(event); } },
285 { DOM_INPUT_EVENT_TRANSLATE, [](const RefPtr<TextFieldComponent>& component,
286 const EventMarker& event) { component->SetOnTranslate(event); } },
287 };
288 auto operatorIter = BinarySearchFindIndex(eventOperators, ArraySize(eventOperators), event.c_str());
289 if (operatorIter != -1) {
290 eventOperators[operatorIter].value(textAreaChild_, EventMarker(GetNodeIdForEvent(), event, pageId));
291 return true;
292 }
293 return false;
294 }
295
CallSpecializedMethod(const std::string & method,const std::string & args)296 void DOMTextarea::CallSpecializedMethod(const std::string& method, const std::string& args)
297 {
298 if (method == DOM_INPUT_METHOD_DELETE) {
299 auto textField = AceType::DynamicCast<TextFieldComponent>(textAreaChild_);
300 if (!textField) {
301 return;
302 }
303 auto controller = textField->GetTextFieldController();
304 if (!controller) {
305 return;
306 }
307 controller->Delete();
308 }
309 }
310
OnRequestFocus(bool shouldFocus)311 void DOMTextarea::OnRequestFocus(bool shouldFocus)
312 {
313 if (!textAreaChild_) {
314 return;
315 }
316 auto textFieldController = textAreaChild_->GetTextFieldController();
317 if (!textFieldController) {
318 return;
319 }
320 textFieldController->Focus(shouldFocus);
321 }
322
PrepareSpecializedComponent()323 void DOMTextarea::PrepareSpecializedComponent()
324 {
325 RefPtr<BoxComponent> boxComponent = GetBoxComponent();
326 if (!boxComponent || !textAreaChild_) {
327 return;
328 }
329 boxComponent_->SetMouseAnimationType(HoverAnimationType::OPACITY);
330 textAreaChild_->SetTextDirection(IsRightToLeft() ? TextDirection::RTL : TextDirection::LTR);
331 textAreaChild_->SetTextStyle(textStyle_);
332 textAreaChild_->SetInputOptions(inputOptions_);
333 textAreaChild_->SetImageFill(GetImageFill());
334 UpdateDecoration();
335 boxComponent->SetPadding(Edge());
336 boxComponent->SetDeliverMinToChild(true);
337 auto theme = GetTheme<TextFieldTheme>();
338 if (boxComponent_->GetHeightDimension().Value() < 0.0 && theme) {
339 boxComponent->SetHeight(theme->GetHeight().Value(), theme->GetHeight().Unit());
340 }
341 textAreaChild_->SetHeight(boxComponent_->GetHeightDimension());
342 if (textAreaChild_->IsExtend()) {
343 boxComponent_->SetHeight(-1.0, DimensionUnit::PX);
344 }
345 }
346
UpdateDecoration()347 void DOMTextarea::UpdateDecoration()
348 {
349 RefPtr<BoxComponent> boxComponent = GetBoxComponent();
350 if (!boxComponent || !textAreaChild_) {
351 return;
352 }
353 RefPtr<Decoration> backDecoration = boxComponent->GetBackDecoration();
354
355 // set box border properties to child component
356 RefPtr<Decoration> decoration = textAreaChild_->GetDecoration();
357 if (backDecoration) {
358 Border boxBorder = backDecoration->GetBorder();
359 if (decoration) {
360 if (hasBoxRadius_) {
361 decoration->SetBorder(boxBorder);
362 } else {
363 Border border = decoration->GetBorder();
364 border.SetLeftEdge(boxBorder.Left());
365 border.SetRightEdge(boxBorder.Right());
366 border.SetTopEdge(boxBorder.Top());
367 border.SetBottomEdge(boxBorder.Bottom());
368 decoration->SetBorder(border);
369 }
370 textAreaChild_->SetOriginBorder(decoration->GetBorder());
371 }
372 // clear box properties
373 if (backDecoration->GetImage() || backDecoration->GetGradient().IsValid()) {
374 // clear box properties except background image
375 backDecoration->SetBackgroundColor(Color::TRANSPARENT);
376 Border border;
377 if (!hasBoxRadius_) {
378 border.SetBorderRadius(defaultRadius_);
379 } else {
380 border.SetTopLeftRadius(boxBorder.TopLeftRadius());
381 border.SetTopRightRadius(boxBorder.TopRightRadius());
382 border.SetBottomLeftRadius(boxBorder.BottomLeftRadius());
383 border.SetBottomRightRadius(boxBorder.BottomRightRadius());
384 }
385 backDecoration->SetBorder(border);
386 } else {
387 backDecoration = AceType::MakeRefPtr<Decoration>();
388 backDecoration->SetBorderRadius(Radius(BOX_HOVER_RADIUS));
389 boxComponent->SetBackDecoration(backDecoration);
390 }
391 }
392 }
393
394 } // namespace OHOS::Ace::Framework
395