1 /*
2 * Copyright (c) 2022-2025 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 "bridge/declarative_frontend/jsview/models/search_model_impl.h"
17
18 #include <utility>
19
20 #include "base/utils/utf_helper.h"
21 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
22 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
23 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
24
25 namespace OHOS::Ace::Framework {
26
27 namespace {
28 const TextInputAction INPUT_TEXTINPUTACTION_VALUE_DEFAULT = TextInputAction::UNSPECIFIED;
29 const std::vector<std::string> INPUT_FONT_FAMILY_VALUE = {
30 "sans-serif",
31 };
32 const std::vector<TextAlign> TEXT_ALIGNS = { TextAlign::START, TextAlign::CENTER, TextAlign::END };
33 Radius defaultRadius;
34 constexpr Dimension BOX_HOVER_RADIUS = 18.0_vp;
35 bool isPaddingChanged;
36 } // namespace
37
Create(const std::optional<std::u16string> & value,const std::optional<std::u16string> & placeholder,const std::optional<std::string> & icon)38 RefPtr<TextFieldControllerBase> SearchModelImpl::Create(const std::optional<std::u16string>& value,
39 const std::optional<std::u16string>& placeholder, const std::optional<std::string>& icon)
40 {
41 auto searchComponent = AceType::MakeRefPtr<OHOS::Ace::SearchComponent>();
42 ViewStackProcessor::GetInstance()->ClaimElementId(searchComponent);
43 ViewStackProcessor::GetInstance()->Push(searchComponent);
44 auto textFieldComponent = AceType::MakeRefPtr<OHOS::Ace::TextFieldComponent>();
45 auto textFieldTheme = JSViewAbstract::GetTheme<TextFieldTheme>();
46 auto searchTheme = JSViewAbstract::GetTheme<SearchTheme>();
47 InitializeComponent(searchComponent, textFieldComponent, searchTheme, textFieldTheme);
48 PrepareSpecializedComponent(searchComponent, textFieldComponent);
49 if (value.has_value()) {
50 textFieldComponent->SetValue(UtfUtils::Str16DebugToStr8(value.value()));
51 }
52 if (placeholder.has_value()) {
53 textFieldComponent->SetPlaceholder(UtfUtils::Str16DebugToStr8(placeholder.value()));
54 }
55 if (icon.has_value()) {
56 textFieldComponent->SetIconImage(icon.value());
57 }
58 return textFieldComponent->GetTextFieldController();
59 }
60
SetSearchButton(const std::string & text)61 void SearchModelImpl::SetSearchButton(const std::string& text)
62 {
63 auto component = ViewStackProcessor::GetInstance()->GetMainComponent();
64 auto searchComponent = AceType::DynamicCast<SearchComponent>(component);
65 if (!searchComponent) {
66 LOGE("component error");
67 return;
68 }
69 searchComponent->SetSearchText(text);
70 }
71
SetPlaceholderColor(const Color & color)72 void SearchModelImpl::SetPlaceholderColor(const Color& color)
73 {
74 auto component = ViewStackProcessor::GetInstance()->GetMainComponent();
75 auto searchComponent = AceType::DynamicCast<SearchComponent>(component);
76 if (!searchComponent) {
77 LOGE("search component error");
78 return;
79 }
80 auto childComponent = searchComponent->GetChild();
81 if (!childComponent) {
82 LOGE("component error");
83 return;
84 }
85 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
86 if (!textFieldComponent) {
87 LOGE("text component error");
88 return;
89 }
90 textFieldComponent->SetFocusPlaceholderColor(color);
91 }
92
SetPlaceholderFont(const Font & font)93 void SearchModelImpl::SetPlaceholderFont(const Font& font)
94 {
95 auto component = ViewStackProcessor::GetInstance()->GetMainComponent();
96 auto searchComponent = AceType::DynamicCast<SearchComponent>(component);
97 if (!searchComponent) {
98 LOGE("search component error");
99 return;
100 }
101 auto childComponent = searchComponent->GetChild();
102 if (!childComponent) {
103 LOGE("component error");
104 return;
105 }
106 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
107 if (!textFieldComponent) {
108 LOGE("text component error");
109 return;
110 }
111 TextStyle textStyle = searchComponent->GetPlaceHoldStyle();
112 if (font.fontSize && font.fontSize->IsNonNegative()) {
113 textStyle.SetFontSize(font.fontSize.value());
114 }
115 if (font.fontWeight) {
116 textStyle.SetFontWeight(font.fontWeight.value());
117 }
118 if (font.fontStyle) {
119 textStyle.SetFontStyle(font.fontStyle.value());
120 }
121 if (!font.fontFamilies.empty()) {
122 textStyle.SetFontFamilies(font.fontFamilies);
123 }
124 textFieldComponent->SetPlaceHoldStyle(textStyle);
125 }
126
SetTextFont(const Font & font)127 void SearchModelImpl::SetTextFont(const Font& font)
128 {
129 auto component = ViewStackProcessor::GetInstance()->GetMainComponent();
130 auto searchComponent = AceType::DynamicCast<SearchComponent>(component);
131 if (!searchComponent) {
132 LOGE("search component error");
133 return;
134 }
135 auto childComponent = searchComponent->GetChild();
136 if (!childComponent) {
137 LOGE("component error");
138 return;
139 }
140 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
141 if (!textFieldComponent) {
142 LOGE("text component error");
143 return;
144 }
145 TextStyle textStyle = searchComponent->GetEditingStyle();
146 if (font.fontSize && font.fontSize->IsNonNegative()) {
147 textStyle.SetFontSize(font.fontSize.value());
148 }
149 if (font.fontWeight) {
150 textStyle.SetFontWeight(font.fontWeight.value());
151 }
152 if (font.fontStyle) {
153 textStyle.SetFontStyle(font.fontStyle.value());
154 }
155 if (!font.fontFamilies.empty()) {
156 textStyle.SetFontFamilies(font.fontFamilies);
157 }
158 textFieldComponent->SetEditingStyle(textStyle);
159 }
160
SetTextAlign(const TextAlign & textAlign)161 void SearchModelImpl::SetTextAlign(const TextAlign& textAlign)
162 {
163 auto* stack = ViewStackProcessor::GetInstance();
164 auto component = AceType::DynamicCast<SearchComponent>(stack->GetMainComponent());
165 CHECK_NULL_VOID(component);
166 auto childComponent = component->GetChild();
167 CHECK_NULL_VOID(childComponent);
168 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
169 CHECK_NULL_VOID(textFieldComponent);
170 textFieldComponent->SetTextAlign(textAlign);
171 }
172
SetCopyOption(const CopyOptions & copyOptions)173 void SearchModelImpl::SetCopyOption(const CopyOptions& copyOptions)
174 {
175 JSViewSetProperty(&TextFieldComponent::SetCopyOption, copyOptions);
176 }
177
SetFocusable(bool focusable)178 void SearchModelImpl::SetFocusable(bool focusable)
179 {
180 auto focusableComponent = ViewStackProcessor::GetInstance()->GetFocusableComponent();
181 CHECK_NULL_VOID(focusableComponent);
182 focusableComponent->SetFocusable(focusable);
183 }
184
SetFocusNode(bool isFocusNode)185 void SearchModelImpl::SetFocusNode(bool isFocusNode)
186 {
187 auto focusableComponent = ViewStackProcessor::GetInstance()->GetFocusableComponent(false);
188 CHECK_NULL_VOID(focusableComponent);
189 focusableComponent->SetFocusNode(!isFocusNode);
190 }
191
SetHeight(const Dimension & value)192 void SearchModelImpl::SetHeight(const Dimension& value)
193 {
194 auto stack = ViewStackProcessor::GetInstance();
195 auto searchComponent = AceType::DynamicCast<SearchComponent>(stack->GetMainComponent());
196 CHECK_NULL_VOID(searchComponent);
197 auto childComponent = searchComponent->GetChild();
198 CHECK_NULL_VOID(childComponent);
199 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
200 CHECK_NULL_VOID(textFieldComponent);
201 textFieldComponent->SetHeight(value);
202 }
203
SetMinFontScale(const float value)204 void SearchModelImpl::SetMinFontScale(const float value) {}
205
SetMaxFontScale(const float value)206 void SearchModelImpl::SetMaxFontScale(const float value) {}
207
SetBackBorder()208 void SearchModelImpl::SetBackBorder()
209 {
210 auto stack = ViewStackProcessor::GetInstance();
211 auto searchComponent = AceType::DynamicCast<SearchComponent>(stack->GetMainComponent());
212 CHECK_NULL_VOID(searchComponent);
213 auto childComponent = searchComponent->GetChild();
214 CHECK_NULL_VOID(childComponent);
215 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
216 CHECK_NULL_VOID(textFieldComponent);
217 auto decoration = textFieldComponent->GetDecoration();
218 CHECK_NULL_VOID(decoration);
219 auto box = ViewStackProcessor::GetInstance()->GetBoxComponent();
220 auto boxDecoration = box->GetBackDecoration();
221 if (boxDecoration) {
222 decoration->SetBorder(boxDecoration->GetBorder());
223 Border border = {};
224 boxDecoration->SetBorder(border);
225 }
226 textFieldComponent->SetOriginBorder(decoration->GetBorder());
227 }
228
SetOnSubmit(std::function<void (const std::string &)> && onSubmit)229 void SearchModelImpl::SetOnSubmit(std::function<void(const std::string&)>&& onSubmit)
230 {
231 auto* stack = ViewStackProcessor::GetInstance();
232 auto component = AceType::DynamicCast<SearchComponent>(stack->GetMainComponent());
233 CHECK_NULL_VOID(component);
234 component->SetOnSubmit(std::move(onSubmit));
235 }
236
SetOnCopy(std::function<void (const std::u16string &)> && func)237 void SearchModelImpl::SetOnCopy(std::function<void(const std::u16string&)>&& func)
238 {
239 auto* stack = ViewStackProcessor::GetInstance();
240 auto component = AceType::DynamicCast<SearchComponent>(stack->GetMainComponent());
241 CHECK_NULL_VOID(component);
242 auto childComponent = component->GetChild();
243 CHECK_NULL_VOID(childComponent);
244 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
245 CHECK_NULL_VOID(textFieldComponent);
246 auto onCopy = [func] (const std::string& value) {
247 if (!func) {
248 func(UtfUtils::Str8DebugToStr16(value));
249 }
250 };
251 textFieldComponent->SetOnCopy(std::move(onCopy));
252 }
253
SetOnCut(std::function<void (const std::u16string &)> && func)254 void SearchModelImpl::SetOnCut(std::function<void(const std::u16string&)>&& func)
255 {
256 auto* stack = ViewStackProcessor::GetInstance();
257 auto component = AceType::DynamicCast<SearchComponent>(stack->GetMainComponent());
258 CHECK_NULL_VOID(component);
259 auto childComponent = component->GetChild();
260 CHECK_NULL_VOID(childComponent);
261 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
262 CHECK_NULL_VOID(textFieldComponent);
263 auto onCut = [func] (const std::string& value) {
264 if (!func) {
265 func(UtfUtils::Str8DebugToStr16(value));
266 }
267 };
268 textFieldComponent->SetOnCut(std::move(onCut));
269 }
270
SetOnPaste(std::function<void (const std::u16string &)> && func)271 void SearchModelImpl::SetOnPaste(std::function<void(const std::u16string&)>&& func)
272 {
273 auto* stack = ViewStackProcessor::GetInstance();
274 auto component = AceType::DynamicCast<SearchComponent>(stack->GetMainComponent());
275 CHECK_NULL_VOID(component);
276 auto childComponent = component->GetChild();
277 CHECK_NULL_VOID(childComponent);
278 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
279 CHECK_NULL_VOID(textFieldComponent);
280 auto onPaste = [func] (const std::string& value) {
281 if (!func) {
282 func(UtfUtils::Str8DebugToStr16(value));
283 }
284 };
285 textFieldComponent->SetOnPaste(std::move(onPaste));
286 }
287
InitializeDefaultValue(const RefPtr<BoxComponent> & boxComponent,const RefPtr<TextFieldComponent> & component,const RefPtr<TextFieldTheme> & theme)288 void SearchModelImpl::InitializeDefaultValue(const RefPtr<BoxComponent>& boxComponent,
289 const RefPtr<TextFieldComponent>& component, const RefPtr<TextFieldTheme>& theme)
290 {
291 component->SetAction(INPUT_TEXTINPUTACTION_VALUE_DEFAULT);
292 component->SetCursorColor(theme->GetCursorColor());
293 component->SetCursorRadius(theme->GetCursorRadius());
294 component->SetPlaceholderColor(theme->GetPlaceholderColor());
295
296 component->SetFocusBgColor(theme->GetFocusBgColor());
297 component->SetFocusPlaceholderColor(theme->GetFocusPlaceholderColor());
298 component->SetFocusTextColor(theme->GetFocusTextColor());
299 component->SetBgColor(theme->GetBgColor());
300 component->SetTextColor(theme->GetTextColor());
301 component->SetSelectedColor(theme->GetSelectedColor());
302 component->SetHoverColor(theme->GetHoverColor());
303 component->SetPressColor(theme->GetPressColor());
304 component->SetNeedFade(theme->NeedFade());
305 component->SetShowEllipsis(theme->ShowEllipsis());
306
307 TextStyle textStyle = component->GetTextStyle();
308 textStyle.SetTextColor(theme->GetTextColor());
309 textStyle.SetFontSize(theme->GetFontSize());
310 textStyle.SetFontWeight(theme->GetFontWeight());
311 textStyle.SetFontFamilies(INPUT_FONT_FAMILY_VALUE);
312 component->SetTextStyle(textStyle);
313
314 component->SetCountTextStyle(theme->GetCountTextStyle());
315 component->SetOverCountStyle(theme->GetOverCountStyle());
316 component->SetCountTextStyleOuter(theme->GetCountTextStyleOuter());
317 component->SetOverCountStyleOuter(theme->GetOverCountStyleOuter());
318
319 component->SetErrorTextStyle(theme->GetErrorTextStyle());
320 component->SetErrorSpacing(theme->GetErrorSpacing());
321 component->SetErrorIsInner(theme->GetErrorIsInner());
322 component->SetErrorBorderWidth(theme->GetErrorBorderWidth());
323 component->SetErrorBorderColor(theme->GetErrorBorderColor());
324
325 RefPtr<Decoration> decoration = AceType::MakeRefPtr<Decoration>();
326 decoration->SetPadding(theme->GetPadding());
327 decoration->SetBackgroundColor(theme->GetBgColor());
328 decoration->SetBorderRadius(theme->GetBorderRadius());
329 defaultRadius = theme->GetBorderRadius();
330 const auto& boxDecoration = boxComponent->GetBackDecoration();
331 if (boxDecoration) {
332 decoration->SetImage(boxDecoration->GetImage());
333 decoration->SetGradient(boxDecoration->GetGradient());
334 }
335 component->SetDecoration(decoration);
336
337 component->SetIconSize(theme->GetIconSize());
338 component->SetIconHotZoneSize(theme->GetIconHotZoneSize());
339
340 boxComponent->SetPadding(theme->GetPadding());
341 component->SetHeight(theme->GetHeight());
342 }
343
UpdateDecorationStyle(const RefPtr<BoxComponent> & boxComponent,const RefPtr<TextFieldComponent> & component,const Border & boxBorder,bool hasBoxRadius)344 void SearchModelImpl::UpdateDecorationStyle(const RefPtr<BoxComponent>& boxComponent,
345 const RefPtr<TextFieldComponent>& component, const Border& boxBorder, bool hasBoxRadius)
346 {
347 RefPtr<Decoration> decoration = component->GetDecoration();
348 if (!decoration) {
349 decoration = AceType::MakeRefPtr<Decoration>();
350 }
351 if (hasBoxRadius) {
352 decoration->SetBorder(boxBorder);
353 } else {
354 Border border = decoration->GetBorder();
355 border.SetLeftEdge(boxBorder.Left());
356 border.SetRightEdge(boxBorder.Right());
357 border.SetTopEdge(boxBorder.Top());
358 border.SetBottomEdge(boxBorder.Bottom());
359 border.SetBorderRadius(defaultRadius);
360 decoration->SetBorder(border);
361 }
362 component->SetOriginBorder(decoration->GetBorder());
363
364 if (!boxComponent) {
365 return;
366 }
367 RefPtr<Decoration> boxDecoration = boxComponent->GetBackDecoration();
368 if (boxDecoration && (boxDecoration->GetImage() || boxDecoration->GetGradient().IsValid())) {
369 // clear box properties except background image and radius.
370 boxDecoration->SetBackgroundColor(Color::TRANSPARENT);
371 Border border;
372 if (!hasBoxRadius) {
373 border.SetBorderRadius(defaultRadius);
374 } else {
375 border.SetTopLeftRadius(boxBorder.TopLeftRadius());
376 border.SetTopRightRadius(boxBorder.TopRightRadius());
377 border.SetBottomLeftRadius(boxBorder.BottomLeftRadius());
378 border.SetBottomRightRadius(boxBorder.BottomRightRadius());
379 }
380 boxDecoration->SetBorder(border);
381 } else {
382 RefPtr<Decoration> backDecoration = AceType::MakeRefPtr<Decoration>();
383 backDecoration->SetBorderRadius(Radius(BOX_HOVER_RADIUS));
384 boxComponent->SetBackDecoration(backDecoration);
385 }
386 boxComponent->SetPadding(Edge());
387 }
388
InitializeComponent(OHOS::Ace::RefPtr<OHOS::Ace::SearchComponent> & searchComponent,OHOS::Ace::RefPtr<OHOS::Ace::TextFieldComponent> & textFieldComponent,const OHOS::Ace::RefPtr<OHOS::Ace::SearchTheme> & searchTheme,const OHOS::Ace::RefPtr<OHOS::Ace::TextFieldTheme> & textFieldTheme)389 void SearchModelImpl::InitializeComponent(OHOS::Ace::RefPtr<OHOS::Ace::SearchComponent>& searchComponent,
390 OHOS::Ace::RefPtr<OHOS::Ace::TextFieldComponent>& textFieldComponent,
391 const OHOS::Ace::RefPtr<OHOS::Ace::SearchTheme>& searchTheme,
392 const OHOS::Ace::RefPtr<OHOS::Ace::TextFieldTheme>& textFieldTheme)
393 {
394 textFieldComponent->SetTextFieldController(AceType::MakeRefPtr<TextFieldController>());
395 textFieldComponent->SetTextEditController(AceType::MakeRefPtr<TextEditController>());
396 auto boxComponent = ViewStackProcessor::GetInstance()->GetBoxComponent();
397 InitializeDefaultValue(boxComponent, textFieldComponent, textFieldTheme);
398 boxComponent->SetBackDecoration(nullptr);
399 boxComponent->SetPadding(Edge());
400 textFieldComponent->SetIconSize(searchTheme->GetIconSize());
401 textFieldComponent->SetIconHotZoneSize(searchTheme->GetCloseIconHotZoneSize());
402 Edge decorationPadding;
403 Dimension leftPadding = searchTheme->GetLeftPadding();
404 Dimension rightPadding = searchTheme->GetRightPadding();
405 decorationPadding = Edge(rightPadding.Value(), 0.0, leftPadding.Value(), 0.0, leftPadding.Unit());
406 auto textFieldDecoration = textFieldComponent->GetDecoration();
407 if (textFieldDecoration) {
408 textFieldDecoration->SetPadding(decorationPadding);
409 textFieldDecoration->SetBorderRadius(searchTheme->GetBorderRadius());
410 textFieldComponent->SetOriginBorder(textFieldDecoration->GetBorder());
411 }
412 textFieldComponent->SetAction(TextInputAction::SEARCH);
413 textFieldComponent->SetWidthReserved(searchTheme->GetTextFieldWidthReserved());
414 textFieldComponent->SetTextColor(searchTheme->GetTextColor());
415 textFieldComponent->SetFocusTextColor(searchTheme->GetFocusTextColor());
416 textFieldComponent->SetPlaceholderColor(searchTheme->GetPlaceholderColor());
417 textFieldComponent->SetFocusPlaceholderColor(searchTheme->GetFocusPlaceholderColor());
418 textFieldComponent->SetBlockRightShade(searchTheme->GetBlockRightShade());
419
420 auto textStyle = textFieldComponent->GetTextStyle();
421 searchComponent->SetPlaceHoldStyle(textStyle);
422 searchComponent->SetEditingStyle(textStyle);
423
424 std::function<void(const std::string&)> submitEvent;
425 searchComponent->SetSubmitEvent(submitEvent);
426 searchComponent->SetChild(textFieldComponent);
427 searchComponent->SetTextEditController(textFieldComponent->GetTextEditController());
428 searchComponent->SetCloseIconSize(searchTheme->GetCloseIconSize());
429 searchComponent->SetCloseIconHotZoneHorizontal(searchTheme->GetCloseIconHotZoneSize());
430 searchComponent->SetHoverColor(textFieldTheme->GetHoverColor());
431 searchComponent->SetPressColor(textFieldTheme->GetPressColor());
432 isPaddingChanged = false;
433 }
434
PrepareSpecializedComponent(OHOS::Ace::RefPtr<OHOS::Ace::SearchComponent> & searchComponent,OHOS::Ace::RefPtr<OHOS::Ace::TextFieldComponent> & textFieldComponent)435 void SearchModelImpl::PrepareSpecializedComponent(OHOS::Ace::RefPtr<OHOS::Ace::SearchComponent>& searchComponent,
436 OHOS::Ace::RefPtr<OHOS::Ace::TextFieldComponent>& textFieldComponent)
437 {
438 Border boxBorder;
439
440 auto boxComponent = ViewStackProcessor::GetInstance()->GetBoxComponent();
441
442 boxComponent->SetMouseAnimationType(HoverAnimationType::BOARD);
443 if (boxComponent->GetBackDecoration()) {
444 boxBorder = boxComponent->GetBackDecoration()->GetBorder();
445 }
446 UpdateDecorationStyle(boxComponent, textFieldComponent, boxBorder, false);
447 if (GreatOrEqual(boxComponent->GetHeightDimension().Value(), 0.0)) {
448 textFieldComponent->SetHeight(boxComponent->GetHeightDimension());
449 }
450 if (isPaddingChanged) {
451 auto padding = textFieldComponent->GetDecoration()->GetPadding();
452 if (searchComponent->GetTextDirection() == TextDirection::RTL) {
453 padding.SetLeft(padding.Left() + searchComponent->GetCloseIconHotZoneHorizontal());
454 } else {
455 padding.SetRight(padding.Right() + searchComponent->GetCloseIconHotZoneHorizontal());
456 }
457 textFieldComponent->GetDecoration()->SetPadding(padding);
458 searchComponent->SetDecoration(textFieldComponent->GetDecoration());
459 isPaddingChanged = false;
460 }
461 }
462
463 } // namespace OHOS::Ace::Framework
464