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 = GetOrCreateEventHub<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 if (SystemProperties::ConfigChangePerform()) {
368 rowNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
369 swiperNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
370 }
371 }
372
373 void CalendarMonthPattern::OnLanguageConfigurationUpdate()
374 {
375 auto host = GetHost();
376 CHECK_NULL_VOID(host);
377 auto pipelineContext = host->GetContext();
378 CHECK_NULL_VOID(pipelineContext);
379 RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
380 CHECK_NULL_VOID(theme);
381 auto swiperNode = host->GetParent();
382 CHECK_NULL_VOID(swiperNode);
383 auto calendarNode = swiperNode->GetParent();
384 CHECK_NULL_VOID(calendarNode);
385 auto scrollNode = calendarNode->GetParent();
386 CHECK_NULL_VOID(scrollNode);
387 auto columnNode = scrollNode->GetParent();
388 CHECK_NULL_VOID(columnNode);
389 auto rowNode = columnNode->GetChildAtIndex(WEEK_ROW_INDEX);
390 CHECK_NULL_VOID(rowNode);
391 auto textNodes = rowNode->GetChildren();
392 std::vector<std::string> weekNumbers = Localization::GetInstance()->GetWeekdays(true);
393 int32_t column = 0;
394 for (auto textNode : textNodes) {
395 std::string weekContent { weekNumbers[column % CALENDAR_WEEK_DAYS] };
396 auto textFrameNode = AceType::DynamicCast<NG::FrameNode>(textNode);
397 CHECK_NULL_VOID(textFrameNode);
398 auto calendarPaintProperty = host->GetPaintProperty<CalendarPaintProperty>();
399 CHECK_NULL_VOID(calendarPaintProperty);
400 auto fontSize = calendarPaintProperty->GetWeekFontSize().value_or(theme->GetCalendarTheme().weekFontSize);
401 auto textLayoutProperty = textFrameNode->GetLayoutProperty<TextLayoutProperty>();
402 CHECK_NULL_VOID(textLayoutProperty);
403 textLayoutProperty->UpdateContent(weekContent);
404 textLayoutProperty->UpdateFontSize(fontSize);
405 ++column;
406 }
407 }
408
409 void CalendarMonthPattern::BeforeSyncGeometryProperties(const DirtySwapConfig& config)
410 {
411 auto host = GetHost();
412 CHECK_NULL_VOID(host);
413 auto width = GetWidth(host);
414 auto pipelineContext = host->GetContext();
415 CHECK_NULL_VOID(pipelineContext);
416 RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
417 CHECK_NULL_VOID(theme);
418 auto calendarDaySize = GetDaySize(theme);
419 auto space = (width - calendarDaySize.ConvertToPx() * CALENDAR_WEEK_DAYS) / (CALENDAR_WEEK_DAYS - 1);
420 Dimension colSpace = 0.0_px;
421 if (Positive(space)) {
422 colSpace.SetValue(space);
423 }
424 auto swiperNode = host->GetParent();
425 CHECK_NULL_VOID(swiperNode);
426 auto calendarNode = swiperNode->GetParent();
427 CHECK_NULL_VOID(calendarNode);
428 auto scrollNode = calendarNode->GetParent();
429 CHECK_NULL_VOID(scrollNode);
430 auto columnNode = scrollNode->GetParent();
431 CHECK_NULL_VOID(columnNode);
432 auto rowNode = columnNode->GetChildAtIndex(WEEK_ROW_INDEX);
433 CHECK_NULL_VOID(rowNode);
434 auto textNodes = rowNode->GetChildren();
435 for (auto textNode : textNodes) {
436 auto textFrameNode = AceType::DynamicCast<NG::FrameNode>(textNode);
437 CHECK_NULL_VOID(textFrameNode);
438 auto calendarPaintProperty = host->GetPaintProperty<CalendarPaintProperty>();
439 CHECK_NULL_VOID(calendarPaintProperty);
440 auto fontSize = calendarPaintProperty->GetWeekFontSize().value_or(theme->GetCalendarTheme().weekFontSize);
441 auto textLayoutProperty = textFrameNode->GetLayoutProperty<TextLayoutProperty>();
442 CHECK_NULL_VOID(textLayoutProperty);
443 textLayoutProperty->UpdateFontSize(fontSize);
444 textLayoutProperty->UpdateTextColor(theme->GetCalendarTheme().weekColor);
445 textLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(calendarDaySize), std::nullopt));
446 }
447
448 auto rowFrameNode = AceType::DynamicCast<NG::FrameNode>(rowNode);
449 CHECK_NULL_VOID(rowFrameNode);
450 auto weekLayoutProperty = rowFrameNode->GetLayoutProperty<LinearLayoutProperty>();
451 CHECK_NULL_VOID(weekLayoutProperty);
452 weekLayoutProperty->UpdateSpace(colSpace);
453 rowFrameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
454 }
455
456 void CalendarMonthPattern::InitTouchEvent()
457 {
458 if (touchListener_) {
459 return;
460 }
461
462 auto host = GetHost();
463 CHECK_NULL_VOID(host);
464 auto gesture = host->GetOrCreateGestureEventHub();
465 CHECK_NULL_VOID(gesture);
466 auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
467 auto calendarPattern = weak.Upgrade();
468 CHECK_NULL_VOID(calendarPattern);
469 if (info.GetTouches().empty()) {
470 return;
471 }
472 if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
473 calendarPattern->OnTouchEvent(info.GetTouches().front().GetLocalLocation(), true);
474 }
475 if (info.GetTouches().front().GetTouchType() == TouchType::UP ||
476 info.GetTouches().front().GetTouchType() == TouchType::CANCEL) {
477 calendarPattern->OnTouchEvent(info.GetTouches().front().GetLocalLocation(), false);
478 }
479 };
480 touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
481 gesture->AddTouchEvent(touchListener_);
482 }
483
484 void CalendarMonthPattern::InitHoverEvent()
485 {
486 if (hoverListener_) {
487 return;
488 }
489
490 auto host = GetHost();
491 CHECK_NULL_VOID(host);
492 auto eventHub = GetOrCreateEventHub<CalendarEventHub>();
493 CHECK_NULL_VOID(eventHub);
494 auto inputHub = eventHub->GetOrCreateInputEventHub();
495 CHECK_NULL_VOID(inputHub);
496 auto hoverCallback = [weak = WeakClaim(this)](bool state) {
497 auto calendarPattern = weak.Upgrade();
498 CHECK_NULL_VOID(calendarPattern);
499 calendarPattern->SetHoverState(state);
500 if (!state) {
501 Offset localLocation;
502 calendarPattern->OnHoverEvent(localLocation, false);
503 }
504 };
505 hoverListener_ = MakeRefPtr<InputEvent>(std::move(hoverCallback));
506 inputHub->AddOnHoverEvent(hoverListener_);
507 auto mouseCallback = [weak = WeakClaim(this)](MouseInfo& info) {
508 auto calendarPattern = weak.Upgrade();
509 CHECK_NULL_VOID(calendarPattern);
510 calendarPattern->OnHoverEvent(info.GetLocalLocation(), calendarPattern->GetHoverState());
511 };
512 inputHub->SetMouseEvent(std::move(mouseCallback));
513 }
514
515 bool CalendarMonthPattern::IsDateInRange(const CalendarDay& day)
516 {
517 PickerDate date;
518 date.SetYear(day.month.year);
519 date.SetMonth(day.month.month);
520 date.SetDay(day.day);
521 for (const auto& range : disabledDateRange_) {
522 if (PickerDate::IsDateInRange(date, range.first, range.second)) {
523 return false;
524 }
525 }
526 return PickerDate::IsDateInRange(date, startDate_, endDate_);
527 }
528
529 void CalendarMonthPattern::OnClick(Offset& localLocation, const ObtainedMonth& obtainedMonth)
530 {
531 auto host = GetHost();
532 CHECK_NULL_VOID(host);
533 auto pattern = host->GetPattern<CalendarMonthPattern>();
534 CHECK_NULL_VOID(pattern);
535 auto index = JudgeArea(localLocation);
536 pattern->obtainedMonth_ = obtainedMonth;
537 if (!obtainedMonth_.days.empty()) {
538 if (!IsDateInRange(obtainedMonth_.days[index])) {
539 return;
540 }
541 for (auto& day : pattern->obtainedMonth_.days) {
542 day.focused = false;
543 }
544 auto calendarEventHub = GetOrCreateEventHub<CalendarEventHub>();
545 CHECK_NULL_VOID(calendarEventHub);
546 if (index >= 0 && index < static_cast<int32_t>(obtainedMonth.days.size())) {
547 pattern->obtainedMonth_.days[index].focused = true;
548 auto json = JsonUtil::Create(true);
549 json->Put("day", obtainedMonth.days[index].day);
550 json->Put("month", obtainedMonth.days[index].month.month);
551 json->Put("year", obtainedMonth.days[index].month.year);
552 calendarEventHub->UpdateSelectedChangeEvent(json->ToString());
553 }
554 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
555 }
556 }
557
558 void CalendarMonthPattern::OnTouchEvent(const Offset& localLocation, bool isPressed)
559 {
560 if (!isCalendarDialog_ || obtainedMonth_.days.empty()) {
561 return;
562 }
563 auto index = JudgeArea(localLocation);
564 if (!(index < 0 || index >= static_cast<int32_t>(obtainedMonth_.days.size())) && isPressed &&
565 IsDateInRange(obtainedMonth_.days[index])) {
566 obtainedMonth_.days[index].isPressing = true;
567 } else {
568 for (auto& day : obtainedMonth_.days) {
569 day.isPressing = false;
570 }
571 }
572
573 auto host = GetHost();
574 CHECK_NULL_VOID(host);
575 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
576 }
577
578 void CalendarMonthPattern::OnHoverEvent(const Offset& localLocation, bool state)
579 {
580 if (!isCalendarDialog_ || obtainedMonth_.days.empty()) {
581 return;
582 }
583 int32_t index = JudgeArea(localLocation);
584 if (index < 0 || index >= static_cast<int32_t>(obtainedMonth_.days.size())) {
585 return;
586 }
587 for (auto& day : obtainedMonth_.days) {
588 day.isHovering = false;
589 }
590 if (state && IsDateInRange(obtainedMonth_.days[index])) {
591 obtainedMonth_.days[index].isHovering = true;
592 }
593
594 auto host = GetHost();
595 CHECK_NULL_VOID(host);
596 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
597 }
598
599 int32_t CalendarMonthPattern::JudgeArea(const Offset& offset)
600 {
601 auto host = GetHost();
602 CHECK_NULL_RETURN(host, false);
603 auto paintProperty = host->GetPaintProperty<CalendarPaintProperty>();
604 CHECK_NULL_RETURN(paintProperty, false);
605 auto pipelineContext = host->GetContext();
606 CHECK_NULL_RETURN(pipelineContext, false);
607 RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
608 CHECK_NULL_RETURN(theme, false);
609 auto gregorianDayHeight = paintProperty->GetGregorianCalendarHeightValue({}).ConvertToPx() <= 0
610 ? theme->GetCalendarTheme().gregorianCalendarHeight.ConvertToPx()
611 : paintProperty->GetGregorianCalendarHeightValue({}).ConvertToPx();
612 auto weekHeight = paintProperty->GetWeekHeight().value_or(theme->GetCalendarTheme().weekHeight).ConvertToPx();
613 auto weekAndDayRowSpace =
614 paintProperty->GetWeekAndDayRowSpace().value_or(theme->GetCalendarTheme().weekAndDayRowSpace).ConvertToPx();
615 auto dayHeight = paintProperty->GetDayHeight().value_or(theme->GetCalendarTheme().dayHeight).ConvertToPx();
616 auto dayWidth = paintProperty->GetDayWidth().value_or(theme->GetCalendarTheme().dayWidth).ConvertToPx();
617 const static int32_t columnsOfData = 7;
618 auto colSpace = paintProperty->GetColSpaceValue({}).ConvertToPx() <= 0
619 ? theme->GetCalendarTheme().colSpace.ConvertToPx()
620 : paintProperty->GetColSpaceValue({}).ConvertToPx();
621 auto dailyFourRowSpace = NonPositive(paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx())
622 ? theme->GetCalendarTheme().dailySixRowSpace.ConvertToPx()
623 : paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx();
624 auto dailyFiveRowSpace = paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx() <= 0
625 ? theme->GetCalendarTheme().dailyFiveRowSpace.ConvertToPx()
626 : paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx();
627 auto dailySixRowSpace = paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx() <= 0
628 ? theme->GetCalendarTheme().dailySixRowSpace.ConvertToPx()
629 : paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx();
630 auto rows = (static_cast<int32_t>(obtainedMonth_.days.size()) / columnsOfData);
631 auto rowSpace = dailySixRowSpace;
632 switch (rows) {
633 case 4: {
634 rowSpace = dailyFourRowSpace;
635 break;
636 }
637 case 5: {
638 rowSpace = dailyFiveRowSpace;
639 break;
640 }
641 default:
642 break;
643 }
644 if (IsLargeSize(theme)) {
645 gregorianDayHeight = GetDaySize(theme).ConvertToPx();
646 }
647 auto browHeight = weekHeight + weekAndDayRowSpace - gregorianDayHeight;
648 auto maxHeight = host->GetGeometryNode()->GetFrameSize().Height();
649 auto maxWidth = host->GetGeometryNode()->GetFrameSize().Width();
650 if ((offset.GetX() < 0) || (offset.GetX() > maxWidth) || (offset.GetY() < browHeight) ||
651 (offset.GetY() > maxHeight) || LessOrEqual(dayHeight, 0.0) || LessOrEqual(dayWidth, 0.0)) {
652 return -1;
653 }
654 auto height = offset.GetY() - browHeight;
655 int32_t y =
656 height < (dayHeight + rowSpace / 2) ? 0 : (height - dayHeight - rowSpace / 2) / (dayHeight + rowSpace) + 1;
657 int32_t x = offset.GetX() < (dayWidth + colSpace / 2)
658 ? 0
659 : (offset.GetX() - dayWidth - colSpace / 2) / (dayWidth + colSpace) + 1;
660 auto textDirection = host->GetLayoutProperty()->GetNonAutoLayoutDirection();
661 if (textDirection == TextDirection::RTL) {
662 x = columnsOfData - x - 1;
663 }
664 return (y * columnsOfData + x);
665 }
666
667 class CalendarAccessibilitySAObserverCallback : public AccessibilitySAObserverCallback {
668 public:
669 CalendarAccessibilitySAObserverCallback(
670 const WeakPtr<CalendarMonthPattern> &weakCalendarPattern, int64_t accessibilityId)
671 : AccessibilitySAObserverCallback(accessibilityId), weakCalendarPattern_(weakCalendarPattern)
672 {}
673
674 ~CalendarAccessibilitySAObserverCallback() override = default;
675
676 bool OnState(bool state) override
677 {
678 auto calendarPattern = weakCalendarPattern_.Upgrade();
679 CHECK_NULL_RETURN(calendarPattern, false);
680 if (state) {
681 calendarPattern->InitCurrentVirtualNode();
682 } else {
683 calendarPattern->ClearCalendarVirtualNode();
684 }
685 return true;
686 }
687 private:
688 WeakPtr<CalendarMonthPattern> weakCalendarPattern_;
689 };
690
691 void CalendarMonthPattern::InitializeCalendarAccessibility()
692 {
693 auto host = GetHost();
694 CHECK_NULL_VOID(host);
695 auto pipeline = host->GetContext();
696 CHECK_NULL_VOID(pipeline);
697 auto accessibilityManager = pipeline->GetAccessibilityManager();
698 CHECK_NULL_VOID(accessibilityManager);
699 accessibilitySAObserverCallback_ = std::make_shared<CalendarAccessibilitySAObserverCallback>(
700 WeakClaim(this), host->GetAccessibilityId());
701 accessibilityManager->RegisterAccessibilitySAObserverCallback(host->GetAccessibilityId(),
702 accessibilitySAObserverCallback_);
703 if (margin_ == 0) {
704 auto paintProperty = host->GetPaintProperty<CalendarPaintProperty>();
705 CHECK_NULL_VOID(paintProperty);
706 dayHeight_ = paintProperty->GetDayHeight().value_or(Dimension(0.0f)).ConvertToPx();
707 dayWidth_ = paintProperty->GetDayWidth().value_or(Dimension(0.0f)).ConvertToPx();
708 RefPtr<CalendarTheme> theme = pipeline->GetTheme<CalendarTheme>();
709 CHECK_NULL_VOID(theme);
710 auto sliderTheme = pipeline->GetTheme<SliderTheme>();
711 CHECK_NULL_VOID(sliderTheme);
712 margin_ = theme->GetDialogMargin().ConvertToPx();
713 selectedTxt_ = sliderTheme->GetSelectedTxt();
714 disabledDesc_ = sliderTheme->GetDisabelDesc();
715 deviceOrientation_ = SystemProperties::GetDeviceOrientation();
716 }
717 }
718
719 void CalendarMonthPattern::InitCurrentVirtualNode()
720 {
721 auto deviceOrientation = SystemProperties::GetDeviceOrientation();
722 if ((!isInitVirtualNode_ || buttonAccessibilityNodeVec_.size() != obtainedMonth_.days.size() ||
723 deviceOrientation_ != deviceOrientation) &&
724 monthState_ == MonthState::CUR_MONTH) {
725 isInitVirtualNode_ = InitCalendarVirtualNode();
726 } else {
727 FireModifyAccessibilityVirtualNode(obtainedMonth_);
728 }
729 }
730
731 void CalendarMonthPattern::ClearFocusCalendarDay()
732 {
733 focusedCalendarDay_.index = 0;
734 deviceOrientation_ = SystemProperties::GetDeviceOrientation();
735 CHECK_NULL_VOID(lineNode_);
736 auto lineNodeProp = lineNode_->GetLayoutProperty();
737 CHECK_NULL_VOID(lineNodeProp);
738 if (monthState_ == MonthState::CUR_MONTH) {
739 lineNodeProp->UpdateVisibility(VisibleType::VISIBLE);
740 } else {
741 lineNodeProp->UpdateVisibility(VisibleType::GONE);
742 }
743 }
744
745 void CalendarMonthPattern::ClearCalendarVirtualNode()
746 {
747 auto host = GetHost();
748 CHECK_NULL_VOID(host);
749 buttonAccessibilityNodeVec_.clear();
750 accessibilityPropertyVec_.clear();
751 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
752 CHECK_NULL_VOID(accessibilityProperty);
753 accessibilityProperty->SaveAccessibilityVirtualNode(nullptr);
754 }
755
756 void CalendarMonthPattern::SetLineNodeSize(RefPtr<FrameNode> lineNode)
757 {
758 auto host = GetHost();
759 CHECK_NULL_VOID(host);
760 auto hostLayoutProperty = host->GetLayoutProperty();
761 CHECK_NULL_VOID(hostLayoutProperty);
762 auto width = hostLayoutProperty->GetLayoutConstraint()->selfIdealSize.Width().value_or(Infinity<float>());
763 auto height = hostLayoutProperty->GetLayoutConstraint()->selfIdealSize.Height().value_or(Infinity<float>());
764 CHECK_NULL_VOID(lineNode);
765 auto layoutProperty = lineNode->GetLayoutProperty<LayoutProperty>();
766 CHECK_NULL_VOID(layoutProperty);
767 layoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(width), CalcLength(height)));
768 }
769
770 void CalendarMonthPattern::SetFocusNode(int32_t index, bool isDeviceOrientation)
771 {
772 auto host = GetHost();
773 CHECK_NULL_VOID(host);
774 auto layoutProperty = host->GetLayoutProperty();
775 CHECK_NULL_VOID(layoutProperty);
776 auto textDirection = layoutProperty->GetNonAutoLayoutDirection();
777 auto remainderWeek = index % CALENDAR_WEEK_DAYS;
778 int32_t selectedIndex = (textDirection == TextDirection::RTL ?
779 CALENDAR_WEEK_DAYS - remainderWeek * 2 + index - 1 : index);
780 if (isDeviceOrientation) {
781 selectedIndex = index;
782 }
783 if (selectedIndex >= static_cast<int32_t>(buttonAccessibilityNodeVec_.size()) || selectedIndex < 0) {
784 return;
785 }
786 buttonAccessibilityNodeVec_[selectedIndex]->OnAccessibilityEvent(AccessibilityEventType::REQUEST_FOCUS);
787 selectedIndex_ = selectedIndex;
788 }
789
790 bool CalendarMonthPattern::InitCalendarVirtualNode()
791 {
792 auto host = GetHost();
793 CHECK_NULL_RETURN(host, false);
794 buttonAccessibilityNodeVec_.clear();
795 accessibilityPropertyVec_.clear();
796 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
797 CHECK_NULL_RETURN(accessibilityProperty, false);
798 accessibilityProperty->SaveAccessibilityVirtualNode(nullptr);
799 auto lineNode = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
800 AceType::MakeRefPtr<LinearLayoutPattern>(true));
801 for (auto& day : obtainedMonth_.days) {
802 auto buttonNode = AddButtonNodeIntoVirtual(day);
803 lineNode->AddChild(buttonNode);
804 buttonAccessibilityNodeVec_.emplace_back(buttonNode);
805 ChangeVirtualNodeContent(day);
806 }
807 SetLineNodeSize(lineNode);
808 SetCalendarAccessibilityLevel(AccessibilityProperty::Level::NO_STR);
809 accessibilityProperty->SetAccessibilityText(" ");
810 auto virtualFrameNode = AceType::DynamicCast<NG::FrameNode>(lineNode);
811 CHECK_NULL_RETURN(virtualFrameNode, false);
812 virtualFrameNode->SetAccessibilityNodeVirtual();
813 virtualFrameNode->SetAccessibilityVirtualNodeParent(AceType::DynamicCast<NG::UINode>(host));
814 virtualFrameNode->SetFirstAccessibilityVirtualNode();
815 FrameNode::ProcessOffscreenNode(virtualFrameNode);
816 accessibilityProperty->SaveAccessibilityVirtualNode(lineNode);
817 lineNode_ = lineNode;
818 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
819 auto deviceOrientation = SystemProperties::GetDeviceOrientation();
820 if (deviceOrientation_ != deviceOrientation && !isFirstEnter_) {
821 SetFocusNode(focusedCalendarDay_.index, true);
822 deviceOrientation_ = deviceOrientation;
823 }
824 if (!isFirstEnter_) {
825 return true;
826 }
827 for (auto &day : obtainedMonth_.days) {
828 if (day.isSelected) {
829 SetFocusNode(day.index);
830 isFirstEnter_ = false;
831 focusedCalendarDay_ = day;
832 break;
833 }
834 }
835 return true;
836 }
837
838 std::pair<int32_t, int32_t> GetXYByIndex(const int32_t index)
839 {
840 if (index < CALENDAR_WEEK_DAYS - 1) {
841 return {index, 0};
842 } else {
843 return {(index % CALENDAR_WEEK_DAYS), (index / CALENDAR_WEEK_DAYS)};
844 }
845 }
846
847 void CalendarMonthPattern::ChangeVirtualNodeState(const CalendarDay& calendarDay)
848 {
849 if (selectedIndex_ >= static_cast<int32_t>(accessibilityPropertyVec_.size()) || selectedIndex_ < 0) {
850 return;
851 }
852 if (calendarDay.index != selectedIndex_ &&
853 calendarDay.month.month == obtainedMonth_.month && calendarDay.month.month == obtainedMonth_.month) {
854 accessibilityPropertyVec_[selectedIndex_]->SetUserSelected(true);
855 }
856 }
857
858 RefPtr<FrameNode> CalendarMonthPattern::AddButtonNodeIntoVirtual(const CalendarDay& calendarDay)
859 {
860 auto buttonNode = FrameNode::CreateFrameNode(
861 V2::BUTTON_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ButtonPattern>());
862 auto buttonAccessibilityProperty = buttonNode->GetAccessibilityProperty<AccessibilityProperty>();
863 CHECK_NULL_RETURN(buttonAccessibilityProperty, nullptr);
864 auto host = GetHost();
865 CHECK_NULL_RETURN(host, nullptr);
866 auto pipeline = host->GetContext();
867 if (pipeline) {
868 UpdateAccessibilityButtonNode(buttonNode, calendarDay.index);
869 } else {
870 UpdateButtonNodeWithoutTheme(buttonNode, calendarDay.index);
871 }
872 accessibilityPropertyVec_.emplace_back(buttonAccessibilityProperty);
873 InitVirtualButtonClickEvent(buttonNode, calendarDay.index);
874 buttonAccessibilityProperty->SetOnAccessibilityFocusCallback([weak = WeakClaim(this), calendarDay](bool focus) {
875 if (focus) {
876 auto calendar = weak.Upgrade();
877 CHECK_NULL_VOID(calendar);
878 calendar->focusedCalendarDay_ = calendarDay;
879 calendar->ChangeVirtualNodeState(calendarDay);
880 }
881 });
882 return buttonNode;
883 }
884
885 float GetRowSpace(RefPtr<CalendarPaintProperty> paintProperty, RefPtr<CalendarTheme> theme, size_t daySize)
886 {
887 auto dailyFourRowSpace = NonPositive(paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx())
888 ? theme->GetCalendarTheme().dailySixRowSpace.ConvertToPx()
889 : paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx();
890 auto dailyFiveRowSpace = paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx() <= 0
891 ? theme->GetCalendarTheme().dailyFiveRowSpace.ConvertToPx()
892 : paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx();
893 auto dailySixRowSpace = paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx() <= 0
894 ? theme->GetCalendarTheme().dailySixRowSpace.ConvertToPx()
895 : paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx();
896 auto rows = (static_cast<int32_t>(daySize) / CALENDAR_WEEK_DAYS);
897 auto rowSpace = dailySixRowSpace;
898 switch (rows) {
899 case DAILY_FOUR_ROWSPACE: {
900 rowSpace = dailyFourRowSpace;
901 break;
902 }
903 case DAILY_FIVE_ROWSPACE: {
904 rowSpace = dailyFiveRowSpace;
905 break;
906 }
907 default:
908 break;
909 }
910 return rowSpace;
911 }
912
913 void CalendarMonthPattern::SetCalendarAccessibilityLevel(const std::string& level)
914 {
915 auto host = GetHost();
916 CHECK_NULL_VOID(host);
917 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
918 CHECK_NULL_VOID(accessibilityProperty);
919 accessibilityProperty->SetAccessibilityLevel(level);
920 auto parent = host->GetParent();
921 while (parent && parent->GetTag() != V2::DIALOG_ETS_TAG) {
922 auto parentNode = AceType::DynamicCast<NG::FrameNode>(parent);
923 CHECK_NULL_VOID(parentNode);
924 auto parentAccessibilityProperty = parentNode->GetAccessibilityProperty<AccessibilityProperty>();
925 CHECK_NULL_VOID(parentAccessibilityProperty);
926 parentAccessibilityProperty->SetAccessibilityLevel(level);
927 parent = parent->GetParent();
928 }
929 }
930
931 void CalendarMonthPattern::InitAccessibilityHoverEvent()
932 {
933 auto host = GetHost();
934 CHECK_NULL_VOID(host);
935 auto eventHub = host->GetOrCreateInputEventHub();
936 CHECK_NULL_VOID(eventHub);
937 eventHub->SetAccessibilityHoverEvent([weak = WeakClaim(this)](bool isHover, AccessibilityHoverInfo& info) {
938 auto calendar = weak.Upgrade();
939 CHECK_NULL_VOID(calendar);
940 calendar->HandleAccessibilityHoverEvent(isHover, info);
941 });
942 }
943
944 void CalendarMonthPattern::HandleAccessibilityHoverEvent(bool isHover, AccessibilityHoverInfo& info)
945 {
946 if (isHover) {
947 isOnHover_ = true;
948 } else if (!isHover) {
949 isOnHover_ = false;
950 }
951 }
952
953 float GetRowSpaceWithoutTheme(RefPtr<CalendarPaintProperty> paintProperty, size_t daySize)
954 {
955 auto dailyFourRowSpace = NonPositive(paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx())
956 ? 0.0f
957 : paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx();
958 auto dailyFiveRowSpace = paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx() <= 0
959 ? 0.0f
960 : paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx();
961 auto dailySixRowSpace = paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx() <= 0
962 ? 0.0f
963 : paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx();
964 auto rows = (static_cast<int32_t>(daySize) / CALENDAR_WEEK_DAYS);
965 auto rowSpace = dailySixRowSpace;
966 switch (rows) {
967 case DAILY_FOUR_ROWSPACE: {
968 rowSpace = dailyFourRowSpace;
969 break;
970 }
971 case DAILY_FIVE_ROWSPACE: {
972 rowSpace = dailyFiveRowSpace;
973 break;
974 }
975 default:
976 break;
977 }
978 return rowSpace;
979 }
980
981 void CalendarMonthPattern::UpdateButtonNodeWithoutTheme(RefPtr<FrameNode> frameNode, int32_t index)
982 {
983 auto host = GetHost();
984 CHECK_NULL_VOID(host);
985 auto paintProperty = host->GetPaintProperty<CalendarPaintProperty>();
986 CHECK_NULL_VOID(paintProperty);
987 auto dayHeight = paintProperty->GetDayHeight().value_or(Dimension(0.0f)).ConvertToPx();
988 auto dayWidth = paintProperty->GetDayWidth().value_or(Dimension(0.0f)).ConvertToPx();
989 CHECK_NULL_VOID(frameNode);
990 auto buttonLayoutProperty = frameNode->GetLayoutProperty<ButtonLayoutProperty>();
991 CHECK_NULL_VOID(buttonLayoutProperty);
992 buttonLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(dayWidth), CalcLength(dayHeight)));
993 auto pos = GetXYByIndex(index);
994 auto colSpace = paintProperty->GetColSpaceValue({}).ConvertToPx() <= 0
995 ? 0.0f
996 : paintProperty->GetColSpaceValue({}).ConvertToPx();
997 Dimension buttonOffsetX = Dimension(margin_ / 2 + (colSpace + dayWidth) * pos.first);
998 auto weekHeight = paintProperty->GetWeekHeight().value_or(Dimension(0.0f)).ConvertToPx();
999 auto rowSpace = GetRowSpaceWithoutTheme(paintProperty, obtainedMonth_.days.size());
1000 auto weekAndDayRowSpace =
1001 paintProperty->GetWeekAndDayRowSpace().value_or(Dimension(0.0f)).ConvertToPx();
1002 auto gregorianDayHeight = paintProperty->GetGregorianCalendarHeightValue({}).ConvertToPx();
1003 auto browHeight = weekHeight + weekAndDayRowSpace - gregorianDayHeight;
1004 Dimension buttonOffsetY = Dimension(browHeight + (dayHeight + rowSpace) * pos.second);
1005 auto renderContext = frameNode->GetRenderContext();
1006 CHECK_NULL_VOID(renderContext);
1007 renderContext->UpdatePosition(OffsetT(buttonOffsetX, buttonOffsetY));
1008 frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1009 }
1010
1011 void CalendarMonthPattern::UpdateAccessibilityButtonNode(RefPtr<FrameNode> frameNode, int32_t index)
1012 {
1013 auto host = GetHost();
1014 CHECK_NULL_VOID(host);
1015 auto paintProperty = host->GetPaintProperty<CalendarPaintProperty>();
1016 CHECK_NULL_VOID(paintProperty);
1017 auto pipelineContext = host->GetContext();
1018 CHECK_NULL_VOID(pipelineContext);
1019 RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
1020 CHECK_NULL_VOID(theme);
1021 auto dayHeight = paintProperty->GetDayHeight().value_or(theme->GetCalendarTheme().dayHeight).ConvertToPx();
1022 auto dayWidth = paintProperty->GetDayWidth().value_or(theme->GetCalendarTheme().dayWidth).ConvertToPx();
1023 CHECK_NULL_VOID(frameNode);
1024 auto buttonLayoutProperty = frameNode->GetLayoutProperty<ButtonLayoutProperty>();
1025 CHECK_NULL_VOID(buttonLayoutProperty);
1026 buttonLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(dayWidth), CalcLength(dayHeight)));
1027 auto renderContext = frameNode->GetRenderContext();
1028 CHECK_NULL_VOID(renderContext);
1029 auto pos = GetXYByIndex(index);
1030 auto colSpace = paintProperty->GetColSpaceValue({}).ConvertToPx() <= 0
1031 ? theme->GetCalendarTheme().colSpace.ConvertToPx()
1032 : paintProperty->GetColSpaceValue({}).ConvertToPx();
1033 colSpace_ = colSpace;
1034 Dimension buttonOffsetX = Dimension(margin_ / 2 + (colSpace + dayWidth) * pos.first);
1035 auto gregorianDayHeight = paintProperty->GetGregorianCalendarHeightValue({}).ConvertToPx() <= 0
1036 ? theme->GetCalendarTheme().gregorianCalendarHeight.ConvertToPx()
1037 : paintProperty->GetGregorianCalendarHeightValue({}).ConvertToPx();
1038 auto weekHeight = paintProperty->GetWeekHeight().value_or(theme->GetCalendarTheme().weekHeight).ConvertToPx();
1039 auto rowSpace = GetRowSpace(paintProperty, theme, obtainedMonth_.days.size());
1040 auto weekAndDayRowSpace =
1041 paintProperty->GetWeekAndDayRowSpace().value_or(theme->GetCalendarTheme().weekAndDayRowSpace).ConvertToPx();
1042 if (IsLargeSize(theme)) {
1043 gregorianDayHeight = GetDaySize(theme).ConvertToPx();
1044 }
1045 auto browHeight = weekHeight + weekAndDayRowSpace - gregorianDayHeight;
1046 Dimension buttonOffsetY = Dimension(browHeight + (dayHeight + rowSpace) * pos.second);
1047 renderContext->UpdatePosition(OffsetT(buttonOffsetX, buttonOffsetY));
1048 frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1049 }
1050
1051 std::string CalendarMonthPattern::GetDayStr(int32_t index)
1052 {
1053 auto host = GetHost();
1054 CHECK_NULL_RETURN(host, "");
1055 auto pipelineContext = host->GetContext();
1056 CHECK_NULL_RETURN(pipelineContext, "");
1057 RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
1058 CHECK_NULL_RETURN(theme, "");
1059 switch (index) {
1060 case MONDAY_INDEX:
1061 return theme->GetCalendarTheme().monday;
1062 case TUESDAY_INDEX:
1063 return theme->GetCalendarTheme().tuesday;
1064 case WEDNESDAY_INDEX:
1065 return theme->GetCalendarTheme().wednesday;
1066 case THURSDAY_INDEX:
1067 return theme->GetCalendarTheme().thursday;
1068 case FRIDAY_INDEX:
1069 return theme->GetCalendarTheme().friday;
1070 case SATURDAY_INDEX:
1071 return theme->GetCalendarTheme().saturday;
1072 default:
1073 return theme->GetCalendarTheme().sunday;
1074 }
1075 }
1076
1077 std::string CalendarMonthPattern::GetTodayStr()
1078 {
1079 auto host = GetHost();
1080 CHECK_NULL_RETURN(host, "");
1081 auto pipelineContext = host->GetContext();
1082 CHECK_NULL_RETURN(pipelineContext, "");
1083 RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
1084 CHECK_NULL_RETURN(theme, "");
1085 return theme->GetCalendarTheme().today;
1086 }
1087
1088 void CalendarMonthPattern::ChangeVirtualNodeContent(const CalendarDay& calendarDay)
1089 {
1090 auto host = GetHost();
1091 CHECK_NULL_VOID(host);
1092 auto layoutProperty = host->GetLayoutProperty();
1093 CHECK_NULL_VOID(layoutProperty);
1094 auto textDirection = layoutProperty->GetNonAutoLayoutDirection();
1095 auto remainderWeek = calendarDay.index % CALENDAR_WEEK_DAYS;
1096 int32_t index = (textDirection == TextDirection::RTL ?
1097 CALENDAR_WEEK_DAYS - remainderWeek * 2 + calendarDay.index - 1 : calendarDay.index);
1098 if (index >= static_cast<int32_t>(buttonAccessibilityNodeVec_.size()) || index < 0) {
1099 return;
1100 }
1101 std::string message;
1102 if (calendarDay.month.year == calendarDay_.month.year && calendarDay.month.month == calendarDay_.month.month &&
1103 calendarDay.day == calendarDay_.day) {
1104 message += GetTodayStr();
1105 }
1106 message += std::to_string(calendarDay.month.year) + "/";
1107 message += std::to_string(calendarDay.month.month) + "/";
1108 message += std::to_string(calendarDay.day);
1109 message += GetDayStr(remainderWeek);
1110 auto node = buttonAccessibilityNodeVec_[index];
1111 auto buttonAccessibilityProperty = node->GetAccessibilityProperty<AccessibilityProperty>();
1112 CHECK_NULL_VOID(buttonAccessibilityProperty);
1113 if (calendarDay.month.month != obtainedMonth_.month) {
1114 buttonAccessibilityProperty->SetAccessibilityDescription(disabledDesc_);
1115 } else if (index == selectedIndex_) {
1116 // Delete the description of the selected node
1117 buttonAccessibilityProperty->SetAccessibilityDescription(" ");
1118 } else {
1119 // Set the default description to other nodes
1120 buttonAccessibilityProperty->SetAccessibilityDescription("");
1121 }
1122 buttonAccessibilityProperty->SetUserDisabled(calendarDay.month.month != obtainedMonth_.month ? true : false);
1123 buttonAccessibilityProperty->SetUserSelected(false);
1124 buttonAccessibilityProperty->SetAccessibilityText(message);
1125 }
1126
1127 void CalendarMonthPattern::FireModifyAccessibilityVirtualNode(const ObtainedMonth& currentData)
1128 {
1129 if (isInitVirtualNode_) {
1130 auto host = GetHost();
1131 CHECK_NULL_VOID(host);
1132 auto pipeline = host->GetContext();
1133 CHECK_NULL_VOID(pipeline);
1134 pipeline->AddAfterRenderTask([weak = WeakClaim(this), currentData]() {
1135 auto calendarMonthPattern = weak.Upgrade();
1136 CHECK_NULL_VOID(calendarMonthPattern);
1137 calendarMonthPattern->ModifyAccessibilityVirtualNode(currentData);
1138 });
1139 }
1140 }
1141
1142 void CalendarMonthPattern::ModifyAccessibilityVirtualNode(const ObtainedMonth& currentData)
1143 {
1144 if (buttonAccessibilityNodeVec_.size() < 1) {
1145 return;
1146 }
1147 for (auto& day : currentData.days) {
1148 ChangeVirtualNodeContent(day);
1149 }
1150 }
1151
1152 void CalendarMonthPattern::UpdateDayRadius(const CalcDimension& dayRadius)
1153 {
1154 auto host = GetHost();
1155 CHECK_NULL_VOID(host);
1156 auto paintProperty = host->GetPaintProperty<CalendarPaintProperty>();
1157 CHECK_NULL_VOID(paintProperty);
1158
1159 auto pipelineContext = host->GetContext();
1160 CHECK_NULL_VOID(pipelineContext);
1161
1162 if (pipelineContext->IsSystmColorChange()) {
1163 paintProperty->UpdateDayRadius(dayRadius);
1164 }
1165
1166 if (host->GetRerenderable()) {
1167 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1168 }
1169 }
1170 } // namespace OHOS::Ace::NG
1171