1 /*
2 * Copyright (c) 2022-2024 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/calendar_month_pattern.h"
17
18 #include "base/geometry/offset.h"
19 #include "base/i18n/localization.h"
20 #include "base/utils/utils.h"
21 #include "core/common/ace_application_info.h"
22 #include "core/components_ng/base/frame_node.h"
23 #include "core/components_ng/pattern/button/button_layout_property.h"
24 #include "core/components_ng/pattern/button/button_pattern.h"
25 #include "core/components_ng/pattern/linear_layout/linear_layout_pattern.h"
26 #include "core/components_ng/pattern/calendar/calendar_paint_method.h"
27 #include "core/components_ng/pattern/calendar_picker/calendar_dialog_view.h"
28 #include "core/components/slider/slider_theme.h"
29 #include "core/pipeline_ng/pipeline_context.h"
30
31 namespace OHOS::Ace::NG {
32 namespace {
33 constexpr int32_t CALENDAR_WEEK_DAYS = 7;
34 constexpr int32_t DAILY_FOUR_ROWSPACE = 4;
35 constexpr int32_t DAILY_FIVE_ROWSPACE = 5;
36 constexpr int32_t CALENDAR_DISTANCE_ADJUST_FOCUSED_SIZE = 2;
37 constexpr Dimension CALENDAR_DISTANCE_ADJUST_FOCUSED_EVENT = 4.0_vp;
38 constexpr int32_t MONDAY_INDEX = 1;
39 constexpr int32_t TUESDAY_INDEX = 2;
40 constexpr int32_t WEDNESDAY_INDEX = 3;
41 constexpr int32_t THURSDAY_INDEX = 4;
42 constexpr int32_t FRIDAY_INDEX = 5;
43 constexpr int32_t SATURDAY_INDEX = 6;
44 constexpr int32_t WEEK_ROW_INDEX = 1;
45 } // namespace
46
CreateNodePaintMethod()47 RefPtr<NodePaintMethod> CalendarMonthPattern::CreateNodePaintMethod()
48 {
49 if (AceApplicationInfo::GetInstance().IsAccessibilityEnabled()) {
50 InitCurrentVirtualNode();
51 }
52 CalendarPaintParams params;
53 params.startDate = startDate_;
54 params.endDate = endDate_;
55 params.markToday = markToday_;
56 params.disabledDateRange = disabledDateRange_;
57 return MakeRefPtr<CalendarPaintMethod>(obtainedMonth_, calendarDay_, params, isCalendarDialog_);
58 }
59
SetCalendarDay(const CalendarDay & calendarDay)60 void CalendarMonthPattern::SetCalendarDay(const CalendarDay& calendarDay)
61 {
62 calendarDay_ = calendarDay;
63 if (monthState_ == MonthState::CUR_MONTH && !obtainedMonth_.days.empty()) {
64 for (auto& day : obtainedMonth_.days) {
65 if (day.month.year == calendarDay.month.year && day.month.month == calendarDay.month.month &&
66 day.day == calendarDay.day) {
67 day.focused = true;
68 }
69 }
70 }
71 }
72
InitFoldState()73 void CalendarMonthPattern::InitFoldState()
74 {
75 auto container = Container::Current();
76 CHECK_NULL_VOID(container);
77 container->InitIsFoldable();
78 if (container->IsFoldable()) {
79 currentFoldStatus_ = container->GetCurrentFoldStatus();
80 }
81 }
82
FireIsFoldStatusChanged()83 void CalendarMonthPattern::FireIsFoldStatusChanged()
84 {
85 auto container = Container::Current();
86 CHECK_NULL_VOID(container);
87 if (!container->IsFoldable()) {
88 return;
89 }
90 auto foldStatus = container->GetCurrentFoldStatus();
91 auto host = GetHost();
92 CHECK_NULL_VOID(host);
93 auto paintProperty = host->GetPaintProperty<CalendarPaintProperty>();
94 CHECK_NULL_VOID(paintProperty);
95 auto colSpace = paintProperty->GetColSpaceValue({}).ConvertToPx();
96 if (foldStatus != currentFoldStatus_ && colSpace_ != colSpace && monthState_ == MonthState::CUR_MONTH) {
97 currentFoldStatus_ = foldStatus;
98 InitCalendarVirtualNode();
99 SetFocusNode(focusedCalendarDay_.index, true);
100 }
101 }
102
OnAttachToFrameNode()103 void CalendarMonthPattern::OnAttachToFrameNode()
104 {
105 auto host = GetHost();
106 CHECK_NULL_VOID(host);
107 host->GetRenderContext()->SetClipToFrame(true);
108 InitFoldState();
109 }
110
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)111 bool CalendarMonthPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
112 {
113 if (IsCalendarDialog()) {
114 SetColRowSpace();
115 }
116
117 return !(config.skipMeasure || dirty->SkipMeasureContent());
118 }
119
~CalendarMonthPattern()120 CalendarMonthPattern::~CalendarMonthPattern()
121 {
122 auto host = GetHost();
123 CHECK_NULL_VOID(host);
124 auto pipeline = host->GetContext();
125 CHECK_NULL_VOID(pipeline);
126 auto accessibilityManager = pipeline->GetAccessibilityManager();
127 CHECK_NULL_VOID(accessibilityManager);
128 accessibilityManager->DeregisterAccessibilitySAObserverCallback(host->GetAccessibilityId());
129 }
130
GetDaySize(const RefPtr<CalendarTheme> & theme)131 Dimension CalendarMonthPattern::GetDaySize(const RefPtr<CalendarTheme>& theme)
132 {
133 auto pipeline = GetHost()->GetContext();
134 CHECK_NULL_RETURN(pipeline, theme->GetCalendarPickerDayWidthOrHeight());
135 auto fontSizeScale = pipeline->GetFontScale();
136 #ifndef ARKUI_WEARABLE
137 if (fontSizeScale < theme->GetCalendarPickerLargeScale() || CalendarDialogView::CheckOrientationChange()) {
138 #else
139 if (fontSizeScale < theme->GetCalendarPickerLargeScale()) {
140 #endif
141 return theme->GetCalendarPickerDayWidthOrHeight();
142 } else {
143 return theme->GetCalendarPickerDayLargeWidthOrHeight();
144 }
145 }
146
147 bool CalendarMonthPattern::IsLargeSize(const RefPtr<CalendarTheme>& theme)
148 {
149 auto pipeline = GetHost()->GetContext();
150 CHECK_NULL_RETURN(pipeline, false);
151 auto fontSizeScale = pipeline->GetFontScale();
152 #ifndef ARKUI_WEARABLE
153 if ((fontSizeScale < theme->GetCalendarPickerLargeScale() || CalendarDialogView::CheckOrientationChange())
154 && theme->GetCalendarPickerDayLargeWidthOrHeight() > theme->GetCalendarPickerDayWidthOrHeight()) {
155 #else
156 if (fontSizeScale < theme->GetCalendarPickerLargeScale()
157 && theme->GetCalendarPickerDayLargeWidthOrHeight() > theme->GetCalendarPickerDayWidthOrHeight()) {
158 #endif
159 return false;
160 } else {
161 return true;
162 }
163 }
164
165 void CalendarMonthPattern::SetColRowSpace()
166 {
167 auto host = GetHost();
168 CHECK_NULL_VOID(host);
169 auto contentConstraint = host->GetLayoutProperty()->GetLayoutConstraint();
170 if (!contentConstraint.has_value()) {
171 return;
172 }
173 auto constraint = contentConstraint.value();
174
175 auto dataSize = GetMonthData().days.size();
176 if (dataSize <= 0) {
177 return;
178 }
179
180 auto pipelineContext = host->GetContext();
181 CHECK_NULL_VOID(pipelineContext);
182 RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
183 CHECK_NULL_VOID(theme);
184 auto selfWidth = constraint.selfIdealSize.Width();
185 if (!selfWidth.has_value()) {
186 return;
187 }
188 auto width = selfWidth.value() - CALENDAR_DISTANCE_ADJUST_FOCUSED_EVENT.ConvertToPx() * 2;
189 auto paintProperty = GetPaintProperty<CalendarPaintProperty>();
190 CHECK_NULL_VOID(paintProperty);
191 auto gregorianDayHeight = paintProperty->GetGregorianCalendarHeightValue({}).ConvertToPx() <= 0
192 ? theme->GetCalendarTheme().gregorianCalendarHeight.ConvertToPx()
193 : paintProperty->GetGregorianCalendarHeightValue({}).ConvertToPx();
194 if (IsLargeSize(theme)) {
195 gregorianDayHeight = GetDaySize(theme).ConvertToPx();
196 }
197 auto selfHeight = constraint.selfIdealSize.Height();
198 if (!selfHeight.has_value()) {
199 return;
200 }
201 auto height = selfHeight.value()
202 - CALENDAR_DISTANCE_ADJUST_FOCUSED_EVENT.ConvertToPx() + gregorianDayHeight;
203 auto calendarDaySize = GetDaySize(theme);
204 auto space = (width - calendarDaySize.ConvertToPx() * CALENDAR_WEEK_DAYS) / (CALENDAR_WEEK_DAYS - 1);
205 if (Positive(space)) {
206 Dimension colSpace = 0.0_px;
207 colSpace.SetValue(space);
208 paintProperty->UpdateColSpace(colSpace);
209 }
210
211 auto rowCount = dataSize / CALENDAR_WEEK_DAYS;
212 space = (height - calendarDaySize.ConvertToPx() * (rowCount + 1)) / rowCount;
213 if (!Positive(space)) {
214 return;
215 }
216 Dimension rowSpace = 0.0_px;
217 rowSpace.SetValue(space);
218 paintProperty->UpdateWeekAndDayRowSpace(rowSpace);
219 switch (rowCount) {
220 case 4:
221 paintProperty->UpdateDailyFourRowSpace(rowSpace);
222 break;
223 case 6:
224 paintProperty->UpdateDailySixRowSpace(rowSpace);
225 break;
226 case 5:
227 default:
228 paintProperty->UpdateDailyFiveRowSpace(rowSpace);
229 break;
230 }
231 }
232
233 void CalendarMonthPattern::OnModifyDone()
234 {
235 Pattern::OnModifyDone();
236 InitClickEvent();
237 InitTouchEvent();
238 InitHoverEvent();
239 InitAccessibilityHoverEvent();
240 InitializeCalendarAccessibility();
241 }
242
243 void CalendarMonthPattern::SetVirtualNodeUserSelected(int32_t index)
244 {
245 if (accessibilityPropertyVec_.size() < 1) {
246 return;
247 }
248 auto host = GetHost();
249 CHECK_NULL_VOID(host);
250 auto layoutProperty = host->GetLayoutProperty();
251 CHECK_NULL_VOID(layoutProperty);
252 auto textDirection = layoutProperty->GetNonAutoLayoutDirection();
253 auto remainderWeek = index % CALENDAR_WEEK_DAYS;
254 int32_t selectedIndex = (textDirection == TextDirection::RTL ?
255 CALENDAR_WEEK_DAYS - remainderWeek * 2 + index - 1 : index);
256 std::string selectMessage;
257 for (int i = 0; i < static_cast<int32_t>(accessibilityPropertyVec_.size()); i++) {
258 if (i == selectedIndex &&
259 obtainedMonth_.days[i].month.month == obtainedMonth_.month &&
260 obtainedMonth_.days[i].month.year == obtainedMonth_.year) {
261 selectMessage += accessibilityPropertyVec_[index]->GetAccessibilityText();
262 continue;
263 }
264 accessibilityPropertyVec_[i]->SetUserSelected(false);
265 }
266 if (!obtainedMonth_.days.empty()) {
267 for (auto& day : obtainedMonth_.days) {
268 day.focused = false;
269 }
270 auto calendarEventHub = GetEventHub<CalendarEventHub>();
271 CHECK_NULL_VOID(calendarEventHub);
272 if (selectedIndex >= 0 && selectedIndex < static_cast<int32_t>(obtainedMonth_.days.size())) {
273 obtainedMonth_.days[selectedIndex].focused = true;
274 auto json = JsonUtil::Create(true);
275 json->Put("day", obtainedMonth_.days[selectedIndex].day);
276 json->Put("month", obtainedMonth_.days[selectedIndex].month.month);
277 json->Put("year", obtainedMonth_.days[selectedIndex].month.year);
278 calendarEventHub->UpdateSelectedChangeEvent(json->ToString());
279 }
280 }
281 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
282 if (!selectMessage.empty()) {
283 host->OnAccessibilityEvent(AccessibilityEventType::ANNOUNCE_FOR_ACCESSIBILITY, selectedTxt_ + selectMessage);
284 selectedIndex_ = index;
285 }
286 }
287
288 void CalendarMonthPattern::InitVirtualButtonClickEvent(RefPtr<FrameNode> frameNode, int32_t index)
289 {
290 CHECK_NULL_VOID(frameNode);
291 auto gesture = frameNode->GetOrCreateGestureEventHub();
292 CHECK_NULL_VOID(gesture);
293 auto clickCallback = [weak = WeakClaim(this), index](GestureEvent& info) {
294 auto calendarPattern = weak.Upgrade();
295 CHECK_NULL_VOID(calendarPattern);
296 calendarPattern->SetVirtualNodeUserSelected(index);
297 };
298 auto clickListener = MakeRefPtr<ClickEvent>(std::move(clickCallback));
299 gesture->AddClickAfterEvent(clickListener);
300 }
301
302 void CalendarMonthPattern::InitClickEvent()
303 {
304 if (clickListener_) {
305 return;
306 }
307
308 auto host = GetHost();
309 CHECK_NULL_VOID(host);
310 auto gesture = host->GetOrCreateGestureEventHub();
311 CHECK_NULL_VOID(gesture);
312 auto obtainedMonth = obtainedMonth_;
313 auto clickCallback = [weak = WeakClaim(this), obtainedMonth](GestureEvent& info) {
314 auto calendarPattern = weak.Upgrade();
315 CHECK_NULL_VOID(calendarPattern);
316 auto localLocation = info.GetFingerList().begin()->localLocation_;
317 calendarPattern->OnClick(localLocation, calendarPattern->obtainedMonth_);
318 };
319 clickListener_ = MakeRefPtr<ClickEvent>(std::move(clickCallback));
320 gesture->AddClickEvent(clickListener_);
321 }
322
323 float CalendarMonthPattern::GetWidth(const RefPtr<FrameNode>& host)
324 {
325 auto width = 0.0f;
326 auto contentConstraint = host->GetLayoutProperty()->GetLayoutConstraint();
327 if (!contentConstraint.has_value()) {
328 return width;
329 }
330 auto constraint = contentConstraint.value();
331 auto selfWidth = constraint.selfIdealSize.Width();
332 if (!selfWidth.has_value()) {
333 return width;
334 }
335 width = selfWidth.value()
336 - CALENDAR_DISTANCE_ADJUST_FOCUSED_EVENT.ConvertToPx() * CALENDAR_DISTANCE_ADJUST_FOCUSED_SIZE;
337 return width;
338 }
339
340 void CalendarMonthPattern::OnColorConfigurationUpdate()
341 {
342 auto host = GetHost();
343 CHECK_NULL_VOID(host);
344 auto pipelineContext = host->GetContext();
345 CHECK_NULL_VOID(pipelineContext);
346 RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
347 CHECK_NULL_VOID(theme);
348 auto swiperNode = host->GetParent();
349 CHECK_NULL_VOID(swiperNode);
350 auto calendarNode = swiperNode->GetParent();
351 CHECK_NULL_VOID(calendarNode);
352 auto scrollNode = calendarNode->GetParent();
353 CHECK_NULL_VOID(scrollNode);
354 auto columnNode = scrollNode->GetParent();
355 CHECK_NULL_VOID(columnNode);
356 auto rowNode = columnNode->GetChildAtIndex(WEEK_ROW_INDEX);
357 CHECK_NULL_VOID(rowNode);
358 auto textNodes = rowNode->GetChildren();
359 for (auto textNode : textNodes) {
360 auto textFrameNode = AceType::DynamicCast<NG::FrameNode>(textNode);
361 CHECK_NULL_VOID(textFrameNode);
362 auto textLayoutProperty = textFrameNode->GetLayoutProperty<TextLayoutProperty>();
363 CHECK_NULL_VOID(textLayoutProperty);
364 textLayoutProperty->UpdateTextColor(theme->GetCalendarTheme().weekColor);
365 }
366 }
367
368 void CalendarMonthPattern::OnLanguageConfigurationUpdate()
369 {
370 auto host = GetHost();
371 CHECK_NULL_VOID(host);
372 auto pipelineContext = host->GetContext();
373 CHECK_NULL_VOID(pipelineContext);
374 RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
375 CHECK_NULL_VOID(theme);
376 auto swiperNode = host->GetParent();
377 CHECK_NULL_VOID(swiperNode);
378 auto calendarNode = swiperNode->GetParent();
379 CHECK_NULL_VOID(calendarNode);
380 auto scrollNode = calendarNode->GetParent();
381 CHECK_NULL_VOID(scrollNode);
382 auto columnNode = scrollNode->GetParent();
383 CHECK_NULL_VOID(columnNode);
384 auto rowNode = columnNode->GetChildAtIndex(WEEK_ROW_INDEX);
385 CHECK_NULL_VOID(rowNode);
386 auto textNodes = rowNode->GetChildren();
387 std::vector<std::string> weekNumbers = Localization::GetInstance()->GetWeekdays(true);
388 int32_t column = 0;
389 for (auto textNode : textNodes) {
390 std::string weekContent { weekNumbers[column % CALENDAR_WEEK_DAYS] };
391 auto textFrameNode = AceType::DynamicCast<NG::FrameNode>(textNode);
392 CHECK_NULL_VOID(textFrameNode);
393 auto calendarPaintProperty = host->GetPaintProperty<CalendarPaintProperty>();
394 CHECK_NULL_VOID(calendarPaintProperty);
395 auto fontSize = calendarPaintProperty->GetWeekFontSize().value_or(theme->GetCalendarTheme().weekFontSize);
396 auto textLayoutProperty = textFrameNode->GetLayoutProperty<TextLayoutProperty>();
397 CHECK_NULL_VOID(textLayoutProperty);
398 textLayoutProperty->UpdateContent(weekContent);
399 textLayoutProperty->UpdateFontSize(fontSize);
400 ++column;
401 }
402 }
403
404 void CalendarMonthPattern::BeforeSyncGeometryProperties(const DirtySwapConfig& config)
405 {
406 auto host = GetHost();
407 CHECK_NULL_VOID(host);
408 auto width = GetWidth(host);
409 auto pipelineContext = host->GetContext();
410 CHECK_NULL_VOID(pipelineContext);
411 RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
412 CHECK_NULL_VOID(theme);
413 auto calendarDaySize = GetDaySize(theme);
414 auto space = (width - calendarDaySize.ConvertToPx() * CALENDAR_WEEK_DAYS) / (CALENDAR_WEEK_DAYS - 1);
415 Dimension colSpace = 0.0_px;
416 if (Positive(space)) {
417 colSpace.SetValue(space);
418 }
419 auto swiperNode = host->GetParent();
420 CHECK_NULL_VOID(swiperNode);
421 auto calendarNode = swiperNode->GetParent();
422 CHECK_NULL_VOID(calendarNode);
423 auto scrollNode = calendarNode->GetParent();
424 CHECK_NULL_VOID(scrollNode);
425 auto columnNode = scrollNode->GetParent();
426 CHECK_NULL_VOID(columnNode);
427 auto rowNode = columnNode->GetChildAtIndex(WEEK_ROW_INDEX);
428 CHECK_NULL_VOID(rowNode);
429 auto textNodes = rowNode->GetChildren();
430 for (auto textNode : textNodes) {
431 auto textFrameNode = AceType::DynamicCast<NG::FrameNode>(textNode);
432 CHECK_NULL_VOID(textFrameNode);
433 auto calendarPaintProperty = host->GetPaintProperty<CalendarPaintProperty>();
434 CHECK_NULL_VOID(calendarPaintProperty);
435 auto fontSize = calendarPaintProperty->GetWeekFontSize().value_or(theme->GetCalendarTheme().weekFontSize);
436 auto textLayoutProperty = textFrameNode->GetLayoutProperty<TextLayoutProperty>();
437 CHECK_NULL_VOID(textLayoutProperty);
438 textLayoutProperty->UpdateFontSize(fontSize);
439 textLayoutProperty->UpdateTextColor(theme->GetCalendarTheme().weekColor);
440 textLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(calendarDaySize), std::nullopt));
441 }
442
443 auto rowFrameNode = AceType::DynamicCast<NG::FrameNode>(rowNode);
444 CHECK_NULL_VOID(rowFrameNode);
445 auto weekLayoutProperty = rowFrameNode->GetLayoutProperty<LinearLayoutProperty>();
446 CHECK_NULL_VOID(weekLayoutProperty);
447 weekLayoutProperty->UpdateSpace(colSpace);
448 rowFrameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
449 }
450
451 void CalendarMonthPattern::InitTouchEvent()
452 {
453 if (touchListener_) {
454 return;
455 }
456
457 auto host = GetHost();
458 CHECK_NULL_VOID(host);
459 auto gesture = host->GetOrCreateGestureEventHub();
460 CHECK_NULL_VOID(gesture);
461 auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
462 auto calendarPattern = weak.Upgrade();
463 CHECK_NULL_VOID(calendarPattern);
464 if (info.GetTouches().empty()) {
465 return;
466 }
467 if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
468 calendarPattern->OnTouchEvent(info.GetTouches().front().GetLocalLocation(), true);
469 }
470 if (info.GetTouches().front().GetTouchType() == TouchType::UP ||
471 info.GetTouches().front().GetTouchType() == TouchType::CANCEL) {
472 calendarPattern->OnTouchEvent(info.GetTouches().front().GetLocalLocation(), false);
473 }
474 };
475 touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
476 gesture->AddTouchEvent(touchListener_);
477 }
478
479 void CalendarMonthPattern::InitHoverEvent()
480 {
481 if (hoverListener_) {
482 return;
483 }
484
485 auto host = GetHost();
486 CHECK_NULL_VOID(host);
487 auto eventHub = GetEventHub<CalendarEventHub>();
488 CHECK_NULL_VOID(eventHub);
489 auto inputHub = eventHub->GetOrCreateInputEventHub();
490 CHECK_NULL_VOID(inputHub);
491 auto hoverCallback = [weak = WeakClaim(this)](bool state) {
492 auto calendarPattern = weak.Upgrade();
493 CHECK_NULL_VOID(calendarPattern);
494 calendarPattern->SetHoverState(state);
495 if (!state) {
496 Offset localLocation;
497 calendarPattern->OnHoverEvent(localLocation, false);
498 }
499 };
500 hoverListener_ = MakeRefPtr<InputEvent>(std::move(hoverCallback));
501 inputHub->AddOnHoverEvent(hoverListener_);
502 auto mouseCallback = [weak = WeakClaim(this)](MouseInfo& info) {
503 auto calendarPattern = weak.Upgrade();
504 CHECK_NULL_VOID(calendarPattern);
505 calendarPattern->OnHoverEvent(info.GetLocalLocation(), calendarPattern->GetHoverState());
506 };
507 inputHub->SetMouseEvent(std::move(mouseCallback));
508 }
509
510 bool CalendarMonthPattern::IsDateInRange(const CalendarDay& day)
511 {
512 PickerDate date;
513 date.SetYear(day.month.year);
514 date.SetMonth(day.month.month);
515 date.SetDay(day.day);
516 for (const auto& range : disabledDateRange_) {
517 if (PickerDate::IsDateInRange(date, range.first, range.second)) {
518 return false;
519 }
520 }
521 return PickerDate::IsDateInRange(date, startDate_, endDate_);
522 }
523
524 void CalendarMonthPattern::OnClick(Offset& localLocation, const ObtainedMonth& obtainedMonth)
525 {
526 auto host = GetHost();
527 CHECK_NULL_VOID(host);
528 auto pattern = host->GetPattern<CalendarMonthPattern>();
529 CHECK_NULL_VOID(pattern);
530 auto index = JudgeArea(localLocation);
531 pattern->obtainedMonth_ = obtainedMonth;
532 if (!obtainedMonth_.days.empty()) {
533 if (!IsDateInRange(obtainedMonth_.days[index])) {
534 return;
535 }
536 for (auto& day : pattern->obtainedMonth_.days) {
537 day.focused = false;
538 }
539 auto calendarEventHub = GetEventHub<CalendarEventHub>();
540 CHECK_NULL_VOID(calendarEventHub);
541 if (index >= 0 && index < static_cast<int32_t>(obtainedMonth.days.size())) {
542 pattern->obtainedMonth_.days[index].focused = true;
543 auto json = JsonUtil::Create(true);
544 json->Put("day", obtainedMonth.days[index].day);
545 json->Put("month", obtainedMonth.days[index].month.month);
546 json->Put("year", obtainedMonth.days[index].month.year);
547 calendarEventHub->UpdateSelectedChangeEvent(json->ToString());
548 }
549 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
550 }
551 }
552
553 void CalendarMonthPattern::OnTouchEvent(const Offset& localLocation, bool isPressed)
554 {
555 if (!isCalendarDialog_ || obtainedMonth_.days.empty()) {
556 return;
557 }
558 auto index = JudgeArea(localLocation);
559 if (!(index < 0 || index >= static_cast<int32_t>(obtainedMonth_.days.size())) && isPressed &&
560 IsDateInRange(obtainedMonth_.days[index])) {
561 obtainedMonth_.days[index].isPressing = true;
562 } else {
563 for (auto& day : obtainedMonth_.days) {
564 day.isPressing = false;
565 }
566 }
567
568 auto host = GetHost();
569 CHECK_NULL_VOID(host);
570 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
571 }
572
573 void CalendarMonthPattern::OnHoverEvent(const Offset& localLocation, bool state)
574 {
575 if (!isCalendarDialog_ || obtainedMonth_.days.empty()) {
576 return;
577 }
578 int32_t index = JudgeArea(localLocation);
579 if (index < 0 || index >= static_cast<int32_t>(obtainedMonth_.days.size())) {
580 return;
581 }
582 for (auto& day : obtainedMonth_.days) {
583 day.isHovering = false;
584 }
585 if (state && IsDateInRange(obtainedMonth_.days[index])) {
586 obtainedMonth_.days[index].isHovering = true;
587 }
588
589 auto host = GetHost();
590 CHECK_NULL_VOID(host);
591 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
592 }
593
594 int32_t CalendarMonthPattern::JudgeArea(const Offset& offset)
595 {
596 auto host = GetHost();
597 CHECK_NULL_RETURN(host, false);
598 auto paintProperty = host->GetPaintProperty<CalendarPaintProperty>();
599 CHECK_NULL_RETURN(paintProperty, false);
600 auto pipelineContext = host->GetContext();
601 CHECK_NULL_RETURN(pipelineContext, false);
602 RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
603 CHECK_NULL_RETURN(theme, false);
604 auto gregorianDayHeight = paintProperty->GetGregorianCalendarHeightValue({}).ConvertToPx() <= 0
605 ? theme->GetCalendarTheme().gregorianCalendarHeight.ConvertToPx()
606 : paintProperty->GetGregorianCalendarHeightValue({}).ConvertToPx();
607 auto weekHeight = paintProperty->GetWeekHeight().value_or(theme->GetCalendarTheme().weekHeight).ConvertToPx();
608 auto weekAndDayRowSpace =
609 paintProperty->GetWeekAndDayRowSpace().value_or(theme->GetCalendarTheme().weekAndDayRowSpace).ConvertToPx();
610 auto dayHeight = paintProperty->GetDayHeight().value_or(theme->GetCalendarTheme().dayHeight).ConvertToPx();
611 auto dayWidth = paintProperty->GetDayWidth().value_or(theme->GetCalendarTheme().dayWidth).ConvertToPx();
612 const static int32_t columnsOfData = 7;
613 auto colSpace = paintProperty->GetColSpaceValue({}).ConvertToPx() <= 0
614 ? theme->GetCalendarTheme().colSpace.ConvertToPx()
615 : paintProperty->GetColSpaceValue({}).ConvertToPx();
616 auto dailyFourRowSpace = NonPositive(paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx())
617 ? theme->GetCalendarTheme().dailySixRowSpace.ConvertToPx()
618 : paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx();
619 auto dailyFiveRowSpace = paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx() <= 0
620 ? theme->GetCalendarTheme().dailyFiveRowSpace.ConvertToPx()
621 : paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx();
622 auto dailySixRowSpace = paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx() <= 0
623 ? theme->GetCalendarTheme().dailySixRowSpace.ConvertToPx()
624 : paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx();
625 auto rows = (static_cast<int32_t>(obtainedMonth_.days.size()) / columnsOfData);
626 auto rowSpace = dailySixRowSpace;
627 switch (rows) {
628 case 4: {
629 rowSpace = dailyFourRowSpace;
630 break;
631 }
632 case 5: {
633 rowSpace = dailyFiveRowSpace;
634 break;
635 }
636 default:
637 break;
638 }
639 if (IsLargeSize(theme)) {
640 gregorianDayHeight = GetDaySize(theme).ConvertToPx();
641 }
642 auto browHeight = weekHeight + weekAndDayRowSpace - gregorianDayHeight;
643 auto maxHeight = host->GetGeometryNode()->GetFrameSize().Height();
644 auto maxWidth = host->GetGeometryNode()->GetFrameSize().Width();
645 if ((offset.GetX() < 0) || (offset.GetX() > maxWidth) || (offset.GetY() < browHeight) ||
646 (offset.GetY() > maxHeight) || LessOrEqual(dayHeight, 0.0) || LessOrEqual(dayWidth, 0.0)) {
647 return -1;
648 }
649 auto height = offset.GetY() - browHeight;
650 int32_t y =
651 height < (dayHeight + rowSpace / 2) ? 0 : (height - dayHeight - rowSpace / 2) / (dayHeight + rowSpace) + 1;
652 int32_t x = offset.GetX() < (dayWidth + colSpace / 2)
653 ? 0
654 : (offset.GetX() - dayWidth - colSpace / 2) / (dayWidth + colSpace) + 1;
655 auto textDirection = host->GetLayoutProperty()->GetNonAutoLayoutDirection();
656 if (textDirection == TextDirection::RTL) {
657 x = columnsOfData - x - 1;
658 }
659 return (y * columnsOfData + x);
660 }
661
662 class CalendarAccessibilitySAObserverCallback : public AccessibilitySAObserverCallback {
663 public:
664 CalendarAccessibilitySAObserverCallback(
665 const WeakPtr<CalendarMonthPattern> &weakCalendarPattern, int64_t accessibilityId)
666 : AccessibilitySAObserverCallback(accessibilityId), weakCalendarPattern_(weakCalendarPattern)
667 {}
668
669 ~CalendarAccessibilitySAObserverCallback() override = default;
670
671 bool OnState(bool state) override
672 {
673 auto calendarPattern = weakCalendarPattern_.Upgrade();
674 CHECK_NULL_RETURN(calendarPattern, false);
675 if (state) {
676 calendarPattern->InitCurrentVirtualNode();
677 } else {
678 calendarPattern->ClearCalendarVirtualNode();
679 }
680 return true;
681 }
682 private:
683 WeakPtr<CalendarMonthPattern> weakCalendarPattern_;
684 };
685
686 void CalendarMonthPattern::InitializeCalendarAccessibility()
687 {
688 auto host = GetHost();
689 CHECK_NULL_VOID(host);
690 auto pipeline = host->GetContext();
691 CHECK_NULL_VOID(pipeline);
692 auto accessibilityManager = pipeline->GetAccessibilityManager();
693 CHECK_NULL_VOID(accessibilityManager);
694 accessibilitySAObserverCallback_ = std::make_shared<CalendarAccessibilitySAObserverCallback>(
695 WeakClaim(this), host->GetAccessibilityId());
696 accessibilityManager->RegisterAccessibilitySAObserverCallback(host->GetAccessibilityId(),
697 accessibilitySAObserverCallback_);
698 if (margin_ == 0) {
699 auto paintProperty = host->GetPaintProperty<CalendarPaintProperty>();
700 CHECK_NULL_VOID(paintProperty);
701 dayHeight_ = paintProperty->GetDayHeight().value_or(Dimension(0.0f)).ConvertToPx();
702 dayWidth_ = paintProperty->GetDayWidth().value_or(Dimension(0.0f)).ConvertToPx();
703 RefPtr<CalendarTheme> theme = pipeline->GetTheme<CalendarTheme>();
704 CHECK_NULL_VOID(theme);
705 auto sliderTheme = pipeline->GetTheme<SliderTheme>();
706 CHECK_NULL_VOID(sliderTheme);
707 margin_ = theme->GetDialogMargin().ConvertToPx();
708 selectedTxt_ = sliderTheme->GetSelectedTxt();
709 disabledDesc_ = sliderTheme->GetDisabelDesc();
710 deviceOrientation_ = SystemProperties::GetDeviceOrientation();
711 }
712 }
713
714 void CalendarMonthPattern::InitCurrentVirtualNode()
715 {
716 auto deviceOrientation = SystemProperties::GetDeviceOrientation();
717 if ((!isInitVirtualNode_ || buttonAccessibilityNodeVec_.size() != obtainedMonth_.days.size() ||
718 deviceOrientation_ != deviceOrientation) &&
719 monthState_ == MonthState::CUR_MONTH) {
720 isInitVirtualNode_ = InitCalendarVirtualNode();
721 } else {
722 FireModifyAccessibilityVirtualNode(obtainedMonth_);
723 }
724 }
725
726 void CalendarMonthPattern::ClearFocusCalendarDay()
727 {
728 focusedCalendarDay_.index = 0;
729 deviceOrientation_ = SystemProperties::GetDeviceOrientation();
730 CHECK_NULL_VOID(lineNode_);
731 auto lineNodeProp = lineNode_->GetLayoutProperty();
732 CHECK_NULL_VOID(lineNodeProp);
733 if (monthState_ == MonthState::CUR_MONTH) {
734 lineNodeProp->UpdateVisibility(VisibleType::VISIBLE);
735 } else {
736 lineNodeProp->UpdateVisibility(VisibleType::GONE);
737 }
738 }
739
740 void CalendarMonthPattern::ClearCalendarVirtualNode()
741 {
742 auto host = GetHost();
743 CHECK_NULL_VOID(host);
744 buttonAccessibilityNodeVec_.clear();
745 accessibilityPropertyVec_.clear();
746 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
747 CHECK_NULL_VOID(accessibilityProperty);
748 accessibilityProperty->SaveAccessibilityVirtualNode(nullptr);
749 }
750
751 void CalendarMonthPattern::SetLineNodeSize(RefPtr<FrameNode> lineNode)
752 {
753 auto host = GetHost();
754 CHECK_NULL_VOID(host);
755 auto hostLayoutProperty = host->GetLayoutProperty();
756 CHECK_NULL_VOID(hostLayoutProperty);
757 auto width = hostLayoutProperty->GetLayoutConstraint()->selfIdealSize.Width().value_or(Infinity<float>());
758 auto height = hostLayoutProperty->GetLayoutConstraint()->selfIdealSize.Height().value_or(Infinity<float>());
759 CHECK_NULL_VOID(lineNode);
760 auto layoutProperty = lineNode->GetLayoutProperty<LayoutProperty>();
761 CHECK_NULL_VOID(layoutProperty);
762 layoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(width), CalcLength(height)));
763 }
764
765 void CalendarMonthPattern::SetFocusNode(int32_t index, bool isDeviceOrientation)
766 {
767 auto host = GetHost();
768 CHECK_NULL_VOID(host);
769 auto layoutProperty = host->GetLayoutProperty();
770 CHECK_NULL_VOID(layoutProperty);
771 auto textDirection = layoutProperty->GetNonAutoLayoutDirection();
772 auto remainderWeek = index % CALENDAR_WEEK_DAYS;
773 int32_t selectedIndex = (textDirection == TextDirection::RTL ?
774 CALENDAR_WEEK_DAYS - remainderWeek * 2 + index - 1 : index);
775 if (isDeviceOrientation) {
776 selectedIndex = index;
777 }
778 if (selectedIndex >= static_cast<int32_t>(buttonAccessibilityNodeVec_.size()) || selectedIndex < 0) {
779 return;
780 }
781 buttonAccessibilityNodeVec_[selectedIndex]->OnAccessibilityEvent(AccessibilityEventType::REQUEST_FOCUS);
782 selectedIndex_ = selectedIndex;
783 }
784
785 bool CalendarMonthPattern::InitCalendarVirtualNode()
786 {
787 auto host = GetHost();
788 CHECK_NULL_RETURN(host, false);
789 buttonAccessibilityNodeVec_.clear();
790 accessibilityPropertyVec_.clear();
791 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
792 CHECK_NULL_RETURN(accessibilityProperty, false);
793 accessibilityProperty->SaveAccessibilityVirtualNode(nullptr);
794 auto lineNode = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
795 AceType::MakeRefPtr<LinearLayoutPattern>(true));
796 for (auto& day : obtainedMonth_.days) {
797 auto buttonNode = AddButtonNodeIntoVirtual(day);
798 lineNode->AddChild(buttonNode);
799 buttonAccessibilityNodeVec_.emplace_back(buttonNode);
800 ChangeVirtualNodeContent(day);
801 }
802 SetLineNodeSize(lineNode);
803 SetCalendarAccessibilityLevel(AccessibilityProperty::Level::NO_STR);
804 accessibilityProperty->SetAccessibilityText(" ");
805 auto virtualFrameNode = AceType::DynamicCast<NG::FrameNode>(lineNode);
806 CHECK_NULL_RETURN(virtualFrameNode, false);
807 virtualFrameNode->SetAccessibilityNodeVirtual();
808 virtualFrameNode->SetAccessibilityVirtualNodeParent(AceType::DynamicCast<NG::UINode>(host));
809 virtualFrameNode->SetFirstAccessibilityVirtualNode();
810 FrameNode::ProcessOffscreenNode(virtualFrameNode);
811 accessibilityProperty->SaveAccessibilityVirtualNode(lineNode);
812 lineNode_ = lineNode;
813 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
814 auto deviceOrientation = SystemProperties::GetDeviceOrientation();
815 if (deviceOrientation_ != deviceOrientation && !isFirstEnter_) {
816 SetFocusNode(focusedCalendarDay_.index, true);
817 deviceOrientation_ = deviceOrientation;
818 }
819 if (!isFirstEnter_) {
820 return true;
821 }
822 for (auto &day : obtainedMonth_.days) {
823 if (day.isSelected) {
824 SetFocusNode(day.index);
825 isFirstEnter_ = false;
826 focusedCalendarDay_ = day;
827 break;
828 }
829 }
830 return true;
831 }
832
833 std::pair<int32_t, int32_t> GetXYByIndex(const int32_t index)
834 {
835 if (index < CALENDAR_WEEK_DAYS - 1) {
836 return {index, 0};
837 } else {
838 return {(index % CALENDAR_WEEK_DAYS), (index / CALENDAR_WEEK_DAYS)};
839 }
840 }
841
842 void CalendarMonthPattern::ChangeVirtualNodeState(const CalendarDay& calendarDay)
843 {
844 if (selectedIndex_ >= static_cast<int32_t>(accessibilityPropertyVec_.size()) || selectedIndex_ < 0) {
845 return;
846 }
847 if (calendarDay.index != selectedIndex_ &&
848 calendarDay.month.month == obtainedMonth_.month && calendarDay.month.month == obtainedMonth_.month) {
849 accessibilityPropertyVec_[selectedIndex_]->SetUserSelected(true);
850 }
851 }
852
853 RefPtr<FrameNode> CalendarMonthPattern::AddButtonNodeIntoVirtual(const CalendarDay& calendarDay)
854 {
855 auto buttonNode = FrameNode::CreateFrameNode(
856 V2::BUTTON_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ButtonPattern>());
857 auto buttonAccessibilityProperty = buttonNode->GetAccessibilityProperty<AccessibilityProperty>();
858 CHECK_NULL_RETURN(buttonAccessibilityProperty, nullptr);
859 auto host = GetHost();
860 CHECK_NULL_RETURN(host, nullptr);
861 auto pipeline = host->GetContext();
862 if (pipeline) {
863 UpdateAccessibilityButtonNode(buttonNode, calendarDay.index);
864 } else {
865 UpdateButtonNodeWithoutTheme(buttonNode, calendarDay.index);
866 }
867 accessibilityPropertyVec_.emplace_back(buttonAccessibilityProperty);
868 InitVirtualButtonClickEvent(buttonNode, calendarDay.index);
869 buttonAccessibilityProperty->SetOnAccessibilityFocusCallback([weak = WeakClaim(this), calendarDay](bool focus) {
870 if (focus) {
871 auto calendar = weak.Upgrade();
872 CHECK_NULL_VOID(calendar);
873 calendar->focusedCalendarDay_ = calendarDay;
874 calendar->ChangeVirtualNodeState(calendarDay);
875 }
876 });
877 return buttonNode;
878 }
879
880 float GetRowSpace(RefPtr<CalendarPaintProperty> paintProperty, RefPtr<CalendarTheme> theme, size_t daySize)
881 {
882 auto dailyFourRowSpace = NonPositive(paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx())
883 ? theme->GetCalendarTheme().dailySixRowSpace.ConvertToPx()
884 : paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx();
885 auto dailyFiveRowSpace = paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx() <= 0
886 ? theme->GetCalendarTheme().dailyFiveRowSpace.ConvertToPx()
887 : paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx();
888 auto dailySixRowSpace = paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx() <= 0
889 ? theme->GetCalendarTheme().dailySixRowSpace.ConvertToPx()
890 : paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx();
891 auto rows = (static_cast<int32_t>(daySize) / CALENDAR_WEEK_DAYS);
892 auto rowSpace = dailySixRowSpace;
893 switch (rows) {
894 case DAILY_FOUR_ROWSPACE: {
895 rowSpace = dailyFourRowSpace;
896 break;
897 }
898 case DAILY_FIVE_ROWSPACE: {
899 rowSpace = dailyFiveRowSpace;
900 break;
901 }
902 default:
903 break;
904 }
905 return rowSpace;
906 }
907
908 void CalendarMonthPattern::SetCalendarAccessibilityLevel(const std::string& level)
909 {
910 auto host = GetHost();
911 CHECK_NULL_VOID(host);
912 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
913 CHECK_NULL_VOID(accessibilityProperty);
914 accessibilityProperty->SetAccessibilityLevel(level);
915 auto parent = host->GetParent();
916 while (parent && parent->GetTag() != V2::DIALOG_ETS_TAG) {
917 auto parentNode = AceType::DynamicCast<NG::FrameNode>(parent);
918 CHECK_NULL_VOID(parentNode);
919 auto parentAccessibilityProperty = parentNode->GetAccessibilityProperty<AccessibilityProperty>();
920 CHECK_NULL_VOID(parentAccessibilityProperty);
921 parentAccessibilityProperty->SetAccessibilityLevel(level);
922 parent = parent->GetParent();
923 }
924 }
925
926 void CalendarMonthPattern::InitAccessibilityHoverEvent()
927 {
928 auto host = GetHost();
929 CHECK_NULL_VOID(host);
930 auto eventHub = host->GetOrCreateInputEventHub();
931 CHECK_NULL_VOID(eventHub);
932 eventHub->SetAccessibilityHoverEvent([weak = WeakClaim(this)](bool isHover, AccessibilityHoverInfo& info) {
933 auto calendar = weak.Upgrade();
934 CHECK_NULL_VOID(calendar);
935 calendar->HandleAccessibilityHoverEvent(isHover, info);
936 });
937 }
938
939 void CalendarMonthPattern::HandleAccessibilityHoverEvent(bool isHover, AccessibilityHoverInfo& info)
940 {
941 if (isHover) {
942 isOnHover_ = true;
943 } else if (!isHover) {
944 isOnHover_ = false;
945 }
946 }
947
948 float GetRowSpaceWithoutTheme(RefPtr<CalendarPaintProperty> paintProperty, size_t daySize)
949 {
950 auto dailyFourRowSpace = NonPositive(paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx())
951 ? 0.0f
952 : paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx();
953 auto dailyFiveRowSpace = paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx() <= 0
954 ? 0.0f
955 : paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx();
956 auto dailySixRowSpace = paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx() <= 0
957 ? 0.0f
958 : paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx();
959 auto rows = (static_cast<int32_t>(daySize) / CALENDAR_WEEK_DAYS);
960 auto rowSpace = dailySixRowSpace;
961 switch (rows) {
962 case DAILY_FOUR_ROWSPACE: {
963 rowSpace = dailyFourRowSpace;
964 break;
965 }
966 case DAILY_FIVE_ROWSPACE: {
967 rowSpace = dailyFiveRowSpace;
968 break;
969 }
970 default:
971 break;
972 }
973 return rowSpace;
974 }
975
976 void CalendarMonthPattern::UpdateButtonNodeWithoutTheme(RefPtr<FrameNode> frameNode, int32_t index)
977 {
978 auto host = GetHost();
979 CHECK_NULL_VOID(host);
980 auto paintProperty = host->GetPaintProperty<CalendarPaintProperty>();
981 CHECK_NULL_VOID(paintProperty);
982 auto dayHeight = paintProperty->GetDayHeight().value_or(Dimension(0.0f)).ConvertToPx();
983 auto dayWidth = paintProperty->GetDayWidth().value_or(Dimension(0.0f)).ConvertToPx();
984 CHECK_NULL_VOID(frameNode);
985 auto buttonLayoutProperty = frameNode->GetLayoutProperty<ButtonLayoutProperty>();
986 CHECK_NULL_VOID(buttonLayoutProperty);
987 buttonLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(dayWidth), CalcLength(dayHeight)));
988 auto pos = GetXYByIndex(index);
989 auto colSpace = paintProperty->GetColSpaceValue({}).ConvertToPx() <= 0
990 ? 0.0f
991 : paintProperty->GetColSpaceValue({}).ConvertToPx();
992 Dimension buttonOffsetX = Dimension(margin_ / 2 + (colSpace + dayWidth) * pos.first);
993 auto weekHeight = paintProperty->GetWeekHeight().value_or(Dimension(0.0f)).ConvertToPx();
994 auto rowSpace = GetRowSpaceWithoutTheme(paintProperty, obtainedMonth_.days.size());
995 auto weekAndDayRowSpace =
996 paintProperty->GetWeekAndDayRowSpace().value_or(Dimension(0.0f)).ConvertToPx();
997 auto gregorianDayHeight = paintProperty->GetGregorianCalendarHeightValue({}).ConvertToPx();
998 auto browHeight = weekHeight + weekAndDayRowSpace - gregorianDayHeight;
999 Dimension buttonOffsetY = Dimension(browHeight + (dayHeight + rowSpace) * pos.second);
1000 auto renderContext = frameNode->GetRenderContext();
1001 CHECK_NULL_VOID(renderContext);
1002 renderContext->UpdatePosition(OffsetT(buttonOffsetX, buttonOffsetY));
1003 frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1004 }
1005
1006 void CalendarMonthPattern::UpdateAccessibilityButtonNode(RefPtr<FrameNode> frameNode, int32_t index)
1007 {
1008 auto host = GetHost();
1009 CHECK_NULL_VOID(host);
1010 auto paintProperty = host->GetPaintProperty<CalendarPaintProperty>();
1011 CHECK_NULL_VOID(paintProperty);
1012 auto pipelineContext = host->GetContext();
1013 CHECK_NULL_VOID(pipelineContext);
1014 RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
1015 CHECK_NULL_VOID(theme);
1016 auto dayHeight = paintProperty->GetDayHeight().value_or(theme->GetCalendarTheme().dayHeight).ConvertToPx();
1017 auto dayWidth = paintProperty->GetDayWidth().value_or(theme->GetCalendarTheme().dayWidth).ConvertToPx();
1018 CHECK_NULL_VOID(frameNode);
1019 auto buttonLayoutProperty = frameNode->GetLayoutProperty<ButtonLayoutProperty>();
1020 CHECK_NULL_VOID(buttonLayoutProperty);
1021 buttonLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(dayWidth), CalcLength(dayHeight)));
1022 auto renderContext = frameNode->GetRenderContext();
1023 CHECK_NULL_VOID(renderContext);
1024 auto pos = GetXYByIndex(index);
1025 auto colSpace = paintProperty->GetColSpaceValue({}).ConvertToPx() <= 0
1026 ? theme->GetCalendarTheme().colSpace.ConvertToPx()
1027 : paintProperty->GetColSpaceValue({}).ConvertToPx();
1028 colSpace_ = colSpace;
1029 Dimension buttonOffsetX = Dimension(margin_ / 2 + (colSpace + dayWidth) * pos.first);
1030 auto gregorianDayHeight = paintProperty->GetGregorianCalendarHeightValue({}).ConvertToPx() <= 0
1031 ? theme->GetCalendarTheme().gregorianCalendarHeight.ConvertToPx()
1032 : paintProperty->GetGregorianCalendarHeightValue({}).ConvertToPx();
1033 auto weekHeight = paintProperty->GetWeekHeight().value_or(theme->GetCalendarTheme().weekHeight).ConvertToPx();
1034 auto rowSpace = GetRowSpace(paintProperty, theme, obtainedMonth_.days.size());
1035 auto weekAndDayRowSpace =
1036 paintProperty->GetWeekAndDayRowSpace().value_or(theme->GetCalendarTheme().weekAndDayRowSpace).ConvertToPx();
1037 if (IsLargeSize(theme)) {
1038 gregorianDayHeight = GetDaySize(theme).ConvertToPx();
1039 }
1040 auto browHeight = weekHeight + weekAndDayRowSpace - gregorianDayHeight;
1041 Dimension buttonOffsetY = Dimension(browHeight + (dayHeight + rowSpace) * pos.second);
1042 renderContext->UpdatePosition(OffsetT(buttonOffsetX, buttonOffsetY));
1043 frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1044 }
1045
1046 std::string CalendarMonthPattern::GetDayStr(int32_t index)
1047 {
1048 auto host = GetHost();
1049 CHECK_NULL_RETURN(host, "");
1050 auto pipelineContext = host->GetContext();
1051 CHECK_NULL_RETURN(pipelineContext, "");
1052 RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
1053 CHECK_NULL_RETURN(theme, "");
1054 switch (index) {
1055 case MONDAY_INDEX:
1056 return theme->GetCalendarTheme().monday;
1057 case TUESDAY_INDEX:
1058 return theme->GetCalendarTheme().tuesday;
1059 case WEDNESDAY_INDEX:
1060 return theme->GetCalendarTheme().wednesday;
1061 case THURSDAY_INDEX:
1062 return theme->GetCalendarTheme().thursday;
1063 case FRIDAY_INDEX:
1064 return theme->GetCalendarTheme().friday;
1065 case SATURDAY_INDEX:
1066 return theme->GetCalendarTheme().saturday;
1067 default:
1068 return theme->GetCalendarTheme().sunday;
1069 }
1070 }
1071
1072 void CalendarMonthPattern::ChangeVirtualNodeContent(const CalendarDay& calendarDay)
1073 {
1074 auto host = GetHost();
1075 CHECK_NULL_VOID(host);
1076 auto layoutProperty = host->GetLayoutProperty();
1077 CHECK_NULL_VOID(layoutProperty);
1078 auto textDirection = layoutProperty->GetNonAutoLayoutDirection();
1079 auto remainderWeek = calendarDay.index % CALENDAR_WEEK_DAYS;
1080 int32_t index = (textDirection == TextDirection::RTL ?
1081 CALENDAR_WEEK_DAYS - remainderWeek * 2 + calendarDay.index - 1 : calendarDay.index);
1082 if (index >= static_cast<int32_t>(buttonAccessibilityNodeVec_.size()) || index < 0) {
1083 return;
1084 }
1085 std::string message;
1086 if (calendarDay.month.year == calendarDay_.month.year && calendarDay.month.month == calendarDay_.month.month &&
1087 calendarDay.day == calendarDay_.day) {
1088 message += Localization::GetInstance()->GetEntryLetters("calendar.today");
1089 }
1090 message += std::to_string(calendarDay.month.year) + "/";
1091 message += std::to_string(calendarDay.month.month) + "/";
1092 message += std::to_string(calendarDay.day);
1093 message += GetDayStr(remainderWeek);
1094 auto node = buttonAccessibilityNodeVec_[index];
1095 auto buttonAccessibilityProperty = node->GetAccessibilityProperty<AccessibilityProperty>();
1096 CHECK_NULL_VOID(buttonAccessibilityProperty);
1097 if (calendarDay.month.month != obtainedMonth_.month) {
1098 buttonAccessibilityProperty->SetAccessibilityDescription(disabledDesc_);
1099 } else if (index == selectedIndex_) {
1100 // Delete the description of the selected node
1101 buttonAccessibilityProperty->SetAccessibilityDescription(" ");
1102 } else {
1103 // Set the default description to other nodes
1104 buttonAccessibilityProperty->SetAccessibilityDescription("");
1105 }
1106 buttonAccessibilityProperty->SetUserDisabled(calendarDay.month.month != obtainedMonth_.month ? true : false);
1107 buttonAccessibilityProperty->SetUserSelected(false);
1108 buttonAccessibilityProperty->SetAccessibilityText(message);
1109 }
1110
1111 void CalendarMonthPattern::FireModifyAccessibilityVirtualNode(const ObtainedMonth& currentData)
1112 {
1113 if (isInitVirtualNode_) {
1114 auto host = GetHost();
1115 CHECK_NULL_VOID(host);
1116 auto pipeline = host->GetContext();
1117 CHECK_NULL_VOID(pipeline);
1118 pipeline->AddAfterRenderTask([weak = WeakClaim(this), currentData]() {
1119 auto calendarMonthPattern = weak.Upgrade();
1120 CHECK_NULL_VOID(calendarMonthPattern);
1121 calendarMonthPattern->ModifyAccessibilityVirtualNode(currentData);
1122 });
1123 }
1124 }
1125
1126 void CalendarMonthPattern::ModifyAccessibilityVirtualNode(const ObtainedMonth& currentData)
1127 {
1128 if (buttonAccessibilityNodeVec_.size() < 1) {
1129 return;
1130 }
1131 for (auto& day : currentData.days) {
1132 ChangeVirtualNodeContent(day);
1133 }
1134 }
1135 } // namespace OHOS::Ace::NG
1136