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/calendar/calendar_component.h"
17 
18 #include "base/i18n/localization.h"
19 #include "core/components/button/render_button.h"
20 #include "core/components/calendar/calendar_element.h"
21 #include "core/components/calendar/render_calendar.h"
22 #include "core/components/display/display_component.h"
23 #include "core/components/flex/flex_item_component.h"
24 #include "core/components/padding/padding_component.h"
25 
26 namespace OHOS::Ace {
27 namespace {
28 
29 constexpr double MAX_OPACITY = 255.0;
30 constexpr int32_t MAX_MONTH_CACHE_NUM = 3;
31 constexpr int32_t DISTANCE_FORM_LAST = MAX_MONTH_CACHE_NUM - 1;
32 constexpr int32_t TODAY_MONTH_INDEX_OF_CONTAINER = 1;
33 constexpr int32_t NEXT_TO_TODAY_MONTH_INDEX_OF_CONTAINER = 2;
34 constexpr int32_t LAST_TO_TODAY_MONTH_INDEX_OF_CONTAINER = 0;
35 constexpr Dimension CALENDAR_BOX_TOP_AND_BOTTOM_MARGIN = 4.0_vp;
36 constexpr Dimension CALENDAR_BOX_LEFT_AND_RIGHT_MARGIN = 24.0_vp;
37 constexpr Dimension CALENDAR_BUTTON_RADIUS = 4.0_vp;
38 
39 } // namespace
40 
CalendarController(const CalendarDataAdapterAction & dataAdapterAction,const WeakPtr<PipelineContext> & pipelineContext)41 CalendarController::CalendarController(
42     const CalendarDataAdapterAction& dataAdapterAction, const WeakPtr<PipelineContext>& pipelineContext)
43     : dataAdapter_(AceType::MakeRefPtr<CalendarDataAdapter>(dataAdapterAction, pipelineContext)) {}
44 
Initialize()45 void CalendarController::Initialize()
46 {
47     currentCalendarMonth_ = dataAdapter_->GetToday().month;
48     dataAdapter_->SetCurrentMonth(currentCalendarMonth_);
49     dataAdapter_->SetSelectedChanged(dataAdapter_->GetToday().day, TODAY_MONTH_INDEX_OF_CONTAINER);
50     CalendarDataRequest requestMonth(currentCalendarMonth_, TODAY_MONTH_INDEX_OF_CONTAINER);
51     requestMonth.state = MonthState::CUR_MONTH;
52     dataAdapter_->RequestData(requestMonth);
53     requestMonth.month = CalendarMonth::GetNextMonth(currentCalendarMonth_);
54     requestMonth.indexOfContainer = NEXT_TO_TODAY_MONTH_INDEX_OF_CONTAINER;
55     requestMonth.state = MonthState::NEXT_MONTH;
56     dataAdapter_->AddPendingRequest(requestMonth);
57     requestMonth.month = CalendarMonth::GetLastMonth(currentCalendarMonth_);
58     requestMonth.indexOfContainer = LAST_TO_TODAY_MONTH_INDEX_OF_CONTAINER;
59     requestMonth.state = MonthState::PRE_MONTH;
60     dataAdapter_->AddPendingRequest(requestMonth);
61 }
62 
GoToPrevMonth(int32_t selected)63 void CalendarController::GoToPrevMonth(int32_t selected)
64 {
65     GoToMonth(SwipeDirection::PREV, selected);
66 }
67 
GoToNextMonth(int32_t selected)68 void CalendarController::GoToNextMonth(int32_t selected)
69 {
70     GoToMonth(SwipeDirection::NEXT, selected);
71 }
72 
GoToMonth(SwipeDirection direction,int32_t selected)73 void CalendarController::GoToMonth(SwipeDirection direction, int32_t selected)
74 {
75     int32_t index;
76     CalendarMonth calendarMonth;
77     CalendarMonth cacheMonth;
78     bool reverse = false;
79     if (direction == SwipeDirection::PREV) {
80         index = (currentMonthIndex_ + 1) % MAX_MONTH_CACHE_NUM;
81         if (--currentMonthIndex_ < 0) {
82             reverse = true;
83             currentMonthIndex_ = MAX_MONTH_CACHE_NUM - 1;
84         }
85         calendarMonth = CalendarMonth::GetLastMonth(currentCalendarMonth_);
86         cacheMonth = CalendarMonth::GetLastMonth(calendarMonth);
87     } else if (direction == SwipeDirection::NEXT) {
88         index = (currentMonthIndex_ + DISTANCE_FORM_LAST) % MAX_MONTH_CACHE_NUM;
89         if (++currentMonthIndex_ >= MAX_MONTH_CACHE_NUM) {
90             reverse = true;
91             currentMonthIndex_ = 0;
92         }
93         calendarMonth = CalendarMonth::GetNextMonth(currentCalendarMonth_);
94         cacheMonth = CalendarMonth::GetNextMonth(calendarMonth);
95     } else {
96         return;
97     }
98 
99     swiperReverseCache_.push({ currentMonthIndex_, reverse });
100     dataAdapter_->SetSelectedChanged(selected, currentMonthIndex_);
101     dataAdapter_->NotifySelectedChanged();
102     currentCalendarMonth_ = calendarMonth;
103     dataAdapter_->SetCurrentMonth(currentCalendarMonth_);
104     dataAdapter_->RequestData({ cacheMonth, index });
105 }
106 
JumpToMonth(const CalendarMonth & calendarMonth,int32_t selected,SwipeDirection direction)107 void CalendarController::JumpToMonth(const CalendarMonth& calendarMonth, int32_t selected, SwipeDirection direction)
108 {
109     if (direction == SwipeDirection::PREV) {
110         // target -- last(replaced by target) -- current -- next
111         int32_t destIndex = (currentMonthIndex_ + DISTANCE_FORM_LAST) % MAX_MONTH_CACHE_NUM;
112         dataAdapter_->RequestData({ calendarMonth, destIndex });
113         CalculateNextIndex(currentMonthIndex_ - 1);
114         dataAdapter_->SetSelectedChanged(selected, currentMonthIndex_);
115 
116         int32_t lastIndex = (destIndex + DISTANCE_FORM_LAST) % MAX_MONTH_CACHE_NUM;
117         dataAdapter_->AddPendingRequest(CalendarMonth::GetLastMonth(calendarMonth), lastIndex);
118 
119         // cache the next month date after the animation ends, otherwise the content of the animation page will be
120         // changed
121         int32_t nextIndex = (destIndex + 1) % MAX_MONTH_CACHE_NUM;
122         dataAdapter_->AddPendingRequest(CalendarMonth::GetNextMonth(calendarMonth), nextIndex);
123     } else if (direction == SwipeDirection::NEXT) {
124         // last -- current -- next(replaced by target) -- target
125         int32_t destIndex = (currentMonthIndex_ + 1) % MAX_MONTH_CACHE_NUM;
126         dataAdapter_->RequestData({ calendarMonth, destIndex });
127         CalculateNextIndex(currentMonthIndex_ + 1);
128         dataAdapter_->SetSelectedChanged(selected, currentMonthIndex_);
129 
130         int32_t nextIndex = (destIndex + 1) % MAX_MONTH_CACHE_NUM;
131         dataAdapter_->AddPendingRequest(CalendarMonth::GetNextMonth(calendarMonth), nextIndex);
132 
133         // cache the previous month date after the animation ends, otherwise the content of the animation page will be
134         // changed
135         int32_t lastIndex = (destIndex + DISTANCE_FORM_LAST) % MAX_MONTH_CACHE_NUM;
136         dataAdapter_->AddPendingRequest(CalendarMonth::GetLastMonth(calendarMonth), lastIndex);
137     }
138 }
139 
GoTo(int32_t year,int32_t month,int32_t day)140 void CalendarController::GoTo(int32_t year, int32_t month, int32_t day)
141 {
142     if (!dataAdapter_) {
143         LOGE("calendar data adapter is nullptr");
144         return;
145     }
146     if (!swiperController_) {
147         LOGE("swiper controller is nullptr");
148         return;
149     }
150 
151     CalendarMonth calendarMonth { year, month };
152     auto nextMonth = CalendarMonth::GetNextMonth(currentCalendarMonth_);
153     auto lastMonth = CalendarMonth::GetLastMonth(currentCalendarMonth_);
154     if (calendarMonth == lastMonth) {
155         GoToPrevMonth(day);
156     } else if (calendarMonth == nextMonth) {
157         GoToNextMonth(day);
158     } else if (calendarMonth < lastMonth) {
159         currentCalendarMonth_ = calendarMonth;
160         dataAdapter_->SetCurrentMonth(currentCalendarMonth_);
161         JumpToMonth(calendarMonth, day, SwipeDirection::PREV);
162     } else if (calendarMonth > nextMonth) {
163         currentCalendarMonth_ = calendarMonth;
164         dataAdapter_->SetCurrentMonth(currentCalendarMonth_);
165         JumpToMonth(calendarMonth, day, SwipeDirection::NEXT);
166     } else {
167         dataAdapter_->SetSelectedChanged(day, currentMonthIndex_);
168         dataAdapter_->NotifySelectedChanged();
169     }
170 }
171 
RequestMonthData(int32_t index)172 void CalendarController::RequestMonthData(int32_t index)
173 {
174     auto tmpPreIndex = (currentMonthIndex_ + 1) % MAX_MONTH_CACHE_NUM;
175     auto tmpNextIndex = (currentMonthIndex_ + DISTANCE_FORM_LAST) % MAX_MONTH_CACHE_NUM;
176     auto nextIndex = currentMonthIndex_;
177     auto preIndex = currentMonthIndex_;
178     CalendarMonth calendarMonth;
179     CalendarMonth cacheMonth;
180     static const int32_t selectedDay = 1;
181     if (++nextIndex >= MAX_MONTH_CACHE_NUM) {
182         nextIndex = 0;
183     }
184     if (--preIndex < 0) {
185         preIndex = MAX_MONTH_CACHE_NUM - 1;
186     }
187     if (nextIndex == index) {
188         currentMonthIndex_ = nextIndex;
189         calendarMonth = CalendarMonth::GetNextMonth(currentCalendarMonth_);
190         cacheMonth = CalendarMonth::GetNextMonth(calendarMonth);
191 
192         dataAdapter_->SetSelectedChanged(selectedDay, currentMonthIndex_);
193         CalendarDataRequest requestMonth(cacheMonth, tmpNextIndex);
194         requestMonth.state = MonthState::NEXT_MONTH;
195         currentCalendarMonth_ = calendarMonth;
196         dataAdapter_->SetCurrentMonth(currentCalendarMonth_);
197         dataAdapter_->RequestData(requestMonth);
198     } else if (preIndex == index) {
199         currentMonthIndex_ = preIndex;
200         calendarMonth = CalendarMonth::GetLastMonth(currentCalendarMonth_);
201         cacheMonth = CalendarMonth::GetLastMonth(calendarMonth);
202 
203         dataAdapter_->SetSelectedChanged(selectedDay, currentMonthIndex_);
204         CalendarDataRequest requestMonth(cacheMonth, tmpPreIndex);
205         requestMonth.state = MonthState::PRE_MONTH;
206         currentCalendarMonth_ = calendarMonth;
207         dataAdapter_->SetCurrentMonth(currentCalendarMonth_);
208         dataAdapter_->RequestData(requestMonth);
209     } else {
210         return;
211     }
212 }
213 
CalculateNextIndex(int32_t index)214 void CalendarController::CalculateNextIndex(int32_t index)
215 {
216     bool reverse = false;
217     if (index >= MAX_MONTH_CACHE_NUM) {
218         reverse = true;
219         index = 0;
220     } else if (index < 0) {
221         reverse = true;
222         index = MAX_MONTH_CACHE_NUM - 1;
223     }
224     currentMonthIndex_ = index;
225     swiperReverseCache_.push({ currentMonthIndex_, reverse });
226 }
227 
UpdateTheme()228 void CalendarController::UpdateTheme()
229 {
230     if (!renderText_) {
231         return;
232     }
233     auto theme = renderText_->GetTheme<CalendarTheme>();
234     if (!leftImage_ || !leftImageComponent_ || !rightImageComponent_ || !rightImage_ || !theme) {
235         return;
236     }
237     TextStyle style;
238     auto cardTheme = theme->GetCardCalendarTheme();
239     style.SetFontSize(cardTheme.titleFontSize);
240     style.SetTextColor(cardTheme.titleTextColor);
241     style.SetFontWeight(FontWeight::W500);
242     style.SetAllowScale(false);
243     renderText_->SetTextStyle(style);
244     renderText_->MarkNeedMeasure();
245     renderText_->MarkNeedLayout();
246     leftImageComponent_->SetImageFill(cardTheme.titleTextColor);
247     leftImage_->Update(leftImageComponent_);
248     leftImage_->MarkNeedLayout();
249     rightImageComponent_->SetImageFill(cardTheme.titleTextColor);
250     rightImage_->Update(rightImageComponent_);
251     rightImage_->MarkNeedLayout();
252     SetButtonClickColor(renderText_, cardTheme.clickEffectColor);
253     SetButtonClickColor(leftImage_, cardTheme.clickEffectColor);
254     SetButtonClickColor(rightImage_, cardTheme.clickEffectColor);
255 }
256 
SetButtonClickColor(const RefPtr<RenderNode> & node,const Color & clickColor) const257 void CalendarController::SetButtonClickColor(const RefPtr<RenderNode>& node, const Color& clickColor) const
258 {
259     auto button = AceType::DynamicCast<RenderButton>(node->GetParent().Upgrade());
260     if (!button) {
261         LOGE("this node is not button");
262         return;
263     }
264     button->SetClickedColor(clickColor);
265     button->MarkNeedLayout();
266 }
267 
UpdateTitle(const CalendarDay & today)268 void CalendarController::UpdateTitle(const CalendarDay& today)
269 {
270     if (!renderText_) {
271         return;
272     }
273     DateTime dateTime;
274     dateTime.year = today.month.year;
275     dateTime.month = today.month.month;
276     auto date = Localization::GetInstance()->FormatDateTime(dateTime, "yyyyMMM");
277     renderText_->SetTextData(date);
278     renderText_->MarkNeedMeasure();
279     renderText_->MarkNeedLayout();
280 }
281 
CalendarComponent(const ComposeId & id,const std::string & name)282 CalendarComponent::CalendarComponent(const ComposeId& id, const std::string& name) : ComposedComponent(id, name) {}
283 
Build(const WeakPtr<PipelineContext> & pipelineContext,const RefPtr<CalendarController> & calendarController)284 RefPtr<Component> CalendarComponent::Build(
285     const WeakPtr<PipelineContext>& pipelineContext, const RefPtr<CalendarController>& calendarController)
286 {
287     auto context = pipelineContext.Upgrade();
288     if (!context || !calendarController) {
289         return nullptr;
290     }
291     calendarController_ = calendarController;
292 
293     auto direction = GetTextDirection();
294 
295     if (!theme_) {
296         auto themeManager = context->GetThemeManager();
297         if (!themeManager) {
298             return nullptr;
299         }
300         theme_ = themeManager->GetTheme<CalendarTheme>();
301     }
302 
303     std::list<RefPtr<Component>> monthChildren;
304     for (int32_t index = 0; index < MAX_MONTH_CACHE_NUM; index++) {
305         auto calendarMonth = AceType::MakeRefPtr<CalendarMonthComponent>(index, calendarController_);
306         auto display = AceType::MakeRefPtr<DisplayComponent>(calendarMonth);
307         calendarMonth->SetSelectedChangeEvent(selectedChangeEvent_);
308         calendarMonth->SetCalendarTheme(theme_);
309         calendarMonth->SetCardCalendar(cardCalendar_);
310         calendarMonth->SetTextDirection(direction);
311         calendarMonth->SetCalendarType(type_);
312         display->SetOpacity(MAX_OPACITY);
313         monthChildren.emplace_back(display);
314     }
315     if (!swiperContainer_) {
316         swiperContainer_ = AceType::MakeRefPtr<SwiperComponent>(monthChildren, false);
317     }
318     swiperContainer_->SetAxis(axis_);
319     swiperContainer_->SetIndex(TODAY_MONTH_INDEX_OF_CONTAINER);
320     swiperContainer_->SetTextDirection(direction);
321     swiperContainer_->SetSlideContinue(true);
322     calendarController_->SetSwiperController(swiperContainer_->GetSwiperController());
323     swiperContainer_->SetMoveCallback([controller = WeakPtr<CalendarController>(calendarController_)](int32_t index) {
324         auto calendarController = controller.Upgrade();
325         if (calendarController) {
326             calendarController->SetHasMoved(true);
327             calendarController->RequestMonthData(index);
328         }
329     });
330     if (type_ == CalendarType::SIMPLE) {
331         swiperContainer_->SetDisabledStatus(true);
332     }
333     if (!needSlide_) {
334         swiperContainer_->SetLoop(false);
335         swiperContainer_->DisableSwipe(true);
336         swiperContainer_->SetDisableRotation(true);
337     }
338     if (!cardCalendar_) {
339         return swiperContainer_;
340     } else {
341         RefPtr<ColumnComponent> colComponent;
342         BuildCardCalendarTitle(colComponent);
343         return colComponent;
344     }
345 }
346 
CreateElement()347 RefPtr<Element> CalendarComponent::CreateElement()
348 {
349     return AceType::MakeRefPtr<CalendarElement>(GetId());
350 }
351 
GoTo(int32_t year,int32_t month,int32_t day)352 void CalendarComponent::GoTo(int32_t year, int32_t month, int32_t day)
353 {
354     if (day < 0) {
355         calendarController_->GoTo(year, month, 1);
356     } else {
357         calendarController_->SetNeedFocus(true);
358         calendarController_->GoTo(year, month, day);
359     }
360 }
361 
BuildCardCalendarTitle(RefPtr<ColumnComponent> & col)362 void CalendarComponent::BuildCardCalendarTitle(RefPtr<ColumnComponent>& col)
363 {
364     auto preButton = InitCardButton(true);
365     auto nextButton = InitCardButton(false);
366     DateTime dateTime;
367     dateTime.year = calendarController_->GetCurrentMonth().year;
368     dateTime.month = calendarController_->GetCurrentMonth().month;
369     auto date = Localization::GetInstance()->FormatDateTime(dateTime, "yyyyMMM");
370     auto text = AceType::MakeRefPtr<TextComponent>(date);
371     TextStyle style;
372     auto calendarTheme = theme_->GetCardCalendarTheme();
373     style.SetFontSize(calendarTheme.titleFontSize);
374     style.SetTextColor(calendarTheme.titleTextColor);
375     style.SetFontWeight(FontWeight::W500);
376     style.SetAllowScale(false);
377     text->SetTextStyle(style);
378 
379     auto box = AceType::MakeRefPtr<BoxComponent>();
380     Edge edge(CALENDAR_BOX_LEFT_AND_RIGHT_MARGIN, CALENDAR_BOX_TOP_AND_BOTTOM_MARGIN,
381         CALENDAR_BOX_LEFT_AND_RIGHT_MARGIN, CALENDAR_BOX_TOP_AND_BOTTOM_MARGIN);
382 
383     std::list<RefPtr<Component>> children;
384     auto padding = AceType::MakeRefPtr<PaddingComponent>();
385     padding->SetChild(text);
386     padding->SetPadding(edge);
387     children.emplace_back(padding);
388     auto button = AceType::MakeRefPtr<ButtonComponent>(children);
389     button->SetHeight(calendarTheme.buttonHeight);
390     button->SetType(ButtonType::TEXT);
391     button->SetBackgroundColor(Color::TRANSPARENT);
392     button->SetClickedColor(calendarTheme.clickEffectColor);
393     button->SetRectRadius(CALENDAR_BUTTON_RADIUS);
394     dateClickId_ = BackEndEventManager<void()>::GetInstance().GetAvailableMarker();
395     button->SetClickedEventId(dateClickId_);
396     box->SetChild(button);
397     auto flexItem = AceType::MakeRefPtr<FlexItemComponent>(0.0, 0.0, 0.0, box);
398 
399     std::list<RefPtr<Component>> rowChildren;
400     rowChildren.emplace_back(preButton);
401     rowChildren.emplace_back(flexItem);
402     rowChildren.emplace_back(nextButton);
403     auto rowComponent = AceType::MakeRefPtr<RowComponent>(FlexAlign::CENTER, FlexAlign::CENTER, rowChildren);
404     rowComponent->SetTextDirection(TextDirection::LTR);
405     std::list<RefPtr<Component>> colChildren;
406     colChildren.emplace_back(rowComponent);
407     colChildren.emplace_back(swiperContainer_);
408     col = AceType::MakeRefPtr<ColumnComponent>(FlexAlign::FLEX_START, FlexAlign::CENTER, colChildren);
409 }
410 
GetSelectedChangeEvent() const411 const EventMarker& CalendarComponent::GetSelectedChangeEvent() const
412 {
413     return selectedChangeEvent_;
414 }
415 
GetRequestDataEvent() const416 const EventMarker& CalendarComponent::GetRequestDataEvent() const
417 {
418     return requestDataEvent_;
419 }
420 
CalendarMonthComponent(int32_t indexOfContainer,const RefPtr<CalendarController> & calendarController)421 CalendarMonthComponent::CalendarMonthComponent(
422     int32_t indexOfContainer, const RefPtr<CalendarController>& calendarController)
423     : indexOfContainer_(indexOfContainer), calendarController_(calendarController) {}
424 
CreateRenderNode()425 RefPtr<RenderNode> CalendarMonthComponent::CreateRenderNode()
426 {
427     return RenderCalendar::Create();
428 }
429 
CreateElement()430 RefPtr<Element> CalendarMonthComponent::CreateElement()
431 {
432     return AceType::MakeRefPtr<CalendarMonthElement>();
433 }
434 
SetCalendarData(const std::string & value)435 void CalendarComponent::SetCalendarData(const std::string& value)
436 {
437     if (calendarController_) {
438         calendarData_ = value;
439         auto dataAdapter = calendarController_->GetDataAdapter();
440         if (dataAdapter) {
441             dataAdapter->SetOffDays(offDays_);
442             dataAdapter->ParseCardCalendarData(calendarData_);
443         }
444         calendarController_->UpdateTheme();
445     } else {
446         calendarData_ = value;
447     }
448 }
449 
InitCardButton(bool isPreArrow)450 RefPtr<ButtonComponent> CalendarComponent::InitCardButton(bool isPreArrow)
451 {
452     auto Arrow = isPreArrow ? AceType::MakeRefPtr<ImageComponent>(InternalResource::ResourceId::LEFT_ARROW_SVG)
453                             : AceType::MakeRefPtr<ImageComponent>(InternalResource::ResourceId::RIGHT_ARROW_SVG);
454     isPreArrow ? calendarController_->SetLeftRowImage(Arrow) : calendarController_->SetRightRowImage(Arrow);
455     auto calendarTheme = theme_->GetCardCalendarTheme();
456     Arrow->SetWidth(calendarTheme.arrowWidth);
457     Arrow->SetHeight(calendarTheme.arrowHeight);
458     std::list<RefPtr<Component>> children;
459     children.emplace_back(Arrow);
460     auto button = AceType::MakeRefPtr<ButtonComponent>(children);
461     button->SetBackgroundColor(Color::TRANSPARENT);
462     button->SetClickedColor(calendarTheme.clickEffectColor);
463     button->SetWidth(calendarTheme.buttonWidth);
464     button->SetHeight(calendarTheme.buttonHeight);
465     button->SetRectRadius(CALENDAR_BUTTON_RADIUS);
466     isPreArrow ? preClickId_ = BackEndEventManager<void()>::GetInstance().GetAvailableMarker()
467                : nextClickId_ = BackEndEventManager<void()>::GetInstance().GetAvailableMarker();
468     isPreArrow ? button->SetClickedEventId(preClickId_) : button->SetClickedEventId(nextClickId_);
469 
470     return button;
471 }
472 
473 } // namespace OHOS::Ace
474