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