1 /*
2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "core/components_ng/pattern/calendar/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_picker/calendar_dialog_view.h"
27 #include "core/components_ng/pattern/swiper/swiper_event_hub.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 Dimension CALENDAR_DISTANCE_ADJUST_FOCUSED_EVENT = 4.0_vp;
37 constexpr int32_t MONDAY_INDEX = 1;
38 constexpr int32_t TUESDAY_INDEX = 2;
39 constexpr int32_t WEDNESDAY_INDEX = 3;
40 constexpr int32_t THURSDAY_INDEX = 4;
41 constexpr int32_t FRIDAY_INDEX = 5;
42 constexpr int32_t SATURDAY_INDEX = 6;
43 } // namespace
OnAttachToFrameNode()44 void CalendarMonthPattern::OnAttachToFrameNode()
45 {
46 auto host = GetHost();
47 CHECK_NULL_VOID(host);
48 host->GetRenderContext()->SetClipToFrame(true);
49 }
50
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)51 bool CalendarMonthPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
52 {
53 if (IsCalendarDialog()) {
54 SetColRowSpace();
55 }
56
57 return !(config.skipMeasure || dirty->SkipMeasureContent());
58 }
59
~CalendarMonthPattern()60 CalendarMonthPattern::~CalendarMonthPattern()
61 {
62 auto host = GetHost();
63 CHECK_NULL_VOID(host);
64 auto pipeline = host->GetContext();
65 CHECK_NULL_VOID(pipeline);
66 auto accessibilityManager = pipeline->GetAccessibilityManager();
67 CHECK_NULL_VOID(accessibilityManager);
68 accessibilityManager->DeregisterAccessibilitySAObserverCallback(host->GetAccessibilityId());
69 }
70
GetDaySize(const RefPtr<CalendarTheme> & theme)71 Dimension CalendarMonthPattern::GetDaySize(const RefPtr<CalendarTheme>& theme)
72 {
73 auto pipeline = GetHost()->GetContext();
74 CHECK_NULL_RETURN(pipeline, theme->GetCalendarPickerDayWidthOrHeight());
75 auto fontSizeScale = pipeline->GetFontScale();
76 if (fontSizeScale < theme->GetCalendarPickerLargeScale() || CalendarDialogView::CheckOrientationChange()) {
77 return theme->GetCalendarPickerDayWidthOrHeight();
78 } else {
79 return theme->GetCalendarPickerDayLargeWidthOrHeight();
80 }
81 }
82
SetColRowSpace()83 void CalendarMonthPattern::SetColRowSpace()
84 {
85 auto host = GetHost();
86 CHECK_NULL_VOID(host);
87 auto contentConstraint = host->GetLayoutProperty()->GetLayoutConstraint();
88 if (!contentConstraint.has_value()) {
89 return;
90 }
91 auto constraint = contentConstraint.value();
92
93 auto dataSize = GetMonthData().days.size();
94 if (dataSize <= 0) {
95 return;
96 }
97
98 auto pipelineContext = host->GetContext();
99 CHECK_NULL_VOID(pipelineContext);
100 RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
101 CHECK_NULL_VOID(theme);
102 auto width = constraint.selfIdealSize.Width().value() - CALENDAR_DISTANCE_ADJUST_FOCUSED_EVENT.ConvertToPx() * 2;
103 auto height = constraint.selfIdealSize.Height().value() - CALENDAR_DISTANCE_ADJUST_FOCUSED_EVENT.ConvertToPx();
104 auto paintProperty = GetPaintProperty<CalendarPaintProperty>();
105 CHECK_NULL_VOID(paintProperty);
106
107 auto calendarDaySize = GetDaySize(theme);
108 auto space = (width - calendarDaySize.ConvertToPx() * CALENDAR_WEEK_DAYS) / (CALENDAR_WEEK_DAYS - 1);
109 if (Positive(space)) {
110 Dimension colSpace = 0.0_px;
111 colSpace.SetValue(space);
112 paintProperty->UpdateColSpace(colSpace);
113 }
114
115 auto rowCount = dataSize / CALENDAR_WEEK_DAYS;
116 space = (height - calendarDaySize.ConvertToPx() * (rowCount + 1)) / rowCount;
117 if (!Positive(space)) {
118 return;
119 }
120 Dimension rowSpace = 0.0_px;
121 rowSpace.SetValue(space);
122 paintProperty->UpdateWeekAndDayRowSpace(rowSpace);
123 switch (rowCount) {
124 case 4:
125 paintProperty->UpdateDailyFourRowSpace(rowSpace);
126 break;
127 case 6:
128 paintProperty->UpdateDailySixRowSpace(rowSpace);
129 break;
130 case 5:
131 default:
132 paintProperty->UpdateDailyFiveRowSpace(rowSpace);
133 break;
134 }
135 }
136
OnModifyDone()137 void CalendarMonthPattern::OnModifyDone()
138 {
139 Pattern::OnModifyDone();
140 InitClickEvent();
141 InitTouchEvent();
142 InitHoverEvent();
143 InitAccessibilityHoverEvent();
144 InitializeCalendarAccessibility();
145 }
146
SetVirtualNodeUserSelected(int32_t index)147 void CalendarMonthPattern::SetVirtualNodeUserSelected(int32_t index)
148 {
149 if (accessibilityPropertyVec_.size() < 1) {
150 return;
151 }
152 auto host = GetHost();
153 CHECK_NULL_VOID(host);
154 auto layoutProperty = host->GetLayoutProperty();
155 CHECK_NULL_VOID(layoutProperty);
156 auto textDirection = layoutProperty->GetNonAutoLayoutDirection();
157 auto remainderWeek = index % CALENDAR_WEEK_DAYS;
158 int32_t selectedIndex = (textDirection == TextDirection::RTL ?
159 CALENDAR_WEEK_DAYS - remainderWeek * 2 + index - 1 : index);
160 std::string selectMessage;
161 for (int i = 0; i < static_cast<int32_t>(accessibilityPropertyVec_.size()); i++) {
162 if (i == selectedIndex &&
163 obtainedMonth_.days[i].month.month == obtainedMonth_.month &&
164 obtainedMonth_.days[i].month.year == obtainedMonth_.year) {
165 selectMessage += accessibilityPropertyVec_[index]->GetAccessibilityText();
166 continue;
167 }
168 accessibilityPropertyVec_[i]->SetUserSelected(false);
169 }
170 if (!obtainedMonth_.days.empty()) {
171 for (auto& day : obtainedMonth_.days) {
172 day.focused = false;
173 }
174 auto calendarEventHub = GetEventHub<CalendarEventHub>();
175 CHECK_NULL_VOID(calendarEventHub);
176 if (selectedIndex >= 0 && selectedIndex < static_cast<int32_t>(obtainedMonth_.days.size())) {
177 obtainedMonth_.days[selectedIndex].focused = true;
178 auto json = JsonUtil::Create(true);
179 json->Put("day", obtainedMonth_.days[selectedIndex].day);
180 json->Put("month", obtainedMonth_.days[selectedIndex].month.month);
181 json->Put("year", obtainedMonth_.days[selectedIndex].month.year);
182 calendarEventHub->UpdateSelectedChangeEvent(json->ToString());
183 }
184 }
185 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
186 if (!selectMessage.empty()) {
187 host->OnAccessibilityEvent(AccessibilityEventType::ANNOUNCE_FOR_ACCESSIBILITY, selectedTxt_ + selectMessage);
188 selectedIndex_ = index;
189 }
190 }
191
InitVirtualButtonClickEvent(RefPtr<FrameNode> frameNode,int32_t index)192 void CalendarMonthPattern::InitVirtualButtonClickEvent(RefPtr<FrameNode> frameNode, int32_t index)
193 {
194 CHECK_NULL_VOID(frameNode);
195 auto gesture = frameNode->GetOrCreateGestureEventHub();
196 CHECK_NULL_VOID(gesture);
197 auto clickCallback = [weak = WeakClaim(this), index](GestureEvent& info) {
198 auto calendarPattern = weak.Upgrade();
199 CHECK_NULL_VOID(calendarPattern);
200 calendarPattern->SetVirtualNodeUserSelected(index);
201 };
202 auto clickListener = MakeRefPtr<ClickEvent>(std::move(clickCallback));
203 gesture->AddClickAfterEvent(clickListener);
204 }
205
InitClickEvent()206 void CalendarMonthPattern::InitClickEvent()
207 {
208 if (clickListener_) {
209 return;
210 }
211
212 auto host = GetHost();
213 CHECK_NULL_VOID(host);
214 auto gesture = host->GetOrCreateGestureEventHub();
215 CHECK_NULL_VOID(gesture);
216 auto obtainedMonth = obtainedMonth_;
217 auto clickCallback = [weak = WeakClaim(this), obtainedMonth](GestureEvent& info) {
218 auto calendarPattern = weak.Upgrade();
219 CHECK_NULL_VOID(calendarPattern);
220 auto localLocation = info.GetFingerList().begin()->localLocation_;
221 calendarPattern->OnClick(localLocation, calendarPattern->obtainedMonth_);
222 };
223 clickListener_ = MakeRefPtr<ClickEvent>(std::move(clickCallback));
224 gesture->AddClickEvent(clickListener_);
225 }
226
InitTouchEvent()227 void CalendarMonthPattern::InitTouchEvent()
228 {
229 if (touchListener_) {
230 return;
231 }
232
233 auto host = GetHost();
234 CHECK_NULL_VOID(host);
235 auto gesture = host->GetOrCreateGestureEventHub();
236 CHECK_NULL_VOID(gesture);
237 auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
238 auto calendarPattern = weak.Upgrade();
239 CHECK_NULL_VOID(calendarPattern);
240 if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
241 calendarPattern->OnTouchEvent(info.GetTouches().front().GetLocalLocation(), true);
242 }
243 if (info.GetTouches().front().GetTouchType() == TouchType::UP ||
244 info.GetTouches().front().GetTouchType() == TouchType::CANCEL) {
245 calendarPattern->OnTouchEvent(info.GetTouches().front().GetLocalLocation(), false);
246 }
247 };
248 touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
249 gesture->AddTouchEvent(touchListener_);
250 }
251
InitHoverEvent()252 void CalendarMonthPattern::InitHoverEvent()
253 {
254 if (hoverListener_) {
255 return;
256 }
257
258 auto host = GetHost();
259 CHECK_NULL_VOID(host);
260 auto eventHub = GetEventHub<CalendarEventHub>();
261 CHECK_NULL_VOID(eventHub);
262 auto inputHub = eventHub->GetOrCreateInputEventHub();
263 CHECK_NULL_VOID(inputHub);
264 auto hoverCallback = [weak = WeakClaim(this)](bool state) {
265 auto calendarPattern = weak.Upgrade();
266 CHECK_NULL_VOID(calendarPattern);
267 calendarPattern->SetHoverState(state);
268 if (!state) {
269 Offset localLocation;
270 calendarPattern->OnHoverEvent(localLocation, false);
271 }
272 };
273 hoverListener_ = MakeRefPtr<InputEvent>(std::move(hoverCallback));
274 inputHub->AddOnHoverEvent(hoverListener_);
275 auto mouseCallback = [weak = WeakClaim(this)](MouseInfo& info) {
276 auto calendarPattern = weak.Upgrade();
277 CHECK_NULL_VOID(calendarPattern);
278 calendarPattern->OnHoverEvent(info.GetLocalLocation(), calendarPattern->GetHoverState());
279 };
280 inputHub->SetMouseEvent(std::move(mouseCallback));
281 }
282
OnClick(Offset & localLocation,const ObtainedMonth & obtainedMonth)283 void CalendarMonthPattern::OnClick(Offset& localLocation, const ObtainedMonth& obtainedMonth)
284 {
285 auto host = GetHost();
286 CHECK_NULL_VOID(host);
287 auto pattern = host->GetPattern<CalendarMonthPattern>();
288 CHECK_NULL_VOID(pattern);
289 auto index = JudgeArea(localLocation);
290 pattern->obtainedMonth_ = obtainedMonth;
291 if (!obtainedMonth_.days.empty()) {
292 for (auto& day : pattern->obtainedMonth_.days) {
293 day.focused = false;
294 }
295 auto calendarEventHub = GetEventHub<CalendarEventHub>();
296 CHECK_NULL_VOID(calendarEventHub);
297 if (index >= 0 && index < static_cast<int32_t>(obtainedMonth.days.size())) {
298 pattern->obtainedMonth_.days[index].focused = true;
299 auto json = JsonUtil::Create(true);
300 json->Put("day", obtainedMonth.days[index].day);
301 json->Put("month", obtainedMonth.days[index].month.month);
302 json->Put("year", obtainedMonth.days[index].month.year);
303 calendarEventHub->UpdateSelectedChangeEvent(json->ToString());
304 }
305 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
306 }
307 }
308
OnTouchEvent(const Offset & localLocation,bool isPressed)309 void CalendarMonthPattern::OnTouchEvent(const Offset& localLocation, bool isPressed)
310 {
311 if (!isCalendarDialog_ || obtainedMonth_.days.empty()) {
312 return;
313 }
314 auto index = JudgeArea(localLocation);
315 if (!((index < 0 || index >= static_cast<int32_t>(obtainedMonth_.days.size()))) && isPressed) {
316 obtainedMonth_.days[index].isPressing = true;
317 } else {
318 for (auto& day : obtainedMonth_.days) {
319 day.isPressing = false;
320 }
321 }
322
323 auto host = GetHost();
324 CHECK_NULL_VOID(host);
325 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
326 }
327
OnHoverEvent(const Offset & localLocation,bool state)328 void CalendarMonthPattern::OnHoverEvent(const Offset& localLocation, bool state)
329 {
330 if (!isCalendarDialog_ || obtainedMonth_.days.empty()) {
331 return;
332 }
333 int32_t index = JudgeArea(localLocation);
334 if (index < 0 || index >= static_cast<int32_t>(obtainedMonth_.days.size())) {
335 return;
336 }
337 for (auto& day : obtainedMonth_.days) {
338 day.isHovering = false;
339 }
340 if (state) {
341 obtainedMonth_.days[index].isHovering = true;
342 }
343
344 auto host = GetHost();
345 CHECK_NULL_VOID(host);
346 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
347 }
348
JudgeArea(const Offset & offset)349 int32_t CalendarMonthPattern::JudgeArea(const Offset& offset)
350 {
351 auto host = GetHost();
352 CHECK_NULL_RETURN(host, false);
353 auto paintProperty = host->GetPaintProperty<CalendarPaintProperty>();
354 CHECK_NULL_RETURN(paintProperty, false);
355 auto pipelineContext = host->GetContext();
356 CHECK_NULL_RETURN(pipelineContext, false);
357 RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
358 CHECK_NULL_RETURN(theme, false);
359 auto topPadding = isCalendarDialog_ ? 0.0 : theme->GetCalendarTheme().topPadding.ConvertToPx();
360 auto weekHeight = paintProperty->GetWeekHeight().value_or(theme->GetCalendarTheme().weekHeight).ConvertToPx();
361 auto weekAndDayRowSpace =
362 paintProperty->GetWeekAndDayRowSpace().value_or(theme->GetCalendarTheme().weekAndDayRowSpace).ConvertToPx();
363 auto dayHeight = paintProperty->GetDayHeight().value_or(theme->GetCalendarTheme().dayHeight).ConvertToPx();
364 auto dayWidth = paintProperty->GetDayWidth().value_or(theme->GetCalendarTheme().dayWidth).ConvertToPx();
365 const static int32_t columnsOfData = 7;
366 auto colSpace = paintProperty->GetColSpaceValue({}).ConvertToPx() <= 0
367 ? theme->GetCalendarTheme().colSpace.ConvertToPx()
368 : paintProperty->GetColSpaceValue({}).ConvertToPx();
369 auto dailyFourRowSpace = NonPositive(paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx())
370 ? theme->GetCalendarTheme().dailySixRowSpace.ConvertToPx()
371 : paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx();
372 auto dailyFiveRowSpace = paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx() <= 0
373 ? theme->GetCalendarTheme().dailyFiveRowSpace.ConvertToPx()
374 : paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx();
375 auto dailySixRowSpace = paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx() <= 0
376 ? theme->GetCalendarTheme().dailySixRowSpace.ConvertToPx()
377 : paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx();
378 auto rows = (static_cast<int32_t>(obtainedMonth_.days.size()) / columnsOfData);
379 auto rowSpace = dailySixRowSpace;
380 switch (rows) {
381 case 4: {
382 rowSpace = dailyFourRowSpace;
383 break;
384 }
385 case 5: {
386 rowSpace = dailyFiveRowSpace;
387 break;
388 }
389 default:
390 break;
391 }
392 auto browHeight = weekHeight + topPadding + weekAndDayRowSpace;
393 auto maxHeight = host->GetGeometryNode()->GetFrameSize().Height();
394 auto maxWidth = host->GetGeometryNode()->GetFrameSize().Width();
395 if ((offset.GetX() < 0) || (offset.GetX() > maxWidth) || (offset.GetY() < browHeight) ||
396 (offset.GetY() > maxHeight) || LessOrEqual(dayHeight, 0.0) || LessOrEqual(dayWidth, 0.0)) {
397 return -1;
398 }
399 auto height = offset.GetY() - browHeight;
400 int32_t y =
401 height < (dayHeight + rowSpace / 2) ? 0 : (height - dayHeight - rowSpace / 2) / (dayHeight + rowSpace) + 1;
402 int32_t x = offset.GetX() < (dayWidth + colSpace / 2)
403 ? 0
404 : (offset.GetX() - dayWidth - colSpace / 2) / (dayWidth + colSpace) + 1;
405 auto textDirection = host->GetLayoutProperty()->GetNonAutoLayoutDirection();
406 if (textDirection == TextDirection::RTL) {
407 x = columnsOfData - x - 1;
408 }
409 return (y * columnsOfData + x);
410 }
411
412 class CalendarAccessibilitySAObserverCallback : public AccessibilitySAObserverCallback {
413 public:
CalendarAccessibilitySAObserverCallback(const WeakPtr<CalendarMonthPattern> & weakCalendarPattern,int64_t accessibilityId)414 CalendarAccessibilitySAObserverCallback(
415 const WeakPtr<CalendarMonthPattern> &weakCalendarPattern, int64_t accessibilityId)
416 : AccessibilitySAObserverCallback(accessibilityId), weakCalendarPattern_(weakCalendarPattern)
417 {}
418
419 ~CalendarAccessibilitySAObserverCallback() override = default;
420
OnState(bool state)421 bool OnState(bool state) override
422 {
423 auto calendarPattern = weakCalendarPattern_.Upgrade();
424 CHECK_NULL_RETURN(calendarPattern, false);
425 if (state) {
426 calendarPattern->InitCurrentVirtualNode();
427 } else {
428 calendarPattern->ClearCalendarVirtualNode();
429 }
430 return true;
431 }
432 private:
433 WeakPtr<CalendarMonthPattern> weakCalendarPattern_;
434 };
435
InitializeCalendarAccessibility()436 void CalendarMonthPattern::InitializeCalendarAccessibility()
437 {
438 auto host = GetHost();
439 CHECK_NULL_VOID(host);
440 auto pipeline = host->GetContext();
441 CHECK_NULL_VOID(pipeline);
442 auto accessibilityManager = pipeline->GetAccessibilityManager();
443 CHECK_NULL_VOID(accessibilityManager);
444 accessibilitySAObserverCallback_ = std::make_shared<CalendarAccessibilitySAObserverCallback>(
445 WeakClaim(this), host->GetAccessibilityId());
446 accessibilityManager->RegisterAccessibilitySAObserverCallback(host->GetAccessibilityId(),
447 accessibilitySAObserverCallback_);
448 if (margin_ == 0) {
449 auto paintProperty = host->GetPaintProperty<CalendarPaintProperty>();
450 CHECK_NULL_VOID(paintProperty);
451 dayHeight_ = paintProperty->GetDayHeight().value_or(Dimension(0.0f)).ConvertToPx();
452 dayWidth_ = paintProperty->GetDayWidth().value_or(Dimension(0.0f)).ConvertToPx();
453 RefPtr<CalendarTheme> theme = pipeline->GetTheme<CalendarTheme>();
454 CHECK_NULL_VOID(theme);
455 auto sliderTheme = pipeline->GetTheme<SliderTheme>();
456 CHECK_NULL_VOID(sliderTheme);
457 margin_ = theme->GetDialogMargin().ConvertToPx();
458 selectedTxt_ = sliderTheme->GetSelectedTxt();
459 disabledDesc_ = sliderTheme->GetDisabelDesc();
460 deviceOrientation_ = SystemProperties::GetDeviceOrientation();
461 }
462 }
463
InitCurrentVirtualNode()464 void CalendarMonthPattern::InitCurrentVirtualNode()
465 {
466 auto deviceOrientation = SystemProperties::GetDeviceOrientation();
467 if ((!isInitVirtualNode_ || buttonAccessibilityNodeVec_.size() != obtainedMonth_.days.size() ||
468 deviceOrientation_ != deviceOrientation) &&
469 monthState_ == MonthState::CUR_MONTH) {
470 isInitVirtualNode_ = InitCalendarVirtualNode();
471 } else {
472 FireModifyAccessibilityVirtualNode(obtainedMonth_);
473 }
474 }
475
ClearFocusCalendarDay()476 void CalendarMonthPattern::ClearFocusCalendarDay()
477 {
478 focusedCalendarDay_.index = 0;
479 deviceOrientation_ = SystemProperties::GetDeviceOrientation();
480 auto lineNode = lineNode_.Upgrade();
481 CHECK_NULL_VOID(lineNode);
482 auto lineNodeProp = lineNode->GetLayoutProperty();
483 CHECK_NULL_VOID(lineNodeProp);
484 if (monthState_ == MonthState::CUR_MONTH) {
485 lineNodeProp->UpdateVisibility(VisibleType::VISIBLE);
486 } else {
487 lineNodeProp->UpdateVisibility(VisibleType::GONE);
488 }
489 }
490
ClearCalendarVirtualNode()491 void CalendarMonthPattern::ClearCalendarVirtualNode()
492 {
493 auto host = GetHost();
494 CHECK_NULL_VOID(host);
495 buttonAccessibilityNodeVec_.clear();
496 accessibilityPropertyVec_.clear();
497 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
498 CHECK_NULL_VOID(accessibilityProperty);
499 accessibilityProperty->SaveAccessibilityVirtualNode(nullptr);
500 }
501
SetLineNodeSize(RefPtr<FrameNode> lineNode)502 void CalendarMonthPattern::SetLineNodeSize(RefPtr<FrameNode> lineNode)
503 {
504 auto host = GetHost();
505 CHECK_NULL_VOID(host);
506 auto hostLayoutProperty = host->GetLayoutProperty();
507 CHECK_NULL_VOID(hostLayoutProperty);
508 auto width = hostLayoutProperty->GetLayoutConstraint()->selfIdealSize.Width().value_or(Infinity<float>());
509 auto height = hostLayoutProperty->GetLayoutConstraint()->selfIdealSize.Height().value_or(Infinity<float>());
510 CHECK_NULL_VOID(lineNode);
511 auto layoutProperty = lineNode->GetLayoutProperty<LayoutProperty>();
512 CHECK_NULL_VOID(layoutProperty);
513 layoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(width), CalcLength(height)));
514 }
515
SetFocusNode(int32_t index,bool isDeviceOrientation)516 void CalendarMonthPattern::SetFocusNode(int32_t index, bool isDeviceOrientation)
517 {
518 auto host = GetHost();
519 CHECK_NULL_VOID(host);
520 auto layoutProperty = host->GetLayoutProperty();
521 CHECK_NULL_VOID(layoutProperty);
522 auto textDirection = layoutProperty->GetNonAutoLayoutDirection();
523 auto remainderWeek = index % CALENDAR_WEEK_DAYS;
524 int32_t selectedIndex = (textDirection == TextDirection::RTL ?
525 CALENDAR_WEEK_DAYS - remainderWeek * 2 + index - 1 : index);
526 if (isDeviceOrientation) {
527 selectedIndex = index;
528 }
529 if (selectedIndex >= static_cast<int32_t>(buttonAccessibilityNodeVec_.size()) || selectedIndex < 0) {
530 return;
531 }
532 buttonAccessibilityNodeVec_[selectedIndex]->OnAccessibilityEvent(AccessibilityEventType::REQUEST_FOCUS);
533 selectedIndex_ = selectedIndex;
534 }
535
InitCalendarVirtualNode()536 bool CalendarMonthPattern::InitCalendarVirtualNode()
537 {
538 auto host = GetHost();
539 CHECK_NULL_RETURN(host, false);
540 buttonAccessibilityNodeVec_.clear();
541 accessibilityPropertyVec_.clear();
542 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
543 CHECK_NULL_RETURN(accessibilityProperty, false);
544 accessibilityProperty->SaveAccessibilityVirtualNode(nullptr);
545 auto lineNode = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
546 AceType::MakeRefPtr<LinearLayoutPattern>(true));
547 for (auto& day : obtainedMonth_.days) {
548 auto buttonNode = AddButtonNodeIntoVirtual(day);
549 lineNode->AddChild(buttonNode);
550 buttonAccessibilityNodeVec_.emplace_back(buttonNode);
551 ChangeVirtualNodeContent(day);
552 }
553 SetLineNodeSize(lineNode);
554 SetCalendarAccessibilityLevel(AccessibilityProperty::Level::NO_STR);
555 accessibilityProperty->SetAccessibilityText(" ");
556 auto virtualFrameNode = AceType::DynamicCast<NG::FrameNode>(lineNode);
557 CHECK_NULL_RETURN(virtualFrameNode, false);
558 virtualFrameNode->SetAccessibilityNodeVirtual();
559 virtualFrameNode->SetAccessibilityVirtualNodeParent(AceType::DynamicCast<NG::UINode>(host));
560 virtualFrameNode->SetFirstAccessibilityVirtualNode();
561 FrameNode::ProcessOffscreenNode(virtualFrameNode);
562 accessibilityProperty->SaveAccessibilityVirtualNode(lineNode);
563 lineNode_ = lineNode;
564 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
565 auto deviceOrientation = SystemProperties::GetDeviceOrientation();
566 if (deviceOrientation_ != deviceOrientation && !isFirstEnter_) {
567 SetFocusNode(focusedCalendarDay_.index, true);
568 deviceOrientation_ = deviceOrientation;
569 }
570 if (!isFirstEnter_) {
571 return true;
572 }
573 for (auto &day : obtainedMonth_.days) {
574 if (day.isSelected) {
575 SetFocusNode(day.index);
576 isFirstEnter_ = false;
577 focusedCalendarDay_ = day;
578 break;
579 }
580 }
581 return true;
582 }
583
GetXYByIndex(const int32_t index)584 std::pair<int32_t, int32_t> GetXYByIndex(const int32_t index)
585 {
586 if (index < CALENDAR_WEEK_DAYS - 1) {
587 return {index, 0};
588 } else {
589 return {(index % CALENDAR_WEEK_DAYS), (index / CALENDAR_WEEK_DAYS)};
590 }
591 }
592
ChangeVirtualNodeState(const CalendarDay & calendarDay)593 void CalendarMonthPattern::ChangeVirtualNodeState(const CalendarDay& calendarDay)
594 {
595 if (selectedIndex_ >= static_cast<int32_t>(accessibilityPropertyVec_.size()) || selectedIndex_ < 0) {
596 return;
597 }
598 if (calendarDay.index != selectedIndex_ && calendarDay.month.month == obtainedMonth_.month) {
599 accessibilityPropertyVec_[selectedIndex_]->SetUserSelected(true);
600 }
601 }
602
AddButtonNodeIntoVirtual(const CalendarDay & calendarDay)603 RefPtr<FrameNode> CalendarMonthPattern::AddButtonNodeIntoVirtual(const CalendarDay& calendarDay)
604 {
605 auto buttonNode = FrameNode::CreateFrameNode(
606 V2::BUTTON_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ButtonPattern>());
607 CHECK_NULL_RETURN(buttonNode, nullptr);
608 auto buttonAccessibilityProperty = buttonNode->GetAccessibilityProperty<AccessibilityProperty>();
609 CHECK_NULL_RETURN(buttonAccessibilityProperty, nullptr);
610 auto host = GetHost();
611 CHECK_NULL_RETURN(host, nullptr);
612 auto pipeline = host->GetContext();
613 if (pipeline) {
614 UpdateAccessibilityButtonNode(buttonNode, calendarDay.index);
615 } else {
616 UpdateButtonNodeWithoutTheme(buttonNode, calendarDay.index);
617 }
618 accessibilityPropertyVec_.emplace_back(buttonAccessibilityProperty);
619 InitVirtualButtonClickEvent(buttonNode, calendarDay.index);
620 buttonAccessibilityProperty->SetOnAccessibilityFocusCallback([weak = WeakClaim(this), calendarDay](bool focus) {
621 if (focus) {
622 auto calendar = weak.Upgrade();
623 CHECK_NULL_VOID(calendar);
624 calendar->focusedCalendarDay_ = calendarDay;
625 calendar->ChangeVirtualNodeState(calendarDay);
626 }
627 });
628 return buttonNode;
629 }
630
GetRowSpace(RefPtr<CalendarPaintProperty> paintProperty,RefPtr<CalendarTheme> theme,size_t daySize)631 float GetRowSpace(RefPtr<CalendarPaintProperty> paintProperty, RefPtr<CalendarTheme> theme, size_t daySize)
632 {
633 auto dailyFourRowSpace = NonPositive(paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx())
634 ? theme->GetCalendarTheme().dailySixRowSpace.ConvertToPx()
635 : paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx();
636 auto dailyFiveRowSpace = paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx() <= 0
637 ? theme->GetCalendarTheme().dailyFiveRowSpace.ConvertToPx()
638 : paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx();
639 auto dailySixRowSpace = paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx() <= 0
640 ? theme->GetCalendarTheme().dailySixRowSpace.ConvertToPx()
641 : paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx();
642 auto rows = (static_cast<int32_t>(daySize) / CALENDAR_WEEK_DAYS);
643 auto rowSpace = dailySixRowSpace;
644 switch (rows) {
645 case DAILY_FOUR_ROWSPACE: {
646 rowSpace = dailyFourRowSpace;
647 break;
648 }
649 case DAILY_FIVE_ROWSPACE: {
650 rowSpace = dailyFiveRowSpace;
651 break;
652 }
653 default:
654 break;
655 }
656 return rowSpace;
657 }
658
SetCalendarAccessibilityLevel(const std::string & level)659 void CalendarMonthPattern::SetCalendarAccessibilityLevel(const std::string& level)
660 {
661 auto host = GetHost();
662 CHECK_NULL_VOID(host);
663 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
664 CHECK_NULL_VOID(accessibilityProperty);
665 accessibilityProperty->SetAccessibilityLevel(level);
666 auto parent = host->GetParent();
667 while (parent && parent->GetTag() != V2::DIALOG_ETS_TAG) {
668 auto parentNode = AceType::DynamicCast<NG::FrameNode>(parent);
669 CHECK_NULL_VOID(parentNode);
670 auto parentAccessibilityProperty = parentNode->GetAccessibilityProperty<AccessibilityProperty>();
671 CHECK_NULL_VOID(parentAccessibilityProperty);
672 parentAccessibilityProperty->SetAccessibilityLevel(level);
673 parent = parent->GetParent();
674 }
675 }
676
InitAccessibilityHoverEvent()677 void CalendarMonthPattern::InitAccessibilityHoverEvent()
678 {
679 auto host = GetHost();
680 CHECK_NULL_VOID(host);
681 auto eventHub = host->GetOrCreateInputEventHub();
682 CHECK_NULL_VOID(eventHub);
683 eventHub->SetAccessibilityHoverEvent([weak = WeakClaim(this)](bool isHover, AccessibilityHoverInfo& info) {
684 auto calendar = weak.Upgrade();
685 CHECK_NULL_VOID(calendar);
686 calendar->HandleAccessibilityHoverEvent(isHover, info);
687 });
688 }
689
HandleAccessibilityHoverEvent(bool isHover,AccessibilityHoverInfo & info)690 void CalendarMonthPattern::HandleAccessibilityHoverEvent(bool isHover, AccessibilityHoverInfo& info)
691 {
692 if (isHover) {
693 isOnHover_ = true;
694 } else if (!isHover) {
695 isOnHover_ = false;
696 }
697 }
698
GetRowSpaceWithoutTheme(RefPtr<CalendarPaintProperty> paintProperty,size_t daySize)699 float GetRowSpaceWithoutTheme(RefPtr<CalendarPaintProperty> paintProperty, size_t daySize)
700 {
701 auto dailyFourRowSpace = NonPositive(paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx())
702 ? 0.0f
703 : paintProperty->GetDailyFourRowSpaceValue({}).ConvertToPx();
704 auto dailyFiveRowSpace = paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx() <= 0
705 ? 0.0f
706 : paintProperty->GetDailyFiveRowSpaceValue({}).ConvertToPx();
707 auto dailySixRowSpace = paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx() <= 0
708 ? 0.0f
709 : paintProperty->GetDailySixRowSpaceValue({}).ConvertToPx();
710 auto rows = (static_cast<int32_t>(daySize) / CALENDAR_WEEK_DAYS);
711 auto rowSpace = dailySixRowSpace;
712 switch (rows) {
713 case DAILY_FOUR_ROWSPACE: {
714 rowSpace = dailyFourRowSpace;
715 break;
716 }
717 case DAILY_FIVE_ROWSPACE: {
718 rowSpace = dailyFiveRowSpace;
719 break;
720 }
721 default:
722 break;
723 }
724 return rowSpace;
725 }
726
UpdateButtonNodeWithoutTheme(RefPtr<FrameNode> frameNode,int32_t index)727 void CalendarMonthPattern::UpdateButtonNodeWithoutTheme(RefPtr<FrameNode> frameNode, int32_t index)
728 {
729 auto host = GetHost();
730 CHECK_NULL_VOID(host);
731 auto paintProperty = host->GetPaintProperty<CalendarPaintProperty>();
732 CHECK_NULL_VOID(paintProperty);
733 auto dayHeight = paintProperty->GetDayHeight().value_or(Dimension(0.0f)).ConvertToPx();
734 auto dayWidth = paintProperty->GetDayWidth().value_or(Dimension(0.0f)).ConvertToPx();
735 CHECK_NULL_VOID(frameNode);
736 auto buttonLayoutProperty = frameNode->GetLayoutProperty<ButtonLayoutProperty>();
737 CHECK_NULL_VOID(buttonLayoutProperty);
738 buttonLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(dayWidth), CalcLength(dayHeight)));
739 auto pos = GetXYByIndex(index);
740 auto colSpace = paintProperty->GetColSpaceValue({}).ConvertToPx() <= 0
741 ? 0.0f
742 : paintProperty->GetColSpaceValue({}).ConvertToPx();
743 Dimension buttonOffsetX = Dimension(margin_ / 2 + (colSpace + dayWidth) * pos.first);
744 auto weekHeight = paintProperty->GetWeekHeight().value_or(Dimension(0.0f)).ConvertToPx();
745 auto rowSpace = GetRowSpaceWithoutTheme(paintProperty, obtainedMonth_.days.size());
746 auto weekAndDayRowSpace =
747 paintProperty->GetWeekAndDayRowSpace().value_or(Dimension(0.0f)).ConvertToPx();
748 auto browHeight = weekHeight + weekAndDayRowSpace;
749 Dimension buttonOffsetY = Dimension(browHeight + (dayHeight + rowSpace) * pos.second);
750 auto renderContext = frameNode->GetRenderContext();
751 CHECK_NULL_VOID(renderContext);
752 renderContext->UpdatePosition(OffsetT(buttonOffsetX, buttonOffsetY));
753 frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
754 }
755
UpdateAccessibilityButtonNode(RefPtr<FrameNode> frameNode,int32_t index)756 void CalendarMonthPattern::UpdateAccessibilityButtonNode(RefPtr<FrameNode> frameNode, int32_t index)
757 {
758 auto host = GetHost();
759 CHECK_NULL_VOID(host);
760 auto paintProperty = host->GetPaintProperty<CalendarPaintProperty>();
761 CHECK_NULL_VOID(paintProperty);
762 auto pipelineContext = host->GetContext();
763 CHECK_NULL_VOID(pipelineContext);
764 RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
765 CHECK_NULL_VOID(theme);
766 auto dayHeight = paintProperty->GetDayHeight().value_or(theme->GetCalendarTheme().dayHeight).ConvertToPx();
767 auto dayWidth = paintProperty->GetDayWidth().value_or(theme->GetCalendarTheme().dayWidth).ConvertToPx();
768 CHECK_NULL_VOID(frameNode);
769 auto buttonLayoutProperty = frameNode->GetLayoutProperty<ButtonLayoutProperty>();
770 CHECK_NULL_VOID(buttonLayoutProperty);
771 buttonLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(dayWidth), CalcLength(dayHeight)));
772 auto renderContext = frameNode->GetRenderContext();
773 CHECK_NULL_VOID(renderContext);
774 auto pos = GetXYByIndex(index);
775 auto colSpace = paintProperty->GetColSpaceValue({}).ConvertToPx() <= 0
776 ? theme->GetCalendarTheme().colSpace.ConvertToPx()
777 : paintProperty->GetColSpaceValue({}).ConvertToPx();
778 Dimension buttonOffsetX = Dimension(margin_ / 2 + (colSpace + dayWidth) * pos.first);
779 auto topPadding = isCalendarDialog_ ? 0.0 : theme->GetCalendarTheme().topPadding.ConvertToPx();
780 auto weekHeight = paintProperty->GetWeekHeight().value_or(theme->GetCalendarTheme().weekHeight).ConvertToPx();
781 auto rowSpace = GetRowSpace(paintProperty, theme, obtainedMonth_.days.size());
782 auto weekAndDayRowSpace =
783 paintProperty->GetWeekAndDayRowSpace().value_or(theme->GetCalendarTheme().weekAndDayRowSpace).ConvertToPx();
784 auto browHeight = weekHeight + topPadding + weekAndDayRowSpace;
785 Dimension buttonOffsetY = Dimension(browHeight + (dayHeight + rowSpace) * pos.second);
786 renderContext->UpdatePosition(OffsetT(buttonOffsetX, buttonOffsetY));
787 frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
788 }
789
GetDayStr(int32_t index)790 std::string CalendarMonthPattern::GetDayStr(int32_t index)
791 {
792 auto host = GetHost();
793 CHECK_NULL_RETURN(host, "");
794 auto pipelineContext = host->GetContext();
795 CHECK_NULL_RETURN(pipelineContext, "");
796 RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
797 CHECK_NULL_RETURN(theme, "");
798 switch (index) {
799 case MONDAY_INDEX:
800 return theme->GetCalendarTheme().monday;
801 case TUESDAY_INDEX:
802 return theme->GetCalendarTheme().tuesday;
803 case WEDNESDAY_INDEX:
804 return theme->GetCalendarTheme().wednesday;
805 case THURSDAY_INDEX:
806 return theme->GetCalendarTheme().thursday;
807 case FRIDAY_INDEX:
808 return theme->GetCalendarTheme().friday;
809 case SATURDAY_INDEX:
810 return theme->GetCalendarTheme().saturday;
811 default:
812 return theme->GetCalendarTheme().sunday;
813 }
814 }
815
ChangeVirtualNodeContent(const CalendarDay & calendarDay)816 void CalendarMonthPattern::ChangeVirtualNodeContent(const CalendarDay& calendarDay)
817 {
818 auto host = GetHost();
819 CHECK_NULL_VOID(host);
820 auto layoutProperty = host->GetLayoutProperty();
821 CHECK_NULL_VOID(layoutProperty);
822 auto textDirection = layoutProperty->GetNonAutoLayoutDirection();
823 auto remainderWeek = calendarDay.index % CALENDAR_WEEK_DAYS;
824 int32_t index = (textDirection == TextDirection::RTL ?
825 CALENDAR_WEEK_DAYS - remainderWeek * 2 + calendarDay.index - 1 : calendarDay.index);
826 if (index >= static_cast<int32_t>(buttonAccessibilityNodeVec_.size()) || index < 0) {
827 return;
828 }
829 std::string message;
830 if (calendarDay.month.year == calendarDay_.month.year && calendarDay.month.month == calendarDay_.month.month &&
831 calendarDay.day == calendarDay_.day) {
832 message += Localization::GetInstance()->GetEntryLetters("calendar.today");
833 }
834 message += std::to_string(calendarDay.month.year) + "/";
835 message += std::to_string(calendarDay.month.month) + "/";
836 message += std::to_string(calendarDay.day);
837 message += GetDayStr(remainderWeek);
838 auto node = buttonAccessibilityNodeVec_[index];
839 auto buttonAccessibilityProperty = node->GetAccessibilityProperty<AccessibilityProperty>();
840 CHECK_NULL_VOID(buttonAccessibilityProperty);
841 if (calendarDay.month.month != obtainedMonth_.month) {
842 buttonAccessibilityProperty->SetAccessibilityDescription(disabledDesc_);
843 } else if (index == selectedIndex_) {
844 buttonAccessibilityProperty->SetAccessibilityDescription(" ");
845 } else {
846 buttonAccessibilityProperty->SetAccessibilityDescription("");
847 }
848 buttonAccessibilityProperty->SetUserDisabled(calendarDay.month.month != obtainedMonth_.month ? true : false);
849 buttonAccessibilityProperty->SetUserSelected(false);
850 buttonAccessibilityProperty->SetAccessibilityText(message);
851 }
852
FireModifyAccessibilityVirtualNode(const ObtainedMonth & currentData)853 void CalendarMonthPattern::FireModifyAccessibilityVirtualNode(const ObtainedMonth& currentData)
854 {
855 if (isInitVirtualNode_) {
856 auto pipeline = PipelineContext::GetCurrentContext();
857 CHECK_NULL_VOID(pipeline);
858 pipeline->AddAfterRenderTask([weak = WeakClaim(this), currentData]() {
859 auto calendarMonthPattern = weak.Upgrade();
860 CHECK_NULL_VOID(calendarMonthPattern);
861 calendarMonthPattern->ModifyAccessibilityVirtualNode(currentData);
862 });
863 }
864 }
865
ModifyAccessibilityVirtualNode(const ObtainedMonth & currentData)866 void CalendarMonthPattern::ModifyAccessibilityVirtualNode(const ObtainedMonth& currentData)
867 {
868 if (buttonAccessibilityNodeVec_.size() < 1) {
869 return;
870 }
871 for (auto& day : currentData.days) {
872 ChangeVirtualNodeContent(day);
873 }
874 }
875 } // namespace OHOS::Ace::NG
876