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