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