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