• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_textpicker.h"
17 
18 #include "base/log/ace_scoring_log.h"
19 #include "bridge/common/utils/engine_helper.h"
20 #include "bridge/declarative_frontend/engine/functions/js_function.h"
21 #include "bridge/declarative_frontend/jsview/js_datepicker.h"
22 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
23 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
24 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
25 #include "bridge/declarative_frontend/jsview/models/textpicker_model_impl.h"
26 #include "bridge/declarative_frontend/view_stack_processor.h"
27 #include "core/components/picker/picker_base_component.h"
28 #include "core/components/picker/picker_theme.h"
29 #include "core/components_ng/pattern/text_picker/textpicker_model.h"
30 #include "core/components_ng/pattern/text_picker/textpicker_model_ng.h"
31 #include "core/pipeline_ng/pipeline_context.h"
32 
33 namespace OHOS::Ace {
34 
35 std::unique_ptr<TextPickerModel> TextPickerModel::textPickerInstance_ = nullptr;
36 
GetInstance()37 TextPickerModel* TextPickerModel::GetInstance()
38 {
39     if (!textPickerInstance_) {
40 #ifdef NG_BUILD
41         textPickerInstance_.reset(new NG::TextPickerModelNG());
42 #else
43         if (Container::IsCurrentUseNewPipeline()) {
44             textPickerInstance_.reset(new NG::TextPickerModelNG());
45         } else {
46             textPickerInstance_.reset(new Framework::TextPickerModelImpl());
47         }
48 #endif
49     }
50     return textPickerInstance_.get();
51 }
52 } // namespace OHOS::Ace
53 
54 namespace OHOS::Ace::Framework {
55 
JSBind(BindingTarget globalObj)56 void JSTextPicker::JSBind(BindingTarget globalObj)
57 {
58     JSClass<JSTextPicker>::Declare("TextPicker");
59     MethodOptions opt = MethodOptions::NONE;
60     JSClass<JSTextPicker>::StaticMethod("create", &JSTextPicker::Create, opt);
61     JSClass<JSTextPicker>::StaticMethod("defaultPickerItemHeight", &JSTextPicker::SetDefaultPickerItemHeight);
62     JSClass<JSTextPicker>::StaticMethod("onAccept", &JSTextPicker::OnAccept);
63     JSClass<JSTextPicker>::StaticMethod("onCancel", &JSTextPicker::OnCancel);
64     JSClass<JSTextPicker>::StaticMethod("onChange", &JSTextPicker::OnChange);
65     JSClass<JSTextPicker>::StaticMethod("backgroundColor", &JSDatePicker::PickerBackgroundColor);
66     JSClass<JSTextPicker>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
67     JSClass<JSTextPicker>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
68     JSClass<JSTextPicker>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
69     JSClass<JSTextPicker>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
70     JSClass<JSTextPicker>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
71     JSClass<JSTextPicker>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
72     JSClass<JSTextPicker>::Inherit<JSViewAbstract>();
73     JSClass<JSTextPicker>::Bind(globalObj);
74 }
75 
Create(const JSCallbackInfo & info)76 void JSTextPicker::Create(const JSCallbackInfo& info)
77 {
78     if (info.Length() >= 1 && info[0]->IsObject()) {
79         auto paramObject = JSRef<JSObject>::Cast(info[0]);
80         auto getSelected = paramObject->GetProperty("selected");
81         auto getValue = paramObject->GetProperty("value");
82         JSRef<JSArray> getRange = paramObject->GetProperty("range");
83         std::vector<std::string> getRangeVector;
84         std::string value = "";
85         uint32_t selected = 0;
86         if (getRange->Length() > 0) {
87             if (!ParseJsStrArray(getRange, getRangeVector)) {
88                 LOGE("parse range failed");
89                 return;
90             }
91             if (!ParseJsString(getValue, value)) {
92                 value = getRangeVector.front();
93             }
94             if (!ParseJsInteger(getSelected, selected) && !value.empty()) {
95                 auto valueIterator = std::find(getRangeVector.begin(), getRangeVector.end(), value);
96                 if (valueIterator != getRangeVector.end()) {
97                     selected = std::distance(getRangeVector.begin(), valueIterator);
98                 }
99             }
100             if (selected < 0 || selected >= getRangeVector.size()) {
101                 LOGE("selected is out of range");
102                 selected = 0;
103             }
104         }
105 
106         auto theme = GetTheme<PickerTheme>();
107         if (!theme) {
108             LOGE("PickerText Theme is null");
109             return;
110         }
111         TextPickerModel::GetInstance()->Create(theme);
112         TextPickerModel::GetInstance()->SetRange(getRangeVector);
113         TextPickerModel::GetInstance()->SetSelected(selected);
114         TextPickerModel::GetInstance()->SetValue(value);
115         JSInteractableView::SetFocusable(false);
116         JSInteractableView::SetFocusNode(true);
117     }
118 }
119 
SetDefaultPickerItemHeight(const JSCallbackInfo & info)120 void JSTextPicker::SetDefaultPickerItemHeight(const JSCallbackInfo& info)
121 {
122     if (info.Length() < 1) {
123         LOGE("The arg is wrong, it is supposed to have atleast 1 argument.");
124         return;
125     }
126     Dimension height;
127     if (info[0]->IsNumber() || info[0]->IsString()) {
128         if (!ParseJsDimensionFp(info[0], height)) {
129             return;
130         }
131     }
132     TextPickerModel::GetInstance()->SetDefaultPickerItemHeight(height);
133 }
134 
OnAccept(const JSCallbackInfo & info)135 void JSTextPicker::OnAccept(const JSCallbackInfo& info) {}
136 
OnCancel(const JSCallbackInfo & info)137 void JSTextPicker::OnCancel(const JSCallbackInfo& info) {}
138 
OnChange(const JSCallbackInfo & info)139 void JSTextPicker::OnChange(const JSCallbackInfo& info)
140 {
141     if (!info[0]->IsFunction()) {
142         return;
143     }
144     auto jsFunc = JSRef<JSFunc>::Cast(info[0]);
145     auto onChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](
146                         const std::string& value, double index) {
147         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
148         ACE_SCORING_EVENT("TextPicker.onChange");
149         auto params = ConvertToJSValues(value, index);
150         func->Call(JSRef<JSObject>(), static_cast<int>(params.size()), params.data());
151     };
152     TextPickerModel::GetInstance()->SetOnChange(std::move(onChange));
153     info.ReturnSelf();
154 }
155 
JSBind(BindingTarget globalObj)156 void JSTextPickerDialog::JSBind(BindingTarget globalObj)
157 {
158     JSClass<JSTextPickerDialog>::Declare("TextPickerDialog");
159     JSClass<JSTextPickerDialog>::StaticMethod("show", &JSTextPickerDialog::Show);
160 
161     JSClass<JSTextPickerDialog>::Bind<>(globalObj);
162 }
163 
Show(const JSCallbackInfo & info)164 void JSTextPickerDialog::Show(const JSCallbackInfo& info)
165 {
166     auto scopedDelegate = EngineHelper::GetCurrentDelegate();
167     if (!scopedDelegate) {
168         // this case usually means there is no foreground container, need to figure out the reason.
169         LOGE("scopedDelegate is null, please check");
170         return;
171     }
172     if (info.Length() < 1 || !info[0]->IsObject()) {
173         LOGE("TextPicker create error, info is non-valid");
174         return;
175     }
176 
177     auto paramObject = JSRef<JSObject>::Cast(info[0]);
178 
179     if (Container::IsCurrentUseNewPipeline()) {
180         auto dialogEvent = DialogEvent(info);
181         auto dialogCancelEvent = DialogCancelEvent(info);
182         TextPickerDialogShow(paramObject, dialogEvent, dialogCancelEvent);
183         return;
184     }
185 
186     auto PickerText = AceType::MakeRefPtr<PickerTextComponent>();
187     if (!PickerText) {
188         LOGE("PickerText Component is null");
189         return;
190     }
191     ParseText(PickerText, paramObject);
192     DialogProperties properties {};
193     properties.alignment = DialogAlignment::CENTER;
194     properties.customComponent = PickerText;
195     properties.customStyle = true;
196     AddEvent(PickerText, info);
197     PickerText->SetDialogName("pickerTextDialog");
198     PickerText->OpenDialog(properties);
199 }
200 
TextPickerDialogShow(const JSRef<JSObject> & paramObj,const std::map<std::string,NG::DialogTextEvent> & dialogEvent,const std::map<std::string,NG::DialogGestureEvent> & dialogCancelEvent)201 void JSTextPickerDialog::TextPickerDialogShow(const JSRef<JSObject>& paramObj,
202     const std::map<std::string, NG::DialogTextEvent>& dialogEvent,
203     const std::map<std::string, NG::DialogGestureEvent>& dialogCancelEvent)
204 {
205     auto container = Container::Current();
206     if (!container) {
207         return;
208     }
209     auto pipelineContext = AccessibilityManager::DynamicCast<NG::PipelineContext>(container->GetPipelineContext());
210     if (!pipelineContext) {
211         return;
212     }
213 
214     auto executor = pipelineContext->GetTaskExecutor();
215     if (!executor) {
216         return;
217     }
218 
219     auto getSelected = paramObj->GetProperty("selected");
220     auto defaultHeight = paramObj->GetProperty("defaultPickerItemHeight");
221     JSRef<JSArray> getRange = paramObj->GetProperty("range");
222     std::vector<std::string> getRangeVector;
223     if (!JSViewAbstract::ParseJsStrArray(getRange, getRangeVector)) {
224         LOGE("parse range failed");
225         return;
226     }
227 
228     std::string value;
229     uint32_t selected = 0;
230     auto getValue = paramObj->GetProperty("value");
231     if (!JSViewAbstract::ParseJsInteger(getSelected, selected) && JSViewAbstract::ParseJsString(getValue, value)) {
232         auto valueIterator = std::find(getRangeVector.begin(), getRangeVector.end(), value);
233         if (valueIterator != getRangeVector.end()) {
234             selected = std::distance(getRangeVector.begin(), valueIterator);
235         }
236     }
237 
238     if (selected < 0 || selected >= getRangeVector.size()) {
239         LOGE("selected is out of range");
240         selected = 0;
241     }
242 
243     Dimension height;
244     if (defaultHeight->IsNumber() || defaultHeight->IsString()) {
245         if (!JSViewAbstract::ParseJsDimensionFp(defaultHeight, height)) {
246             return;
247         }
248     }
249 
250     auto theme = JSDatePicker::GetTheme<DialogTheme>();
251     if (!theme) {
252         LOGE("DialogTheme is null");
253         return;
254     }
255 
256     DialogProperties properties;
257     ButtonInfo buttonInfo;
258     if (SystemProperties::GetDeviceType() == DeviceType::PHONE) {
259         properties.alignment = DialogAlignment::BOTTOM;
260     } else {
261         properties.alignment = DialogAlignment::CENTER;
262     }
263     properties.customStyle = false;
264     properties.offset = DimensionOffset(Offset(0, -theme->GetMarginBottom().ConvertToPx()));
265 
266     auto context = AccessibilityManager::DynamicCast<NG::PipelineContext>(pipelineContext);
267     auto overlayManager = context ? context->GetOverlayManager() : nullptr;
268     executor->PostTask(
269         [properties, selected, getRangeVector, dialogEvent, height, dialogCancelEvent,
270             weak = WeakPtr<NG::OverlayManager>(overlayManager)] {
271             auto overlayManager = weak.Upgrade();
272             CHECK_NULL_VOID(overlayManager);
273             overlayManager->ShowTextDialog(
274                 properties, selected, height, getRangeVector, dialogEvent, dialogCancelEvent);
275         },
276         TaskExecutor::TaskType::UI);
277 }
278 
DialogEvent(const JSCallbackInfo & info)279 std::map<std::string, NG::DialogTextEvent> JSTextPickerDialog::DialogEvent(const JSCallbackInfo& info)
280 {
281     std::map<std::string, NG::DialogTextEvent> dialogEvent;
282     if (info.Length() < 1 || !info[0]->IsObject()) {
283         LOGE("TextPicker AddEvent error, info is non-valid");
284         return dialogEvent;
285     }
286     auto paramObject = JSRef<JSObject>::Cast(info[0]);
287     auto onAccept = paramObject->GetProperty("onAccept");
288     if (!onAccept->IsUndefined() && onAccept->IsFunction()) {
289         auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onAccept));
290         auto acceptId = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](const std::string& info) {
291             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
292             std::vector<std::string> keys = { "value", "index" };
293             ACE_SCORING_EVENT("TextPickerDialog.onAccept");
294             func->Execute(keys, info);
295         };
296         dialogEvent["acceptId"] = acceptId;
297     }
298     auto onChange = paramObject->GetProperty("onChange");
299     if (!onChange->IsUndefined() && onChange->IsFunction()) {
300         auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onChange));
301         auto changeId = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](const std::string& info) {
302             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
303             std::vector<std::string> keys = { "value", "index" };
304             ACE_SCORING_EVENT("TextPickerDialog.onChange");
305             func->Execute(keys, info);
306         };
307         dialogEvent["changeId"] = changeId;
308     }
309     return dialogEvent;
310 }
311 
DialogCancelEvent(const JSCallbackInfo & info)312 std::map<std::string, NG::DialogGestureEvent> JSTextPickerDialog::DialogCancelEvent(const JSCallbackInfo& info)
313 {
314     std::map<std::string, NG::DialogGestureEvent> dialogCancelEvent;
315     if (info.Length() < 1 || !info[0]->IsObject()) {
316         LOGE("TextPicker AddEvent error, info is non-valid");
317         return dialogCancelEvent;
318     }
319     auto paramObject = JSRef<JSObject>::Cast(info[0]);
320     auto onCancel = paramObject->GetProperty("onCancel");
321     if (!onCancel->IsUndefined() && onCancel->IsFunction()) {
322         auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onCancel));
323         auto cancelId = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](const GestureEvent& /*info*/) {
324             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
325             ACE_SCORING_EVENT("TextPickerDialog.onCancel");
326             func->Execute();
327         };
328         dialogCancelEvent["cancelId"] = cancelId;
329     }
330     return dialogCancelEvent;
331 }
332 
AddEvent(RefPtr<PickerTextComponent> & picker,const JSCallbackInfo & info)333 void JSTextPickerDialog::AddEvent(RefPtr<PickerTextComponent>& picker, const JSCallbackInfo& info)
334 {
335     if (info.Length() < 1 || !info[0]->IsObject()) {
336         LOGE("TextPicker AddEvent error, info is non-valid");
337         return;
338     }
339     auto paramObject = JSRef<JSObject>::Cast(info[0]);
340     auto onAccept = paramObject->GetProperty("onAccept");
341     if (!onAccept->IsUndefined() && onAccept->IsFunction()) {
342         auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onAccept));
343         auto acceptId =
344             EventMarker([execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](const std::string& info) {
345                 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
346                 std::vector<std::string> keys = { "value", "index" };
347                 ACE_SCORING_EVENT("TextPickerDialog.onAccept");
348                 func->Execute(keys, info);
349             });
350         picker->SetDialogAcceptEvent(acceptId);
351     }
352     auto onCancel = paramObject->GetProperty("onCancel");
353     if (!onCancel->IsUndefined() && onCancel->IsFunction()) {
354         auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onCancel));
355         auto cancelId = EventMarker([execCtx = info.GetExecutionContext(), func = std::move(jsFunc)]() {
356             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
357             ACE_SCORING_EVENT("TextPickerDialog.onCancel");
358             func->Execute();
359         });
360         picker->SetDialogCancelEvent(cancelId);
361     }
362     auto onChange = paramObject->GetProperty("onChange");
363     if (!onChange->IsUndefined() && onChange->IsFunction()) {
364         auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onChange));
365         auto changeId =
366             EventMarker([execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](const std::string& info) {
367                 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
368                 std::vector<std::string> keys = { "value", "index" };
369                 ACE_SCORING_EVENT("TextPickerDialog.onChange");
370                 func->Execute(keys, info);
371             });
372         picker->SetDialogChangeEvent(changeId);
373     }
374 }
375 
ParseText(RefPtr<PickerTextComponent> & component,const JSRef<JSObject> & paramObj)376 void JSTextPickerDialog::ParseText(RefPtr<PickerTextComponent>& component, const JSRef<JSObject>& paramObj)
377 {
378     auto getSelected = paramObj->GetProperty("selected");
379     auto defaultHeight = paramObj->GetProperty("defaultPickerItemHeight");
380     JSRef<JSArray> getRange = paramObj->GetProperty("range");
381     std::vector<std::string> getRangeVector;
382     if (!JSViewAbstract::ParseJsStrArray(getRange, getRangeVector)) {
383         LOGE("parse range failed");
384         return;
385     }
386 
387     std::string value = "";
388     uint32_t selected = 0;
389     auto getValue = paramObj->GetProperty("value");
390     if (!JSViewAbstract::ParseJsInteger(getSelected, selected) && JSViewAbstract::ParseJsString(getValue, value)) {
391         auto valueIterator = std::find(getRangeVector.begin(), getRangeVector.end(), value);
392         if (valueIterator != getRangeVector.end()) {
393             selected = std::distance(getRangeVector.begin(), valueIterator);
394         }
395     }
396 
397     if (selected < 0 || selected >= getRangeVector.size()) {
398         LOGE("selected is out of range");
399         selected = 0;
400     }
401 
402     Dimension height;
403     if (defaultHeight->IsNumber() || defaultHeight->IsString()) {
404         if (!JSViewAbstract::ParseJsDimensionFp(defaultHeight, height)) {
405             return;
406         }
407     }
408 
409     component->SetIsDialog(true);
410     component->SetIsCreateDialogComponent(true);
411     if (!defaultHeight->IsEmpty()) {
412         component->SetColumnHeight(height);
413         component->SetDefaultHeight(true);
414     }
415     component->SetSelected(selected);
416     component->SetRange(getRangeVector);
417 }
418 } // namespace OHOS::Ace::Framework
419