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 "core/components/picker/picker_datetime_component.h"
17 
18 namespace OHOS::Ace {
19 
PickerDateTimeComponent()20 PickerDateTimeComponent::PickerDateTimeComponent() {}
21 
OnTitleBuilding()22 void PickerDateTimeComponent::OnTitleBuilding()
23 {
24     auto theme = GetTheme();
25     if (!theme) {
26         LOGE("theme is null.");
27         return;
28     }
29     SetHasTitle(theme->GetShowButtons());
30     SetHasButtons(theme->GetShowButtons());
31     SetHasTriangle(true); // triangle only for datetime picker.
32 
33     auto titleComponent = GetTitle();
34     if (!titleComponent) {
35         LOGE("title component is null.");
36         return;
37     }
38     titleComponent->SetData(currentDate_.ToString(false));
39 }
40 
OnColumnsBuilding()41 void PickerDateTimeComponent::OnColumnsBuilding()
42 {
43     PickerTimeComponent::OnColumnsBuilding();
44     FillSolarLunarDays(lunar_, selectedDate_);
45     InitDatePicker();
46 }
47 
OnSelectedSaving()48 void PickerDateTimeComponent::OnSelectedSaving()
49 {
50     PickerTimeComponent::OnSelectedSaving();
51     selectedDate_ = currentDate_;
52 }
53 
GetSelectedObject(bool isColumnChange,const std::string & changeColumnTag,int32_t status) const54 std::string PickerDateTimeComponent::GetSelectedObject(bool isColumnChange,
55     const std::string& changeColumnTag, int32_t status) const
56 {
57     if (isColumnChange) {
58         LOGE("datetime picker not support column change event.");
59         return "{}";
60     }
61 
62     auto date = selectedDate_;
63     // W3C's month is between 0 to 11, need to reduce one.
64     date.SetMonth(date.GetMonth() ? date.GetMonth() - 1 : 0);
65     PickerDateTime dateTime(date, GetSelectedTime());
66     return dateTime.ToString(true, status);
67 }
68 
OnLunarCallback(bool checked,std::vector<std::string> & resultTags)69 void PickerDateTimeComponent::OnLunarCallback(bool checked, std::vector<std::string>& resultTags)
70 {
71     FillSolarLunarDays(checked, currentDate_);
72     resultTags.emplace_back(PICKER_MONTHDAY_COLUMN);
73 }
74 
OnTriangleCallback(bool value)75 void PickerDateTimeComponent::OnTriangleCallback(bool value)
76 {
77     if (value) {
78         HideDatePicker();
79     } else {
80         ShowDatePicker();
81     }
82 }
83 
InitDatePicker()84 void PickerDateTimeComponent::InitDatePicker()
85 {
86     if (datePicker_) {
87         return; // already init.
88     }
89     datePicker_ = AceType::MakeRefPtr<PickerDateComponent>();
90     datePicker_->SetSubsidiary(true);
91     datePicker_->SetHasLunar(false);
92     datePicker_->SetIsDialog(true);
93     datePicker_->SetTextDirection(GetTextDirection());
94     datePicker_->SetTheme(GetTheme());
95     datePicker_->SetColumnHeight(GetColumnHeight());
96     datePicker_->SetOnDateChange([weak = WeakClaim(this)] (const PickerDate& date) {
97         auto refPtr = weak.Upgrade();
98         if (!refPtr) {
99             return;
100         }
101         refPtr->OnSubsidiaryChange(date);
102     });
103     auto controller = AceType::MakeRefPtr<PickerAnimationController>();
104     controller->SetOutStopCallback([weak = AceType::WeakClaim(AceType::RawPtr(datePicker_))] {
105         auto refPtr = weak.Upgrade();
106         if (!refPtr) {
107             return;
108         }
109         refPtr->HideDialog();
110     });
111     SetAnimationController(controller);
112     datePicker_->SetAnimationController(controller);
113 }
114 
OnSubsidiaryChange(const PickerDate & date)115 void PickerDateTimeComponent::OnSubsidiaryChange(const PickerDate& date)
116 {
117     auto days = date.ToDays();
118     days--;
119     currentDate_.FromDays(days);
120 
121     auto monthDay = GetColumn(PICKER_MONTHDAY_COLUMN);
122     if (!monthDay) {
123         return;
124     }
125 
126     monthDay->HandleChangeCallback(true, false);
127 }
128 
ShowDatePicker()129 void PickerDateTimeComponent::ShowDatePicker()
130 {
131     InitDatePicker();
132 #if defined(PREVIEW)
133     datePicker_->SetPickerBaseId(GetPickerBaseId());
134 #endif
135     datePicker_->SetShowLunar(lunar_);
136     datePicker_->SetSelectedDate(currentDate_);
137     datePicker_->SetMasterHasLunar(GetHasLunar());
138     datePicker_->ShowDialog(GetStack(), false);
139 }
140 
HideDatePicker()141 void PickerDateTimeComponent::HideDatePicker()
142 {
143     InitDatePicker();
144     auto controller = GetAnimationController();
145     if (!controller) {
146         LOGE("controller is null.");
147         return;
148     }
149 
150     controller->Play(false);
151 }
152 
OnColumnsCreating()153 void PickerDateTimeComponent::OnColumnsCreating()
154 {
155     RemoveColumn(PICKER_MONTHDAY_COLUMN);
156     auto monthDay = AceType::MakeRefPtr<PickerColumnComponent>();
157     monthDay->SetColumnTag(PICKER_MONTHDAY_COLUMN);
158     if (GetHour24()) {
159         monthDay->SetWidthRatio(3); // date:hour:minute:second = 3:2:2:2
160     } else {
161         monthDay->SetWidthRatio(4); // date:amPm:hour:minute:second = 4:2:2:2:2
162     }
163     AppendColumn(monthDay);
164 
165     PickerTimeComponent::OnColumnsCreating();
166 }
167 
GetMonthDayFormatString(bool lunar,uint32_t days) const168 std::string PickerDateTimeComponent::GetMonthDayFormatString(bool lunar, uint32_t days) const
169 {
170     PickerDate outDate;
171     outDate.FromDays(days);
172     auto nowadays = PickerDate::Current().ToDays();
173     if (days == nowadays) {
174         return Localization::GetInstance()->GetRelativeDateTime(0.0);
175     }
176     if (!lunar) {
177         DateTime dateTime;
178         dateTime.year = outDate.GetYear();
179         dateTime.month = outDate.GetMonth() ? outDate.GetMonth() - 1 : 0; // W3C's month start from 0 to 11
180         dateTime.day = outDate.GetDay();
181         return Localization::GetInstance()->FormatDateTime(dateTime, "MMMd");
182     }
183     Date date;
184     date.year = outDate.GetYear();
185     date.month = outDate.GetMonth();
186     date.day = outDate.GetDay();
187     auto lunarDate = Localization::GetInstance()->GetLunarDate(date);
188     return Localization::GetInstance()->GetLunarMonth(lunarDate.month, lunarDate.isLeapMonth) +
189         Localization::GetInstance()->GetLunarDay(lunarDate.day);
190 }
191 
FillSolarLunarDays(bool lunar,const PickerDate & currentDate)192 void PickerDateTimeComponent::FillSolarLunarDays(bool lunar, const PickerDate& currentDate)
193 {
194     auto monthDay = GetColumn(PICKER_MONTHDAY_COLUMN);
195     if (!monthDay) {
196         return;
197     }
198     monthDay->ClearOption();
199     monthDay->SetCurrentIndex(3); // total option is fixed 7. center index is 3 which is current.
200     uint32_t centerDays = currentDate.ToDays();
201     uint32_t startDays = centerDays - 3; // start day 3 days before center day.
202     uint32_t endDays = centerDays + 3; // end day 3 days after center day.
203     for (uint32_t days = startDays; days <= endDays; ++days) {
204         monthDay->AppendOption(GetMonthDayFormatString(lunar, days));
205     }
206     lunar_ = lunar;
207     currentDate_ = currentDate;
208 }
209 
OnDataLinking(const std::string & tag,bool isAdd,uint32_t index,std::vector<std::string> & resultTags)210 void PickerDateTimeComponent::OnDataLinking(const std::string& tag, bool isAdd, uint32_t index,
211     std::vector<std::string>& resultTags)
212 {
213     PickerTimeComponent::OnDataLinking(tag, isAdd, index, resultTags);
214     if (tag == PICKER_MONTHDAY_COLUMN) {
215         // linked by month day column itself.
216         auto days = currentDate_.ToDays();
217         days = (isAdd ? days + 1 : (days ? days - 1 : 0)); // add one day or reduce one day.
218         PickerDate date;
219         date.FromDays(days);
220         FillSolarLunarDays(lunar_, date);
221         resultTags.emplace_back(PICKER_MONTHDAY_COLUMN);
222         return;
223     }
224     auto it = std::find(resultTags.begin(), resultTags.end(), PICKER_MONTHDAY_COLUMN);
225     if (it != resultTags.end()) {
226         // linked by other column
227         auto days = currentDate_.ToDays();
228         days = (isAdd ? days + 1 : (days ? days - 1 : 0));
229         PickerDate date;
230         date.FromDays(days);
231         FillSolarLunarDays(lunar_, date);
232     }
233 }
234 
235 } // namespace OHOS::Ace
236