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 "core/components_ng/pattern/calendar_picker/calendar_dialog_pattern.h"
17 
18 #include "base/i18n/localization.h"
19 #include "base/utils/date_util.h"
20 #include "core/components/calendar/calendar_data_adapter.h"
21 #include "core/components/dialog/dialog_theme.h"
22 #include "core/components_ng/pattern/calendar/calendar_model_ng.h"
23 #include "core/components_ng/pattern/calendar/calendar_month_pattern.h"
24 #include "core/components_ng/pattern/calendar_picker/calendar_dialog_view.h"
25 #include "core/components_ng/pattern/calendar_picker/calendar_picker_pattern.h"
26 #include "core/components_ng/pattern/dialog/dialog_layout_property.h"
27 #include "core/components_ng/pattern/image/image_layout_property.h"
28 #include "core/components_ng/pattern/text/text_layout_property.h"
29 #include "core/components_ng/pattern/button/button_layout_property.h"
30 #include "core/pipeline_ng/pipeline_context.h"
31 
32 namespace OHOS::Ace::NG {
33 namespace {
34 constexpr int32_t TITLE_NODE_INDEX = 0;
35 constexpr int32_t CALENDAR_NODE_INDEX = 1;
36 constexpr int32_t OPTIONS_NODE_INDEX = 2;
37 constexpr int32_t TITLE_LAST_YEAR_BUTTON_NODE_INDEX = 0;
38 constexpr int32_t TITLE_LAST_MONTH_BUTTON_NODE_INDEX = 1;
39 constexpr int32_t TITLE_TEXT_NODE_INDEX = 2;
40 constexpr int32_t TITLE_NEXT_MONTH_BUTTON_NODE_INDEX = 3;
41 constexpr int32_t TITLE_NEXT_YEAR_BUTTON_NODE_INDEX = 4;
42 constexpr int32_t OPTIONS_DIVIDER_NODE_INDEX = 1;
43 constexpr int32_t SWIPER_CHILDREN_SIZE = 3;
44 constexpr int32_t WEEK_DAYS = 7;
45 constexpr int32_t MAX_MONTH = 12;
46 constexpr int32_t MIN_YEAR = 1;
47 constexpr int32_t MAX_YEAR = 5000;
48 constexpr size_t CANCEL_BUTTON_FONT_COLOR_INDEX = 0;
49 constexpr size_t CANCEL_BUTTON_BACKGROUND_COLOR_INDEX = 1;
50 constexpr size_t ACCEPT_BUTTON_FONT_COLOR_INDEX = 2;
51 constexpr size_t ACCEPT_BUTTON_BACKGROUND_COLOR_INDEX = 3;
52 constexpr size_t OPTION_CANCEL_BUTTON_INDEX = 0;
53 constexpr size_t OPTION_ACCEPT_BUTTON_INDEX = 1;
54 } // namespace
OnModifyDone()55 void CalendarDialogPattern::OnModifyDone()
56 {
57     LinearLayoutPattern::OnModifyDone();
58     UpdateCalendarLayout();
59 
60     InitClickEvent();
61     InitOnKeyEvent();
62     InitOnTouchEvent();
63     InitHoverEvent();
64     InitTitleArrowsEvent();
65     InitEntryChangeEvent();
66 
67     UpdateTitleArrowsImage();
68     UpdateOptionsButton();
69     UpdateDialogBackgroundColor();
70     UpdateTitleArrowsColor();
71     UpdateOptionsButtonColor();
72 }
73 
UpdateCalendarLayout()74 void CalendarDialogPattern::UpdateCalendarLayout()
75 {
76     auto entryNode = entryNode_.Upgrade();
77     CHECK_NULL_VOID(entryNode);
78     auto textDirection = entryNode->GetLayoutProperty()->GetNonAutoLayoutDirection();
79     auto calendarNode = GetCalendarFrameNode();
80     CHECK_NULL_VOID(calendarNode);
81     const auto& calendarLayoutProperty = calendarNode->GetLayoutProperty();
82     CHECK_NULL_VOID(calendarLayoutProperty);
83     calendarLayoutProperty->UpdateLayoutDirection(textDirection);
84 
85     auto host = GetHost();
86     CHECK_NULL_VOID(host);
87     auto hostNode = AceType::DynamicCast<FrameNode>(host);
88     CHECK_NULL_VOID(hostNode);
89     auto title = host->GetChildAtIndex(TITLE_NODE_INDEX);
90     CHECK_NULL_VOID(title);
91     auto titleNode = AceType::DynamicCast<FrameNode>(title);
92     CHECK_NULL_VOID(titleNode);
93     auto titleLayoutProperty = titleNode->GetLayoutProperty();
94     CHECK_NULL_VOID(titleLayoutProperty);
95     titleLayoutProperty->UpdateLayoutDirection(textDirection);
96 }
UpdateDialogBackgroundColor()97 void CalendarDialogPattern::UpdateDialogBackgroundColor()
98 {
99     auto host = GetHost();
100     CHECK_NULL_VOID(host);
101     auto wrapperNode = AceType::DynamicCast<FrameNode>(host->GetParent());
102     CHECK_NULL_VOID(wrapperNode);
103     auto pipelineContext = host->GetContext();
104     CHECK_NULL_VOID(pipelineContext);
105     RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
106     auto contentRenderContext = wrapperNode->GetRenderContext();
107     CHECK_NULL_VOID(contentRenderContext);
108     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
109         contentRenderContext->UpdateBackgroundColor(theme->GetDialogBackgroundColor());
110     }
111 }
112 
UpdateTitleArrowsColor()113 void CalendarDialogPattern::UpdateTitleArrowsColor()
114 {
115     auto host = GetHost();
116     CHECK_NULL_VOID(host);
117     auto title = host->GetChildAtIndex(TITLE_NODE_INDEX);
118     CHECK_NULL_VOID(title);
119     auto pipelineContext = host->GetContext();
120     CHECK_NULL_VOID(pipelineContext);
121     RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
122     CHECK_NULL_VOID(theme);
123 
124     for (const auto& child : title->GetChildren()) {
125         CHECK_NULL_VOID(child);
126         if (child->GetTag() == V2::BUTTON_ETS_TAG) {
127             auto buttonNode = AceType::DynamicCast<FrameNode>(child);
128             CHECK_NULL_VOID(buttonNode);
129             buttonNode->GetRenderContext()->UpdateBackgroundColor(Color::TRANSPARENT);
130             buttonNode->MarkModifyDone();
131 
132             auto image = buttonNode->GetChildren().front();
133             CHECK_NULL_VOID(image);
134             auto imageNode = AceType::DynamicCast<FrameNode>(image);
135             auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
136             CHECK_NULL_VOID(imageLayoutProperty);
137             auto imageInfo = imageLayoutProperty->GetImageSourceInfo();
138             imageInfo->SetFillColor(theme->GetEntryArrowColor());
139             imageLayoutProperty->UpdateImageSourceInfo(imageInfo.value());
140             imageNode->MarkModifyDone();
141         }
142     }
143 }
144 
UpdateTitleArrowsImage()145 void CalendarDialogPattern::UpdateTitleArrowsImage()
146 {
147     auto host = GetHost();
148     CHECK_NULL_VOID(host);
149     auto title = host->GetChildAtIndex(TITLE_NODE_INDEX);
150     CHECK_NULL_VOID(title);
151     auto titleNode = AceType::DynamicCast<FrameNode>(title);
152     CHECK_NULL_VOID(titleNode);
153     auto titleLayoutProperty = titleNode->GetLayoutProperty();
154     CHECK_NULL_VOID(titleLayoutProperty);
155     auto textDirection = titleLayoutProperty->GetNonAutoLayoutDirection();
156 
157     auto lastYearNode = AceType::DynamicCast<FrameNode>(title->GetChildAtIndex(TITLE_LAST_YEAR_BUTTON_NODE_INDEX));
158     CHECK_NULL_VOID(lastYearNode);
159     auto lastMonthNode = AceType::DynamicCast<FrameNode>(title->GetChildAtIndex(TITLE_LAST_MONTH_BUTTON_NODE_INDEX));
160     CHECK_NULL_VOID(lastMonthNode);
161     auto nextMonthNode = AceType::DynamicCast<FrameNode>(title->GetChildAtIndex(TITLE_NEXT_MONTH_BUTTON_NODE_INDEX));
162     CHECK_NULL_VOID(nextMonthNode);
163     auto nextYearNode = AceType::DynamicCast<FrameNode>(title->GetChildAtIndex(TITLE_NEXT_YEAR_BUTTON_NODE_INDEX));
164     CHECK_NULL_VOID(nextYearNode);
165 
166     if (textDirection == TextDirection::RTL) {
167         UpdateImage(lastYearNode, InternalResource::ResourceId::IC_PUBLIC_DOUBLE_ARROW_RIGHT_SVG);
168         UpdateImage(lastMonthNode, InternalResource::ResourceId::IC_PUBLIC_ARROW_RIGHT_SVG);
169         UpdateImage(nextMonthNode, InternalResource::ResourceId::IC_PUBLIC_ARROW_LEFT_SVG);
170         UpdateImage(nextYearNode, InternalResource::ResourceId::IC_PUBLIC_DOUBLE_ARROW_LEFT_SVG);
171     } else {
172         UpdateImage(lastYearNode, InternalResource::ResourceId::IC_PUBLIC_DOUBLE_ARROW_LEFT_SVG);
173         UpdateImage(lastMonthNode, InternalResource::ResourceId::IC_PUBLIC_ARROW_LEFT_SVG);
174         UpdateImage(nextMonthNode, InternalResource::ResourceId::IC_PUBLIC_ARROW_RIGHT_SVG);
175         UpdateImage(nextYearNode, InternalResource::ResourceId::IC_PUBLIC_DOUBLE_ARROW_RIGHT_SVG);
176     }
177 }
178 
UpdateImage(const RefPtr<FrameNode> & buttonNode,const InternalResource::ResourceId & resourceId)179 void CalendarDialogPattern::UpdateImage(
180     const RefPtr<FrameNode>& buttonNode, const InternalResource::ResourceId& resourceId)
181 {
182     auto image = buttonNode->GetChildren().front();
183     CHECK_NULL_VOID(image);
184     auto imageNode = AceType::DynamicCast<FrameNode>(image);
185     auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
186     CHECK_NULL_VOID(imageLayoutProperty);
187 
188     ImageSourceInfo imageSourceInfo;
189     imageSourceInfo.SetResourceId(resourceId);
190     imageLayoutProperty->UpdateImageSourceInfo(imageSourceInfo);
191     imageNode->MarkModifyDone();
192 }
193 
UpdateOptionsButton()194 void CalendarDialogPattern::UpdateOptionsButton()
195 {
196     auto host = GetHost();
197     CHECK_NULL_VOID(host);
198     auto options = host->GetChildAtIndex(OPTIONS_NODE_INDEX);
199     CHECK_NULL_VOID(options);
200 
201     size_t buttonIndex = OPTION_CANCEL_BUTTON_INDEX;
202     for (const auto& child : options->GetChildren()) {
203         CHECK_NULL_VOID(child);
204         if (child->GetTag() == V2::BUTTON_ETS_TAG) {
205             auto button = AceType::DynamicCast<FrameNode>(child);
206             CHECK_NULL_VOID(button);
207             auto buttonLayoutProperty = button->GetLayoutProperty<ButtonLayoutProperty>();
208             CHECK_NULL_VOID(buttonLayoutProperty);
209             if (buttonIndex == OPTION_ACCEPT_BUTTON_INDEX) {
210                 buttonLayoutProperty->UpdateLabel(Localization::GetInstance()->GetEntryLetters("common.ok"));
211             } else {
212                 buttonLayoutProperty->UpdateLabel(Localization::GetInstance()->GetEntryLetters("common.cancel"));
213             }
214             button->MarkDirtyNode();
215             buttonIndex++;
216         }
217     }
218 }
219 
UpdateOptionsButtonColor()220 void CalendarDialogPattern::UpdateOptionsButtonColor()
221 {
222     auto host = GetHost();
223     CHECK_NULL_VOID(host);
224     auto options = host->GetChildAtIndex(OPTIONS_NODE_INDEX);
225     CHECK_NULL_VOID(options);
226     auto pipeline = host->GetContext();
227     CHECK_NULL_VOID(pipeline);
228     auto calendarTheme = pipeline->GetTheme<CalendarTheme>();
229     CHECK_NULL_VOID(calendarTheme);
230     auto pickerTheme = pipeline->GetTheme<PickerTheme>();
231     CHECK_NULL_VOID(pickerTheme);
232 
233     size_t buttonIndex = OPTION_CANCEL_BUTTON_INDEX;
234     for (const auto& child : options->GetChildren()) {
235         CHECK_NULL_VOID(child);
236         if (child->GetTag() == V2::BUTTON_ETS_TAG) {
237             auto button = AceType::DynamicCast<FrameNode>(child);
238             CHECK_NULL_VOID(button);
239             bool cancelNotUpdateBGColor =
240                 buttonIndex == OPTION_CANCEL_BUTTON_INDEX && !updateColorFlags[CANCEL_BUTTON_BACKGROUND_COLOR_INDEX];
241             bool acceptNotUpdateBGColor =
242                 buttonIndex == OPTION_ACCEPT_BUTTON_INDEX && !updateColorFlags[ACCEPT_BUTTON_BACKGROUND_COLOR_INDEX];
243             if (!(cancelNotUpdateBGColor || acceptNotUpdateBGColor)) {
244                 auto defaultBGColor = calendarTheme->GetIsButtonTransparent()
245                                           ? Color::TRANSPARENT
246                                           : calendarTheme->GetDialogButtonBackgroundColor();
247                 button->GetRenderContext()->UpdateBackgroundColor(defaultBGColor);
248             }
249             button->MarkModifyDone();
250 
251             auto text = button->GetChildren().front();
252             CHECK_NULL_VOID(text);
253             auto textNode = AceType::DynamicCast<FrameNode>(text);
254             auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
255             CHECK_NULL_VOID(textLayoutProperty);
256             bool cancelNotUpdateFontColor =
257                 buttonIndex == OPTION_CANCEL_BUTTON_INDEX && !updateColorFlags[CANCEL_BUTTON_FONT_COLOR_INDEX];
258             bool acceptNotUpdateFontColor =
259                 buttonIndex == OPTION_ACCEPT_BUTTON_INDEX && !updateColorFlags[ACCEPT_BUTTON_FONT_COLOR_INDEX];
260             if (!(cancelNotUpdateFontColor || acceptNotUpdateFontColor)) {
261                 textLayoutProperty->UpdateTextColor(pickerTheme->GetOptionStyle(true, false).GetTextColor());
262             }
263             textNode->MarkModifyDone();
264 
265             buttonIndex++;
266         }
267     }
268 }
269 
SetOptionsButtonUpdateColorFlags(size_t index,bool isUpdate)270 void CalendarDialogPattern::SetOptionsButtonUpdateColorFlags(size_t index, bool isUpdate)
271 {
272     if (index >= updateColorFlags.size()) {
273         return;
274     }
275 
276     updateColorFlags[index] = isUpdate;
277 }
278 
InitHoverEvent()279 void CalendarDialogPattern::InitHoverEvent()
280 {
281     CHECK_NULL_VOID(!hoverListener_);
282     auto host = GetHost();
283     CHECK_NULL_VOID(host);
284     auto inputHub = host->GetOrCreateInputEventHub();
285     CHECK_NULL_VOID(inputHub);
286 
287     auto mouseCallback = [weak = WeakClaim(this)](const MouseInfo& info) {
288         auto pattern = weak.Upgrade();
289         CHECK_NULL_VOID(pattern);
290         CHECK_NULL_VOID(pattern->GetHoverState());
291         pattern->HandleEntryNodeHoverEvent(
292             pattern->IsInEntryRegion(info.GetGlobalLocation()), info.GetGlobalLocation());
293     };
294     inputHub->SetMouseEvent(std::move(mouseCallback));
295 
296     auto hoverCallback = [weak = WeakClaim(this)](bool state) {
297         auto pattern = weak.Upgrade();
298         CHECK_NULL_VOID(pattern);
299         pattern->SetHoverState(state);
300         if (!state) {
301             Offset location;
302             pattern->HandleEntryNodeHoverEvent(false, location);
303         }
304     };
305     hoverListener_ = MakeRefPtr<InputEvent>(std::move(hoverCallback));
306     inputHub->AddOnHoverEvent(hoverListener_);
307 }
308 
IsInEntryRegion(const Offset & globalLocation)309 bool CalendarDialogPattern::IsInEntryRegion(const Offset& globalLocation)
310 {
311     auto entryNode = entryNode_.Upgrade();
312     CHECK_NULL_RETURN(entryNode, false);
313     return entryNode->GetTransformRectRelativeToWindow().IsInRegion(
314         PointF(globalLocation.GetX(), globalLocation.GetY()));
315 }
316 
HandleEntryNodeHoverEvent(bool state,const Offset & globalLocation)317 void CalendarDialogPattern::HandleEntryNodeHoverEvent(bool state, const Offset& globalLocation)
318 {
319     auto entryNode = entryNode_.Upgrade();
320     CHECK_NULL_VOID(entryNode);
321     auto pattern = entryNode->GetPattern<CalendarPickerPattern>();
322     CHECK_NULL_VOID(pattern);
323     pattern->HandleHoverEvent(state, globalLocation);
324 }
325 
HandleEntryNodeTouchEvent(bool isPressed,const Offset & globalLocation)326 void CalendarDialogPattern::HandleEntryNodeTouchEvent(bool isPressed, const Offset& globalLocation)
327 {
328     auto entryNode = entryNode_.Upgrade();
329     CHECK_NULL_VOID(entryNode);
330     if (IsInEntryRegion(globalLocation)) {
331         auto pattern = entryNode->GetPattern<CalendarPickerPattern>();
332         CHECK_NULL_VOID(pattern);
333         pattern->HandleTouchEvent(isPressed, globalLocation);
334     }
335 }
336 
InitClickEvent()337 void CalendarDialogPattern::InitClickEvent()
338 {
339     auto host = GetHost();
340     CHECK_NULL_VOID(host);
341     auto gesture = host->GetOrCreateGestureEventHub();
342     CHECK_NULL_VOID(gesture);
343     auto clickCallback = [weak = WeakClaim(this)](const GestureEvent& info) {
344         auto pattern = weak.Upgrade();
345         CHECK_NULL_VOID(pattern);
346         pattern->HandleClickEvent(info);
347     };
348     auto onClick = AceType::MakeRefPtr<ClickEvent>(clickCallback);
349     gesture->AddClickEvent(onClick);
350 }
351 
HandleClickEvent(const GestureEvent & info)352 void CalendarDialogPattern::HandleClickEvent(const GestureEvent& info)
353 {
354     auto entryNode = entryNode_.Upgrade();
355     CHECK_NULL_VOID(entryNode);
356     if (IsInEntryRegion(info.GetGlobalLocation())) {
357         auto pattern = entryNode->GetPattern<CalendarPickerPattern>();
358         CHECK_NULL_VOID(pattern);
359         pattern->HandleClickEvent(info.GetGlobalLocation());
360     }
361 }
362 
InitOnTouchEvent()363 void CalendarDialogPattern::InitOnTouchEvent()
364 {
365     if (touchListener_) {
366         return;
367     }
368     auto host = GetHost();
369     CHECK_NULL_VOID(host);
370     auto gesture = host->GetOrCreateGestureEventHub();
371     CHECK_NULL_VOID(gesture);
372     auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
373         auto pattern = weak.Upgrade();
374         CHECK_NULL_VOID(pattern);
375         if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
376             pattern->HandleEntryNodeTouchEvent(true, info.GetTouches().front().GetGlobalLocation());
377             if (!pattern->isFocused_) {
378                 return;
379             }
380             pattern->isFocused_ = false;
381             pattern->isCalendarFirstFocused_ = false;
382             pattern->ClearCalendarFocusedState();
383         }
384         if (info.GetTouches().front().GetTouchType() == TouchType::UP ||
385             info.GetTouches().front().GetTouchType() == TouchType::CANCEL) {
386             pattern->HandleEntryNodeTouchEvent(false, info.GetTouches().front().GetGlobalLocation());
387         }
388     };
389     touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
390     gesture->AddTouchEvent(touchListener_);
391 }
392 
InitOnKeyEvent()393 void CalendarDialogPattern::InitOnKeyEvent()
394 {
395     auto host = GetHost();
396     CHECK_NULL_VOID(host);
397     auto focusHub = host->GetOrCreateFocusHub();
398     CHECK_NULL_VOID(focusHub);
399 
400     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
401         auto pattern = wp.Upgrade();
402         CHECK_NULL_RETURN(pattern, false);
403         if (event.IsNumberKey() && event.action == KeyAction::DOWN) {
404             auto entryNode = pattern->entryNode_.Upgrade();
405             CHECK_NULL_RETURN(entryNode, false);
406             auto entryPattern = entryNode->GetPattern<CalendarPickerPattern>();
407             CHECK_NULL_RETURN(entryPattern, false);
408             entryPattern->HandleNumberKeyEvent(event);
409         }
410         if (pattern->isFocused_ && !pattern->isCalendarFirstFocused_ &&
411             pattern->focusAreaID_ == CALENDAR_NODE_INDEX) {
412             pattern->isCalendarFirstFocused_ = true;
413             pattern->FocusedLastFocusedDay();
414             return true;
415         }
416         if (pattern->isFocused_ && event.action == KeyAction::DOWN) {
417             return pattern->HandleKeyEvent(event);
418         }
419         if (!pattern->isFocused_ && !pattern->hasTabKeyDown_ && event.action == KeyAction::DOWN) {
420             pattern->OnEnterKeyEvent(event);
421         }
422         return false;
423     };
424     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
425 
426     auto blurTask = [weak = WeakClaim(this)]() {
427         auto pattern = weak.Upgrade();
428         CHECK_NULL_VOID(pattern);
429         pattern->isFocused_ = false;
430         pattern->isCalendarFirstFocused_ = false;
431         pattern->ClearCalendarFocusedState();
432     };
433     focusHub->SetOnBlurInternal(std::move(blurTask));
434 
435     auto getInnerPaintRectCallback = [wp = WeakClaim(this)](RoundRect& paintRect) {
436         auto pattern = wp.Upgrade();
437         CHECK_NULL_VOID(pattern);
438         pattern->GetInnerFocusPaintRect(paintRect);
439     };
440     focusHub->SetInnerFocusPaintRectCallback(getInnerPaintRectCallback);
441 }
442 
InitEntryChangeEvent()443 void CalendarDialogPattern::InitEntryChangeEvent()
444 {
445     auto entryNode = entryNode_.Upgrade();
446     CHECK_NULL_VOID(entryNode);
447     auto eventHub = entryNode->GetEventHub<CalendarPickerEventHub>();
448     CHECK_NULL_VOID(eventHub);
449     auto callback = [weak = WeakClaim(this)](const std::string& info) {
450         auto pattern = weak.Upgrade();
451         CHECK_NULL_VOID(pattern);
452         pattern->HandleEntryChange(info);
453     };
454     eventHub->SetInputChangeEvent(std::move(callback));
455     auto layoutChangeEvent = [weak = WeakClaim(this)]() {
456         auto pattern = weak.Upgrade();
457         CHECK_NULL_VOID(pattern);
458         pattern->HandleEntryLayoutChange();
459     };
460     eventHub->SetLayoutChangeEvent(layoutChangeEvent);
461 }
462 
HandleKeyEvent(const KeyEvent & event)463 bool CalendarDialogPattern::HandleKeyEvent(const KeyEvent& event)
464 {
465     auto host = GetHost();
466     CHECK_NULL_RETURN(host, false);
467 
468     if (event.code == KeyCode::KEY_TAB) {
469         return HandleTabKeyEvent(event);
470     }
471     if (focusAreaID_ == CALENDAR_NODE_INDEX) {
472         return HandleCalendarNodeKeyEvent(event);
473     }
474     if (focusAreaID_ != TITLE_NODE_INDEX && focusAreaID_ != OPTIONS_NODE_INDEX) {
475         return false;
476     }
477 
478     auto calendarNode = GetCalendarFrameNode();
479     CHECK_NULL_RETURN(calendarNode, false);
480     auto textDirection = calendarNode->GetLayoutProperty()->GetNonAutoLayoutDirection();
481     switch (event.code) {
482         case KeyCode::KEY_DPAD_LEFT: {
483             if (textDirection == TextDirection::RTL) {
484                 if ((focusAreaID_ == TITLE_NODE_INDEX && focusAreaChildID_ == TITLE_TEXT_NODE_INDEX - 1) ||
485                     (focusAreaID_ == OPTIONS_NODE_INDEX && focusAreaChildID_ == OPTIONS_DIVIDER_NODE_INDEX - 1)) {
486                     focusAreaChildID_++;
487                 }
488                 focusAreaChildID_++;
489                 auto childSize = static_cast<int32_t>(host->GetChildAtIndex(focusAreaID_)->GetChildren().size());
490                 if (focusAreaChildID_ > childSize - 1) {
491                     focusAreaChildID_ = childSize - 1;
492                 }
493             } else {
494                 if ((focusAreaID_ == TITLE_NODE_INDEX && focusAreaChildID_ == TITLE_TEXT_NODE_INDEX + 1) ||
495                     (focusAreaID_ == OPTIONS_NODE_INDEX && focusAreaChildID_ == OPTIONS_DIVIDER_NODE_INDEX + 1)) {
496                     focusAreaChildID_--;
497                 }
498                 focusAreaChildID_--;
499                 if (focusAreaChildID_ < 0) {
500                     focusAreaChildID_ = 0;
501                 }
502             }
503 
504             PaintFocusState();
505             ChangeEntryState();
506             return true;
507         }
508         case KeyCode::KEY_DPAD_RIGHT: {
509             if (textDirection == TextDirection::RTL) {
510                 if ((focusAreaID_ == TITLE_NODE_INDEX && focusAreaChildID_ == TITLE_TEXT_NODE_INDEX + 1) ||
511                     (focusAreaID_ == OPTIONS_NODE_INDEX && focusAreaChildID_ == OPTIONS_DIVIDER_NODE_INDEX + 1)) {
512                     focusAreaChildID_--;
513                 }
514 
515                 focusAreaChildID_--;
516                 if (focusAreaChildID_ < 0) {
517                     focusAreaChildID_ = 0;
518                 }
519             } else {
520                 if ((focusAreaID_ == TITLE_NODE_INDEX && focusAreaChildID_ == TITLE_TEXT_NODE_INDEX - 1) ||
521                     (focusAreaID_ == OPTIONS_NODE_INDEX && focusAreaChildID_ == OPTIONS_DIVIDER_NODE_INDEX - 1)) {
522                     focusAreaChildID_++;
523                 }
524 
525                 focusAreaChildID_++;
526                 auto childSize = static_cast<int32_t>(host->GetChildAtIndex(focusAreaID_)->GetChildren().size());
527                 if (focusAreaChildID_ > childSize - 1) {
528                     focusAreaChildID_ = childSize - 1;
529                 }
530             }
531 
532             PaintFocusState();
533             ChangeEntryState();
534             return true;
535         }
536         case KeyCode::KEY_MOVE_HOME: {
537             focusAreaChildID_ = 0;
538             PaintFocusState();
539             ChangeEntryState();
540             return true;
541         }
542         case KeyCode::KEY_MOVE_END: {
543             focusAreaChildID_ = static_cast<int32_t>(host->GetChildAtIndex(focusAreaID_)->GetChildren().size()) - 1;
544             PaintFocusState();
545             ChangeEntryState();
546             return true;
547         }
548         case KeyCode::KEY_SPACE:
549         case KeyCode::KEY_NUMPAD_ENTER:
550         case KeyCode::KEY_ENTER: {
551             return ActClick(focusAreaID_, focusAreaChildID_);
552         }
553         default:
554             break;
555     }
556     return false;
557 }
558 
HandleCalendarNodeKeyEvent(const KeyEvent & event)559 bool CalendarDialogPattern::HandleCalendarNodeKeyEvent(const KeyEvent& event)
560 {
561     auto swiperPattern = GetSwiperPattern();
562     CHECK_NULL_RETURN(swiperPattern, false);
563     auto swiperController = swiperPattern->GetSwiperController();
564     CHECK_NULL_RETURN(swiperController, false);
565     swiperController->FinishAnimation();
566     auto calendarPattern = GetCalendarPattern();
567     CHECK_NULL_RETURN(calendarPattern, false);
568     ObtainedMonth currentMonthData = calendarPattern->GetCurrentMonthData();
569     auto calendarNode = GetCalendarFrameNode();
570     CHECK_NULL_RETURN(calendarNode, false);
571     auto textDirection = calendarNode->GetLayoutProperty()->GetNonAutoLayoutDirection();
572     int32_t focusedDayIndex = GetIndexByFocusedDay();
573 
574     switch (event.code) {
575         case KeyCode::KEY_DPAD_LEFT: {
576             if (textDirection == TextDirection::RTL) {
577                 focusedDayIndex++;
578             } else {
579                 focusedDayIndex--;
580             }
581 
582             PaintMonthFocusState(focusedDayIndex);
583             return true;
584         }
585         case KeyCode::KEY_DPAD_RIGHT: {
586             if (textDirection == TextDirection::RTL) {
587                 focusedDayIndex--;
588             } else {
589                 focusedDayIndex++;
590             }
591 
592             PaintMonthFocusState(focusedDayIndex);
593             return true;
594         }
595         case KeyCode::KEY_DPAD_UP: {
596             focusedDayIndex -= WEEK_DAYS;
597             if (IsIndexInCurrentMonth(focusedDayIndex, currentMonthData)) {
598                 focusedDay_ = currentMonthData.days[focusedDayIndex];
599                 PaintCurrentMonthFocusState();
600                 return true;
601             }
602             break;
603         }
604         case KeyCode::KEY_DPAD_DOWN: {
605             focusedDayIndex += WEEK_DAYS;
606             if (IsIndexInCurrentMonth(focusedDayIndex, currentMonthData)) {
607                 focusedDay_ = currentMonthData.days[focusedDayIndex];
608                 PaintCurrentMonthFocusState();
609                 return true;
610             }
611             break;
612         }
613         case KeyCode::KEY_MOVE_HOME: {
614             auto it = std::find_if(currentMonthData.days.begin(), currentMonthData.days.end(),
615                 [currentMonthData](const CalendarDay& day) {
616                     return day.month.year == currentMonthData.year && day.month.month == currentMonthData.month;
617                 });
618             if (it != currentMonthData.days.end()) {
619                 focusedDay_ = currentMonthData.days[it->index];
620                 PaintCurrentMonthFocusState();
621                 return true;
622             }
623             break;
624         }
625         case KeyCode::KEY_MOVE_END: {
626             auto it = std::find_if(currentMonthData.days.rbegin(), currentMonthData.days.rend(),
627                 [currentMonthData](const CalendarDay& day) {
628                     return day.month.year == currentMonthData.year && day.month.month == currentMonthData.month;
629                 });
630             if (it != currentMonthData.days.rend()) {
631                 focusedDay_ = currentMonthData.days[it->index];
632                 PaintCurrentMonthFocusState();
633                 return true;
634             }
635             break;
636         }
637         case KeyCode::KEY_SPACE:
638         case KeyCode::KEY_NUMPAD_ENTER:
639         case KeyCode::KEY_ENTER: {
640             PickerDate selectedDay(focusedDay_.month.year, focusedDay_.month.month, focusedDay_.day);
641             calendarPattern->SetSelectedDay(selectedDay);
642             auto calendarFrameNode = GetCalendarFrameNode();
643             CHECK_NULL_RETURN(calendarFrameNode, false);
644             calendarFrameNode->MarkModifyDone();
645             FireChangeByKeyEvent(selectedDay);
646             return true;
647         }
648         default:
649             break;
650     }
651     return false;
652 }
653 
PaintMonthFocusState(int32_t focusedDayIndex)654 void CalendarDialogPattern::PaintMonthFocusState(int32_t focusedDayIndex)
655 {
656     auto calendarPattern = GetCalendarPattern();
657     CHECK_NULL_VOID(calendarPattern);
658     ObtainedMonth currentMonthData = calendarPattern->GetCurrentMonthData();
659     if (IsIndexInCurrentMonth(focusedDayIndex, currentMonthData)) {
660         focusedDay_ = currentMonthData.days[focusedDayIndex];
661         PaintCurrentMonthFocusState();
662     } else {
663         PaintNonCurrentMonthFocusState(focusedDayIndex);
664     }
665 }
666 
IsIndexInCurrentMonth(int32_t focusedDayIndex,const ObtainedMonth & currentMonthData)667 bool CalendarDialogPattern::IsIndexInCurrentMonth(int32_t focusedDayIndex, const ObtainedMonth& currentMonthData)
668 {
669     return focusedDayIndex >= 0 && focusedDayIndex < static_cast<int32_t>(currentMonthData.days.size()) &&
670            currentMonthData.days[focusedDayIndex].month.year == currentMonthData.year &&
671            currentMonthData.days[focusedDayIndex].month.month == currentMonthData.month;
672 }
673 
HandleTabKeyEvent(const KeyEvent & event)674 bool CalendarDialogPattern::HandleTabKeyEvent(const KeyEvent& event)
675 {
676     hasTabKeyDown_ = true;
677     auto host = GetHost();
678     CHECK_NULL_RETURN(host, false);
679     auto childSize = static_cast<int32_t>(host->GetChildren().size());
680     if (event.IsShiftWith(KeyCode::KEY_TAB)) {
681         focusAreaID_ = (focusAreaID_ + childSize - 1) % childSize;
682     } else {
683         focusAreaID_ = (focusAreaID_ + 1) % childSize;
684     }
685 
686     if (focusAreaID_ == CALENDAR_NODE_INDEX) {
687         isCalendarFirstFocused_ = true;
688         FocusedLastFocusedDay();
689     } else {
690         ClearCalendarFocusedState();
691         focusAreaChildID_ = 0;
692         PaintFocusState();
693     }
694     ChangeEntryState();
695     return true;
696 }
697 
FocusedLastFocusedDay()698 void CalendarDialogPattern::FocusedLastFocusedDay()
699 {
700     auto calendarPattern = GetCalendarPattern();
701     CHECK_NULL_VOID(calendarPattern);
702     if (focusedDay_.day < 0) {
703         PickerDate selectedDay = calendarPattern->GetSelectedDay();
704         focusedDay_.month.year = static_cast<int32_t>(selectedDay.GetYear());
705         focusedDay_.month.month = static_cast<int32_t>(selectedDay.GetMonth());
706         focusedDay_.day = static_cast<int32_t>(selectedDay.GetDay());
707     }
708 
709     ObtainedMonth currentMonthData = calendarPattern->GetCurrentMonthData();
710     if (currentMonthData.year == focusedDay_.month.year && currentMonthData.month == focusedDay_.month.month) {
711         PaintCurrentMonthFocusState();
712         return;
713     }
714     ObtainedMonth monthData;
715     GetCalendarMonthData(focusedDay_.month.year, focusedDay_.month.month, monthData);
716     auto isPrev = currentMonthData.year > focusedDay_.month.year ||
717         (currentMonthData.year == focusedDay_.month.year && currentMonthData.month > focusedDay_.month.month);
718     auto it = std::find_if(monthData.days.begin(), monthData.days.end(),
719         [this](CalendarDay day) {
720             return day.day == focusedDay_.day && day.month == focusedDay_.month;
721         });
722     if (it != monthData.days.end()) {
723         focusedDay_ = *it;
724         it->isKeyFocused = true;
725         calendarPattern->SetDialogClickEventState(true);
726         UpdateSwiperNode(monthData, isPrev);
727     }
728 }
729 
UpdateSwiperNode(const ObtainedMonth & monthData,bool isPrev)730 void CalendarDialogPattern::UpdateSwiperNode(const ObtainedMonth& monthData, bool isPrev)
731 {
732     auto calendarPattern = GetCalendarPattern();
733     CHECK_NULL_VOID(calendarPattern);
734     auto swiperPattern = GetSwiperPattern();
735     CHECK_NULL_VOID(swiperPattern);
736     int32_t currentIndex = swiperPattern->GetCurrentIndex();
737     int32_t targetIndex = (currentIndex + SWIPER_CHILDREN_SIZE + (isPrev ? -1 : 1)) % SWIPER_CHILDREN_SIZE;
738 
739     auto swiperNode = GetSwiperFrameNode();
740     CHECK_NULL_VOID(swiperNode);
741     auto monthFrameNode = AceType::DynamicCast<FrameNode>(swiperNode->GetChildAtIndex(targetIndex));
742     CHECK_NULL_VOID(monthFrameNode);
743     auto monthPattern = monthFrameNode->GetPattern<CalendarMonthPattern>();
744     CHECK_NULL_VOID(monthPattern);
745 
746     if (isPrev) {
747         monthPattern->SetMonthData(monthData, MonthState::PRE_MONTH);
748         calendarPattern->SetPreMonthData(monthData);
749     } else {
750         monthPattern->SetMonthData(monthData, MonthState::NEXT_MONTH);
751         calendarPattern->SetNextMonthData(monthData);
752     }
753 
754     monthFrameNode->MarkModifyDone();
755     monthFrameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
756     if (isPrev) {
757         swiperPattern->ShowPrevious();
758     } else {
759         swiperPattern->ShowNext();
760     }
761 }
762 
UpdateSwiperNodeFocusedDay(const CalendarDay & focusedDay,bool isPrev)763 void CalendarDialogPattern::UpdateSwiperNodeFocusedDay(const CalendarDay& focusedDay, bool isPrev)
764 {
765     auto calendarPattern = GetCalendarPattern();
766     CHECK_NULL_VOID(calendarPattern);
767     ObtainedMonth targetMonthData = isPrev ? calendarPattern->GetPreMonthData() : calendarPattern->GetNextMonthData();
768     if (focusedDay.month.year != targetMonthData.year || focusedDay.month.month != targetMonthData.month) {
769         return;
770     }
771 
772     auto it = std::find_if(targetMonthData.days.begin(), targetMonthData.days.end(),
773         [focusedDay](CalendarDay day) {
774             return day.day == focusedDay.day && day.month == focusedDay.month;
775         });
776     if (it != targetMonthData.days.end()) {
777         focusedDay_ = *it;
778         it->isKeyFocused = true;
779         auto swiperPattern = GetSwiperPattern();
780         CHECK_NULL_VOID(swiperPattern);
781         if (isPrev) {
782             calendarPattern->SetPreMonthData(targetMonthData);
783             swiperPattern->ShowPrevious();
784         } else {
785             calendarPattern->SetNextMonthData(targetMonthData);
786             swiperPattern->ShowNext();
787         }
788     }
789 }
790 
ActClick(int32_t focusAreaID,int32_t focusAreaChildID)791 bool CalendarDialogPattern::ActClick(int32_t focusAreaID, int32_t focusAreaChildID)
792 {
793     auto host = GetHost();
794     CHECK_NULL_RETURN(host, false);
795     auto focusArea = host->GetChildAtIndex(focusAreaID);
796     CHECK_NULL_RETURN(focusArea, false);
797     auto child = focusArea->GetChildAtIndex(focusAreaChildID);
798     CHECK_NULL_RETURN(child, false);
799     auto childFrameNode = AceType::DynamicCast<FrameNode>(child);
800     CHECK_NULL_RETURN(childFrameNode, false);
801     auto gestureEventHub = childFrameNode->GetOrCreateGestureEventHub();
802     return gestureEventHub->ActClick();
803 }
804 
PaintCurrentMonthFocusState()805 void CalendarDialogPattern::PaintCurrentMonthFocusState()
806 {
807     auto calendarFrameNode = GetCalendarFrameNode();
808     CHECK_NULL_VOID(calendarFrameNode);
809     auto calendarPattern = calendarFrameNode->GetPattern<CalendarPattern>();
810     CHECK_NULL_VOID(calendarPattern);
811     ObtainedMonth currentMonthData = calendarPattern->GetCurrentMonthData();
812     for (auto& day : currentMonthData.days) {
813         day.isKeyFocused = currentMonthData.year == focusedDay_.month.year &&
814             currentMonthData.month == focusedDay_.month.month &&
815             day.month == focusedDay_.month && day.day == focusedDay_.day;
816     }
817     calendarPattern->SetCurrentMonthData(currentMonthData);
818     calendarFrameNode->MarkModifyDone();
819 }
820 
PaintNonCurrentMonthFocusState(int32_t focusedDayIndex)821 void CalendarDialogPattern::PaintNonCurrentMonthFocusState(int32_t focusedDayIndex)
822 {
823     auto calendarPattern = GetCalendarPattern();
824     CHECK_NULL_VOID(calendarPattern);
825     auto swiperPattern = GetSwiperPattern();
826     CHECK_NULL_VOID(swiperPattern);
827 
828     ObtainedMonth currentMonthData = calendarPattern->GetCurrentMonthData();
829     ObtainedMonth preMonthData = calendarPattern->GetPreMonthData();
830     ObtainedMonth nextMonthData = calendarPattern->GetNextMonthData();
831 
832     for (auto& day : currentMonthData.days) {
833         day.isKeyFocused = false;
834     }
835     calendarPattern->SetCurrentMonthData(currentMonthData);
836     calendarPattern->SetDialogClickEventState(true);
837     if (focusedDayIndex == -1) {
838         focusedDay_ = preMonthData.days[preMonthData.days.size() ? preMonthData.days.size() - 1 : 0];
839         preMonthData.days[preMonthData.days.size() ? preMonthData.days.size() - 1 : 0].isKeyFocused = true;
840         calendarPattern->SetPreMonthData(preMonthData);
841         swiperPattern->ShowPrevious();
842         return;
843     } else if (focusedDayIndex == static_cast<int32_t>(currentMonthData.days.size())) {
844         focusedDay_ = nextMonthData.days[0];
845         nextMonthData.days[0].isKeyFocused = true;
846         calendarPattern->SetNextMonthData(nextMonthData);
847         swiperPattern->ShowNext();
848         return;
849     }
850     UpdateNonCurrentMonthFocusedDay(focusedDayIndex);
851 }
852 
UpdateNonCurrentMonthFocusedDay(int32_t focusedDayIndex)853 void CalendarDialogPattern::UpdateNonCurrentMonthFocusedDay(int32_t focusedDayIndex)
854 {
855     auto calendarPattern = GetCalendarPattern();
856     CHECK_NULL_VOID(calendarPattern);
857 
858     ObtainedMonth currentMonthData = calendarPattern->GetCurrentMonthData();
859     ObtainedMonth preMonthData = calendarPattern->GetPreMonthData();
860     ObtainedMonth nextMonthData = calendarPattern->GetNextMonthData();
861 
862     if (focusedDayIndex < 0 || focusedDayIndex >= static_cast<int32_t>(currentMonthData.days.size())) {
863         return;
864     }
865 
866     CalendarDay focusedDay = currentMonthData.days[focusedDayIndex];
867     if (focusedDay.month.year == preMonthData.year && focusedDay.month.month == preMonthData.month) {
868         return UpdateSwiperNodeFocusedDay(focusedDay, true);
869     }
870 
871     if (focusedDay.month.year == nextMonthData.year && focusedDay.month.month == nextMonthData.month) {
872         return UpdateSwiperNodeFocusedDay(focusedDay, false);
873     }
874 }
875 
PaintFocusState()876 void CalendarDialogPattern::PaintFocusState()
877 {
878     auto host = GetHost();
879     CHECK_NULL_VOID(host);
880     RoundRect focusRect;
881     GetInnerFocusPaintRect(focusRect);
882     auto focusHub = host->GetFocusHub();
883     CHECK_NULL_VOID(focusHub);
884     focusHub->PaintInnerFocusState(focusRect);
885     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
886 }
887 
GetInnerFocusPaintRect(RoundRect & paintRect)888 void CalendarDialogPattern::GetInnerFocusPaintRect(RoundRect& paintRect)
889 {
890     isFocused_ = true;
891     if (focusAreaID_ == CALENDAR_NODE_INDEX) {
892         paintRect.SetRect(RectF());
893         return;
894     }
895     auto host = GetHost();
896     CHECK_NULL_VOID(host);
897     auto focusArea = DynamicCast<FrameNode>(host->GetChildAtIndex(focusAreaID_));
898     CHECK_NULL_VOID(focusArea);
899     auto focusGeometryNode = focusArea->GetGeometryNode();
900     CHECK_NULL_VOID(focusGeometryNode);
901     auto focusAreaOffset = focusGeometryNode->GetFrameOffset();
902     auto child = DynamicCast<FrameNode>(focusArea->GetChildAtIndex(focusAreaChildID_));
903     CHECK_NULL_VOID(child);
904     auto childOffset = child->GetGeometryNode()->GetFrameOffset() + focusAreaOffset;
905     auto childSize = child->GetGeometryNode()->GetFrameSize();
906     paintRect.SetRect(RectF(childOffset, childSize));
907     auto renderContext = child->GetRenderContext();
908     CHECK_NULL_VOID(renderContext);
909     auto radius = renderContext->GetBorderRadius();
910     if (radius.has_value()) {
911         if (radius->radiusTopLeft.has_value()) {
912             paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_LEFT_POS,
913                 static_cast<float>(radius->radiusTopLeft->ConvertToPx()),
914                 static_cast<float>(radius->radiusTopLeft->ConvertToPx()));
915         }
916         if (radius->radiusTopRight.has_value()) {
917             paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_RIGHT_POS,
918                 static_cast<float>(radius->radiusTopRight->ConvertToPx()),
919                 static_cast<float>(radius->radiusTopRight->ConvertToPx()));
920         }
921         if (radius->radiusBottomLeft.has_value()) {
922             paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_LEFT_POS,
923                 static_cast<float>(radius->radiusBottomLeft->ConvertToPx()),
924                 static_cast<float>(radius->radiusBottomLeft->ConvertToPx()));
925         }
926         if (radius->radiusBottomRight.has_value()) {
927             paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_RIGHT_POS,
928                 static_cast<float>(radius->radiusBottomRight->ConvertToPx()),
929                 static_cast<float>(radius->radiusBottomRight->ConvertToPx()));
930         }
931     }
932     ChangeEntryState();
933 }
934 
ClearCalendarFocusedState()935 void CalendarDialogPattern::ClearCalendarFocusedState()
936 {
937     auto calendarFrameNode = GetCalendarFrameNode();
938     CHECK_NULL_VOID(calendarFrameNode);
939     auto calendarPattern = calendarFrameNode->GetPattern<CalendarPattern>();
940     CHECK_NULL_VOID(calendarPattern);
941     ObtainedMonth currentMonthData = calendarPattern->GetCurrentMonthData();
942     for (auto& day : currentMonthData.days) {
943         day.isKeyFocused = false;
944     }
945     calendarPattern->SetCurrentMonthData(currentMonthData);
946     calendarFrameNode->MarkModifyDone();
947 }
948 
ChangeEntryState()949 void CalendarDialogPattern::ChangeEntryState()
950 {
951     auto entryNode = entryNode_.Upgrade();
952     CHECK_NULL_VOID(entryNode);
953     auto enrtyPattern = entryNode->GetPattern<CalendarPickerPattern>();
954     CHECK_NULL_VOID(enrtyPattern);
955     if (focusAreaID_ == TITLE_NODE_INDEX) {
956         if (focusAreaChildID_ == TITLE_LAST_YEAR_BUTTON_NODE_INDEX ||
957             focusAreaChildID_ == TITLE_NEXT_YEAR_BUTTON_NODE_INDEX) {
958             enrtyPattern->SetSelectedType(CalendarPickerSelectedType::YEAR);
959         } else if (focusAreaChildID_ == TITLE_LAST_MONTH_BUTTON_NODE_INDEX ||
960                    focusAreaChildID_ == TITLE_NEXT_MONTH_BUTTON_NODE_INDEX) {
961             enrtyPattern->SetSelectedType(CalendarPickerSelectedType::MONTH);
962         }
963     } else if (focusAreaID_ == CALENDAR_NODE_INDEX) {
964         enrtyPattern->SetSelectedType(CalendarPickerSelectedType::DAY);
965     }
966 }
967 
InitTitleArrowsEvent()968 void CalendarDialogPattern::InitTitleArrowsEvent()
969 {
970     auto host = GetHost();
971     CHECK_NULL_VOID(host);
972     auto title = host->GetChildAtIndex(TITLE_NODE_INDEX);
973     CHECK_NULL_VOID(title);
974     for (const auto& child : title->GetChildren()) {
975         CHECK_NULL_VOID(child);
976         if (child->GetTag() == V2::TEXT_ETS_TAG) {
977             continue;
978         }
979         int32_t childIndex = title->GetChildIndex(child);
980         auto buttonNode = AceType::DynamicCast<FrameNode>(child);
981         CHECK_NULL_VOID(buttonNode);
982         auto event = [childIndex, wp = WeakClaim(this)](GestureEvent& /* info */) {
983             auto pattern = wp.Upgrade();
984             if (pattern) {
985                 pattern->HandleTitleArrowsClickEvent(childIndex);
986             }
987         };
988 
989         auto buttonNodeId = buttonNode->GetId();
990         if (clickEvents_.find(buttonNodeId) == clickEvents_.end()) {
991             auto gestureHub = buttonNode->GetOrCreateGestureEventHub();
992             CHECK_NULL_VOID(gestureHub);
993             auto clickEvent = AceType::MakeRefPtr<ClickEvent>(std::move(event));
994             clickEvents_[buttonNodeId] = clickEvent;
995             gestureHub->AddClickEvent(clickEvent);
996         }
997     }
998 }
999 
HandleTitleArrowsClickEvent(int32_t nodeIndex)1000 void CalendarDialogPattern::HandleTitleArrowsClickEvent(int32_t nodeIndex)
1001 {
1002     auto swiperPattern = GetSwiperPattern();
1003     CHECK_NULL_VOID(swiperPattern);
1004     swiperPattern->GetSwiperController()->FinishAnimation();
1005 
1006     auto calendarPattern = GetCalendarPattern();
1007     CHECK_NULL_VOID(calendarPattern);
1008     ObtainedMonth currentObtainedMonth = calendarPattern->GetCurrentMonthData();
1009     CalendarMonth currentMonth { .year = currentObtainedMonth.year, .month = currentObtainedMonth.month };
1010 
1011     calendarPattern->SetDialogClickEventState(true);
1012     switch (nodeIndex) {
1013         case TITLE_LAST_YEAR_BUTTON_NODE_INDEX: {
1014             currentMonth.year = currentMonth.year == MIN_YEAR ? MAX_YEAR : currentMonth.year - 1;
1015             ObtainedMonth monthData;
1016             GetCalendarMonthData(currentMonth.year, currentMonth.month, monthData);
1017             UpdateSwiperNode(monthData, true);
1018             break;
1019         }
1020         case TITLE_LAST_MONTH_BUTTON_NODE_INDEX: {
1021             swiperPattern->ShowPrevious();
1022             break;
1023         }
1024         case TITLE_NEXT_MONTH_BUTTON_NODE_INDEX: {
1025             swiperPattern->ShowNext();
1026             break;
1027         }
1028         case TITLE_NEXT_YEAR_BUTTON_NODE_INDEX: {
1029             currentMonth.year = currentMonth.year == MAX_YEAR ? MIN_YEAR : currentMonth.year + 1;
1030             ObtainedMonth monthData;
1031             GetCalendarMonthData(currentMonth.year, currentMonth.month, monthData);
1032             UpdateSwiperNode(monthData, false);
1033             break;
1034         }
1035         default:
1036             break;
1037     }
1038 }
1039 
GetCalendarMonthData(int32_t year,int32_t month,ObtainedMonth & calendarMonthData)1040 void CalendarDialogPattern::GetCalendarMonthData(int32_t year, int32_t month, ObtainedMonth& calendarMonthData)
1041 {
1042     calendarMonthData.year = year;
1043     calendarMonthData.month = month;
1044     calendarMonthData.firstDayIndex = 0;
1045 
1046     CalendarMonth currentMonth { .year = year, .month = month };
1047 
1048     int32_t currentMonthMaxDay = static_cast<int32_t>(PickerDate::GetMaxDay(year, month));
1049     int32_t preMonthMaxDay =
1050         static_cast<int32_t>(PickerDate::GetMaxDay(GetLastMonth(currentMonth).year, GetLastMonth(currentMonth).month));
1051     int32_t preMonthDaysCount = (Date::CalculateWeekDay(year, month, 1) + 1) % WEEK_DAYS;
1052     int32_t nextMonthDaysCount = 6 - ((Date::CalculateWeekDay(year, month, currentMonthMaxDay) + 1) % WEEK_DAYS);
1053 
1054     int32_t index = 0;
1055     for (int32_t i = 0; i < preMonthDaysCount; i++, index++) {
1056         calendarMonthData.days.emplace_back(CalendarDay { .index = index,
1057             .day = preMonthMaxDay - preMonthDaysCount + index + 1,
1058             .month = GetLastMonth(currentMonth) });
1059     }
1060 
1061     for (int32_t i = 0; i < currentMonthMaxDay; i++, index++) {
1062         calendarMonthData.days.emplace_back(CalendarDay { .index = index, .day = i + 1, .month = currentMonth });
1063     }
1064 
1065     for (int32_t i = 0; i < nextMonthDaysCount; i++, index++) {
1066         calendarMonthData.days.emplace_back(
1067             CalendarDay { .index = index, .day = i + 1, .month = GetNextMonth(currentMonth) });
1068     }
1069 
1070     auto calendarPattern = GetCalendarPattern();
1071     CHECK_NULL_VOID(calendarPattern);
1072     PickerDate selectedDay = calendarPattern->GetSelectedDay();
1073     for (size_t i = 0; i < calendarMonthData.days.size(); i++) {
1074         calendarMonthData.days[i].isKeyFocused = isFocused_
1075                                                      ? ((focusedDay_.month.year == calendarMonthData.year) &&
1076                                                            (focusedDay_.month.month == calendarMonthData.month) &&
1077                                                            (focusedDay_.month == calendarMonthData.days[i].month) &&
1078                                                            (focusedDay_.day == calendarMonthData.days[i].day))
1079                                                      : false;
1080         calendarMonthData.days[i].isSelected =
1081             (selectedDay.GetYear() == static_cast<uint32_t>(calendarMonthData.year)) &&
1082             (selectedDay.GetMonth() == static_cast<uint32_t>(calendarMonthData.month)) &&
1083             (selectedDay.GetYear() == static_cast<uint32_t>(calendarMonthData.days[i].month.year)) &&
1084             (selectedDay.GetMonth() == static_cast<uint32_t>(calendarMonthData.days[i].month.month)) &&
1085             (selectedDay.GetDay() == static_cast<uint32_t>(calendarMonthData.days[i].day));
1086     }
1087 }
1088 
AddHotZoneRect()1089 void CalendarDialogPattern::AddHotZoneRect()
1090 {
1091     auto entryNode = entryNode_.Upgrade();
1092     CHECK_NULL_VOID(entryNode);
1093     auto rect = entryNode->GetPaintRectWithTransform();
1094     DimensionRect hotZoneRegion;
1095     hotZoneRegion.SetSize(DimensionSize(
1096         Dimension(rect.Width()), Dimension(rect.Height())));
1097     hotZoneRegion.SetOffset(DimensionOffset(
1098         Dimension(rect.Left() - dialogOffset_.GetX()), Dimension(rect.Top() - dialogOffset_.GetY())));
1099 
1100     auto host = GetHost();
1101     CHECK_NULL_VOID(host);
1102     auto geometryNode = host->GetGeometryNode();
1103     CHECK_NULL_VOID(geometryNode);
1104     DimensionRect hotZoneRegionHost;
1105     hotZoneRegionHost.SetSize(DimensionSize(
1106         Dimension(geometryNode->GetFrameRect().Width()), Dimension(geometryNode->GetFrameRect().Height())));
1107 
1108     host->AddHotZoneRect(hotZoneRegion);
1109     host->AddHotZoneRect(hotZoneRegionHost);
1110 }
1111 
FireChangeByKeyEvent(PickerDate & selectedDay)1112 void CalendarDialogPattern::FireChangeByKeyEvent(PickerDate& selectedDay)
1113 {
1114     auto calendarNode = GetCalendarFrameNode();
1115     CHECK_NULL_VOID(calendarNode);
1116 
1117     auto swiperNode = AceType::DynamicCast<FrameNode>(calendarNode->GetFirstChild());
1118     CHECK_NULL_VOID(swiperNode);
1119     auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
1120     CHECK_NULL_VOID(swiperPattern);
1121     auto monthNode = AceType::DynamicCast<FrameNode>(swiperNode->GetChildAtIndex(swiperPattern->GetCurrentIndex()));
1122     CHECK_NULL_VOID(monthNode);
1123     auto eventHub = monthNode->GetEventHub<CalendarEventHub>();
1124     CHECK_NULL_VOID(eventHub);
1125     eventHub->UpdateSelectedChangeEvent(selectedDay.ToString(true));
1126 }
1127 
GetNextMonth(const CalendarMonth & calendarMonth)1128 CalendarMonth CalendarDialogPattern::GetNextMonth(const CalendarMonth& calendarMonth)
1129 {
1130     CalendarMonth nextMonth = calendarMonth;
1131     if (calendarMonth.month + 1 > MAX_MONTH) {
1132         nextMonth.month = 1;
1133         nextMonth.year = nextMonth.year == MAX_YEAR ? MIN_YEAR : nextMonth.year + 1;
1134     } else {
1135         ++nextMonth.month;
1136     }
1137     return nextMonth;
1138 }
1139 
GetLastMonth(const CalendarMonth & calendarMonth)1140 CalendarMonth CalendarDialogPattern::GetLastMonth(const CalendarMonth& calendarMonth)
1141 {
1142     CalendarMonth lastMonth = calendarMonth;
1143     if (calendarMonth.month - 1 <= 0) {
1144         lastMonth.month = MAX_MONTH;
1145         lastMonth.year = lastMonth.year == MIN_YEAR ? MAX_YEAR : lastMonth.year - 1;
1146     } else {
1147         --lastMonth.month;
1148     }
1149     return lastMonth;
1150 }
1151 
GetIndexByFocusedDay()1152 int32_t CalendarDialogPattern::GetIndexByFocusedDay()
1153 {
1154     ObtainedMonth monthData;
1155     GetCalendarMonthData(focusedDay_.month.year, focusedDay_.month.month, monthData);
1156     auto it = std::find_if(monthData.days.begin(), monthData.days.end(),
1157         [this](CalendarDay day) {
1158             return day.day == focusedDay_.day && day.month == focusedDay_.month;
1159         });
1160     if (it != monthData.days.end()) {
1161         return it->index;
1162     }
1163     return -1;
1164 }
1165 
HandleEntryLayoutChange()1166 void CalendarDialogPattern::HandleEntryLayoutChange()
1167 {
1168     auto entryNode = entryNode_.Upgrade();
1169     CHECK_NULL_VOID(entryNode);
1170     auto host = GetHost();
1171     CHECK_NULL_VOID(host);
1172     auto wrapperNode = host->GetParent();
1173     CHECK_NULL_VOID(wrapperNode);
1174     auto dialogNode = AceType::DynamicCast<FrameNode>(wrapperNode->GetParent());
1175     CHECK_NULL_VOID(dialogNode);
1176     auto dialogLayoutProp = dialogNode->GetLayoutProperty<DialogLayoutProperty>();
1177     CHECK_NULL_VOID(dialogLayoutProp);
1178     auto pattern = entryNode->GetPattern<CalendarPickerPattern>();
1179     CHECK_NULL_VOID(pattern);
1180     dialogLayoutProp->UpdateDialogOffset(DimensionOffset(pattern->CalculateDialogOffset()));
1181     dialogOffset_ = pattern->CalculateDialogOffset();
1182     dialogNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1183     isFirstAddhotZoneRect_ = false;
1184 }
1185 
HandleEntryChange(const std::string & info)1186 void CalendarDialogPattern::HandleEntryChange(const std::string& info)
1187 {
1188     auto calendarNode = GetCalendarFrameNode();
1189     CHECK_NULL_VOID(calendarNode);
1190     auto calendarPattern = calendarNode->GetPattern<CalendarPattern>();
1191     CHECK_NULL_VOID(calendarPattern);
1192 
1193     auto calendarJson = JsonUtil::ParseJsonString(calendarPattern->GetSelectDate());
1194     auto entryJson = JsonUtil::ParseJsonString(info);
1195     auto entryYear = entryJson->GetUInt("year");
1196     auto entryMonth = entryJson->GetUInt("month");
1197     auto entryDay = entryJson->GetUInt("day");
1198 
1199     PickerDate selectedDay = PickerDate(entryYear, entryMonth, entryDay);
1200     if (entryYear != calendarJson->GetUInt("year") || entryMonth != calendarJson->GetUInt("month")) {
1201         CalendarData calendarData;
1202         CalendarMonth calendarMonth { .year = entryYear, .month = entryMonth };
1203 
1204         GetCalendarMonthData(entryYear, entryMonth, calendarData.currentData);
1205         calendarPattern->SetCurrentMonthData(calendarData.currentData);
1206         GetCalendarMonthData(
1207             GetLastMonth(calendarMonth).year, GetLastMonth(calendarMonth).month, calendarData.preData);
1208         calendarPattern->SetPreMonthData(calendarData.preData);
1209         GetCalendarMonthData(
1210             GetNextMonth(calendarMonth).year, GetNextMonth(calendarMonth).month, calendarData.nextData);
1211         calendarPattern->SetNextMonthData(calendarData.nextData);
1212     }
1213 
1214     calendarPattern->SetSelectedDay(selectedDay);
1215     calendarNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
1216     calendarNode->MarkModifyDone();
1217 }
1218 
GetCalendarFrameNode()1219 RefPtr<FrameNode> CalendarDialogPattern::GetCalendarFrameNode()
1220 {
1221     auto host = GetHost();
1222     CHECK_NULL_RETURN(host, nullptr);
1223     auto calendarNode = host->GetChildAtIndex(CALENDAR_NODE_INDEX);
1224     return AceType::DynamicCast<FrameNode>(calendarNode);
1225 }
1226 
GetCalendarPattern()1227 RefPtr<CalendarPattern> CalendarDialogPattern::GetCalendarPattern()
1228 {
1229     auto calendarFrameNode = GetCalendarFrameNode();
1230     CHECK_NULL_RETURN(calendarFrameNode, nullptr);
1231     return calendarFrameNode->GetPattern<CalendarPattern>();
1232 }
1233 
GetSwiperFrameNode()1234 RefPtr<FrameNode> CalendarDialogPattern::GetSwiperFrameNode()
1235 {
1236     auto calendarFrameNode = GetCalendarFrameNode();
1237     CHECK_NULL_RETURN(calendarFrameNode, nullptr);
1238     if (calendarFrameNode->GetChildren().empty()) {
1239         return nullptr;
1240     }
1241     auto swiperNode = calendarFrameNode->GetChildren().front();
1242     CHECK_NULL_RETURN(swiperNode, nullptr);
1243     return AceType::DynamicCast<FrameNode>(swiperNode);
1244 }
1245 
GetSwiperPattern()1246 RefPtr<SwiperPattern> CalendarDialogPattern::GetSwiperPattern()
1247 {
1248     auto swiperFrameNode = GetSwiperFrameNode();
1249     CHECK_NULL_RETURN(swiperFrameNode, nullptr);
1250     return swiperFrameNode->GetPattern<SwiperPattern>();
1251 }
1252 
OnEnterKeyEvent(const KeyEvent & event)1253 void CalendarDialogPattern::OnEnterKeyEvent(const KeyEvent& event)
1254 {
1255     bool checkKeyCode = (event.code == KeyCode::KEY_ENTER || event.code == KeyCode::KEY_NUMPAD_ENTER ||
1256         event.code == KeyCode::KEY_SPACE);
1257     if (!checkKeyCode) {
1258         return;
1259     }
1260     auto host = GetHost();
1261     CHECK_NULL_VOID(host);
1262     auto options = host->GetChildAtIndex(OPTIONS_NODE_INDEX);
1263     CHECK_NULL_VOID(options);
1264 
1265     for (const auto& child : options->GetChildren()) {
1266         CHECK_NULL_VOID(child);
1267         if (child->GetTag() != V2::BUTTON_ETS_TAG) {
1268             continue;
1269         }
1270         auto button = AceType::DynamicCast<FrameNode>(child);
1271         CHECK_NULL_VOID(button);
1272         auto focusHub = button->GetOrCreateFocusHub();
1273         if (focusHub && focusHub->IsDefaultFocus()) {
1274             auto gesture = button->GetOrCreateGestureEventHub();
1275             CHECK_NULL_VOID(gesture);
1276             gesture->ActClick();
1277         }
1278     }
1279 }
1280 
OnLanguageConfigurationUpdate()1281 void CalendarDialogPattern::OnLanguageConfigurationUpdate()
1282 {
1283     auto host = GetHost();
1284     CHECK_NULL_VOID(host);
1285     auto calendarNode = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(CALENDAR_NODE_INDEX));
1286     CHECK_NULL_VOID(calendarNode);
1287     auto swiperNode = AceType::DynamicCast<FrameNode>(calendarNode->GetFirstChild());
1288     CHECK_NULL_VOID(swiperNode);
1289 
1290     for (auto&& child : swiperNode->GetChildren()) {
1291         auto monthFrameNode = AceType::DynamicCast<FrameNode>(child);
1292         CHECK_NULL_VOID(monthFrameNode);
1293         auto pipelineContext = GetContext();
1294         CHECK_NULL_VOID(pipelineContext);
1295         RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
1296         CHECK_NULL_VOID(theme);
1297 
1298         auto fontSizeScale = pipelineContext->GetFontScale();
1299         auto fontSize = theme->GetCalendarDayFontSize();
1300         if (AceApplicationInfo::GetInstance().GetLanguage() != "zh") {
1301             ACE_UPDATE_NODE_PAINT_PROPERTY(
1302                 CalendarPaintProperty, WeekFontSize, theme->GetCalendarSmallDayFontSize(), monthFrameNode);
1303         } else {
1304             if (fontSizeScale < theme->GetCalendarPickerLargeScale() || CalendarDialogView::CheckOrientationChange()) {
1305                 ACE_UPDATE_NODE_PAINT_PROPERTY(CalendarPaintProperty, WeekFontSize, fontSize, monthFrameNode);
1306             } else {
1307                 fontSizeScale = fontSizeScale > theme->GetCalendarPickerLargerScale()
1308                                     ? theme->GetCalendarPickerLargerScale()
1309                                     : fontSizeScale;
1310                 ACE_UPDATE_NODE_PAINT_PROPERTY(
1311                     CalendarPaintProperty, WeekFontSize, fontSize * fontSizeScale, monthFrameNode);
1312             }
1313         }
1314         monthFrameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1315     }
1316 }
1317 
InitSurfaceChangedCallback()1318 void CalendarDialogPattern::InitSurfaceChangedCallback()
1319 {
1320     auto pipelineContext = GetContext();
1321     CHECK_NULL_VOID(pipelineContext);
1322     if (!HasSurfaceChangedCallback()) {
1323         auto callbackId = pipelineContext->RegisterSurfaceChangedCallback(
1324             [weak = WeakClaim(this)](int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight,
1325                 WindowSizeChangeReason type) {
1326                 auto pattern = weak.Upgrade();
1327                 if (pattern) {
1328                     pattern->HandleSurfaceChanged(newWidth, newHeight, prevWidth, prevHeight);
1329                 }
1330             });
1331         UpdateSurfaceChangedCallbackId(callbackId);
1332     }
1333 }
1334 
HandleSurfaceChanged(int32_t newWidth,int32_t newHeight,int32_t prevWidth,int32_t prevHeight)1335 void CalendarDialogPattern::HandleSurfaceChanged(
1336     int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight)
1337 {
1338     if (newWidth == prevWidth && newHeight == prevHeight) {
1339         return;
1340     }
1341 
1342     auto tmpHost = GetHost();
1343     CHECK_NULL_VOID(tmpHost);
1344     tmpHost->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1345     UpdateCaretInfoToController();
1346 }
1347 
UpdateCaretInfoToController()1348 void CalendarDialogPattern::UpdateCaretInfoToController()
1349 {
1350     auto host = GetHost();
1351     CHECK_NULL_VOID(host);
1352     auto titleNode = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(TITLE_NODE_INDEX));
1353     CHECK_NULL_VOID(titleNode);
1354     auto layoutProps = titleNode->GetLayoutProperty<LinearLayoutProperty>();
1355     CHECK_NULL_VOID(layoutProps);
1356     auto pipelineContext = GetContext();
1357     CHECK_NULL_VOID(pipelineContext);
1358     auto calendarTheme = pipelineContext->GetTheme<CalendarTheme>();
1359     CHECK_NULL_VOID(calendarTheme);
1360     auto calendarNode = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(CALENDAR_NODE_INDEX));
1361     CHECK_NULL_VOID(calendarNode);
1362     auto calendarLayoutProperty = calendarNode->GetLayoutProperty();
1363     CHECK_NULL_VOID(calendarLayoutProperty);
1364     CalendarDialogView::UpdateIdealSize(calendarTheme, layoutProps, calendarLayoutProperty);
1365 
1366     auto swiperNode = AceType::DynamicCast<FrameNode>(calendarNode->GetFirstChild());
1367     CHECK_NULL_VOID(swiperNode);
1368     for (auto&& child : swiperNode->GetChildren()) {
1369         auto monthFrameNode = AceType::DynamicCast<FrameNode>(child);
1370         CHECK_NULL_VOID(monthFrameNode);
1371         auto monthPattern = monthFrameNode->GetPattern<CalendarMonthPattern>();
1372         CHECK_NULL_VOID(monthPattern);
1373         CalendarDialogView::UpdatePaintProperties(monthFrameNode, currentSettingData_);
1374         monthPattern->UpdateColRowSpace();
1375         monthFrameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1376     }
1377 
1378     if (host->GetTotalChildCount() > OPTIONS_NODE_INDEX) {
1379         auto contentRow = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(OPTIONS_NODE_INDEX));
1380         CHECK_NULL_VOID(contentRow);
1381         size_t buttonIndex = OPTION_CANCEL_BUTTON_INDEX;
1382         for (auto&& child : contentRow->GetChildren()) {
1383             auto buttonNode = AceType::DynamicCast<FrameNode>(child);
1384             CHECK_NULL_VOID(buttonNode);
1385             CalendarDialogView::UpdateButtons(buttonNode, buttonIndex, currentButtonInfos_);
1386             buttonIndex++;
1387         }
1388     }
1389 
1390     auto calendarPattern = GetCalendarPattern();
1391     CHECK_NULL_VOID(calendarPattern);
1392     calendarPattern->UpdateTitleNode();
1393 
1394     auto wrapperNode = host->GetParent();
1395     CHECK_NULL_VOID(wrapperNode);
1396     auto dialogNode = AceType::DynamicCast<FrameNode>(wrapperNode->GetParent());
1397     CHECK_NULL_VOID(dialogNode);
1398     dialogNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1399     CalendarDialogView::SetPreviousOrientation();
1400 }
1401 } // namespace OHOS::Ace::NG
1402