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_search.h"
17 
18 #include "core/components/search/search_theme.h"
19 #include "frameworks/bridge/common/dom/input/dom_textfield_util.h"
20 #include "frameworks/bridge/common/utils/utils.h"
21 
22 namespace OHOS::Ace::Framework {
23 namespace {
24 
25 struct StyleParseHolder {
26     const DOMSearch& node;
27     RefPtr<SearchComponent>& search;
28     RefPtr<TextFieldComponent>& textField;
29     TextStyle& textStyle;
30     bool& isPaddingChanged;
31 };
32 
33 } // namespace
34 
DOMSearch(NodeId nodeId,const std::string & nodeName)35 DOMSearch::DOMSearch(NodeId nodeId, const std::string& nodeName) : DOMNode(nodeId, nodeName)
36 {
37     searchChild_ = AceType::MakeRefPtr<SearchComponent>();
38     textFieldComponent_ = AceType::MakeRefPtr<TextFieldComponent>();
39     DOMTextFieldUtil::InitController(textFieldComponent_);
40 }
41 
ResetInitializedStyle()42 void DOMSearch::ResetInitializedStyle()
43 {
44     InitializeStyle();
45 }
46 
InitializeStyle()47 void DOMSearch::InitializeStyle()
48 {
49     auto textFieldTheme = GetTheme<TextFieldTheme>();
50     auto searchTheme = GetTheme<SearchTheme>();
51     if (!textFieldTheme || !searchTheme) {
52         // theme is null, set default decoration to text field component
53         RefPtr<Decoration> decoration = AceType::MakeRefPtr<Decoration>();
54         textFieldComponent_->SetDecoration(decoration);
55         LOGW("textFieldTheme or searchTheme is null");
56         return;
57     }
58 
59     DOMTextFieldUtil::InitDefaultValue(boxComponent_, textFieldComponent_, textFieldTheme);
60     boxComponent_->SetBackDecoration(nullptr);
61     boxComponent_->SetPadding(Edge());
62     textFieldComponent_->SetIconSize(searchTheme->GetIconSize());
63     textFieldComponent_->SetIconHotZoneSize(searchTheme->GetCloseIconHotZoneSize());
64     Edge decorationPadding;
65     Dimension leftPadding = searchTheme->GetLeftPadding();
66     Dimension rightPadding = searchTheme->GetRightPadding();
67     if (IsRightToLeft()) {
68         decorationPadding = Edge(rightPadding.Value(), 0.0, leftPadding.Value(), 0.0, leftPadding.Unit());
69     } else {
70         decorationPadding = Edge(leftPadding.Value(), 0.0, rightPadding.Value(), 0.0, leftPadding.Unit());
71     }
72     auto textFieldDecoration = textFieldComponent_->GetDecoration();
73     if (textFieldDecoration) {
74         textFieldDecoration->SetPadding(decorationPadding);
75         textFieldDecoration->SetBorderRadius(searchTheme->GetBorderRadius());
76         textFieldComponent_->SetOriginBorder(textFieldDecoration->GetBorder());
77     }
78     textFieldComponent_->SetAction(TextInputAction::SEARCH);
79     textFieldComponent_->SetWidthReserved(searchTheme->GetTextFieldWidthReserved());
80     textFieldComponent_->SetTextColor(searchTheme->GetTextColor());
81     textFieldComponent_->SetFocusTextColor(searchTheme->GetFocusTextColor());
82     textFieldComponent_->SetPlaceholderColor(searchTheme->GetPlaceholderColor());
83     textFieldComponent_->SetFocusPlaceholderColor(searchTheme->GetFocusPlaceholderColor());
84     textFieldComponent_->SetBlockRightShade(searchTheme->GetBlockRightShade());
85     textStyle_ = textFieldComponent_->GetTextStyle();
86 
87     std::function<void(const std::string&)> submitEvent;
88     searchChild_->SetSubmitEvent(submitEvent);
89     searchChild_->SetChild(textFieldComponent_);
90     searchChild_->SetTextEditController(textFieldComponent_->GetTextEditController());
91     searchChild_->SetCloseIconSize(searchTheme->GetCloseIconSize());
92     searchChild_->SetCloseIconHotZoneHorizontal(searchTheme->GetCloseIconHotZoneSize());
93     searchChild_->SetHoverColor(textFieldTheme->GetHoverColor());
94     searchChild_->SetPressColor(textFieldTheme->GetPressColor());
95     SetHeight(searchTheme->GetHeight());
96     isPaddingChanged_ = true;
97 }
98 
SetSpecializedAttr(const std::pair<std::string,std::string> & attr)99 bool DOMSearch::SetSpecializedAttr(const std::pair<std::string, std::string>& attr)
100 {
101     static const LinearMapNode<void (*)(const std::string&, SearchComponent&, TextFieldComponent&, TextStyle&)>
102         searchAttrOperators[] = {
103             { DOM_AUTO_FOCUS,
104                 [](const std::string& val, SearchComponent& searchComponent, TextFieldComponent& textFieldComponent,
105                     TextStyle& textStyle) { textFieldComponent.SetAutoFocus(StringToBool(val)); } },
106             { DOM_SEARCH_HINT,
107                 [](const std::string& val, SearchComponent& searchComponent, TextFieldComponent& textFieldComponent,
108                     TextStyle& textStyle) { textFieldComponent.SetPlaceholder(val); } },
109             { DOM_SEARCH_ICON,
110                 [](const std::string& val, SearchComponent& searchComponent, TextFieldComponent& textFieldComponent,
111                     TextStyle& textStyle) {
112                     if (SystemProperties::GetDeviceType() == DeviceType::TV) {
113                         return;
114                     }
115                     textFieldComponent.SetIconImage(val);
116                 } },
117             { DOM_SEARCH_BUTTON,
118                 [](const std::string& val, SearchComponent& searchComponent, TextFieldComponent& textFieldComponent,
119                     TextStyle& textStyle) { searchComponent.SetSearchText(val); } },
120             { DOM_INPUT_SELECTED_END,
121                 [](const std::string& val, SearchComponent& searchComponent, TextFieldComponent& textFieldComponent,
122                     TextStyle& textStyle) { textFieldComponent.SetSelectedEnd(StringToInt(val)); } },
123             { DOM_INPUT_SELECTED_START,
124                 [](const std::string& val, SearchComponent& searchComponent, TextFieldComponent& textFieldComponent,
125                     TextStyle& textStyle) { textFieldComponent.SetSelectedStart(StringToInt(val)); } },
126             { DOM_INPUT_SOFT_KEYBOARD_ENABLED,
127                 [](const std::string& val, SearchComponent& searchComponent, TextFieldComponent& textFieldComponent,
128                     TextStyle& textStyle) { textFieldComponent.SetSoftKeyboardEnabled(StringToBool(val)); } },
129             { DOM_SEARCH_VALUE,
130                 [](const std::string& val, SearchComponent& searchComponent, TextFieldComponent& textFieldComponent,
131                     TextStyle& textStyle) {
132                     textFieldComponent.SetValue(val);
133                     textFieldComponent.SetResetToStart(false);
134                 } },
135         };
136     auto operatorIter = BinarySearchFindIndex(searchAttrOperators, ArraySize(searchAttrOperators), attr.first.c_str());
137     if (operatorIter != -1) {
138         searchAttrOperators[operatorIter].value(attr.second, *searchChild_, *textFieldComponent_, textStyle_);
139         return true;
140     }
141     return false;
142 }
143 
SetSpecializedStyle(const std::pair<std::string,std::string> & style)144 bool DOMSearch::SetSpecializedStyle(const std::pair<std::string, std::string>& style)
145 {
146     // static linear map must be sorted by key.
147     static const LinearMapNode<void (*)(const std::string&, StyleParseHolder&)> searchStyleSize[] = {
148         { DOM_TEXT_ALLOW_SCALE, [](const std::string& val,
149                                     StyleParseHolder& holder) { holder.textStyle.SetAllowScale(StringToBool(val)); } },
150         { DOM_BACKGROUND_COLOR,
151             [](const std::string& val, StyleParseHolder& holder) {
152                 holder.textField->SetBgColor(holder.node.ParseColor(val));
153                 holder.textField->SetFocusBgColor(holder.node.ParseColor(val));
154             } },
155         { DOM_CARET_COLOR,
156             [](const std::string& val, StyleParseHolder& holder) {
157                 holder.textField->SetCursorColor(holder.node.ParseColor(val));
158             } },
159         { DOM_COLOR,
160             [](const std::string& val, StyleParseHolder& holder) {
161                 auto color = holder.node.ParseColor(val);
162                 holder.textStyle.SetTextColor(color);
163                 holder.textField->SetTextColor(color);
164                 holder.textField->SetFocusTextColor(color);
165             } },
166         { DOM_TEXT_FONT_FAMILY,
167             [](const std::string& val, StyleParseHolder& holder) {
168                 holder.textStyle.SetFontFamilies(holder.node.ParseFontFamilies(val));
169             } },
170         { DOM_TEXT_FONT_SIZE,
171             [](const std::string& val, StyleParseHolder& holder) {
172                 holder.textStyle.SetFontSize(holder.node.ParseDimension(val));
173             } },
174         { DOM_TEXT_FONT_WEIGHT,
175             [](const std::string& val, StyleParseHolder& holder) {
176                 holder.textStyle.SetFontWeight(ConvertStrToFontWeight(val));
177             } },
178         { DOM_PADDING,
179             [](const std::string& val, StyleParseHolder& holder) {
180                 Edge padding;
181                 if (Edge::FromString(val, padding)) {
182                     holder.textField->GetDecoration()->SetPadding(padding);
183                     holder.isPaddingChanged = true;
184                 }
185             } },
186         { DOM_PADDING_BOTTOM,
187             [](const std::string& val, StyleParseHolder& holder) {
188                 auto padding = holder.textField->GetDecoration()->GetPadding();
189                 padding.SetBottom(holder.node.ParseDimension(val));
190                 holder.textField->GetDecoration()->SetPadding(padding);
191                 holder.isPaddingChanged = true;
192             } },
193         { DOM_PADDING_END,
194             [](const std::string& val, StyleParseHolder& holder) {
195                 auto padding = holder.textField->GetDecoration()->GetPadding();
196                 (holder.search->GetTextDirection() == TextDirection::RTL)
197                     ? padding.SetLeft(holder.node.ParseDimension(val))
198                     : padding.SetRight(holder.node.ParseDimension(val));
199                 holder.textField->GetDecoration()->SetPadding(padding);
200                 holder.isPaddingChanged = true;
201             } },
202         { DOM_PADDING_LEFT,
203             [](const std::string& val, StyleParseHolder& holder) {
204                 auto padding = holder.textField->GetDecoration()->GetPadding();
205                 padding.SetLeft(holder.node.ParseDimension(val));
206                 holder.textField->GetDecoration()->SetPadding(padding);
207                 holder.isPaddingChanged = true;
208             } },
209         { DOM_PADDING_RIGHT,
210             [](const std::string& val, StyleParseHolder& holder) {
211                 auto padding = holder.textField->GetDecoration()->GetPadding();
212                 padding.SetRight(holder.node.ParseDimension(val));
213                 holder.textField->GetDecoration()->SetPadding(padding);
214                 holder.isPaddingChanged = true;
215             } },
216         { DOM_PADDING_START,
217             [](const std::string& val, StyleParseHolder& holder) {
218                 auto padding = holder.textField->GetDecoration()->GetPadding();
219                 (holder.search->GetTextDirection() == TextDirection::RTL)
220                     ? padding.SetRight(holder.node.ParseDimension(val))
221                     : padding.SetLeft(holder.node.ParseDimension(val));
222                 holder.textField->GetDecoration()->SetPadding(padding);
223                 holder.isPaddingChanged = true;
224             } },
225         { DOM_PADDING_TOP,
226             [](const std::string& val, StyleParseHolder& holder) {
227                 auto padding = holder.textField->GetDecoration()->GetPadding();
228                 padding.SetTop(holder.node.ParseDimension(val));
229                 holder.textField->GetDecoration()->SetPadding(padding);
230                 holder.isPaddingChanged = true;
231             } },
232         { DOM_INPUT_PLACEHOLDER_COLOR,
233             [](const std::string& val, StyleParseHolder& holder) {
234                 auto color = holder.node.ParseColor(val);
235                 holder.textField->SetPlaceholderColor(color);
236                 holder.textField->SetFocusPlaceholderColor(color);
237             } },
238     };
239     auto operatorIter = BinarySearchFindIndex(searchStyleSize, ArraySize(searchStyleSize), style.first.c_str());
240     if (operatorIter != -1) {
241         StyleParseHolder holder = { .node = *this,
242             .search = searchChild_,
243             .textField = textFieldComponent_,
244             .textStyle = textStyle_,
245             .isPaddingChanged = isPaddingChanged_ };
246         searchStyleSize[operatorIter].value(style.second, holder);
247         return true;
248     }
249     if (DOMTextFieldUtil::IsRadiusStyle(style.first)) {
250         hasBoxRadius_ = true;
251     }
252     return false;
253 }
254 
AddSpecializedEvent(int32_t pageId,const std::string & event)255 bool DOMSearch::AddSpecializedEvent(int32_t pageId, const std::string& event)
256 {
257     static const LinearMapNode<void (*)(
258         const RefPtr<SearchComponent>&, const RefPtr<TextFieldComponent>&, const EventMarker&)>
259         eventOperators[] = {
260             { DOM_CATCH_BUBBLE_CLICK,
261                 [](const RefPtr<SearchComponent>& search, const RefPtr<TextFieldComponent>& textField,
262                     const EventMarker& event) {
263                     EventMarker eventMarker(event);
264                     eventMarker.SetCatchMode(true);
265                     textField->SetOnTap(eventMarker);
266                 } },
267             { DOM_CHANGE, [](const RefPtr<SearchComponent>& search, const RefPtr<TextFieldComponent>& textField,
268                               const EventMarker& event) { search->SetChangeEventId(event); } },
269             { DOM_CLICK,
270                 [](const RefPtr<SearchComponent>& search, const RefPtr<TextFieldComponent>& textField,
271                     const EventMarker& event) {
272                     EventMarker eventMarker(event);
273                     eventMarker.SetCatchMode(false);
274                     textField->SetOnTap(eventMarker);
275                 } },
276             { DOM_LONG_PRESS, [](const RefPtr<SearchComponent>& search, const RefPtr<TextFieldComponent>& textField,
277                                   const EventMarker& event) { textField->SetOnLongPress(event); } },
278             { DOM_INPUT_EVENT_OPTION_SELECT,
279                 [](const RefPtr<SearchComponent>& search, const RefPtr<TextFieldComponent>& textField,
280                     const EventMarker& event) { textField->SetOnOptionsClick(event); } },
281             { DOM_INPUT_EVENT_SEARCH,
282                 [](const RefPtr<SearchComponent>& search, const RefPtr<TextFieldComponent>& textField,
283                     const EventMarker& event) { textField->SetOnSearch(event); } },
284             { DOM_INPUT_EVENT_SELECT_CHANGE,
285                 [](const RefPtr<SearchComponent>& search, const RefPtr<TextFieldComponent>& textField,
286                     const EventMarker& event) { textField->SetOnSelectChange(event); } },
287             { DOM_INPUT_EVENT_SHARE,
288                 [](const RefPtr<SearchComponent>& search, const RefPtr<TextFieldComponent>& textField,
289                     const EventMarker& event) { textField->SetOnShare(event); } },
290             { DOM_SUBMIT, [](const RefPtr<SearchComponent>& search, const RefPtr<TextFieldComponent>& textField,
291                               const EventMarker& event) { search->SetSubmitEventId(event); } },
292             { DOM_INPUT_EVENT_TRANSLATE,
293                 [](const RefPtr<SearchComponent>& search, const RefPtr<TextFieldComponent>& textField,
294                     const EventMarker& event) { textField->SetOnTranslate(event); } },
295         };
296     auto operatorIter = BinarySearchFindIndex(eventOperators, ArraySize(eventOperators), event.c_str());
297     if (operatorIter != -1) {
298         eventOperators[operatorIter].value(
299             searchChild_, textFieldComponent_, EventMarker(GetNodeIdForEvent(), event, pageId));
300         return true;
301     }
302     return false;
303 }
304 
CallSpecializedMethod(const std::string & method,const std::string & args)305 void DOMSearch::CallSpecializedMethod(const std::string& method, const std::string& args)
306 {
307     auto textField = AceType::DynamicCast<TextFieldComponent>(textFieldComponent_);
308     if (!textField) {
309         return;
310     }
311     auto controller = textField->GetTextFieldController();
312     if (!controller) {
313         return;
314     }
315     if (method == DOM_INPUT_METHOD_DELETE) {
316         controller->Delete();
317     }
318 }
319 
PrepareSpecializedComponent()320 void DOMSearch::PrepareSpecializedComponent()
321 {
322     Border boxBorder;
323     // [boxComponent_] is created when [DomNode] is constructed so it won't be null
324     boxComponent_->SetMouseAnimationType(HoverAnimationType::OPACITY);
325     if (boxComponent_->GetBackDecoration()) {
326         boxBorder = boxComponent_->GetBackDecoration()->GetBorder();
327     }
328     searchChild_->SetTextDirection(IsRightToLeft() ? TextDirection::RTL : TextDirection::LTR);
329     textFieldComponent_->SetTextDirection(IsRightToLeft() ? TextDirection::RTL : TextDirection::LTR);
330     // [textFieldComponent_] is created when [DomSearch] is constructed so it won't be null
331     textFieldComponent_->SetTextStyle(textStyle_);
332     textFieldComponent_->SetInputOptions(inputOptions_);
333     textFieldComponent_->SetImageFill(GetImageFill());
334     DOMTextFieldUtil::UpdateDecorationStyle(boxComponent_, textFieldComponent_, boxBorder, hasBoxRadius_);
335     if (GreatOrEqual(boxComponent_->GetHeightDimension().Value(), 0.0)) {
336         textFieldComponent_->SetHeight(boxComponent_->GetHeightDimension());
337     }
338     if (isPaddingChanged_) {
339         auto padding = textFieldComponent_->GetDecoration()->GetPadding();
340         if (searchChild_->GetTextDirection() == TextDirection::RTL) {
341             padding.SetLeft(padding.Left() + searchChild_->GetCloseIconHotZoneHorizontal());
342         } else {
343             padding.SetRight(padding.Right() + searchChild_->GetCloseIconHotZoneHorizontal());
344         }
345         textFieldComponent_->GetDecoration()->SetPadding(padding);
346         searchChild_->SetDecoration(textFieldComponent_->GetDecoration());
347         isPaddingChanged_ = false;
348     }
349 }
350 
OnRequestFocus(bool shouldFocus)351 void DOMSearch::OnRequestFocus(bool shouldFocus)
352 {
353     if (IsNodeDisabled() || !textFieldComponent_) {
354         return;
355     }
356     auto textFieldController = textFieldComponent_->GetTextFieldController();
357     if (!textFieldController) {
358         return;
359     }
360     textFieldController->Focus(shouldFocus);
361 }
362 
363 } // namespace OHOS::Ace::Framework
364