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_OVERFLOWX,
125             [](const std::string& val, DOMTextarea& textarea) {
126                 textarea.textAreaChild_->SetOverflowX(ConvertStrToTextFieldOverflowX(val));
127             } },
128         { DOM_TEXTAREA_EXTEND,
129             [](const std::string& val, DOMTextarea& textarea) {
130                 textarea.textAreaChild_->SetExtend(StringToBool(val));
131             } },
132         { DOM_TEXTAREA_SHOW_COUNTER,
133             [](const std::string& val, DOMTextarea& textarea) {
134                 textarea.textAreaChild_->SetShowCounter(StringToBool(val));
135             } },
136         { DOM_ICON_SRC,
137             [](const std::string& val, DOMTextarea& textarea) { textarea.textAreaChild_->SetIconImage(val); } },
138         { DOM_INPUT_SELECTED_START,
139             [](const std::string& val, DOMTextarea& textarea) {
140                 textarea.textAreaChild_->SetSelectedStart(StringToInt(val));
141             } },
142         { DOM_INPUT_SELECTED_END,
143             [](const std::string& val, DOMTextarea& textarea) {
144                 textarea.textAreaChild_->SetSelectedEnd(StringToInt(val)); } },
145         { DOM_INPUT_SOFT_KEYBOARD_ENABLED,
146             [](const std::string& val, DOMTextarea& textarea) {
147                 textarea.textAreaChild_->SetSoftKeyboardEnabled(StringToBool(val));
148             } },
149     };
150     auto textareaAttrIter = textAreaAttrMap.find(attr.first);
151     if (textareaAttrIter != textAreaAttrMap.end()) {
152         textareaAttrIter->second(attr.second, *this);
153         return true;
154     }
155     return false;
156 }
157 
SetSpecializedStyle(const std::pair<std::string, std::string>& style)158 bool DOMTextarea::SetSpecializedStyle(const std::pair<std::string, std::string>& style)
159 {
160     if (!textAreaChild_) {
161         return false;
162     }
163     static const DOMTextareaMap textAreaStyleMap = {
164         { DOM_BACKGROUND_COLOR,
165             [](const std::string& val, DOMTextarea& textarea) {
166                 textarea.textAreaChild_->SetBgColor(textarea.ParseColor(val));
167                 textarea.textAreaChild_->SetFocusBgColor(textarea.ParseColor(val));
168             } },
169         { DOM_TEXTAREA_COLOR,
170             [](const std::string& val, DOMTextarea& textarea) {
171                 textarea.textStyle_.SetTextColor(textarea.ParseColor(val));
172                 textarea.textAreaChild_->SetFocusTextColor(textarea.ParseColor(val));
173             } },
174         { DOM_TEXTAREA_FONT_SIZE,
175             [](const std::string& val, DOMTextarea& textarea) {
176                 textarea.textStyle_.SetFontSize(textarea.ParseDimension(val));
177             } },
178         { DOM_TEXTAREA_FONT_WEIGHT,
179             [](const std::string& val, DOMTextarea& textarea) {
180                 textarea.textStyle_.SetFontWeight(ConvertStrToFontWeight(val));
181             } },
182         { DOM_TEXTAREA_PLACEHOLDER_COLOR,
183             [](const std::string& val, DOMTextarea& textarea) {
184                 textarea.textAreaChild_->SetPlaceholderColor(textarea.ParseColor(val));
185                 textarea.textAreaChild_->SetFocusPlaceholderColor(textarea.ParseColor(val));
186             } },
187         { DOM_TEXTAREA_FONT_FAMILY,
188             [](const std::string& val, DOMTextarea& textarea) {
189                 textarea.textStyle_.SetFontFamilies(textarea.ParseFontFamilies(val));
190             } },
191         { DOM_TEXT_ALLOW_SCALE, [](const std::string& val,
192                                 DOMTextarea& textarea) { textarea.textStyle_.SetAllowScale(StringToBool(val)); } },
193         { DOM_TEXTAREA_CURSOR_COLOR,
194             [](const std::string& val, DOMTextarea& textarea) {
195                 textarea.textAreaChild_->SetCursorColor(textarea.ParseColor(val));
196             } },
197         { DOM_CARET_COLOR,
198             [](const std::string& val, DOMTextarea& textarea) {
199                 textarea.textAreaChild_->SetCursorColor(textarea.ParseColor(val));
200             } },
201         { DOM_PADDING,
202             [](const std::string& val, DOMTextarea& textarea) {
203                 Edge padding;
204                 if (Edge::FromString(val, padding)) {
205                     textarea.textAreaChild_->GetDecoration()->SetPadding(padding);
206                 }
207             } },
208         { DOM_PADDING_LEFT,
209             [](const std::string& val, DOMTextarea& textarea) {
210                 auto padding = textarea.textAreaChild_->GetDecoration()->GetPadding();
211                 padding.SetLeft(textarea.ParseDimension(val));
212                 textarea.textAreaChild_->GetDecoration()->SetPadding(padding);
213             } },
214         { DOM_PADDING_RIGHT,
215             [](const std::string& val, DOMTextarea& textarea) {
216                 auto padding = textarea.textAreaChild_->GetDecoration()->GetPadding();
217                 padding.SetRight(textarea.ParseDimension(val));
218                 textarea.textAreaChild_->GetDecoration()->SetPadding(padding);
219             } },
220         { DOM_PADDING_TOP,
221             [](const std::string& val, DOMTextarea& textarea) {
222                 auto padding = textarea.textAreaChild_->GetDecoration()->GetPadding();
223                 padding.SetTop(textarea.ParseDimension(val));
224                 textarea.textAreaChild_->GetDecoration()->SetPadding(padding);
225             } },
226         { DOM_PADDING_BOTTOM,
227             [](const std::string& val, DOMTextarea& textarea) {
228                 auto padding = textarea.textAreaChild_->GetDecoration()->GetPadding();
229                 padding.SetBottom(textarea.ParseDimension(val));
230                 textarea.textAreaChild_->GetDecoration()->SetPadding(padding);
231             } },
232         { DOM_PADDING_START,
233             [](const std::string& val, DOMTextarea& textarea) {
234                 auto padding = textarea.textAreaChild_->GetDecoration()->GetPadding();
235                 textarea.IsRightToLeft() ? padding.SetRight(textarea.ParseDimension(val))
236                                          : padding.SetLeft(textarea.ParseDimension(val));
237                 textarea.textAreaChild_->GetDecoration()->SetPadding(padding);
238             } },
239         { DOM_PADDING_END,
240             [](const std::string& val, DOMTextarea& textarea) {
241                 auto padding = textarea.textAreaChild_->GetDecoration()->GetPadding();
242                 textarea.IsRightToLeft() ? padding.SetLeft(textarea.ParseDimension(val))
243                                          : padding.SetRight(textarea.ParseDimension(val));
244                 textarea.textAreaChild_->GetDecoration()->SetPadding(padding);
245             } },
246     };
247     auto textareaStyleIter = textAreaStyleMap.find(style.first);
248     if (textareaStyleIter != textAreaStyleMap.end()) {
249         textareaStyleIter->second(style.second, *this);
250         return true;
251     }
252     hasBoxRadius_ = DOMTextFieldUtil::IsRadiusStyle(style.first);
253     return false;
254 }
255 
AddSpecializedEvent(int32_t pageId, const std::string& event)256 bool DOMTextarea::AddSpecializedEvent(int32_t pageId, const std::string& event)
257 {
258     static const LinearMapNode<void (*)(const RefPtr<TextFieldComponent>&, const EventMarker&)> eventOperators[] = {
259         { DOM_CATCH_BUBBLE_CLICK,
260             [](const RefPtr<TextFieldComponent>& component, const EventMarker& event) {
261                 EventMarker eventMarker(event);
262                 eventMarker.SetCatchMode(true);
263                 component->SetOnTap(eventMarker);
264             } },
265         { DOM_CHANGE, [](const RefPtr<TextFieldComponent>& component,
266                       const EventMarker& event) { component->SetOnTextChange(event); } },
267         { DOM_CLICK,
268             [](const RefPtr<TextFieldComponent>& component, const EventMarker& event) {
269                 EventMarker eventMarker(event);
270                 eventMarker.SetCatchMode(false);
271                 component->SetOnTap(eventMarker);
272             } },
273         { DOM_LONG_PRESS, [](const RefPtr<TextFieldComponent>& component,
274                           const EventMarker& event) { component->SetOnLongPress(event); } },
275         { DOM_INPUT_EVENT_OPTION_SELECT, [](const RefPtr<TextFieldComponent>& component,
276                                          const EventMarker& event) { component->SetOnOptionsClick(event); } },
277         { DOM_INPUT_EVENT_SEARCH, [](const RefPtr<TextFieldComponent>& component,
278                                   const EventMarker& event) { component->SetOnSearch(event); } },
279         { DOM_INPUT_EVENT_SELECT_CHANGE, [](const RefPtr<TextFieldComponent>& component,
280                                              const EventMarker& event) { component->SetOnSelectChange(event); } },
281         { DOM_INPUT_EVENT_SHARE, [](const RefPtr<TextFieldComponent>& component,
282                                  const EventMarker& event) { component->SetOnShare(event); } },
283         { DOM_INPUT_EVENT_TRANSLATE, [](const RefPtr<TextFieldComponent>& component,
284                                      const EventMarker& event) { component->SetOnTranslate(event); } },
285     };
286     auto operatorIter = BinarySearchFindIndex(eventOperators, ArraySize(eventOperators), event.c_str());
287     if (operatorIter != -1) {
288         eventOperators[operatorIter].value(textAreaChild_, EventMarker(GetNodeIdForEvent(), event, pageId));
289         return true;
290     }
291     return false;
292 }
293 
CallSpecializedMethod(const std::string& method, const std::string& args)294 void DOMTextarea::CallSpecializedMethod(const std::string& method, const std::string& args)
295 {
296     if (method == DOM_INPUT_METHOD_DELETE) {
297         auto textField = AceType::DynamicCast<TextFieldComponent>(textAreaChild_);
298         if (!textField) {
299             return;
300         }
301         auto controller = textField->GetTextFieldController();
302         if (!controller) {
303             return;
304         }
305         controller->Delete();
306     }
307 }
308 
OnRequestFocus(bool shouldFocus)309 void DOMTextarea::OnRequestFocus(bool shouldFocus)
310 {
311     if (!textAreaChild_) {
312         return;
313     }
314     auto textFieldController = textAreaChild_->GetTextFieldController();
315     if (!textFieldController) {
316         return;
317     }
318     textFieldController->Focus(shouldFocus);
319 }
320 
PrepareSpecializedComponent()321 void DOMTextarea::PrepareSpecializedComponent()
322 {
323     RefPtr<BoxComponent> boxComponent = GetBoxComponent();
324     if (!boxComponent || !textAreaChild_) {
325         return;
326     }
327     boxComponent_->SetMouseAnimationType(HoverAnimationType::OPACITY);
328     textAreaChild_->SetTextDirection(IsRightToLeft() ? TextDirection::RTL : TextDirection::LTR);
329     textAreaChild_->SetTextStyle(textStyle_);
330     textAreaChild_->SetInputOptions(inputOptions_);
331     textAreaChild_->SetImageFill(GetImageFill());
332     UpdateDecoration();
333     boxComponent->SetPadding(Edge());
334     boxComponent->SetDeliverMinToChild(true);
335     auto theme = GetTheme<TextFieldTheme>();
336     if (boxComponent_->GetHeightDimension().Value() < 0.0 && theme) {
337         boxComponent->SetHeight(theme->GetHeight().Value(), theme->GetHeight().Unit());
338     }
339     textAreaChild_->SetHeight(boxComponent_->GetHeightDimension());
340     if (textAreaChild_->IsExtend()) {
341         boxComponent_->SetHeight(-1.0, DimensionUnit::PX);
342     }
343 }
344 
UpdateDecoration()345 void DOMTextarea::UpdateDecoration()
346 {
347     RefPtr<BoxComponent> boxComponent = GetBoxComponent();
348     if (!boxComponent || !textAreaChild_) {
349         return;
350     }
351     RefPtr<Decoration> backDecoration = boxComponent->GetBackDecoration();
352 
353     // set box border properties to child component
354     RefPtr<Decoration> decoration = textAreaChild_->GetDecoration();
355     if (backDecoration) {
356         Border boxBorder = backDecoration->GetBorder();
357         if (decoration) {
358             if (hasBoxRadius_) {
359                 decoration->SetBorder(boxBorder);
360             } else {
361                 Border border = decoration->GetBorder();
362                 border.SetLeftEdge(boxBorder.Left());
363                 border.SetRightEdge(boxBorder.Right());
364                 border.SetTopEdge(boxBorder.Top());
365                 border.SetBottomEdge(boxBorder.Bottom());
366                 decoration->SetBorder(border);
367             }
368             textAreaChild_->SetOriginBorder(decoration->GetBorder());
369         }
370         // clear box properties
371         if (backDecoration->GetImage() || backDecoration->GetGradient().IsValid()) {
372             // clear box properties except background image
373             backDecoration->SetBackgroundColor(Color::TRANSPARENT);
374             Border border;
375             if (!hasBoxRadius_) {
376                 border.SetBorderRadius(defaultRadius_);
377             } else {
378                 border.SetTopLeftRadius(boxBorder.TopLeftRadius());
379                 border.SetTopRightRadius(boxBorder.TopRightRadius());
380                 border.SetBottomLeftRadius(boxBorder.BottomLeftRadius());
381                 border.SetBottomRightRadius(boxBorder.BottomRightRadius());
382             }
383             backDecoration->SetBorder(border);
384         } else {
385             backDecoration = AceType::MakeRefPtr<Decoration>();
386             backDecoration->SetBorderRadius(Radius(BOX_HOVER_RADIUS));
387             boxComponent->SetBackDecoration(backDecoration);
388         }
389     }
390 }
391 
392 } // namespace OHOS::Ace::Framework
393