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