• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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/declarative_frontend/jsview/js_calendar_picker.h"
17 
18 #include "base/log/ace_scoring_log.h"
19 #include "base/utils/date_util.h"
20 #include "bridge/common/utils/engine_helper.h"
21 #include "bridge/declarative_frontend/engine/functions/js_function.h"
22 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
23 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
24 #include "bridge/declarative_frontend/jsview/models/calendar_picker_model_impl.h"
25 #include "core/components/dialog/dialog_theme.h"
26 #include "core/components_ng/pattern/calendar_picker/calendar_picker_model_ng.h"
27 #include "core/pipeline_ng/pipeline_context.h"
28 
29 namespace OHOS::Ace {
30 std::unique_ptr<CalendarPickerModel> CalendarPickerModel::instance_ = nullptr;
31 std::mutex CalendarPickerModel::mutex_;
GetInstance()32 CalendarPickerModel* CalendarPickerModel::GetInstance()
33 {
34     if (!instance_) {
35         std::lock_guard<std::mutex> lock(mutex_);
36         if (!instance_) {
37 #ifdef NG_BUILD
38             instance_.reset(new NG::CalendarPickerModelNG());
39 #else
40             if (Container::IsCurrentUseNewPipeline()) {
41                 instance_.reset(new NG::CalendarPickerModelNG());
42             } else {
43                 instance_.reset(new Framework::CalendarPickerModelImpl());
44             }
45 #endif
46         }
47     }
48     return instance_.get();
49 }
50 } // namespace OHOS::Ace
51 
52 namespace OHOS::Ace::Framework {
JSBind(BindingTarget globalObj)53 void JSCalendarPicker::JSBind(BindingTarget globalObj)
54 {
55     JSClass<JSCalendarPicker>::Declare("CalendarPicker");
56     JSClass<JSCalendarPicker>::StaticMethod("create", &JSCalendarPicker::Create, MethodOptions::NONE);
57     JSClass<JSCalendarPicker>::StaticMethod("edgeAlign", &JSCalendarPicker::SetEdgeAlign);
58     JSClass<JSCalendarPicker>::StaticMethod("textStyle", &JSCalendarPicker::SetTextStyle);
59     JSClass<JSCalendarPicker>::StaticMethod("onChange", &JSCalendarPicker::SetOnChange);
60     JSClass<JSCalendarPicker>::InheritAndBind<JSViewAbstract>(globalObj);
61 }
62 
SetEdgeAlign(const JSCallbackInfo & info)63 void JSCalendarPicker::SetEdgeAlign(const JSCallbackInfo& info)
64 {
65     NG::CalendarEdgeAlign alignType = NG::CalendarEdgeAlign::EDGE_ALIGN_END;
66     DimensionOffset offset;
67     if (!info[0]->IsNumber()) {
68         CalendarPickerModel::GetInstance()->SetEdgeAlign(alignType, offset);
69         return;
70     }
71     alignType = static_cast<NG::CalendarEdgeAlign>(info[0]->ToNumber<int32_t>());
72 
73     if (!info[1]->IsObject()) {
74         CalendarPickerModel::GetInstance()->SetEdgeAlign(alignType, offset);
75         return;
76     }
77     auto offsetObj = JSRef<JSObject>::Cast(info[1]);
78     CalcDimension dx;
79     auto dxValue = offsetObj->GetProperty("dx");
80     ParseJsDimensionVp(dxValue, dx);
81     CalcDimension dy;
82     auto dyValue = offsetObj->GetProperty("dy");
83     ParseJsDimensionVp(dyValue, dy);
84     offset = DimensionOffset(dx, dy);
85 
86     CalendarPickerModel::GetInstance()->SetEdgeAlign(alignType, offset);
87 }
88 
SetTextStyle(const JSCallbackInfo & info)89 void JSCalendarPicker::SetTextStyle(const JSCallbackInfo& info)
90 {
91     auto pipeline = PipelineBase::GetCurrentContext();
92     CHECK_NULL_VOID(pipeline);
93     RefPtr<CalendarTheme> calendarTheme = pipeline->GetTheme<CalendarTheme>();
94     CHECK_NULL_VOID(calendarTheme);
95     NG::PickerTextStyle textStyle;
96     textStyle.fontSize = calendarTheme->GetEntryFontSize();
97     textStyle.textColor = calendarTheme->GetEntryFontColor();
98     textStyle.fontWeight = FontWeight::NORMAL;
99     if (!info[0]->IsObject()) {
100         CalendarPickerModel::GetInstance()->SetTextStyle(textStyle);
101         return;
102     }
103     JSCalendarPicker::ParseTextStyle(info[0], textStyle);
104     CalendarPickerModel::GetInstance()->SetTextStyle(textStyle);
105 }
106 
SetOnChange(const JSCallbackInfo & info)107 void JSCalendarPicker::SetOnChange(const JSCallbackInfo& info)
108 {
109     if (!info[0]->IsFunction()) {
110         return;
111     }
112 
113     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
114     auto onChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](const std::string& info) {
115         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
116         std::vector<std::string> keys = { "year", "month", "day" };
117         ACE_SCORING_EVENT("CalendarPicker.onChange");
118         func->Execute(keys, info);
119     };
120     CalendarPickerModel::GetInstance()->SetOnChange(std::move(onChange));
121 }
122 
ParseSelectedDateObject(const JSCallbackInfo & info,const JSRef<JSObject> & selectedObject)123 void JSCalendarPicker::ParseSelectedDateObject(const JSCallbackInfo& info, const JSRef<JSObject>& selectedObject)
124 {
125     JSRef<JSVal> changeEventVal = selectedObject->GetProperty("changeEvent");
126     if (!changeEventVal->IsFunction()) {
127         return;
128     }
129     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(changeEventVal));
130     auto changeEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](const std::string& info) {
131         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
132         ACE_SCORING_EVENT("DatePicker.SelectedDateTimeChangeEvent");
133         auto selectedJson = JsonUtil::ParseJsonString(info);
134         if (!selectedJson || selectedJson->IsNull()) {
135             return;
136         }
137 
138         std::tm dateTime = { 0 };
139         auto year = selectedJson->GetValue("year");
140         if (year && year->IsNumber()) {
141             dateTime.tm_year = year->GetInt() - 1900; // local date start from 1900
142         }
143         auto month = selectedJson->GetValue("month");
144         if (month && month->IsNumber()) {
145             dateTime.tm_mon = month->GetInt();
146         }
147         auto day = selectedJson->GetValue("day");
148         if (day && day->IsNumber()) {
149             dateTime.tm_mday = day->GetInt();
150         }
151 
152         auto milliseconds = Date::GetMilliSecondsByDateTime(dateTime);
153         auto dateObj = JSDate::New(milliseconds);
154         func->ExecuteJS(1, &dateObj);
155     };
156     CalendarPickerModel::GetInstance()->SetChangeEvent(std::move(changeEvent));
157 }
158 
Create(const JSCallbackInfo & info)159 void JSCalendarPicker::Create(const JSCallbackInfo& info)
160 {
161     NG::CalendarSettingData settingData;
162     RefPtr<CalendarTheme> calendarTheme = GetTheme<CalendarTheme>();
163     CHECK_NULL_VOID(calendarTheme);
164     CalcDimension dayRadius;
165     if (info[0]->IsObject()) {
166         auto obj = JSRef<JSObject>::Cast(info[0]);
167         if (!ParseJsDimensionVp(obj->GetProperty("hintRadius"), dayRadius)) {
168             dayRadius = calendarTheme->GetCalendarDayRadius();
169         }
170         auto selected = obj->GetProperty("selected");
171         if (selected->IsObject()) {
172             JSRef<JSObject> selectedDateObj = JSRef<JSObject>::Cast(selected);
173             JSRef<JSVal> changeEventVal = selectedDateObj->GetProperty("changeEvent");
174             if (!changeEventVal->IsUndefined() && changeEventVal->IsFunction()) {
175                 ParseSelectedDateObject(info, selectedDateObj);
176                 settingData.selectedDate = ParseDate(selectedDateObj->GetProperty("value"));
177             } else {
178                 settingData.selectedDate = ParseDate(selectedDateObj);
179             }
180         }
181     } else {
182         dayRadius = calendarTheme->GetCalendarDayRadius();
183     }
184     settingData.dayRadius = dayRadius;
185     CalendarPickerModel::GetInstance()->Create(settingData);
186 }
187 
ParseTextStyle(const JSRef<JSObject> & paramObj,NG::PickerTextStyle & textStyle)188 void JSCalendarPicker::ParseTextStyle(const JSRef<JSObject>& paramObj, NG::PickerTextStyle& textStyle)
189 {
190     auto fontColor = paramObj->GetProperty("color");
191     auto fontStyle = paramObj->GetProperty("font");
192 
193     Color color;
194     if (ParseJsColor(fontColor, color)) {
195         textStyle.textColor = color;
196     }
197 
198     if (!fontStyle->IsObject()) {
199         return;
200     }
201     JSRef<JSObject> fontObj = JSRef<JSObject>::Cast(fontStyle);
202     auto fontSize = fontObj->GetProperty("size");
203     auto fontWeight = fontObj->GetProperty("weight");
204     if (fontSize->IsNull() || fontSize->IsUndefined()) {
205         textStyle.fontSize = Dimension(-1);
206     } else {
207         CalcDimension size;
208         if (!ParseJsDimensionFp(fontSize, size) || size.Unit() == DimensionUnit::PERCENT) {
209             textStyle.fontSize = Dimension(-1);
210         } else {
211             textStyle.fontSize = size;
212         }
213     }
214 
215     if (!fontWeight->IsNull() && !fontWeight->IsUndefined()) {
216         std::string weight;
217         if (fontWeight->IsNumber()) {
218             weight = std::to_string(fontWeight->ToNumber<int32_t>());
219         } else {
220             ParseJsString(fontWeight, weight);
221         }
222         textStyle.fontWeight = ConvertStrToFontWeight(weight);
223     }
224 }
225 
ParseDate(const JSRef<JSVal> & dateVal)226 PickerDate JSCalendarPicker::ParseDate(const JSRef<JSVal>& dateVal)
227 {
228     auto pickerDate = PickerDate::Current();
229     if (!dateVal->IsObject()) {
230         return pickerDate;
231     }
232     auto dateObj = JSRef<JSObject>::Cast(dateVal);
233     auto yearFuncJsVal = dateObj->GetProperty("getFullYear");
234     auto monthFuncJsVal = dateObj->GetProperty("getMonth");
235     auto dateFuncJsVal = dateObj->GetProperty("getDate");
236     if (!(yearFuncJsVal->IsFunction() && monthFuncJsVal->IsFunction() && dateFuncJsVal->IsFunction())) {
237         return pickerDate;
238     }
239     auto yearFunc = JSRef<JSFunc>::Cast(yearFuncJsVal);
240     auto monthFunc = JSRef<JSFunc>::Cast(monthFuncJsVal);
241     auto dateFunc = JSRef<JSFunc>::Cast(dateFuncJsVal);
242     JSRef<JSVal> year = yearFunc->Call(dateObj);
243     JSRef<JSVal> month = monthFunc->Call(dateObj);
244     JSRef<JSVal> date = dateFunc->Call(dateObj);
245 
246     if (year->IsNumber() && month->IsNumber() && date->IsNumber()) {
247         pickerDate.SetYear(year->ToNumber<int32_t>());
248         pickerDate.SetMonth(month->ToNumber<int32_t>() + 1); // 0-11 means 1 to 12 months
249         pickerDate.SetDay(date->ToNumber<int32_t>());
250     }
251     return pickerDate;
252 }
253 
JSBind(BindingTarget globalObj)254 void JSCalendarPickerDialog::JSBind(BindingTarget globalObj)
255 {
256     JSClass<JSCalendarPickerDialog>::Declare("CalendarPickerDialog");
257     JSClass<JSCalendarPickerDialog>::StaticMethod("show", &JSCalendarPickerDialog::Show);
258     JSClass<JSCalendarPickerDialog>::Bind<>(globalObj);
259 }
260 
Show(const JSCallbackInfo & info)261 void JSCalendarPickerDialog::Show(const JSCallbackInfo& info)
262 {
263     auto scopedDelegate = EngineHelper::GetCurrentDelegate();
264     CHECK_NULL_VOID(scopedDelegate);
265     if (!info[0]->IsObject()) {
266         return;
267     }
268 
269     if (Container::IsCurrentUseNewPipeline()) {
270         auto paramObject = JSRef<JSObject>::Cast(info[0]);
271         auto dialogEvent = ChangeDialogEvent(info);
272         auto dialogCancelEvent = DialogCancelEvent(info);
273         CalendarPickerDialogShow(paramObject, dialogEvent, dialogCancelEvent);
274     }
275 }
276 
ChangeDialogEvent(const JSCallbackInfo & info)277 std::map<std::string, NG::DialogEvent> JSCalendarPickerDialog::ChangeDialogEvent(const JSCallbackInfo& info)
278 {
279     std::map<std::string, NG::DialogEvent> dialogEvent;
280     if (!info[0]->IsObject()) {
281         return dialogEvent;
282     }
283     auto paramObject = JSRef<JSObject>::Cast(info[0]);
284     auto onChange = paramObject->GetProperty("onChange");
285     if (!onChange->IsUndefined() && onChange->IsFunction()) {
286         auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onChange));
287         auto changeId = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](const std::string& info) {
288             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
289             std::vector<std::string> keys = { "year", "month", "day" };
290             ACE_SCORING_EVENT("CalendarDialog.onChange");
291             func->Execute(keys, info);
292         };
293         dialogEvent["changeId"] = changeId;
294     }
295     auto onAccept = paramObject->GetProperty("onAccept");
296     if (!onAccept->IsUndefined() && onAccept->IsFunction()) {
297         auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onAccept));
298         auto acceptId = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](const std::string& info) {
299             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
300             std::vector<std::string> keys = { "year", "month", "day" };
301             ACE_SCORING_EVENT("CalendarDialog.onAccept");
302             func->Execute(keys, info);
303         };
304         dialogEvent["acceptId"] = acceptId;
305     }
306     return dialogEvent;
307 }
308 
DialogCancelEvent(const JSCallbackInfo & info)309 std::map<std::string, NG::DialogGestureEvent> JSCalendarPickerDialog::DialogCancelEvent(const JSCallbackInfo& info)
310 {
311     std::map<std::string, NG::DialogGestureEvent> dialogCancelEvent;
312     if (!info[0]->IsObject()) {
313         return dialogCancelEvent;
314     }
315     auto paramObject = JSRef<JSObject>::Cast(info[0]);
316     auto onCancel = paramObject->GetProperty("onCancel");
317     if (!onCancel->IsUndefined() && onCancel->IsFunction()) {
318         auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onCancel));
319         auto cancelId = [execCtx = info.GetExecutionContext(),
320             func = std::move(jsFunc)](const GestureEvent& /* info */) {
321             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
322             ACE_SCORING_EVENT("CalendarDialog.onCancel");
323             func->Execute();
324         };
325         dialogCancelEvent["cancelId"] = cancelId;
326     }
327     return dialogCancelEvent;
328 }
329 
ParseDate(const JSRef<JSVal> & dateVal)330 PickerDate JSCalendarPickerDialog::ParseDate(const JSRef<JSVal>& dateVal)
331 {
332     auto pickerDate = PickerDate();
333     if (!dateVal->IsObject()) {
334         return pickerDate;
335     }
336     auto dateObj = JSRef<JSObject>::Cast(dateVal);
337 
338     auto yearFuncJsVal = dateObj->GetProperty("getFullYear");
339     auto monthFuncJsVal = dateObj->GetProperty("getMonth");
340     auto dateFuncJsVal = dateObj->GetProperty("getDate");
341     if (!(yearFuncJsVal->IsFunction() && monthFuncJsVal->IsFunction() && dateFuncJsVal->IsFunction())) {
342         return pickerDate;
343     }
344     auto yearFunc = JSRef<JSFunc>::Cast(yearFuncJsVal);
345     auto monthFunc = JSRef<JSFunc>::Cast(monthFuncJsVal);
346     auto dateFunc = JSRef<JSFunc>::Cast(dateFuncJsVal);
347     JSRef<JSVal> year = yearFunc->Call(dateObj);
348     JSRef<JSVal> month = monthFunc->Call(dateObj);
349     JSRef<JSVal> date = dateFunc->Call(dateObj);
350 
351     if (year->IsNumber() && month->IsNumber() && date->IsNumber()) {
352         pickerDate.SetYear(year->ToNumber<int32_t>());
353         pickerDate.SetMonth(month->ToNumber<int32_t>() + 1); // 0-11 means 1 to 12 months
354         pickerDate.SetDay(date->ToNumber<int32_t>());
355     }
356     return pickerDate;
357 }
358 
CalendarPickerDialogShow(const JSRef<JSObject> & paramObj,const std::map<std::string,NG::DialogEvent> & dialogEvent,const std::map<std::string,NG::DialogGestureEvent> & dialogCancelEvent)359 void JSCalendarPickerDialog::CalendarPickerDialogShow(const JSRef<JSObject>& paramObj,
360     const std::map<std::string, NG::DialogEvent>& dialogEvent,
361     const std::map<std::string, NG::DialogGestureEvent>& dialogCancelEvent)
362 {
363     auto container = Container::Current();
364     CHECK_NULL_VOID(container);
365     auto pipelineContext = AccessibilityManager::DynamicCast<NG::PipelineContext>(container->GetPipelineContext());
366     CHECK_NULL_VOID(pipelineContext);
367     auto executor = pipelineContext->GetTaskExecutor();
368     CHECK_NULL_VOID(executor);
369 
370     auto theme = GetTheme<DialogTheme>();
371     CHECK_NULL_VOID(theme);
372     auto calendarTheme = pipelineContext->GetTheme<CalendarTheme>();
373     NG::CalendarSettingData settingData;
374     auto selectedDate = paramObj->GetProperty("selected");
375     auto parseSelectedDate = ParseDate(selectedDate);
376 
377     if (selectedDate->IsObject()) {
378         settingData.selectedDate = parseSelectedDate;
379     }
380 
381     CalcDimension radius;
382     if (ParseJsDimensionVp(paramObj->GetProperty("hintRadius"), radius)) {
383         settingData.dayRadius = radius;
384     }
385 
386     DialogProperties properties;
387     if (SystemProperties::GetDeviceType() == DeviceType::PHONE) {
388         properties.alignment = DialogAlignment::BOTTOM;
389     } else {
390         properties.alignment = DialogAlignment::CENTER;
391     }
392     properties.customStyle = false;
393     properties.offset = DimensionOffset(Offset(0, -theme->GetMarginBottom().ConvertToPx()));
394     NG::BorderRadiusProperty dialogRadius;
395     dialogRadius.SetRadius(calendarTheme->GetDialogBorderRadius());
396     properties.borderRadius = dialogRadius;
397 
398     auto context = AccessibilityManager::DynamicCast<NG::PipelineContext>(pipelineContext);
399     auto overlayManager = context ? context->GetOverlayManager() : nullptr;
400     executor->PostTask(
401         [properties, settingData, dialogEvent, dialogCancelEvent, weak = WeakPtr<NG::OverlayManager>(overlayManager)] {
402             auto overlayManager = weak.Upgrade();
403             CHECK_NULL_VOID(overlayManager);
404             overlayManager->ShowCalendarDialog(properties, settingData, dialogEvent, dialogCancelEvent);
405         },
406         TaskExecutor::TaskType::UI);
407 }
408 } // namespace OHOS::Ace::Framework
409