1 /*
2 * Copyright (c) 2021-2022 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/js_search.h"
17
18 #include <optional>
19 #include <string>
20
21 #include "base/log/ace_scoring_log.h"
22 #include "bridge/declarative_frontend/jsview/js_textfield.h"
23 #include "bridge/declarative_frontend/jsview/js_textinput.h"
24 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
25 #include "bridge/declarative_frontend/jsview/models/search_model_impl.h"
26 #include "bridge/declarative_frontend/jsview/models/view_abstract_model_impl.h"
27 #include "bridge/declarative_frontend/view_stack_processor.h"
28 #include "core/components/common/layout/constants.h"
29 #include "core/components/search/search_component.h"
30 #include "core/components/search/search_theme.h"
31 #include "core/components/text_field/text_field_component.h"
32 #include "core/components_ng/base/view_abstract.h"
33 #include "core/components_ng/base/view_stack_processor.h"
34 #include "core/components_ng/pattern/search/search_model_ng.h"
35
36 namespace OHOS::Ace {
37
38 std::unique_ptr<SearchModel> SearchModel::instance_ = nullptr;
39
GetInstance()40 SearchModel* SearchModel::GetInstance()
41 {
42 if (!instance_) {
43 #ifdef NG_BUILD
44 instance_.reset(new NG::SearchModelNG());
45 #else
46 if (Container::IsCurrentUseNewPipeline()) {
47 instance_.reset(new NG::SearchModelNG());
48 } else {
49 instance_.reset(new Framework::SearchModelImpl());
50 }
51 #endif
52 }
53 return instance_.get();
54 }
55
56 } // namespace OHOS::Ace
57
58 namespace OHOS::Ace::Framework {
59
60 namespace {
61 const std::vector<TextAlign> TEXT_ALIGNS = { TextAlign::START, TextAlign::CENTER, TextAlign::END };
62 } // namespace
63
JSBind(BindingTarget globalObj)64 void JSSearch::JSBind(BindingTarget globalObj)
65 {
66 JSClass<JSSearch>::Declare("Search");
67 MethodOptions opt = MethodOptions::NONE;
68
69 JSClass<JSSearch>::StaticMethod("create", &JSSearch::Create, opt);
70 JSClass<JSSearch>::StaticMethod("searchButton", &JSSearch::SetSearchButton, opt);
71 JSClass<JSSearch>::StaticMethod("placeholderColor", &JSSearch::SetPlaceholderColor, opt);
72 JSClass<JSSearch>::StaticMethod("placeholderFont", &JSSearch::SetPlaceholderFont, opt);
73 JSClass<JSSearch>::StaticMethod("textFont", &JSSearch::SetTextFont, opt);
74 JSClass<JSSearch>::StaticMethod("textAlign", &JSSearch::SetTextAlign, opt);
75 JSClass<JSSearch>::StaticMethod("onSubmit", &JSSearch::OnSubmit, opt);
76 JSClass<JSSearch>::StaticMethod("onChange", &JSSearch::OnChange, opt);
77 JSClass<JSSearch>::StaticMethod("border", &JSSearch::JsBorder);
78 JSClass<JSSearch>::StaticMethod("borderWidth", &JSSearch::JsBorderWidth);
79 JSClass<JSSearch>::StaticMethod("borderColor", &JSSearch::JsBorderColor);
80 JSClass<JSSearch>::StaticMethod("borderStyle", &JSSearch::JsBorderStyle);
81 JSClass<JSSearch>::StaticMethod("borderRadius", &JSSearch::JsBorderRadius);
82 JSClass<JSSearch>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
83 JSClass<JSSearch>::StaticMethod("height", &JSSearch::SetHeight);
84 JSClass<JSSearch>::StaticMethod("width", &JSViewAbstract::JsWidth);
85 JSClass<JSSearch>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
86 JSClass<JSSearch>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
87 JSClass<JSSearch>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
88 JSClass<JSSearch>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
89 JSClass<JSSearch>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
90 JSClass<JSSearch>::StaticMethod("onCopy", &JSSearch::SetOnCopy);
91 JSClass<JSSearch>::StaticMethod("onCut", &JSSearch::SetOnCut);
92 JSClass<JSSearch>::StaticMethod("onPaste", &JSSearch::SetOnPaste);
93 JSClass<JSSearch>::StaticMethod("copyOption", &JSSearch::SetCopyOption);
94 JSClass<JSSearch>::Inherit<JSViewAbstract>();
95 JSClass<JSSearch>::Bind(globalObj);
96 }
97
Create(const JSCallbackInfo & info)98 void JSSearch::Create(const JSCallbackInfo& info)
99 {
100 std::optional<std::string> key;
101 std::optional<std::string> tip;
102 std::optional<std::string> src;
103 JSSearchController* jsController = nullptr;
104 if (info[0]->IsObject()) {
105 auto param = JSRef<JSObject>::Cast(info[0]);
106 std::string placeholder;
107 if (param->GetProperty("placeholder")->IsUndefined()) {
108 tip = "";
109 }
110 if (ParseJsString(param->GetProperty("placeholder"), placeholder)) {
111 tip = placeholder;
112 }
113 std::string text;
114 if (param->GetProperty("value")->IsUndefined()) {
115 key = "";
116 }
117 if (ParseJsString(param->GetProperty("value"), text)) {
118 key = text;
119 }
120 std::string icon;
121 if (ParseJsString(param->GetProperty("icon"), icon)) {
122 src = icon;
123 }
124 auto controllerObj = param->GetProperty("controller");
125 if (!controllerObj->IsUndefined() && !controllerObj->IsNull()) {
126 jsController = JSRef<JSObject>::Cast(controllerObj)->Unwrap<JSSearchController>();
127 }
128 }
129 auto controller = SearchModel::GetInstance()->Create(key, tip, src);
130 if (jsController) {
131 jsController->SetController(controller);
132 }
133 if (!Container::IsCurrentUseNewPipeline()) {
134 JSInteractableView::SetFocusable(true);
135 JSInteractableView::SetFocusNode(true);
136 }
137 }
138
SetSearchButton(const std::string & text)139 void JSSearch::SetSearchButton(const std::string& text)
140 {
141 SearchModel::GetInstance()->SetSearchButton(text);
142 }
143
SetPlaceholderColor(const JSCallbackInfo & info)144 void JSSearch::SetPlaceholderColor(const JSCallbackInfo& info)
145 {
146 auto value = JSRef<JSVal>::Cast(info[0]);
147 Color colorVal;
148 if (ParseJsColor(value, colorVal)) {
149 SearchModel::GetInstance()->SetPlaceholderColor(colorVal);
150 }
151 }
152
SetPlaceholderFont(const JSCallbackInfo & info)153 void JSSearch::SetPlaceholderFont(const JSCallbackInfo& info)
154 {
155 if (info.Length() < 1 || !info[0]->IsObject()) {
156 return;
157 }
158 auto param = JSRef<JSObject>::Cast(info[0]);
159 Font font;
160 auto fontSize = param->GetProperty("size");
161 if (fontSize->IsNull() || fontSize->IsUndefined()) {
162 font.fontSize = Dimension(-1);
163 } else {
164 Dimension size;
165 if (!ParseJsDimensionFp(fontSize, size) || size.Unit() == DimensionUnit::PERCENT) {
166 font.fontSize = Dimension(-1);
167 LOGW("Parse to dimension FP failed.");
168 } else {
169 font.fontSize = size;
170 }
171 }
172
173 auto weight = param->GetProperty("weight");
174 if (!weight->IsNull()) {
175 std::string weightVal;
176 if (weight->IsNumber()) {
177 weightVal = std::to_string(weight->ToNumber<int32_t>());
178 } else {
179 ParseJsString(weight, weightVal);
180 }
181 font.fontWeight = ConvertStrToFontWeight(weightVal);
182 }
183
184 auto family = param->GetProperty("family");
185 if (!family->IsNull() && family->IsString()) {
186 auto familyVal = family->ToString();
187 font.fontFamilies = ConvertStrToFontFamilies(familyVal);
188 }
189
190 auto style = param->GetProperty("style");
191 if (!style->IsNull() && style->IsNumber()) {
192 FontStyle styleVal = static_cast<FontStyle>(style->ToNumber<int32_t>());
193 font.fontStyle = styleVal;
194 }
195 SearchModel::GetInstance()->SetPlaceholderFont(font);
196 }
197
SetTextFont(const JSCallbackInfo & info)198 void JSSearch::SetTextFont(const JSCallbackInfo& info)
199 {
200 if (info.Length() < 1 || !info[0]->IsObject()) {
201 return;
202 }
203 auto param = JSRef<JSObject>::Cast(info[0]);
204 Font font;
205 auto fontSize = param->GetProperty("size");
206 if (fontSize->IsNull() || fontSize->IsUndefined()) {
207 font.fontSize = Dimension(-1);
208 } else {
209 Dimension size;
210 if (!ParseJsDimensionFp(fontSize, size) || size.Unit() == DimensionUnit::PERCENT) {
211 font.fontSize = Dimension(-1);
212 LOGW("Parse to dimension FP failed.");
213 } else {
214 font.fontSize = size;
215 }
216 }
217
218 auto weight = param->GetProperty("weight");
219 if (!weight->IsNull()) {
220 std::string weightVal;
221 if (weight->IsNumber()) {
222 weightVal = std::to_string(weight->ToNumber<int32_t>());
223 } else {
224 ParseJsString(weight, weightVal);
225 }
226 font.fontWeight = ConvertStrToFontWeight(weightVal);
227 }
228
229 auto family = param->GetProperty("family");
230 if (!family->IsNull() && family->IsString()) {
231 auto familyVal = family->ToString();
232 font.fontFamilies = ConvertStrToFontFamilies(familyVal);
233 }
234
235 auto style = param->GetProperty("style");
236 if (!style->IsNull() && style->IsNumber()) {
237 FontStyle styleVal = static_cast<FontStyle>(style->ToNumber<int32_t>());
238 font.fontStyle = styleVal;
239 }
240 SearchModel::GetInstance()->SetTextFont(font);
241 }
242
SetTextAlign(int32_t value)243 void JSSearch::SetTextAlign(int32_t value)
244 {
245 if (value >= 0 && value < static_cast<int32_t>(TEXT_ALIGNS.size())) {
246 SearchModel::GetInstance()->SetTextAlign(TEXT_ALIGNS[value]);
247 } else {
248 LOGE("the value is error");
249 }
250 }
251
JsBorder(const JSCallbackInfo & info)252 void JSSearch::JsBorder(const JSCallbackInfo& info)
253 {
254 if (Container::IsCurrentUseNewPipeline()) {
255 JSViewAbstract::JsBorder(info);
256 return;
257 }
258 if (!info[0]->IsObject()) {
259 LOGE("args is not a object. %s", info[0]->ToString().c_str());
260 return;
261 }
262 RefPtr<Decoration> decoration = nullptr;
263 auto component = ViewStackProcessor::GetInstance()->GetMainComponent();
264 auto searchComponent = AceType::DynamicCast<SearchComponent>(component);
265 if (!searchComponent) {
266 LOGE("search component error");
267 return;
268 }
269 auto childComponent = searchComponent->GetChild();
270 if (!childComponent) {
271 LOGE("component error");
272 return;
273 }
274 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
275 if (!textFieldComponent) {
276 LOGE("text component error");
277 return;
278 }
279 decoration = textFieldComponent->GetDecoration();
280 JSRef<JSObject> object = JSRef<JSObject>::Cast(info[0]);
281 auto valueWidth = object->GetProperty("width");
282 if (!valueWidth->IsUndefined()) {
283 ParseBorderWidth(valueWidth);
284 }
285 auto valueColor = object->GetProperty("color");
286 if (!valueColor->IsUndefined()) {
287 ParseBorderColor(valueColor);
288 }
289 auto valueRadius = object->GetProperty("radius");
290 if (!valueRadius->IsUndefined()) {
291 ParseBorderRadius(valueRadius);
292 }
293 auto valueStyle = object->GetProperty("style");
294 if (!valueStyle->IsUndefined()) {
295 ParseBorderStyle(valueStyle);
296 }
297 ViewAbstractModelImpl::SwapBackBorder(decoration);
298 textFieldComponent->SetOriginBorder(decoration->GetBorder());
299 info.ReturnSelf();
300 }
301
JsBorderWidth(const JSCallbackInfo & info)302 void JSSearch::JsBorderWidth(const JSCallbackInfo& info)
303 {
304 if (Container::IsCurrentUseNewPipeline()) {
305 JSViewAbstract::JsBorderWidth(info);
306 return;
307 }
308 if (!info[0]->IsObject() && !info[0]->IsString() && !info[0]->IsNumber()) {
309 LOGE("args need a string or number or object");
310 return;
311 }
312 RefPtr<Decoration> decoration = nullptr;
313 auto component = ViewStackProcessor::GetInstance()->GetMainComponent();
314 auto searchComponent = AceType::DynamicCast<SearchComponent>(component);
315 if (!searchComponent) {
316 LOGE("search component error");
317 return;
318 }
319 auto childComponent = searchComponent->GetChild();
320 if (!childComponent) {
321 LOGE("component error");
322 return;
323 }
324 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
325 if (!textFieldComponent) {
326 LOGE("text component error");
327 return;
328 }
329 decoration = textFieldComponent->GetDecoration();
330 JSViewAbstract::ParseBorderWidth(info[0]);
331 ViewAbstractModelImpl::SwapBackBorder(decoration);
332 textFieldComponent->SetOriginBorder(decoration->GetBorder());
333 }
334
JsBorderColor(const JSCallbackInfo & info)335 void JSSearch::JsBorderColor(const JSCallbackInfo& info)
336 {
337 if (Container::IsCurrentUseNewPipeline()) {
338 JSViewAbstract::JsBorderColor(info);
339 return;
340 }
341 if (!info[0]->IsObject() && !info[0]->IsString() && !info[0]->IsNumber()) {
342 LOGE("args need a string or number or object");
343 return;
344 }
345 RefPtr<Decoration> decoration = nullptr;
346 auto component = ViewStackProcessor::GetInstance()->GetMainComponent();
347 auto searchComponent = AceType::DynamicCast<SearchComponent>(component);
348 if (!searchComponent) {
349 LOGE("search component error");
350 return;
351 }
352 auto childComponent = searchComponent->GetChild();
353 if (!childComponent) {
354 LOGE("component error");
355 return;
356 }
357 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
358 if (!textFieldComponent) {
359 LOGE("text component error");
360 return;
361 }
362 decoration = textFieldComponent->GetDecoration();
363 JSViewAbstract::ParseBorderColor(info[0]);
364 ViewAbstractModelImpl::SwapBackBorder(decoration);
365 textFieldComponent->SetOriginBorder(decoration->GetBorder());
366 }
367
JsBorderStyle(const JSCallbackInfo & info)368 void JSSearch::JsBorderStyle(const JSCallbackInfo& info)
369 {
370 if (Container::IsCurrentUseNewPipeline()) {
371 JSViewAbstract::JsBorderStyle(info);
372 return;
373 }
374 if (!info[0]->IsObject() && !info[0]->IsNumber()) {
375 LOGE("args need a string or number or object");
376 return;
377 }
378 RefPtr<Decoration> decoration = nullptr;
379 auto component = ViewStackProcessor::GetInstance()->GetMainComponent();
380 auto searchComponent = AceType::DynamicCast<SearchComponent>(component);
381 if (!searchComponent) {
382 LOGE("search component error");
383 return;
384 }
385 auto childComponent = searchComponent->GetChild();
386 if (!childComponent) {
387 LOGE("component error");
388 return;
389 }
390 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
391 if (!textFieldComponent) {
392 LOGE("text component error");
393 return;
394 }
395 decoration = textFieldComponent->GetDecoration();
396 JSViewAbstract::ParseBorderStyle(info[0]);
397 ViewAbstractModelImpl::SwapBackBorder(decoration);
398 textFieldComponent->SetOriginBorder(decoration->GetBorder());
399 }
400
JsBorderRadius(const JSCallbackInfo & info)401 void JSSearch::JsBorderRadius(const JSCallbackInfo& info)
402 {
403 if (Container::IsCurrentUseNewPipeline()) {
404 JSViewAbstract::JsBorderRadius(info);
405 return;
406 }
407 if (!info[0]->IsObject() && !info[0]->IsString() && !info[0]->IsNumber()) {
408 LOGE("args need a string or number or object");
409 return;
410 }
411 RefPtr<Decoration> decoration = nullptr;
412 auto component = ViewStackProcessor::GetInstance()->GetMainComponent();
413 auto searchComponent = AceType::DynamicCast<SearchComponent>(component);
414 if (!searchComponent) {
415 LOGE("search component error");
416 return;
417 }
418 auto childComponent = searchComponent->GetChild();
419 if (!childComponent) {
420 LOGE("component error");
421 return;
422 }
423 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
424 if (!textFieldComponent) {
425 LOGE("text component error");
426 return;
427 }
428 decoration = textFieldComponent->GetDecoration();
429 JSViewAbstract::ParseBorderRadius(info[0]);
430 ViewAbstractModelImpl::SwapBackBorder(decoration);
431 textFieldComponent->SetOriginBorder(decoration->GetBorder());
432 }
433
OnSubmit(const JSCallbackInfo & info)434 void JSSearch::OnSubmit(const JSCallbackInfo& info)
435 {
436 CHECK_NULL_VOID(info[0]->IsFunction());
437 JsEventCallback<void(const std::string&)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
438 SearchModel::GetInstance()->SetOnSubmit(std::move(callback));
439 }
440
OnChange(const JSCallbackInfo & info)441 void JSSearch::OnChange(const JSCallbackInfo& info)
442 {
443 CHECK_NULL_VOID(info[0]->IsFunction());
444 JsEventCallback<void(const std::string&)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
445 SearchModel::GetInstance()->SetOnChange(std::move(callback));
446 }
447
SetHeight(const JSCallbackInfo & info)448 void JSSearch::SetHeight(const JSCallbackInfo& info)
449 {
450 JSViewAbstract::JsHeight(info);
451 if (info.Length() < 1) {
452 LOGE("The arg is wrong, it is supposed to have at least 1 arguments");
453 return;
454 }
455 Dimension value;
456 if (!ParseJsDimensionVp(info[0], value)) {
457 LOGE("The arg is wrong, it is supposed to be a number arguments");
458 return;
459 }
460 if (LessNotEqual(value.Value(), 0.0)) {
461 value.SetValue(0.0);
462 }
463
464 if (Container::IsCurrentUseNewPipeline()) {
465 NG::ViewAbstract::SetHeight(NG::CalcLength(value));
466 return;
467 }
468
469 auto stack = ViewStackProcessor::GetInstance();
470 auto searchComponent = AceType::DynamicCast<SearchComponent>(stack->GetMainComponent());
471 if (!searchComponent) {
472 LOGE("SearchComponent set height failed, SearchComponent is null.");
473 return;
474 }
475 auto childComponent = searchComponent->GetChild();
476 if (!childComponent) {
477 LOGE("component error");
478 return;
479 }
480 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
481 if (!textFieldComponent) {
482 LOGE("text component error");
483 return;
484 }
485 textFieldComponent->SetHeight(value);
486 }
487
SetOnCopy(const JSCallbackInfo & info)488 void JSSearch::SetOnCopy(const JSCallbackInfo& info)
489 {
490 CHECK_NULL_VOID(info[0]->IsFunction());
491 JsEventCallback<void(const std::string&)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
492 SearchModel::GetInstance()->SetOnCopy(std::move(callback));
493 }
494
SetOnCut(const JSCallbackInfo & info)495 void JSSearch::SetOnCut(const JSCallbackInfo& info)
496 {
497 CHECK_NULL_VOID(info[0]->IsFunction());
498 JsEventCallback<void(const std::string&)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
499 SearchModel::GetInstance()->SetOnCut(std::move(callback));
500 }
501
SetOnPaste(const JSCallbackInfo & info)502 void JSSearch::SetOnPaste(const JSCallbackInfo& info)
503 {
504 CHECK_NULL_VOID(info[0]->IsFunction());
505 JsEventCallback<void(const std::string&)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
506 SearchModel::GetInstance()->SetOnPaste(std::move(callback));
507 }
508
SetCopyOption(const JSCallbackInfo & info)509 void JSSearch::SetCopyOption(const JSCallbackInfo& info)
510 {
511 if (info.Length() == 0) {
512 return;
513 }
514 auto copyOptions = CopyOptions::None;
515 if (info[0]->IsNumber()) {
516 auto emunNumber = info[0]->ToNumber<int>();
517 copyOptions = static_cast<CopyOptions>(emunNumber);
518 }
519 SearchModel::GetInstance()->SetCopyOption(copyOptions);
520 }
521
JSBind(BindingTarget globalObj)522 void JSSearchController::JSBind(BindingTarget globalObj)
523 {
524 JSClass<JSSearchController>::Declare("SearchController");
525 JSClass<JSSearchController>::Method("caretPosition", &JSSearchController::CaretPosition);
526 JSClass<JSSearchController>::Bind(globalObj, JSSearchController::Constructor, JSSearchController::Destructor);
527 }
528
Constructor(const JSCallbackInfo & args)529 void JSSearchController::Constructor(const JSCallbackInfo& args)
530 {
531 auto scroller = Referenced::MakeRefPtr<JSSearchController>();
532 scroller->IncRefCount();
533 args.SetReturnValue(Referenced::RawPtr(scroller));
534 }
535
Destructor(JSSearchController * scroller)536 void JSSearchController::Destructor(JSSearchController* scroller)
537 {
538 if (scroller != nullptr) {
539 scroller->DecRefCount();
540 }
541 }
542
CaretPosition(int32_t caretPosition)543 void JSSearchController::CaretPosition(int32_t caretPosition)
544 {
545 auto controller = controller_.Upgrade();
546 if (controller) {
547 controller->CaretPosition(caretPosition);
548 }
549 }
550
551 } // namespace OHOS::Ace::Framework
552