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