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 auto param = JSRef<JSObject>::Cast(info[0]);
156 Font font;
157 auto fontSize = param->GetProperty("size");
158 if (fontSize->IsNull() || fontSize->IsUndefined()) {
159 font.fontSize = Dimension(-1);
160 } else {
161 Dimension size;
162 if (!ParseJsDimensionFp(fontSize, size) || size.Unit() == DimensionUnit::PERCENT) {
163 font.fontSize = Dimension(-1);
164 LOGW("Parse to dimension FP failed.");
165 } else {
166 font.fontSize = size;
167 }
168 }
169
170 auto weight = param->GetProperty("weight");
171 if (!weight->IsNull()) {
172 std::string weightVal;
173 if (weight->IsNumber()) {
174 weightVal = std::to_string(weight->ToNumber<int32_t>());
175 } else {
176 ParseJsString(weight, weightVal);
177 }
178 font.fontWeight = ConvertStrToFontWeight(weightVal);
179 }
180
181 auto family = param->GetProperty("family");
182 if (!family->IsNull() && family->IsString()) {
183 auto familyVal = family->ToString();
184 font.fontFamilies = ConvertStrToFontFamilies(familyVal);
185 }
186
187 auto style = param->GetProperty("style");
188 if (!style->IsNull() && style->IsNumber()) {
189 FontStyle styleVal = static_cast<FontStyle>(style->ToNumber<int32_t>());
190 font.fontStyle = styleVal;
191 }
192 SearchModel::GetInstance()->SetPlaceholderFont(font);
193 }
194
SetTextFont(const JSCallbackInfo & info)195 void JSSearch::SetTextFont(const JSCallbackInfo& info)
196 {
197 auto param = JSRef<JSObject>::Cast(info[0]);
198 Font font;
199 auto fontSize = param->GetProperty("size");
200 if (fontSize->IsNull() || fontSize->IsUndefined()) {
201 font.fontSize = Dimension(-1);
202 } else {
203 Dimension size;
204 if (!ParseJsDimensionFp(fontSize, size) || size.Unit() == DimensionUnit::PERCENT) {
205 font.fontSize = Dimension(-1);
206 LOGW("Parse to dimension FP failed.");
207 } else {
208 font.fontSize = size;
209 }
210 }
211
212 auto weight = param->GetProperty("weight");
213 if (!weight->IsNull()) {
214 std::string weightVal;
215 if (weight->IsNumber()) {
216 weightVal = std::to_string(weight->ToNumber<int32_t>());
217 } else {
218 ParseJsString(weight, weightVal);
219 }
220 font.fontWeight = ConvertStrToFontWeight(weightVal);
221 }
222
223 auto family = param->GetProperty("family");
224 if (!family->IsNull() && family->IsString()) {
225 auto familyVal = family->ToString();
226 font.fontFamilies = ConvertStrToFontFamilies(familyVal);
227 }
228
229 auto style = param->GetProperty("style");
230 if (!style->IsNull() && style->IsNumber()) {
231 FontStyle styleVal = static_cast<FontStyle>(style->ToNumber<int32_t>());
232 font.fontStyle = styleVal;
233 }
234 SearchModel::GetInstance()->SetTextFont(font);
235 }
236
SetTextAlign(int32_t value)237 void JSSearch::SetTextAlign(int32_t value)
238 {
239 if (value >= 0 && value < static_cast<int32_t>(TEXT_ALIGNS.size())) {
240 SearchModel::GetInstance()->SetTextAlign(TEXT_ALIGNS[value]);
241 } else {
242 LOGE("the value is error");
243 }
244 }
245
JsBorder(const JSCallbackInfo & info)246 void JSSearch::JsBorder(const JSCallbackInfo& info)
247 {
248 if (Container::IsCurrentUseNewPipeline()) {
249 JSViewAbstract::JsBorder(info);
250 return;
251 }
252 if (!info[0]->IsObject()) {
253 LOGE("args is not a object. %s", info[0]->ToString().c_str());
254 return;
255 }
256 RefPtr<Decoration> decoration = nullptr;
257 auto component = ViewStackProcessor::GetInstance()->GetMainComponent();
258 auto searchComponent = AceType::DynamicCast<SearchComponent>(component);
259 if (!searchComponent) {
260 LOGE("search component error");
261 return;
262 }
263 auto childComponent = searchComponent->GetChild();
264 if (!childComponent) {
265 LOGE("component error");
266 return;
267 }
268 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
269 if (!textFieldComponent) {
270 LOGE("text component error");
271 return;
272 }
273 decoration = textFieldComponent->GetDecoration();
274 JSRef<JSObject> object = JSRef<JSObject>::Cast(info[0]);
275 auto valueWidth = object->GetProperty("width");
276 if (!valueWidth->IsUndefined()) {
277 ParseBorderWidth(valueWidth);
278 }
279 auto valueColor = object->GetProperty("color");
280 if (!valueColor->IsUndefined()) {
281 ParseBorderColor(valueColor);
282 }
283 auto valueRadius = object->GetProperty("radius");
284 if (!valueRadius->IsUndefined()) {
285 ParseBorderRadius(valueRadius);
286 }
287 auto valueStyle = object->GetProperty("style");
288 if (!valueStyle->IsUndefined()) {
289 ParseBorderStyle(valueStyle);
290 }
291 ViewAbstractModelImpl::SwapBackBorder(decoration);
292 textFieldComponent->SetOriginBorder(decoration->GetBorder());
293 info.ReturnSelf();
294 }
295
JsBorderWidth(const JSCallbackInfo & info)296 void JSSearch::JsBorderWidth(const JSCallbackInfo& info)
297 {
298 if (Container::IsCurrentUseNewPipeline()) {
299 JSViewAbstract::JsBorderWidth(info);
300 return;
301 }
302 if (!info[0]->IsObject() && !info[0]->IsString() && !info[0]->IsNumber()) {
303 LOGE("args need a string or number or object");
304 return;
305 }
306 RefPtr<Decoration> decoration = nullptr;
307 auto component = ViewStackProcessor::GetInstance()->GetMainComponent();
308 auto searchComponent = AceType::DynamicCast<SearchComponent>(component);
309 if (!searchComponent) {
310 LOGE("search component error");
311 return;
312 }
313 auto childComponent = searchComponent->GetChild();
314 if (!childComponent) {
315 LOGE("component error");
316 return;
317 }
318 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
319 if (!textFieldComponent) {
320 LOGE("text component error");
321 return;
322 }
323 decoration = textFieldComponent->GetDecoration();
324 JSViewAbstract::ParseBorderWidth(info[0]);
325 ViewAbstractModelImpl::SwapBackBorder(decoration);
326 textFieldComponent->SetOriginBorder(decoration->GetBorder());
327 }
328
JsBorderColor(const JSCallbackInfo & info)329 void JSSearch::JsBorderColor(const JSCallbackInfo& info)
330 {
331 if (Container::IsCurrentUseNewPipeline()) {
332 JSViewAbstract::JsBorderColor(info);
333 return;
334 }
335 if (!info[0]->IsObject() && !info[0]->IsString() && !info[0]->IsNumber()) {
336 LOGE("args need a string or number or object");
337 return;
338 }
339 RefPtr<Decoration> decoration = nullptr;
340 auto component = ViewStackProcessor::GetInstance()->GetMainComponent();
341 auto searchComponent = AceType::DynamicCast<SearchComponent>(component);
342 if (!searchComponent) {
343 LOGE("search component error");
344 return;
345 }
346 auto childComponent = searchComponent->GetChild();
347 if (!childComponent) {
348 LOGE("component error");
349 return;
350 }
351 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
352 if (!textFieldComponent) {
353 LOGE("text component error");
354 return;
355 }
356 decoration = textFieldComponent->GetDecoration();
357 JSViewAbstract::ParseBorderColor(info[0]);
358 ViewAbstractModelImpl::SwapBackBorder(decoration);
359 textFieldComponent->SetOriginBorder(decoration->GetBorder());
360 }
361
JsBorderStyle(const JSCallbackInfo & info)362 void JSSearch::JsBorderStyle(const JSCallbackInfo& info)
363 {
364 if (Container::IsCurrentUseNewPipeline()) {
365 JSViewAbstract::JsBorderStyle(info);
366 return;
367 }
368 if (!info[0]->IsObject() && !info[0]->IsNumber()) {
369 LOGE("args need a string or number or object");
370 return;
371 }
372 RefPtr<Decoration> decoration = nullptr;
373 auto component = ViewStackProcessor::GetInstance()->GetMainComponent();
374 auto searchComponent = AceType::DynamicCast<SearchComponent>(component);
375 if (!searchComponent) {
376 LOGE("search component error");
377 return;
378 }
379 auto childComponent = searchComponent->GetChild();
380 if (!childComponent) {
381 LOGE("component error");
382 return;
383 }
384 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
385 if (!textFieldComponent) {
386 LOGE("text component error");
387 return;
388 }
389 decoration = textFieldComponent->GetDecoration();
390 JSViewAbstract::ParseBorderStyle(info[0]);
391 ViewAbstractModelImpl::SwapBackBorder(decoration);
392 textFieldComponent->SetOriginBorder(decoration->GetBorder());
393 }
394
JsBorderRadius(const JSCallbackInfo & info)395 void JSSearch::JsBorderRadius(const JSCallbackInfo& info)
396 {
397 if (Container::IsCurrentUseNewPipeline()) {
398 JSViewAbstract::JsBorderRadius(info);
399 return;
400 }
401 if (!info[0]->IsObject() && !info[0]->IsString() && !info[0]->IsNumber()) {
402 LOGE("args need a string or number or object");
403 return;
404 }
405 RefPtr<Decoration> decoration = nullptr;
406 auto component = ViewStackProcessor::GetInstance()->GetMainComponent();
407 auto searchComponent = AceType::DynamicCast<SearchComponent>(component);
408 if (!searchComponent) {
409 LOGE("search component error");
410 return;
411 }
412 auto childComponent = searchComponent->GetChild();
413 if (!childComponent) {
414 LOGE("component error");
415 return;
416 }
417 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
418 if (!textFieldComponent) {
419 LOGE("text component error");
420 return;
421 }
422 decoration = textFieldComponent->GetDecoration();
423 JSViewAbstract::ParseBorderRadius(info[0]);
424 ViewAbstractModelImpl::SwapBackBorder(decoration);
425 textFieldComponent->SetOriginBorder(decoration->GetBorder());
426 }
427
OnSubmit(const JSCallbackInfo & info)428 void JSSearch::OnSubmit(const JSCallbackInfo& info)
429 {
430 CHECK_NULL_VOID(info[0]->IsFunction());
431 JsEventCallback<void(const std::string&)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
432 SearchModel::GetInstance()->SetOnSubmit(std::move(callback));
433 }
434
OnChange(const JSCallbackInfo & info)435 void JSSearch::OnChange(const JSCallbackInfo& info)
436 {
437 CHECK_NULL_VOID(info[0]->IsFunction());
438 JsEventCallback<void(const std::string&)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
439 SearchModel::GetInstance()->SetOnChange(std::move(callback));
440 }
441
SetHeight(const JSCallbackInfo & info)442 void JSSearch::SetHeight(const JSCallbackInfo& info)
443 {
444 JSViewAbstract::JsHeight(info);
445 if (info.Length() < 1) {
446 LOGE("The arg is wrong, it is supposed to have at least 1 arguments");
447 return;
448 }
449 Dimension value;
450 if (!ParseJsDimensionVp(info[0], value)) {
451 LOGE("The arg is wrong, it is supposed to be a number arguments");
452 return;
453 }
454 if (LessNotEqual(value.Value(), 0.0)) {
455 value.SetValue(0.0);
456 }
457
458 if (Container::IsCurrentUseNewPipeline()) {
459 NG::ViewAbstract::SetHeight(NG::CalcLength(value));
460 return;
461 }
462
463 auto stack = ViewStackProcessor::GetInstance();
464 auto searchComponent = AceType::DynamicCast<SearchComponent>(stack->GetMainComponent());
465 if (!searchComponent) {
466 LOGE("SearchComponent set height failed, SearchComponent is null.");
467 return;
468 }
469 auto childComponent = searchComponent->GetChild();
470 if (!childComponent) {
471 LOGE("component error");
472 return;
473 }
474 auto textFieldComponent = AceType::DynamicCast<TextFieldComponent>(childComponent);
475 if (!textFieldComponent) {
476 LOGE("text component error");
477 return;
478 }
479 textFieldComponent->SetHeight(value);
480 }
481
SetOnCopy(const JSCallbackInfo & info)482 void JSSearch::SetOnCopy(const JSCallbackInfo& info)
483 {
484 CHECK_NULL_VOID(info[0]->IsFunction());
485 JsEventCallback<void(const std::string&)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
486 SearchModel::GetInstance()->SetOnCopy(std::move(callback));
487 }
488
SetOnCut(const JSCallbackInfo & info)489 void JSSearch::SetOnCut(const JSCallbackInfo& info)
490 {
491 CHECK_NULL_VOID(info[0]->IsFunction());
492 JsEventCallback<void(const std::string&)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
493 SearchModel::GetInstance()->SetOnCut(std::move(callback));
494 }
495
SetOnPaste(const JSCallbackInfo & info)496 void JSSearch::SetOnPaste(const JSCallbackInfo& info)
497 {
498 CHECK_NULL_VOID(info[0]->IsFunction());
499 JsEventCallback<void(const std::string&)> callback(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0]));
500 SearchModel::GetInstance()->SetOnPaste(std::move(callback));
501 }
502
SetCopyOption(const JSCallbackInfo & info)503 void JSSearch::SetCopyOption(const JSCallbackInfo& info)
504 {
505 if (info.Length() == 0) {
506 return;
507 }
508 auto copyOptions = CopyOptions::None;
509 if (info[0]->IsNumber()) {
510 auto emunNumber = info[0]->ToNumber<int>();
511 copyOptions = static_cast<CopyOptions>(emunNumber);
512 }
513 SearchModel::GetInstance()->SetCopyOption(copyOptions);
514 }
515
JSBind(BindingTarget globalObj)516 void JSSearchController::JSBind(BindingTarget globalObj)
517 {
518 JSClass<JSSearchController>::Declare("SearchController");
519 JSClass<JSSearchController>::Method("caretPosition", &JSSearchController::CaretPosition);
520 JSClass<JSSearchController>::Bind(globalObj, JSSearchController::Constructor, JSSearchController::Destructor);
521 }
522
Constructor(const JSCallbackInfo & args)523 void JSSearchController::Constructor(const JSCallbackInfo& args)
524 {
525 auto scroller = Referenced::MakeRefPtr<JSSearchController>();
526 scroller->IncRefCount();
527 args.SetReturnValue(Referenced::RawPtr(scroller));
528 }
529
Destructor(JSSearchController * scroller)530 void JSSearchController::Destructor(JSSearchController* scroller)
531 {
532 if (scroller != nullptr) {
533 scroller->DecRefCount();
534 }
535 }
536
CaretPosition(int32_t caretPosition)537 void JSSearchController::CaretPosition(int32_t caretPosition)
538 {
539 auto controller = controller_.Upgrade();
540 if (controller) {
541 controller->CaretPosition(caretPosition);
542 }
543 }
544
545 } // namespace OHOS::Ace::Framework
546