• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_picker_pattern.h"
17 
18 #include <algorithm>
19 
20 #include "base/i18n/localization.h"
21 #include "base/utils/utf_helper.h"
22 #include "core/components/calendar/calendar_theme.h"
23 #include "core/components_ng/pattern/calendar_picker/calendar_dialog_view.h"
24 #include "core/components_ng/pattern/container_modal/container_modal_pattern.h"
25 #include "core/components_ng/pattern/image/image_layout_property.h"
26 #include "core/components_ng/pattern/text/text_layout_property.h"
27 #include "core/pipeline_ng/pipeline_context.h"
28 
29 namespace OHOS::Ace::NG {
30 namespace {
31 constexpr int32_t YEAR_INDEX = 0;
32 constexpr int32_t FIRST_SLASH = 1;
33 constexpr int32_t MONTH_INDEX = 2;
34 constexpr int32_t SECOND_SLASH = 3;
35 constexpr int32_t DAY_INDEX = 4;
36 constexpr int32_t YEAR_LENTH = 4;
37 constexpr int32_t ADD_BUTTON_INDEX = 0;
38 constexpr int32_t SUB_BUTTON_INDEX = 1;
39 constexpr int32_t DATE_NODE_COUNT = 3;
40 constexpr uint32_t MIN_YEAR = 1;
41 constexpr uint32_t MAX_YEAR = 5000;
42 constexpr uint32_t DELAY_TIME = 2000;
43 constexpr uint32_t MAX_MONTH = 12;
44 constexpr Dimension DIALOG_HEIGHT = 348.0_vp;
45 constexpr Dimension DIALOG_WIDTH = 336.0_vp;
46 constexpr double DISABLE_ALPHA = 0.4;
47 } // namespace
OnModifyDone()48 void CalendarPickerPattern::OnModifyDone()
49 {
50     Pattern::OnModifyDone();
51 
52     auto host = GetHost();
53     CHECK_NULL_VOID(host);
54     InitDateIndex();
55     InitClickEvent();
56     InitOnKeyEvent();
57     InitOnHoverEvent();
58     HandleEnable();
59     FlushTextStyle();
60     auto pipelineContext = host->GetContext();
61     CHECK_NULL_VOID(pipelineContext);
62     pipelineContext->AddWindowSizeChangeCallback(host->GetId());
63     UpdateEntryButtonColor();
64     UpdateEntryButtonBorderWidth();
65     UpdateAccessibilityText();
66 }
67 
UpdateAccessibilityText()68 void CalendarPickerPattern::UpdateAccessibilityText()
69 {
70     auto host = GetHost();
71     CHECK_NULL_VOID(host);
72     auto contentNode = AceType::DynamicCast<FrameNode>(host->GetFirstChild());
73     CHECK_NULL_VOID(contentNode);
74     int32_t hoverIndexs[] = { yearIndex_, FIRST_SLASH, monthIndex_, SECOND_SLASH, dayIndex_ };
75     std::string message;
76     for (auto index : hoverIndexs) {
77         auto textFrameNode = DynamicCast<FrameNode>(contentNode->GetChildAtIndex(index));
78         CHECK_NULL_VOID(textFrameNode);
79         auto textLayoutProperty = textFrameNode->GetLayoutProperty<TextLayoutProperty>();
80         CHECK_NULL_VOID(textLayoutProperty);
81         message += UtfUtils::Str16ToStr8(textLayoutProperty->GetContent().value_or(u""));
82     }
83     auto textAccessibilityProperty = contentNode->GetAccessibilityProperty<AccessibilityProperty>();
84     CHECK_NULL_VOID(textAccessibilityProperty);
85     textAccessibilityProperty->SetAccessibilityText(message);
86 }
87 
InitDateIndex()88 void CalendarPickerPattern::InitDateIndex()
89 {
90     std::vector<std::string> outOrder;
91     bool result = Localization::GetInstance()->GetDateOrder(outOrder);
92     if (!result || outOrder.size() < DATE_NODE_COUNT) {
93         yearIndex_ = YEAR_INDEX;
94         monthIndex_ = MONTH_INDEX;
95         dayIndex_ = DAY_INDEX;
96     } else {
97         size_t index = 0;
98         for (size_t i = 0; i < outOrder.size(); ++i) {
99             if (outOrder[i] == "year") {
100                 yearIndex_ = static_cast<int32_t>(i + index);
101             }
102 
103             if (outOrder[i] == "month") {
104                 monthIndex_ = static_cast<int32_t>(i + index);
105             }
106 
107             if (outOrder[i] == "day") {
108                 dayIndex_ = static_cast<int32_t>(i + index);
109             }
110 
111             index++;
112         }
113     }
114 }
115 
UpdateEntryButtonColor()116 void CalendarPickerPattern::UpdateEntryButtonColor()
117 {
118     auto host = GetHost();
119     CHECK_NULL_VOID(host);
120     auto buttonFlexNode = host->GetLastChild();
121     CHECK_NULL_VOID(buttonFlexNode);
122 
123     auto pipelineContext = host->GetContext();
124     CHECK_NULL_VOID(pipelineContext);
125     RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
126     CHECK_NULL_VOID(theme);
127 
128     int32_t buttonIndex = 0;
129     for (const auto& child : buttonFlexNode->GetChildren()) {
130         CHECK_NULL_VOID(child);
131         if (child->GetTag() == V2::BUTTON_ETS_TAG) {
132             auto buttonNode = AceType::DynamicCast<FrameNode>(child);
133             CHECK_NULL_VOID(buttonNode);
134             BorderColorProperty borderColor;
135             borderColor.SetColor(theme->GetEntryBorderColor());
136             buttonNode->GetRenderContext()->UpdateBorderColor(borderColor);
137             buttonNode->GetRenderContext()->UpdateBackgroundColor(Color::TRANSPARENT);
138             buttonNode->MarkModifyDone();
139 
140             auto image = buttonNode->GetChildren().front();
141             CHECK_NULL_VOID(image);
142             auto imageNode = AceType::DynamicCast<FrameNode>(image);
143             auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
144             CHECK_NULL_VOID(imageLayoutProperty);
145             auto imageInfo = imageLayoutProperty->GetImageSourceInfo();
146             CHECK_NULL_VOID(imageInfo);
147             auto buttonColor = theme->GetEntryArrowColor();
148             if (!IsAddOrSubButtonEnable(buttonIndex)) {
149                 buttonColor = buttonColor.ChangeOpacity(DISABLE_ALPHA);
150             }
151             imageInfo->SetFillColor(buttonColor);
152             buttonIndex++;
153             imageLayoutProperty->UpdateImageSourceInfo(imageInfo.value());
154             imageNode->MarkModifyDone();
155         }
156     }
157 }
158 
UpdateEntryButtonBorderWidth()159 void CalendarPickerPattern::UpdateEntryButtonBorderWidth()
160 {
161     auto host = GetHost();
162     CHECK_NULL_VOID(host);
163     auto buttonFlexNode = host->GetLastChild();
164     CHECK_NULL_VOID(buttonFlexNode);
165     auto pipelineContext = host->GetContext();
166     CHECK_NULL_VOID(pipelineContext);
167     RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
168     CHECK_NULL_VOID(theme);
169 
170     auto addButtonNode = AceType::DynamicCast<FrameNode>(buttonFlexNode->GetChildAtIndex(ADD_BUTTON_INDEX));
171     CHECK_NULL_VOID(addButtonNode);
172     auto subButtonNode = AceType::DynamicCast<FrameNode>(buttonFlexNode->GetChildAtIndex(SUB_BUTTON_INDEX));
173     CHECK_NULL_VOID(subButtonNode);
174 
175     auto textDirection = host->GetLayoutProperty()->GetNonAutoLayoutDirection();
176     BorderWidthProperty addBorderWidth;
177     BorderWidthProperty subBorderWidth;
178     if (textDirection == TextDirection::RTL) {
179         addBorderWidth.rightDimen = theme->GetEntryBorderWidth();
180         subBorderWidth.rightDimen = theme->GetEntryBorderWidth();
181         addBorderWidth.leftDimen = 0.0_vp;
182         subBorderWidth.leftDimen = 0.0_vp;
183     } else {
184         addBorderWidth.rightDimen = 0.0_vp;
185         subBorderWidth.rightDimen = 0.0_vp;
186         addBorderWidth.leftDimen = theme->GetEntryBorderWidth();
187         subBorderWidth.leftDimen = theme->GetEntryBorderWidth();
188     }
189     addButtonNode->GetLayoutProperty()->UpdateBorderWidth(addBorderWidth);
190     subButtonNode->GetLayoutProperty()->UpdateBorderWidth(subBorderWidth);
191     addButtonNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
192     subButtonNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
193 }
194 
UpdateEdgeAlign()195 void CalendarPickerPattern::UpdateEdgeAlign()
196 {
197     auto host = GetHost();
198     CHECK_NULL_VOID(host);
199     auto layoutProperty = host->GetLayoutProperty<CalendarPickerLayoutProperty>();
200     CHECK_NULL_VOID(layoutProperty);
201     auto textDirection = layoutProperty->GetNonAutoLayoutDirection();
202 
203     auto rtlAlignType = align_;
204     auto rtlX = offset_.GetX().Value();
205     if (textDirection == TextDirection::RTL) {
206         switch (align_) {
207             case CalendarEdgeAlign::EDGE_ALIGN_START:
208                 rtlAlignType = CalendarEdgeAlign::EDGE_ALIGN_END;
209                 break;
210             case CalendarEdgeAlign::EDGE_ALIGN_END:
211                 rtlAlignType = CalendarEdgeAlign::EDGE_ALIGN_START;
212                 break;
213             default:
214                 break;
215         }
216         rtlX = -offset_.GetX().Value();
217     }
218 
219     layoutProperty->UpdateDialogAlignType(rtlAlignType);
220     layoutProperty->UpdateDialogOffset(DimensionOffset(Dimension(rtlX), offset_.GetY()));
221 }
222 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,bool,bool)223 bool CalendarPickerPattern::OnDirtyLayoutWrapperSwap(
224     const RefPtr<LayoutWrapper>& dirty, bool /* skipMeasure */, bool /* skipLayout */)
225 {
226     if (!IsDialogShow()) {
227         return true;
228     }
229 
230     auto eventHub = GetEventHub<CalendarPickerEventHub>();
231     CHECK_NULL_RETURN(eventHub, true);
232     eventHub->FireLayoutChangeEvent();
233     return true;
234 }
235 
InitClickEvent()236 void CalendarPickerPattern::InitClickEvent()
237 {
238     if (clickListener_) {
239         return;
240     }
241     auto host = GetHost();
242     CHECK_NULL_VOID(host);
243     auto gesture = host->GetOrCreateGestureEventHub();
244     CHECK_NULL_VOID(gesture);
245     auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
246         auto pattern = weak.Upgrade();
247         CHECK_NULL_VOID(pattern);
248         pattern->HandleClickEvent(info.GetGlobalLocation());
249     };
250     clickListener_ = AceType::MakeRefPtr<ClickEvent>(std::move(clickCallback));
251     gesture->AddClickEvent(clickListener_);
252 }
253 
HandleHoverEvent(bool state,const Offset & globalLocation)254 void CalendarPickerPattern::HandleHoverEvent(bool state, const Offset& globalLocation)
255 {
256     bool yearState = false, monthState = false, dayState = false, addState = false, subState = false;
257     if (state) {
258         auto currSelectdDate = calendarData_.selectedDate;
259         switch (CheckRegion(globalLocation)) {
260             case CalendarPickerSelectedType::YEAR:
261                 yearState = true;
262                 break;
263             case CalendarPickerSelectedType::MONTH:
264                 monthState = true;
265                 break;
266             case CalendarPickerSelectedType::DAY:
267                 dayState = true;
268                 break;
269             case CalendarPickerSelectedType::ADDBTN:
270                 NextDateBySelectedType(currSelectdDate);
271                 if (PickerDate::IsDateInRange(currSelectdDate, calendarData_.startDate, calendarData_.endDate)) {
272                     addState = true;
273                 }
274                 break;
275             case CalendarPickerSelectedType::SUBBTN:
276                 PrevDateBySelectedType(currSelectdDate);
277                 if (PickerDate::IsDateInRange(currSelectdDate, calendarData_.startDate, calendarData_.endDate)) {
278                     subState = true;
279                 }
280                 break;
281             default:
282                 break;
283         }
284     }
285     HandleTextHoverEvent(yearState, yearIndex_);
286     HandleTextHoverEvent(monthState, monthIndex_);
287     HandleTextHoverEvent(dayState, dayIndex_);
288     HandleButtonHoverEvent(addState, ADD_BUTTON_INDEX);
289     HandleButtonHoverEvent(subState, SUB_BUTTON_INDEX);
290 }
291 
HandleTouchEvent(bool isPressed,const Offset & globalLocation)292 void CalendarPickerPattern::HandleTouchEvent(bool isPressed, const Offset& globalLocation)
293 {
294     bool addState = false, subState = false;
295     if (isPressed) {
296         auto currSelectdDate = calendarData_.selectedDate;
297         switch (CheckRegion(globalLocation)) {
298             case CalendarPickerSelectedType::ADDBTN:
299                 NextDateBySelectedType(currSelectdDate);
300                 if (PickerDate::IsDateInRange(currSelectdDate, calendarData_.startDate, calendarData_.endDate)) {
301                     addState = true;
302                 }
303                 break;
304             case CalendarPickerSelectedType::SUBBTN:
305                 PrevDateBySelectedType(currSelectdDate);
306                 if (PickerDate::IsDateInRange(currSelectdDate, calendarData_.startDate, calendarData_.endDate)) {
307                     subState = true;
308                 }
309                 break;
310             default:
311                 break;
312         }
313     }
314     HandleButtonTouchEvent(addState, ADD_BUTTON_INDEX);
315     HandleButtonTouchEvent(subState, SUB_BUTTON_INDEX);
316 }
317 
InitOnHoverEvent()318 void CalendarPickerPattern::InitOnHoverEvent()
319 {
320     if (hoverListener_) {
321         return;
322     }
323     auto host = GetHost();
324     CHECK_NULL_VOID(host);
325     auto contentNode = AceType::DynamicCast<FrameNode>(host->GetFirstChild());
326     CHECK_NULL_VOID(contentNode);
327     int32_t hoverIndexs[] = { yearIndex_, monthIndex_, dayIndex_ };
328     for (auto index : hoverIndexs) {
329         auto textFrameNode = DynamicCast<FrameNode>(contentNode->GetChildAtIndex(index));
330         CHECK_NULL_VOID(textFrameNode);
331         auto inputHub = textFrameNode->GetOrCreateInputEventHub();
332         CHECK_NULL_VOID(inputHub);
333         auto hoverCallback = [weak = WeakClaim(this), index](bool state) {
334             auto pattern = weak.Upgrade();
335             CHECK_NULL_VOID(pattern);
336             pattern->HandleTextHoverEvent(state, index);
337         };
338         hoverListener_ = AceType::MakeRefPtr<InputEvent>(std::move(hoverCallback));
339         inputHub->AddOnHoverEvent(hoverListener_);
340     }
341 }
342 
HandleClickEvent(const Offset & globalLocation)343 void CalendarPickerPattern::HandleClickEvent(const Offset& globalLocation)
344 {
345     switch (CheckRegion(globalLocation)) {
346         case CalendarPickerSelectedType::YEAR:
347             ShowDialog();
348             SetSelectedType(CalendarPickerSelectedType::YEAR);
349             return;
350         case CalendarPickerSelectedType::MONTH:
351             ShowDialog();
352             SetSelectedType(CalendarPickerSelectedType::MONTH);
353             return;
354         case CalendarPickerSelectedType::DAY:
355             ShowDialog();
356             SetSelectedType(CalendarPickerSelectedType::DAY);
357             return;
358         case CalendarPickerSelectedType::ADDBTN:
359             HandleAddButtonClick();
360             return;
361         case CalendarPickerSelectedType::SUBBTN:
362             HandleSubButtonClick();
363             return;
364         default:
365             SetSelectedType(CalendarPickerSelectedType::OTHER);
366             break;
367     }
368 
369     if (!IsDialogShow()) {
370         ShowDialog();
371     }
372 }
373 
ResetTextState()374 void CalendarPickerPattern::ResetTextState()
375 {
376     auto host = GetHost();
377     CHECK_NULL_VOID(host);
378     auto layoutProperty = host->GetLayoutProperty<CalendarPickerLayoutProperty>();
379     CHECK_NULL_VOID(layoutProperty);
380     auto contentNode = AceType::DynamicCast<FrameNode>(host->GetFirstChild());
381     CHECK_NULL_VOID(contentNode);
382     ResetTextStateByNode(DynamicCast<FrameNode>(contentNode->GetChildAtIndex(yearIndex_)));
383     ResetTextStateByNode(DynamicCast<FrameNode>(contentNode->GetChildAtIndex(FIRST_SLASH)));
384     ResetTextStateByNode(DynamicCast<FrameNode>(contentNode->GetChildAtIndex(monthIndex_)));
385     ResetTextStateByNode(DynamicCast<FrameNode>(contentNode->GetChildAtIndex(SECOND_SLASH)));
386     ResetTextStateByNode(DynamicCast<FrameNode>(contentNode->GetChildAtIndex(dayIndex_)));
387 }
388 
ResetTextStateByNode(const RefPtr<FrameNode> & textFrameNode)389 void CalendarPickerPattern::ResetTextStateByNode(const RefPtr<FrameNode>& textFrameNode)
390 {
391     CHECK_NULL_VOID(textFrameNode);
392     textFrameNode->GetRenderContext()->UpdateBackgroundColor(Color::TRANSPARENT);
393     auto host = GetHost();
394     CHECK_NULL_VOID(host);
395     auto layoutProperty = host->GetLayoutProperty<CalendarPickerLayoutProperty>();
396     CHECK_NULL_VOID(layoutProperty);
397     auto pipeline = PipelineBase::GetCurrentContext();
398     CHECK_NULL_VOID(pipeline);
399     RefPtr<CalendarTheme> calendarTheme = pipeline->GetTheme<CalendarTheme>();
400     CHECK_NULL_VOID(calendarTheme);
401     textFrameNode->GetRenderContext()->UpdateForegroundColor(
402         layoutProperty->GetColor().value_or(calendarTheme->GetEntryFontColor()));
403     textFrameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
404 }
405 
CheckRegion(const Offset & globalLocation)406 CalendarPickerSelectedType CalendarPickerPattern::CheckRegion(const Offset& globalLocation)
407 {
408     auto host = GetHost();
409     CHECK_NULL_RETURN(host, CalendarPickerSelectedType::OTHER);
410     auto contentNode = AceType::DynamicCast<FrameNode>(host->GetFirstChild());
411     CHECK_NULL_RETURN(contentNode, CalendarPickerSelectedType::OTHER);
412 
413     auto location = PointF(globalLocation.GetX(), globalLocation.GetY());
414     if (IsInNodeRegion(DynamicCast<FrameNode>(contentNode->GetChildAtIndex(yearIndex_)), location)) {
415         return CalendarPickerSelectedType::YEAR;
416     }
417     if (IsInNodeRegion(DynamicCast<FrameNode>(contentNode->GetChildAtIndex(monthIndex_)), location)) {
418         return CalendarPickerSelectedType::MONTH;
419     }
420     if (IsInNodeRegion(DynamicCast<FrameNode>(contentNode->GetChildAtIndex(dayIndex_)), location)) {
421         return CalendarPickerSelectedType::DAY;
422     }
423 
424     auto buttonFlexNode = AceType::DynamicCast<FrameNode>(host->GetLastChild());
425     CHECK_NULL_RETURN(buttonFlexNode, CalendarPickerSelectedType::OTHER);
426     if (IsInNodeRegion(DynamicCast<FrameNode>(buttonFlexNode->GetChildAtIndex(ADD_BUTTON_INDEX)), location)) {
427         return CalendarPickerSelectedType::ADDBTN;
428     }
429     if (IsInNodeRegion(DynamicCast<FrameNode>(buttonFlexNode->GetChildAtIndex(SUB_BUTTON_INDEX)), location)) {
430         return CalendarPickerSelectedType::SUBBTN;
431     }
432     return CalendarPickerSelectedType::OTHER;
433 }
434 
IsInNodeRegion(const RefPtr<FrameNode> & node,const PointF & point)435 bool CalendarPickerPattern::IsInNodeRegion(const RefPtr<FrameNode>& node, const PointF& point)
436 {
437     CHECK_NULL_RETURN(node, false);
438     auto rect = node->GetTransformRectRelativeToWindow();
439     return rect.IsInRegion(point);
440 }
441 
ReportChangeEvent(const std::string & compName,const std::string & eventName,const std::string & eventData)442 bool CalendarPickerPattern::ReportChangeEvent(const std::string& compName,
443     const std::string& eventName, const std::string& eventData)
444 {
445     auto host = GetHost();
446     CHECK_NULL_RETURN(host, false);
447     PickerDate pickerDate;
448     CHECK_NULL_RETURN(CalendarDialogView::GetReportChangeEventDate(pickerDate, eventData), false);
449     CHECK_NULL_RETURN(CalendarDialogView::CanReportChangeEvent(reportedPickerDate_, pickerDate), false);
450     return CalendarDialogView::ReportChangeEvent(host->GetId(), compName, eventName, pickerDate);
451 }
452 
FireChangeEvents(const std::string & info)453 void CalendarPickerPattern::FireChangeEvents(const std::string& info)
454 {
455     ReportChangeEvent("CalendarPicker", "onChange", info);
456     auto eventHub = GetEventHub<CalendarPickerEventHub>();
457     CHECK_NULL_VOID(eventHub);
458     eventHub->UpdateInputChangeEvent(info);
459     eventHub->UpdateOnChangeEvent(info);
460     eventHub->UpdateChangeEvent(info);
461 }
462 
ShowDialog()463 void CalendarPickerPattern::ShowDialog()
464 {
465     if (IsDialogShow()) {
466         return;
467     }
468     auto host = GetHost();
469     CHECK_NULL_VOID(host);
470     auto pipeline = host->GetContext();
471     CHECK_NULL_VOID(pipeline);
472     auto overlayManager = pipeline->GetOverlayManager();
473 
474     std::map<std::string, NG::DialogEvent> dialogEvent;
475     auto changeId = [weak = WeakClaim(this)](const std::string& info) {
476         auto pattern = weak.Upgrade();
477         CHECK_NULL_VOID(pattern);
478         pattern->ReportChangeEvent("CalendarPicker", "onChange", info);
479         pattern->SetDate(info);
480     };
481     auto acceptId = [weak = WeakClaim(this)](const std::string& /* info */) {
482         auto pattern = weak.Upgrade();
483         CHECK_NULL_VOID(pattern);
484         pattern->SetDialogShow(false);
485     };
486     dialogEvent["changeId"] = changeId;
487     dialogEvent["acceptId"] = acceptId;
488     std::map<std::string, NG::DialogGestureEvent> dialogCancelEvent;
489     auto cancelId = [weak = WeakClaim(this)](const GestureEvent& /* info */) {
490         auto pattern = weak.Upgrade();
491         CHECK_NULL_VOID(pattern);
492         pattern->SetDialogShow(false);
493     };
494     dialogCancelEvent["cancelId"] = cancelId;
495     calendarData_.entryNode = AceType::DynamicCast<FrameNode>(host);
496     calendarData_.markToday = isMarkToday_;
497     DialogProperties properties;
498     InitDialogProperties(properties);
499     overlayManager->ShowCalendarDialog(properties, calendarData_, dialogEvent, dialogCancelEvent);
500     SetDialogShow(true);
501 }
502 
InitOnKeyEvent()503 void CalendarPickerPattern::InitOnKeyEvent()
504 {
505     auto host = GetHost();
506     CHECK_NULL_VOID(host);
507     auto focusHub = host->GetOrCreateFocusHub();
508     focusHub->SetIsFocusOnTouch(true);
509     auto keyTask = [weak = WeakClaim(this)](const KeyEvent& keyEvent) -> bool {
510         auto pattern = weak.Upgrade();
511         CHECK_NULL_RETURN(pattern, false);
512         return pattern->HandleKeyEvent(keyEvent);
513     };
514     focusHub->SetOnKeyEventInternal(std::move(keyTask));
515 
516     auto blurTask = [weak = WeakClaim(this)]() {
517         auto pattern = weak.Upgrade();
518         CHECK_NULL_VOID(pattern);
519         pattern->HandleBlurEvent();
520     };
521     focusHub->SetOnBlurInternal(std::move(blurTask));
522 }
523 
HandleBlurEvent()524 void CalendarPickerPattern::HandleBlurEvent()
525 {
526     if (IsDialogShow()) {
527         return;
528     }
529     selected_ = CalendarPickerSelectedType::OTHER;
530     ResetTextState();
531 }
532 
HandleKeyEvent(const KeyEvent & event)533 bool CalendarPickerPattern::HandleKeyEvent(const KeyEvent& event)
534 {
535     if (event.action != KeyAction::DOWN && (event.code != KeyCode::KEY_TAB || !isFirtFocus_)) {
536         return false;
537     }
538     if (event.IsNumberKey()) {
539         return HandleNumberKeyEvent(event);
540     }
541     return HandleFocusEvent(event);
542 }
543 
HandleFocusEvent(const KeyEvent & event)544 bool CalendarPickerPattern::HandleFocusEvent(const KeyEvent& event)
545 {
546     auto host = GetHost();
547     CHECK_NULL_RETURN(host, false);
548 
549     switch (event.code) {
550         case KeyCode::KEY_TAB: {
551             ResetTextState();
552             if (isFirtFocus_) {
553                 selected_ = CalendarPickerSelectedType::YEAR;
554                 HandleTextFocusEvent(yearIndex_);
555                 if (!IsDialogShow()) {
556                     ShowDialog();
557                 }
558                 isFirtFocus_ = false;
559                 return true;
560             }
561             if (selected_ != CalendarPickerSelectedType::OTHER) {
562                 selected_ = CalendarPickerSelectedType::OTHER;
563                 isFirtFocus_ = true;
564                 return HandleBlurEvent(event);
565             }
566             return false;
567         }
568         case KeyCode::KEY_DPAD_LEFT: {
569             if (selected_ == CalendarPickerSelectedType::DAY) {
570                 ResetTextState();
571                 selected_ = CalendarPickerSelectedType::MONTH;
572                 HandleTextFocusEvent(monthIndex_);
573             } else if (selected_ == CalendarPickerSelectedType::MONTH) {
574                 ResetTextState();
575                 selected_ = CalendarPickerSelectedType::YEAR;
576                 HandleTextFocusEvent(yearIndex_);
577             }
578             return true;
579         }
580         case KeyCode::KEY_DPAD_RIGHT: {
581             if (selected_ == CalendarPickerSelectedType::YEAR) {
582                 ResetTextState();
583                 selected_ = CalendarPickerSelectedType::MONTH;
584                 HandleTextFocusEvent(monthIndex_);
585             } else if (selected_ == CalendarPickerSelectedType::MONTH) {
586                 ResetTextState();
587                 selected_ = CalendarPickerSelectedType::DAY;
588                 HandleTextFocusEvent(dayIndex_);
589             }
590             return true;
591         }
592         case KeyCode::KEY_DPAD_UP: {
593             if (!isFirtFocus_ || selected_ != CalendarPickerSelectedType::OTHER) {
594                 HandleAddButtonClick();
595             }
596             return true;
597         }
598         case KeyCode::KEY_DPAD_DOWN: {
599             if (!isFirtFocus_ || selected_ != CalendarPickerSelectedType::OTHER) {
600                 HandleSubButtonClick();
601             }
602             return true;
603         }
604         case KeyCode::KEY_MOVE_HOME: {
605             ResetTextState();
606             selected_ = CalendarPickerSelectedType::YEAR;
607             HandleTextFocusEvent(yearIndex_);
608             return true;
609         }
610         case KeyCode::KEY_MOVE_END: {
611             ResetTextState();
612             selected_ = CalendarPickerSelectedType::DAY;
613             HandleTextFocusEvent(dayIndex_);
614             return true;
615         }
616         case KeyCode::KEY_SPACE:
617         case KeyCode::KEY_NUMPAD_ENTER:
618         case KeyCode::KEY_ENTER: {
619             if (!IsDialogShow()) {
620                 ShowDialog();
621             }
622             return true;
623         }
624         default:
625             break;
626     }
627     return false;
628 }
629 
HandleBlurEvent(const KeyEvent & event)630 bool CalendarPickerPattern::HandleBlurEvent(const KeyEvent& event)
631 {
632     auto host = GetHost();
633     CHECK_NULL_RETURN(host, false);
634     auto contentNode = AceType::DynamicCast<FrameNode>(host->GetFirstChild());
635     CHECK_NULL_RETURN(contentNode, false);
636     auto textFrameNode = DynamicCast<FrameNode>(contentNode->GetChildAtIndex(yearIndex_));
637     CHECK_NULL_RETURN(textFrameNode, false);
638     auto focusHub = textFrameNode->GetOrCreateFocusHub();
639     CHECK_NULL_RETURN(focusHub, false);
640     return focusHub->HandleEvent(event);
641 }
642 
HandleYearKeyWaitingEvent(const uint32_t number,const std::function<void ()> & task,const std::function<void ()> & zeroStartTask)643 bool CalendarPickerPattern::HandleYearKeyWaitingEvent(
644     const uint32_t number, const std::function<void()>& task, const std::function<void()>& zeroStartTask)
645 {
646     auto json = JsonUtil::ParseJsonString(GetEntryDateInfo());
647     if (yearPrefixZeroCount_ > 0 && yearPrefixZeroCount_ < YEAR_LENTH - 1 && number == 0 &&
648         yearEnterCount_ == yearPrefixZeroCount_ + 1) {
649         yearPrefixZeroCount_++;
650         PostTaskToUI(std::move(zeroStartTask), "ArkUICalendarPickerYearKeyWaitingZeroStart");
651         return true;
652     } else if (yearPrefixZeroCount_ >= YEAR_LENTH - 1 && number == 0) {
653         yearPrefixZeroCount_ = 0;
654         yearEnterCount_ = 0;
655         isKeyWaiting_ = false;
656         return false;
657     }
658 
659     auto newYear = json->GetUInt("year") * 10 + number;
660 
661     if (yearPrefixZeroCount_ > 0 && yearEnterCount_ == yearPrefixZeroCount_ + 1) {
662         newYear = number;
663     }
664     if (yearEnterCount_ < YEAR_LENTH) {
665         json->Replace("year", static_cast<int32_t>(newYear));
666         SetDate(json->ToString());
667         PostTaskToUI(std::move(task), "ArkUICalendarPickerYearKeyWaitingChange");
668         return true;
669     }
670     newYear = std::max(newYear, MIN_YEAR);
671     newYear = std::min(newYear, MAX_YEAR);
672     json->Replace("year", static_cast<int32_t>(newYear));
673     auto maxDay = PickerDate::GetMaxDay(newYear, json->GetUInt("month"));
674     if (json->GetUInt("day") > maxDay) {
675         json->Replace("day", static_cast<int32_t>(maxDay));
676     }
677     SetDate(json->ToString());
678     FireChangeEvents(json->ToString());
679     if (yearEnterCount_ >= YEAR_LENTH) {
680         yearPrefixZeroCount_ = 0;
681         yearEnterCount_ = 0;
682     }
683     isKeyWaiting_ = false;
684     return true;
685 }
686 
HandleYearKeyEvent(uint32_t number)687 bool CalendarPickerPattern::HandleYearKeyEvent(uint32_t number)
688 {
689     auto json = JsonUtil::ParseJsonString(GetEntryDateInfo());
690     auto taskCallback = [weak = WeakClaim(this)]() {
691         auto pattern = weak.Upgrade();
692         CHECK_NULL_VOID(pattern);
693         pattern->HandleTaskCallback();
694     };
695     auto zeroStartTaskCallback = [weak = WeakClaim(this)]() {
696         auto pattern = weak.Upgrade();
697         CHECK_NULL_VOID(pattern);
698         pattern->HandleTaskCallback();
699     };
700     if (yearEnterCount_ < YEAR_LENTH) {
701         yearEnterCount_++;
702     } else {
703         return false;
704     }
705     if (isKeyWaiting_) {
706         return HandleYearKeyWaitingEvent(number, taskCallback, zeroStartTaskCallback);
707     } else {
708         if (number == 0) {
709             yearPrefixZeroCount_++;
710             PostTaskToUI(std::move(zeroStartTaskCallback), "ArkUICalendarPickerYearZeroStart");
711             isKeyWaiting_ = true;
712         } else {
713             json->Replace("year", static_cast<int32_t>(number));
714             SetDate(json->ToString());
715             PostTaskToUI(std::move(taskCallback), "ArkUICalendarPickerYearChange");
716             isKeyWaiting_ = true;
717         }
718     }
719     return true;
720 }
721 
HandleMonthKeyEvent(uint32_t number)722 bool CalendarPickerPattern::HandleMonthKeyEvent(uint32_t number)
723 {
724     auto json = JsonUtil::ParseJsonString(GetEntryDateInfo());
725     auto taskCallback = [weak = WeakClaim(this)]() {
726         auto pattern = weak.Upgrade();
727         CHECK_NULL_VOID(pattern);
728         pattern->HandleTaskCallback();
729     };
730     auto zeroStartTaskCallback = [weak = WeakClaim(this)]() {
731         auto pattern = weak.Upgrade();
732         CHECK_NULL_VOID(pattern);
733         pattern->HandleTaskCallback();
734     };
735 
736     if (isKeyWaiting_) {
737         if (monthPrefixZeroCount_ == 1 && number == 0) {
738             monthPrefixZeroCount_ = 0;
739             isKeyWaiting_ = false;
740             return false;
741         }
742 
743         auto newMonth = json->GetUInt("month") * 10 + number;
744 
745         if (monthPrefixZeroCount_ == 1) {
746             newMonth = number;
747         }
748         if (newMonth < 1 || newMonth > MAX_MONTH) {
749             return true;
750         }
751         json->Replace("month", static_cast<int32_t>(newMonth));
752         auto maxDay = PickerDate::GetMaxDay(json->GetUInt("year"), newMonth);
753         if (json->GetUInt("day") > maxDay) {
754             json->Replace("day", static_cast<int32_t>(maxDay));
755         }
756         SetDate(json->ToString());
757         FireChangeEvents(json->ToString());
758         isKeyWaiting_ = false;
759         monthPrefixZeroCount_ = 0;
760     } else {
761         if (number == 0) {
762             monthPrefixZeroCount_++;
763             PostTaskToUI(std::move(zeroStartTaskCallback), "ArkUICalendarPickerMonthZeroStart");
764             isKeyWaiting_ = true;
765         } else {
766             json->Replace("month", static_cast<int32_t>(number));
767             SetDate(json->ToString());
768 
769             PostTaskToUI(std::move(taskCallback), "ArkUICalendarPickerMonthChange");
770             isKeyWaiting_ = true;
771         }
772     }
773 
774     return true;
775 }
776 
HandleDayKeyEvent(uint32_t number)777 bool CalendarPickerPattern::HandleDayKeyEvent(uint32_t number)
778 {
779     auto json = JsonUtil::ParseJsonString(GetEntryDateInfo());
780     auto taskCallback = [weak = WeakClaim(this)]() {
781         auto pattern = weak.Upgrade();
782         CHECK_NULL_VOID(pattern);
783         pattern->HandleTaskCallback();
784     };
785     auto zeroStartTaskCallback = [weak = WeakClaim(this)]() {
786         auto pattern = weak.Upgrade();
787         CHECK_NULL_VOID(pattern);
788         pattern->HandleTaskCallback();
789     };
790 
791     if (isKeyWaiting_) {
792         if (dayPrefixZeroCount_ == 1 && number == 0) {
793             dayPrefixZeroCount_ = 0;
794             isKeyWaiting_ = false;
795             return false;
796         }
797 
798         auto newDay = json->GetUInt("day") * 10 + number;
799 
800         if (dayPrefixZeroCount_ == 1) {
801             newDay = number;
802         }
803         if (newDay <= PickerDate::GetMaxDay(json->GetUInt("year"), json->GetUInt("month"))) {
804             json->Replace("day", static_cast<int32_t>(newDay));
805             SetDate(json->ToString());
806             FireChangeEvents(json->ToString());
807             isKeyWaiting_ = false;
808             dayPrefixZeroCount_ = 0;
809         }
810     } else {
811         if (number == 0) {
812             dayPrefixZeroCount_++;
813             PostTaskToUI(std::move(zeroStartTaskCallback), "ArkUICalendarPickerDayZeroStart");
814             isKeyWaiting_ = true;
815         } else {
816             json->Replace("day", static_cast<int32_t>(number));
817             SetDate(json->ToString());
818 
819             PostTaskToUI(std::move(taskCallback), "ArkUICalendarPickerDayChange");
820             isKeyWaiting_ = true;
821         }
822     }
823 
824     return true;
825 }
826 
HandleNumberKeyEvent(const KeyEvent & event)827 bool CalendarPickerPattern::HandleNumberKeyEvent(const KeyEvent& event)
828 {
829     if (!event.IsNumberKey()) {
830         return false;
831     }
832 
833     uint32_t number = 0;
834     if (KeyCode::KEY_0 <= event.code && event.code <= KeyCode::KEY_9) {
835         number = static_cast<uint32_t>(event.code) - static_cast<uint32_t>(KeyCode::KEY_0);
836     }
837     if (KeyCode::KEY_NUMPAD_0 <= event.code && event.code <= KeyCode::KEY_NUMPAD_9) {
838         number = static_cast<uint32_t>(event.code) - static_cast<uint32_t>(KeyCode::KEY_NUMPAD_0);
839     }
840 
841     switch (GetSelectedType()) {
842         case CalendarPickerSelectedType::YEAR:
843             return HandleYearKeyEvent(number);
844         case CalendarPickerSelectedType::MONTH:
845             return HandleMonthKeyEvent(number);
846         case CalendarPickerSelectedType::DAY:
847             return HandleDayKeyEvent(number);
848         default:
849             break;
850     }
851     return false;
852 }
853 
PostTaskToUI(const std::function<void ()> & task,const std::string & name)854 void CalendarPickerPattern::PostTaskToUI(const std::function<void()>& task, const std::string& name)
855 {
856     CHECK_NULL_VOID(task);
857     auto host = GetHost();
858     CHECK_NULL_VOID(host);
859     auto context = host->GetContext();
860     CHECK_NULL_VOID(context);
861 
862     auto taskExecutor = context->GetTaskExecutor();
863     CHECK_NULL_VOID(taskExecutor);
864 
865     taskCount_++;
866     taskExecutor->PostDelayedTask(task, TaskExecutor::TaskType::UI, DELAY_TIME, name);
867 }
868 
HandleTaskCallback()869 void CalendarPickerPattern::HandleTaskCallback()
870 {
871     taskCount_--;
872     if (taskCount_ > 0) {
873         return;
874     } else if (taskCount_ < 0) {
875         taskCount_ = 0;
876     }
877     if (!isKeyWaiting_) {
878         return;
879     }
880 
881     auto json = JsonUtil::ParseJsonString(GetEntryDateInfo());
882     auto newYear = json->GetUInt("year");
883     newYear = std::max(newYear, MIN_YEAR);
884     newYear = std::min(newYear, MAX_YEAR);
885     json->Replace("year", static_cast<int32_t>(newYear));
886     auto maxDay = PickerDate::GetMaxDay(newYear, json->GetUInt("month"));
887     if (json->GetUInt("day") > maxDay) {
888         json->Replace("day", static_cast<int32_t>(maxDay));
889     }
890     SetDate(json->ToString());
891     FireChangeEvents(json->ToString());
892     yearEnterCount_ = 0;
893     yearPrefixZeroCount_ = 0;
894     monthPrefixZeroCount_ = 0;
895     dayPrefixZeroCount_ = 0;
896     isKeyWaiting_ = false;
897 }
898 
HandleZeroStartTaskCallback()899 void CalendarPickerPattern::HandleZeroStartTaskCallback()
900 {
901     taskCount_--;
902     if (taskCount_ > 0) {
903         return;
904     } else if (taskCount_ < 0) {
905         taskCount_ = 0;
906     }
907     if (!isKeyWaiting_) {
908         return;
909     }
910 
911     yearEnterCount_ = 0;
912     yearPrefixZeroCount_ = 0;
913     monthPrefixZeroCount_ = 0;
914     dayPrefixZeroCount_ = 0;
915     isKeyWaiting_ = false;
916 }
917 
HandleTextFocusEvent(int32_t index)918 void CalendarPickerPattern::HandleTextFocusEvent(int32_t index)
919 {
920     auto host = GetHost();
921     CHECK_NULL_VOID(host);
922     auto contentNode = AceType::DynamicCast<FrameNode>(host->GetFirstChild());
923     CHECK_NULL_VOID(contentNode);
924     auto textFrameNode = DynamicCast<FrameNode>(contentNode->GetChildAtIndex(index));
925     CHECK_NULL_VOID(textFrameNode);
926     auto pipelineContext = host->GetContext();
927     CHECK_NULL_VOID(pipelineContext);
928     RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
929     CHECK_NULL_VOID(theme);
930     textFrameNode->GetRenderContext()->UpdateBackgroundColor(theme->GetSelectBackgroundColor());
931     textFrameNode->GetRenderContext()->UpdateForegroundColor(Color::WHITE);
932     textFrameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
933     FlushAddAndSubButton();
934 }
935 
HandleTextHoverEvent(bool state,int32_t index)936 void CalendarPickerPattern::HandleTextHoverEvent(bool state, int32_t index)
937 {
938     if ((GetSelectedType() == CalendarPickerSelectedType::YEAR && index == yearIndex_) ||
939         (GetSelectedType() == CalendarPickerSelectedType::MONTH && index == monthIndex_) ||
940         (GetSelectedType() == CalendarPickerSelectedType::DAY && index == dayIndex_)) {
941         return;
942     }
943     auto host = GetHost();
944     CHECK_NULL_VOID(host);
945     auto contentNode = AceType::DynamicCast<FrameNode>(host->GetFirstChild());
946     CHECK_NULL_VOID(contentNode);
947     auto textFrameNode = DynamicCast<FrameNode>(contentNode->GetChildAtIndex(index));
948     CHECK_NULL_VOID(textFrameNode);
949     auto pipelineContext = host->GetContext();
950     CHECK_NULL_VOID(pipelineContext);
951     RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
952     CHECK_NULL_VOID(theme);
953     if (state) {
954         textFrameNode->GetRenderContext()->UpdateBackgroundColor(theme->GetBackgroundHoverColor());
955     } else {
956         textFrameNode->GetRenderContext()->UpdateBackgroundColor(Color::TRANSPARENT);
957         auto layoutProperty = host->GetLayoutProperty<CalendarPickerLayoutProperty>();
958         CHECK_NULL_VOID(layoutProperty);
959         textFrameNode->GetRenderContext()->UpdateForegroundColor(
960             layoutProperty->GetColor().value_or(theme->GetEntryFontColor()));
961     }
962 }
963 
FlushAddAndSubButton()964 void CalendarPickerPattern::FlushAddAndSubButton()
965 {
966     auto host = GetHost();
967     CHECK_NULL_VOID(host);
968     auto buttonFlexNode = AceType::DynamicCast<FrameNode>(host->GetLastChild());
969     CHECK_NULL_VOID(buttonFlexNode);
970 
971     auto pipelineContext = host->GetContext();
972     CHECK_NULL_VOID(pipelineContext);
973     RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
974     CHECK_NULL_VOID(theme);
975     int32_t buttonIndex = 0;
976     for (const auto& child : buttonFlexNode->GetChildren()) {
977         CHECK_NULL_VOID(child);
978         auto buttonNode = AceType::DynamicCast<FrameNode>(child);
979         auto image = buttonNode->GetChildren().front();
980         CHECK_NULL_VOID(image);
981         auto imageNode = AceType::DynamicCast<FrameNode>(image);
982         auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
983         CHECK_NULL_VOID(imageLayoutProperty);
984         auto imageInfo = imageLayoutProperty->GetImageSourceInfo();
985         auto buttonColor = theme->GetEntryArrowColor();
986         if (!IsAddOrSubButtonEnable(buttonIndex)) {
987             buttonColor = buttonColor.ChangeOpacity(DISABLE_ALPHA);
988         }
989         imageInfo->SetFillColor(buttonColor);
990         imageLayoutProperty->UpdateImageSourceInfo(imageInfo.value());
991         imageNode->MarkModifyDone();
992         buttonIndex++;
993     }
994 }
995 
IsAddOrSubButtonEnable(int32_t buttonIndex)996 bool CalendarPickerPattern::IsAddOrSubButtonEnable(int32_t buttonIndex)
997 {
998     PickerDate dateObj = calendarData_.selectedDate;
999     if (buttonIndex == ADD_BUTTON_INDEX) {
1000         NextDateBySelectedType(dateObj);
1001         dateObj = PickerDate::GetAvailableNextDay(
1002             dateObj, calendarData_.startDate, calendarData_.endDate, calendarData_.disabledDateRange, true);
1003     }
1004     if (buttonIndex == SUB_BUTTON_INDEX) {
1005         PrevDateBySelectedType(dateObj);
1006         dateObj = PickerDate::GetAvailableNextDay(
1007             dateObj, calendarData_.startDate, calendarData_.endDate, calendarData_.disabledDateRange, false);
1008     }
1009     return dateObj.GetYear() > 0;
1010 }
1011 
HandleButtonHoverEvent(bool state,int32_t index)1012 void CalendarPickerPattern::HandleButtonHoverEvent(bool state, int32_t index)
1013 {
1014     auto host = GetHost();
1015     CHECK_NULL_VOID(host);
1016     auto buttonFlexNode = AceType::DynamicCast<FrameNode>(host->GetLastChild());
1017     CHECK_NULL_VOID(buttonFlexNode);
1018     auto buttonFrameNode = DynamicCast<FrameNode>(buttonFlexNode->GetChildAtIndex(index));
1019     CHECK_NULL_VOID(buttonFrameNode);
1020     buttonFrameNode->GetRenderContext()->AnimateHoverEffectBoard(state);
1021 }
1022 
HandleButtonTouchEvent(bool isPressed,int32_t index)1023 void CalendarPickerPattern::HandleButtonTouchEvent(bool isPressed, int32_t index)
1024 {
1025     auto host = GetHost();
1026     CHECK_NULL_VOID(host);
1027     auto buttonFlexNode = AceType::DynamicCast<FrameNode>(host->GetLastChild());
1028     CHECK_NULL_VOID(buttonFlexNode);
1029     auto buttonFrameNode = DynamicCast<FrameNode>(buttonFlexNode->GetChildAtIndex(index));
1030     CHECK_NULL_VOID(buttonFrameNode);
1031     auto pipelineContext = host->GetContext();
1032     CHECK_NULL_VOID(pipelineContext);
1033     RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
1034     CHECK_NULL_VOID(theme);
1035     if (isPressed) {
1036         buttonFrameNode->GetRenderContext()->UpdateBackgroundColor(theme->GetBackgroundPressColor());
1037     } else {
1038         buttonFrameNode->GetRenderContext()->UpdateBackgroundColor(Color::TRANSPARENT);
1039     }
1040 }
1041 
NextDateBySelectedType(PickerDate & dateObj)1042 void CalendarPickerPattern::NextDateBySelectedType(PickerDate& dateObj)
1043 {
1044     switch (GetSelectedType()) {
1045         case CalendarPickerSelectedType::YEAR: {
1046             dateObj.SetYear(dateObj.GetYear() == MAX_YEAR ? MIN_YEAR : dateObj.GetYear() + 1);
1047             auto maxDay = PickerDate::GetMaxDay(dateObj.GetYear(), dateObj.GetMonth());
1048             if (maxDay < dateObj.GetDay()) {
1049                 dateObj.SetDay(maxDay);
1050             }
1051             break;
1052         }
1053         case CalendarPickerSelectedType::MONTH: {
1054             dateObj.SetMonth(dateObj.GetMonth() % MAX_MONTH + 1);
1055             if (dateObj.GetMonth() == 1) {
1056                 dateObj.SetYear(dateObj.GetYear() == MAX_YEAR ? MIN_YEAR : dateObj.GetYear() + 1);
1057             }
1058             auto maxDay = PickerDate::GetMaxDay(dateObj.GetYear(), dateObj.GetMonth());
1059             if (maxDay < dateObj.GetDay()) {
1060                 dateObj.SetDay(maxDay);
1061             }
1062             break;
1063         }
1064         case CalendarPickerSelectedType::DAY:
1065         default: {
1066             auto maxDay = PickerDate::GetMaxDay(dateObj.GetYear(), dateObj.GetMonth());
1067             if (maxDay > dateObj.GetDay()) {
1068                 dateObj.SetDay(dateObj.GetDay() + 1);
1069                 break;
1070             }
1071             dateObj.SetDay(1);
1072             if (dateObj.GetMonth() < MAX_MONTH) {
1073                 dateObj.SetMonth(dateObj.GetMonth() + 1);
1074                 break;
1075             }
1076             dateObj.SetMonth(1);
1077             dateObj.SetYear(dateObj.GetYear() == MAX_YEAR ? MIN_YEAR : dateObj.GetYear() + 1);
1078             break;
1079         }
1080     }
1081 }
1082 
PrevDateBySelectedType(PickerDate & dateObj)1083 void CalendarPickerPattern::PrevDateBySelectedType(PickerDate& dateObj)
1084 {
1085     switch (GetSelectedType()) {
1086         case CalendarPickerSelectedType::YEAR: {
1087             auto getYear = dateObj.GetYear();
1088             dateObj.SetYear(dateObj.GetYear() == MIN_YEAR ? MAX_YEAR : (getYear > 0 ? getYear - 1 : 0));
1089             auto maxDay = PickerDate::GetMaxDay(dateObj.GetYear(), dateObj.GetMonth());
1090             if (maxDay < dateObj.GetDay())
1091                 dateObj.SetDay(maxDay);
1092             break;
1093         }
1094         case CalendarPickerSelectedType::MONTH: {
1095             auto getMonth = dateObj.GetMonth();
1096             auto newMonth = getMonth > 0 ? getMonth - 1 : 0;
1097             if (newMonth == 0) {
1098                 dateObj.SetMonth(MAX_MONTH);
1099                 auto getYear = dateObj.GetYear();
1100                 dateObj.SetYear(dateObj.GetYear() == MIN_YEAR ? MAX_YEAR : (getYear > 0 ? getYear - 1 : 0));
1101             } else {
1102                 dateObj.SetMonth(newMonth);
1103             }
1104             auto maxDay = PickerDate::GetMaxDay(dateObj.GetYear(), dateObj.GetMonth());
1105             if (maxDay < dateObj.GetDay())
1106                 dateObj.SetDay(maxDay);
1107             break;
1108         }
1109         case CalendarPickerSelectedType::DAY:
1110         default: {
1111             if (dateObj.GetDay() > 1) {
1112                 dateObj.SetDay(dateObj.GetDay() - 1);
1113                 break;
1114             }
1115             if (dateObj.GetMonth() == 1) {
1116                 dateObj.SetMonth(MAX_MONTH);
1117                 auto getYear = dateObj.GetYear();
1118                 dateObj.SetYear(dateObj.GetYear() == MIN_YEAR ? MAX_YEAR : (getYear > 0 ? getYear - 1 : 0));
1119             } else {
1120                 auto getMonth = dateObj.GetMonth();
1121                 dateObj.SetMonth(getMonth > 0 ? getMonth - 1 : 0);
1122             }
1123             dateObj.SetDay(PickerDate::GetMaxDay(dateObj.GetYear(), dateObj.GetMonth()));
1124             break;
1125         }
1126     }
1127 }
1128 
HandleAddButtonClick()1129 void CalendarPickerPattern::HandleAddButtonClick()
1130 {
1131     auto json = JsonUtil::ParseJsonString(GetEntryDateInfo());
1132     PickerDate dateObj = PickerDate(json->GetUInt("year"), json->GetUInt("month"), json->GetUInt("day"));
1133     NextDateBySelectedType(dateObj);
1134     dateObj = PickerDate::GetAvailableNextDay(
1135         dateObj, calendarData_.startDate, calendarData_.endDate, calendarData_.disabledDateRange, true);
1136     if (dateObj.GetYear() > 0) {
1137         if (GetSelectedType() != CalendarPickerSelectedType::YEAR &&
1138             GetSelectedType() != CalendarPickerSelectedType::MONTH) {
1139             SetSelectedType(CalendarPickerSelectedType::DAY);
1140         }
1141         SetDate(dateObj.ToString(true));
1142         FireChangeEvents(dateObj.ToString(true));
1143         FlushAddAndSubButton();
1144     }
1145 }
1146 
HandleSubButtonClick()1147 void CalendarPickerPattern::HandleSubButtonClick()
1148 {
1149     auto json = JsonUtil::ParseJsonString(GetEntryDateInfo());
1150     PickerDate dateObj = PickerDate(json->GetUInt("year"), json->GetUInt("month"), json->GetUInt("day"));
1151     PrevDateBySelectedType(dateObj);
1152     dateObj = PickerDate::GetAvailableNextDay(
1153         dateObj, calendarData_.startDate, calendarData_.endDate, calendarData_.disabledDateRange, false);
1154     if (dateObj.GetYear() > 0) {
1155         if (GetSelectedType() != CalendarPickerSelectedType::YEAR &&
1156             GetSelectedType() != CalendarPickerSelectedType::MONTH) {
1157             SetSelectedType(CalendarPickerSelectedType::DAY);
1158         }
1159         SetDate(dateObj.ToString(true));
1160         FireChangeEvents(dateObj.ToString(true));
1161         FlushAddAndSubButton();
1162     }
1163 }
1164 
HandleEnable()1165 void CalendarPickerPattern::HandleEnable()
1166 {
1167     auto host = GetHost();
1168     CHECK_NULL_VOID(host);
1169     auto eventHub = host->GetEventHub<EventHub>();
1170     CHECK_NULL_VOID(eventHub);
1171     auto enabled = eventHub->IsEnabled();
1172     auto renderContext = host->GetRenderContext();
1173     CHECK_NULL_VOID(renderContext);
1174     auto originalOpacity = renderContext->GetOpacityValue(1.0);
1175     renderContext->OnOpacityUpdate(enabled ? originalOpacity : DISABLE_ALPHA * originalOpacity);
1176 }
1177 
CalculateDialogOffset()1178 OffsetF CalendarPickerPattern::CalculateDialogOffset()
1179 {
1180     UpdateEdgeAlign();
1181     auto host = GetHost();
1182     CHECK_NULL_RETURN(host, OffsetF());
1183     auto layoutProperty = host->GetLayoutProperty<CalendarPickerLayoutProperty>();
1184     CHECK_NULL_RETURN(layoutProperty, OffsetF());
1185     float x = 0.0f;
1186     float y = 0.0f;
1187     auto hostOffset = host->GetOffsetRelativeToWindow();
1188     auto hostSize = host->GetGeometryNode()->GetFrameSize();
1189 
1190     auto pipelineContext = host->GetContext();
1191     CHECK_NULL_RETURN(pipelineContext, OffsetF());
1192     RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
1193     CHECK_NULL_RETURN(theme, OffsetF());
1194 
1195     float dialogHeight = pipelineContext->GetRootHeight();
1196     if (IsContainerModal()) {
1197         auto rootNode = pipelineContext->GetRootElement();
1198         CHECK_NULL_RETURN(rootNode, OffsetF());
1199         auto containerNode = AceType::DynamicCast<FrameNode>(rootNode->GetChildren().front());
1200         CHECK_NULL_RETURN(containerNode, OffsetF());
1201         auto containerPattern = containerNode->GetPattern<ContainerModalPattern>();
1202         CHECK_NULL_RETURN(containerPattern, OffsetF());
1203         auto titleHeight = containerPattern->GetContainerModalTitleHeight();
1204         dialogHeight -= titleHeight;
1205         hostOffset -= OffsetF(0, titleHeight);
1206     }
1207 
1208     auto hostRect = RectF(hostOffset, hostSize);
1209     if (hostRect.Bottom() + (DIALOG_HEIGHT).ConvertToPx() > dialogHeight) {
1210         y = std::max(static_cast<float>(hostRect.Top() - (DIALOG_HEIGHT).ConvertToPx()), 0.0f);
1211     } else {
1212         y = hostRect.Bottom() + (theme->GetDialogMargin()).ConvertToPx();
1213     }
1214 
1215     CalendarEdgeAlign align = layoutProperty->GetDialogAlignType().value_or(CalendarEdgeAlign::EDGE_ALIGN_END);
1216     if (align == CalendarEdgeAlign::EDGE_ALIGN_START) {
1217         x = std::min(
1218             hostRect.Left(), static_cast<float>(pipelineContext->GetRootWidth() - (DIALOG_WIDTH).ConvertToPx()));
1219     } else if (align == CalendarEdgeAlign::EDGE_ALIGN_CENTER) {
1220         auto hostCenterX = (hostRect.Left() + hostRect.Right()) / 2;
1221         x = std::max(0.0f, static_cast<float>(hostCenterX - (DIALOG_WIDTH).ConvertToPx() / 2));
1222         x = std::min(x, static_cast<float>(pipelineContext->GetRootWidth() - (DIALOG_WIDTH).ConvertToPx()));
1223     } else {
1224         x = std::max(0.0f, static_cast<float>(hostRect.Right() - (DIALOG_WIDTH).ConvertToPx()));
1225     }
1226 
1227     auto offset = layoutProperty->GetDialogOffset().value_or(DimensionOffset());
1228 
1229     return OffsetF(x + offset.GetX().ConvertToPx(), y + offset.GetY().ConvertToPx());
1230 }
1231 
InitDialogProperties(DialogProperties & properties)1232 void CalendarPickerPattern::InitDialogProperties(DialogProperties& properties)
1233 {
1234     properties.customStyle = true;
1235     properties.maskColor = Color(0);
1236     properties.offset = DimensionOffset(CalculateDialogOffset());
1237     properties.alignment = DialogAlignment::TOP_START;
1238     auto cancelId = [weak = WeakClaim(this)]() {
1239         auto pattern = weak.Upgrade();
1240         CHECK_NULL_VOID(pattern);
1241         pattern->SetDialogShow(false);
1242     };
1243     properties.onCancel = cancelId;
1244 }
1245 
OnWindowSizeChanged(int32_t width,int32_t height,WindowSizeChangeReason type)1246 void CalendarPickerPattern::OnWindowSizeChanged(int32_t width, int32_t height, WindowSizeChangeReason type)
1247 {
1248     if (type != WindowSizeChangeReason::ROTATION && type != WindowSizeChangeReason::DRAG) {
1249         return;
1250     }
1251 
1252     auto host = GetHost();
1253     CHECK_NULL_VOID(host);
1254     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1255 }
1256 
OnColorConfigurationUpdate()1257 void CalendarPickerPattern::OnColorConfigurationUpdate()
1258 {
1259     if (IsDialogShow()) {
1260         return;
1261     }
1262     selected_ = CalendarPickerSelectedType::OTHER;
1263     ResetTextState();
1264 }
1265 
GetEntryDateInfo()1266 std::string CalendarPickerPattern::GetEntryDateInfo()
1267 {
1268     if (!HasContentNode()) {
1269         return "";
1270     }
1271     auto host = GetHost();
1272     CHECK_NULL_RETURN(host, "");
1273     auto contentNode = AceType::DynamicCast<FrameNode>(host->GetFirstChild());
1274     CHECK_NULL_RETURN(contentNode, "");
1275     auto json = JsonUtil::Create(true);
1276     auto yearNode = AceType::DynamicCast<FrameNode>(contentNode->GetChildAtIndex(yearIndex_));
1277     CHECK_NULL_RETURN(yearNode, "");
1278     auto textLayoutProperty = yearNode->GetLayoutProperty<TextLayoutProperty>();
1279     CHECK_NULL_RETURN(textLayoutProperty, "");
1280     json->Put("year",
1281         StringUtils::StringToInt(UtfUtils::Str16ToStr8(textLayoutProperty->GetContent().value_or(u"1970"))));
1282 
1283     auto monthNode = AceType::DynamicCast<FrameNode>(contentNode->GetChildAtIndex(monthIndex_));
1284     CHECK_NULL_RETURN(monthNode, "");
1285     textLayoutProperty = monthNode->GetLayoutProperty<TextLayoutProperty>();
1286     CHECK_NULL_RETURN(textLayoutProperty, "");
1287     json->Put("month",
1288         StringUtils::StringToInt(UtfUtils::Str16ToStr8(textLayoutProperty->GetContent().value_or(u"01"))));
1289 
1290     auto dayNode = AceType::DynamicCast<FrameNode>(contentNode->GetChildAtIndex(dayIndex_));
1291     CHECK_NULL_RETURN(dayNode, "");
1292     textLayoutProperty = dayNode->GetLayoutProperty<TextLayoutProperty>();
1293     CHECK_NULL_RETURN(textLayoutProperty, "");
1294     json->Put("day",
1295         StringUtils::StringToInt(UtfUtils::Str16ToStr8(textLayoutProperty->GetContent().value_or(u"01"))));
1296 
1297     return json->ToString();
1298 }
1299 
SetDate(const std::string & info)1300 void CalendarPickerPattern::SetDate(const std::string& info)
1301 {
1302     if (!HasContentNode()) {
1303         return;
1304     }
1305     auto host = GetHost();
1306     CHECK_NULL_VOID(host);
1307     auto contentNode = AceType::DynamicCast<FrameNode>(host->GetFirstChild());
1308     CHECK_NULL_VOID(contentNode);
1309     auto json = JsonUtil::ParseJsonString(info);
1310     auto selectedDate = PickerDate(json->GetUInt("year"), json->GetUInt("month"), json->GetUInt("day"));
1311     calendarData_.selectedDate =
1312         PickerDate::AdjustDateToRange(selectedDate, calendarData_.startDate, calendarData_.endDate);
1313     auto yearNode = AceType::DynamicCast<FrameNode>(contentNode->GetChildAtIndex(yearIndex_));
1314     CHECK_NULL_VOID(yearNode);
1315     auto textLayoutProperty = yearNode->GetLayoutProperty<TextLayoutProperty>();
1316     CHECK_NULL_VOID(textLayoutProperty);
1317     auto yearNum = json->GetUInt("year");
1318     auto yearStr = std::to_string(yearNum);
1319     yearStr = (yearNum < 1000 ? "0" : "") + yearStr;
1320     yearStr = (yearNum < 100 ? "0" : "") + yearStr;
1321     yearStr = (yearNum < 10 ? "0" : "") + yearStr;
1322     textLayoutProperty->UpdateContent(yearStr);
1323     yearNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1324 
1325     auto monthNode = AceType::DynamicCast<FrameNode>(contentNode->GetChildAtIndex(monthIndex_));
1326     CHECK_NULL_VOID(monthNode);
1327     textLayoutProperty = monthNode->GetLayoutProperty<TextLayoutProperty>();
1328     CHECK_NULL_VOID(textLayoutProperty);
1329     auto monthString = (json->GetUInt("month") < 10 ? "0" : "") + std::to_string(json->GetUInt("month"));
1330     textLayoutProperty->UpdateContent(monthString);
1331     monthNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1332 
1333     auto dayNode = AceType::DynamicCast<FrameNode>(contentNode->GetChildAtIndex(dayIndex_));
1334     CHECK_NULL_VOID(dayNode);
1335     textLayoutProperty = dayNode->GetLayoutProperty<TextLayoutProperty>();
1336     CHECK_NULL_VOID(textLayoutProperty);
1337     auto dayString = (json->GetUInt("day") < 10 ? "0" : "") + std::to_string(json->GetUInt("day"));
1338     textLayoutProperty->UpdateContent(dayString);
1339     dayNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1340     UpdateAccessibilityText();
1341     FlushAddAndSubButton();
1342 }
1343 
FlushTextStyle()1344 void CalendarPickerPattern::FlushTextStyle()
1345 {
1346     if (!HasContentNode()) {
1347         return;
1348     }
1349     auto host = GetHost();
1350     CHECK_NULL_VOID(host);
1351     auto layoutProperty = host->GetLayoutProperty<CalendarPickerLayoutProperty>();
1352     CHECK_NULL_VOID(layoutProperty);
1353     auto contentNode = AceType::DynamicCast<FrameNode>(host->GetFirstChild());
1354     CHECK_NULL_VOID(contentNode);
1355     int32_t len = static_cast<int32_t>(contentNode->GetChildren().size());
1356     for (int32_t i = 0; i < len; i++) {
1357         auto textNode = AceType::DynamicCast<FrameNode>(contentNode->GetChildAtIndex(i));
1358         CHECK_NULL_VOID(textNode);
1359         auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
1360         CHECK_NULL_VOID(textLayoutProperty);
1361 
1362         if (selected_ != CalendarPickerSelectedType::YEAR && selected_ != CalendarPickerSelectedType::MONTH &&
1363             selected_ != CalendarPickerSelectedType::DAY && layoutProperty->HasColor()) {
1364             ResetTextStateByNode(textNode);
1365         } else {
1366             SetSelectedType(selected_);
1367         }
1368         if (layoutProperty->HasFontSize()) {
1369             textLayoutProperty->UpdateFontSize(layoutProperty->GetFontSize().value());
1370         }
1371         if (layoutProperty->HasWeight()) {
1372             textLayoutProperty->UpdateFontWeight(layoutProperty->GetWeight().value());
1373         }
1374         textNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1375     }
1376 }
1377 
SetSelectedType(CalendarPickerSelectedType type)1378 void CalendarPickerPattern::SetSelectedType(CalendarPickerSelectedType type)
1379 {
1380     selected_ = type;
1381     switch (selected_) {
1382         case CalendarPickerSelectedType::YEAR:
1383             ResetTextState();
1384             HandleTextFocusEvent(yearIndex_);
1385             break;
1386         case CalendarPickerSelectedType::MONTH:
1387             ResetTextState();
1388             HandleTextFocusEvent(monthIndex_);
1389             break;
1390         case CalendarPickerSelectedType::DAY:
1391             ResetTextState();
1392             HandleTextFocusEvent(dayIndex_);
1393             break;
1394         default:
1395             break;
1396     }
1397 }
1398 
IsContainerModal()1399 bool CalendarPickerPattern::IsContainerModal()
1400 {
1401     auto host = GetHost();
1402     CHECK_NULL_RETURN(host, false);
1403     auto pipelineContext = host->GetContext();
1404     CHECK_NULL_RETURN(pipelineContext, false);
1405     auto windowManager = pipelineContext->GetWindowManager();
1406     return pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
1407                             windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
1408 }
1409 
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const1410 void CalendarPickerPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
1411 {
1412     /* no fixed attr below, just return */
1413     if (filter.IsFastFilter()) {
1414         return;
1415     }
1416     json->PutExtAttr("markToday", calendarData_.markToday ? "true" : "false", filter);
1417     std::string disabledDateRangeStr = "";
1418     for (const auto& range : calendarData_.disabledDateRange) {
1419         disabledDateRangeStr += range.first.ToString(false) + "," + range.second.ToString(false) + ",";
1420     }
1421     if (!disabledDateRangeStr.empty() && disabledDateRangeStr.back() == ',') {
1422         disabledDateRangeStr.pop_back();
1423     }
1424     json->PutExtAttr("disabledDateRange", disabledDateRangeStr.c_str(), filter);
1425 
1426     if (calendarData_.startDate.ToDays() == PickerDate().ToDays()) {
1427         json->PutExtAttr("start", "undefined", filter);
1428     } else {
1429         json->PutExtAttr("start", calendarData_.startDate.ToString(false).c_str(), filter);
1430     }
1431     if (calendarData_.endDate.ToDays() == PickerDate().ToDays()) {
1432         json->PutExtAttr("end", "undefined", filter);
1433     } else {
1434         json->PutExtAttr("end", calendarData_.endDate.ToString(false).c_str(), filter);
1435     }
1436 }
1437 
SetMarkToday(bool isMarkToday)1438 void CalendarPickerPattern::SetMarkToday(bool isMarkToday)
1439 {
1440     isMarkToday_ = isMarkToday;
1441     calendarData_.markToday = isMarkToday;
1442 }
1443 
GetMarkToday()1444 bool CalendarPickerPattern::GetMarkToday()
1445 {
1446     return isMarkToday_;
1447 }
1448 
SetDisabledDateRange(const std::vector<std::pair<PickerDate,PickerDate>> & disabledDateRange)1449 void CalendarPickerPattern::SetDisabledDateRange(
1450     const std::vector<std::pair<PickerDate, PickerDate>>& disabledDateRange)
1451 {
1452     calendarData_.disabledDateRange = disabledDateRange;
1453 }
1454 
GetDisabledDateRange()1455 std::string CalendarPickerPattern::GetDisabledDateRange()
1456 {
1457     std::string disabledDateRangeStr;
1458     for (const auto& range : calendarData_.disabledDateRange) {
1459         disabledDateRangeStr += std::to_string(range.first.GetYear()) + "-" + std::to_string(range.first.GetMonth()) +
1460                                 "-" + std::to_string(range.first.GetDay()) + "," +
1461                                 std::to_string(range.second.GetYear()) + "-" + std::to_string(range.second.GetMonth()) +
1462                                 "-" + std::to_string(range.second.GetDay()) + ",";
1463     }
1464     if (!disabledDateRangeStr.empty() && disabledDateRangeStr.back() == ',') {
1465         disabledDateRangeStr.pop_back(); // remove the last comma.
1466     }
1467     return disabledDateRangeStr;
1468 }
1469 } // namespace OHOS::Ace::NG
1470