1 /*
2 * Copyright (c) 2022 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/time_picker/timepicker_row_pattern.h"
17 #include <cstdint>
18
19 #include "base/geometry/ng/size_t.h"
20 #include "base/utils/utils.h"
21 #include "core/components/picker/picker_theme.h"
22 #include "core/components_ng/pattern/button/button_pattern.h"
23 #include "core/components_ng/pattern/stack/stack_pattern.h"
24 #include "core/components_ng/pattern/text/text_pattern.h"
25 #include "core/components_ng/pattern/time_picker/toss_animation_controller.h"
26 #include "core/components_v2/inspector/inspector_constants.h"
27 #include "core/pipeline_ng/ui_task_scheduler.h"
28
29 namespace OHOS::Ace::NG {
30 namespace {
31 // TODO timepicker style modification
32 constexpr int32_t CHILD_WITH_AMPM_SIZE = 3;
33 constexpr int32_t CHILD_WITHOUT_AMPM_SIZE = 2;
34 constexpr uint32_t AM_PM_HOUR_12 = 12;
35 constexpr uint32_t AM_PM_HOUR_11 = 11;
36 const int32_t AM_PM_COUNT = 3;
37 const Dimension PRESS_INTERVAL = 4.0_vp;
38 const Dimension PRESS_RADIUS = 8.0_vp;
39 const int32_t UNOPTION_COUNT = 2;
40 } // namespace
41
OnAttachToFrameNode()42 void TimePickerRowPattern::OnAttachToFrameNode()
43 {
44 auto host = GetHost();
45 CHECK_NULL_VOID(host);
46 host->GetRenderContext()->SetClipToFrame(true);
47 host->GetRenderContext()->UpdateClipEdge(true);
48 }
49
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)50 bool TimePickerRowPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
51 {
52 CHECK_NULL_RETURN_NOLOG(config.frameSizeChange, false);
53 CHECK_NULL_RETURN(dirty, false);
54 SetButtonIdeaSize();
55 return true;
56 }
57
SetButtonIdeaSize()58 void TimePickerRowPattern::SetButtonIdeaSize()
59 {
60 auto host = GetHost();
61 CHECK_NULL_VOID(host);
62 auto context = host->GetContext();
63 CHECK_NULL_VOID(context);
64 auto pickerTheme = context->GetTheme<PickerTheme>();
65 CHECK_NULL_VOID(pickerTheme);
66 auto children = host->GetChildren();
67 auto height = pickerTheme->GetDividerSpacing();
68 auto width = host->GetGeometryNode()->GetFrameSize().Width() / static_cast<float>(children.size());
69 auto defaultWidth = height.ConvertToPx() * 2;
70 if (width > defaultWidth) {
71 width = static_cast<float>(defaultWidth);
72 }
73 for (const auto& child : children) {
74 auto buttonNode = DynamicCast<FrameNode>(child->GetFirstChild());
75 auto buttonLayoutProperty = buttonNode->GetLayoutProperty<ButtonLayoutProperty>();
76 buttonLayoutProperty->UpdateMeasureType(MeasureType::MATCH_PARENT_MAIN_AXIS);
77 buttonLayoutProperty->UpdateType(ButtonType::NORMAL);
78 buttonLayoutProperty->UpdateBorderRadius(BorderRadiusProperty(PRESS_RADIUS));
79 buttonLayoutProperty->UpdateUserDefinedIdealSize(
80 CalcSize(CalcLength(width - PRESS_INTERVAL.ConvertToPx()), CalcLength(height - PRESS_INTERVAL)));
81 auto buttonConfirmRenderContext = buttonNode->GetRenderContext();
82 buttonConfirmRenderContext->UpdateBackgroundColor(Color::TRANSPARENT);
83 buttonNode->MarkModifyDone();
84 }
85 }
86
OnModifyDone()87 void TimePickerRowPattern::OnModifyDone()
88 {
89 auto host = GetHost();
90 CHECK_NULL_VOID(host);
91 CreateAmPmNode();
92 OnColumnsBuilding();
93 FlushColumn();
94 InitDisabled();
95 SetChangeCallback([weak = WeakClaim(this)](const RefPtr<FrameNode>& tag, bool add, uint32_t index, bool notify) {
96 auto refPtr = weak.Upgrade();
97 CHECK_NULL_VOID_NOLOG(refPtr);
98 refPtr->HandleColumnChange(tag, add, index, notify);
99 });
100 SetEventCallback([weak = WeakClaim(this)](bool refresh) {
101 auto refPtr = weak.Upgrade();
102 CHECK_NULL_VOID_NOLOG(refPtr);
103 refPtr->FireChangeEvent(refresh);
104 });
105 auto focusHub = host->GetFocusHub();
106 if (focusHub) {
107 InitOnKeyEvent(focusHub);
108 }
109 if (HasTitleNode()) {
110 auto textTitleNode = FrameNode::GetOrCreateFrameNode(
111 V2::TEXT_ETS_TAG, GetTitleId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
112 auto str = GetDialogTitleDate();
113 CHECK_NULL_VOID(textTitleNode);
114 auto textLayoutProperty = textTitleNode->GetLayoutProperty<TextLayoutProperty>();
115 CHECK_NULL_VOID(textLayoutProperty);
116 textLayoutProperty->UpdateContent(str.ToString(false));
117 }
118 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
119 }
120
InitDisabled()121 void TimePickerRowPattern::InitDisabled()
122 {
123 auto host = GetHost();
124 CHECK_NULL_VOID(host);
125 auto eventHub = host->GetEventHub<EventHub>();
126 CHECK_NULL_VOID(eventHub);
127 auto renderContext = host->GetRenderContext();
128 enabled_ = eventHub->IsEnabled();
129 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
130 }
131
CreateAmPmNode()132 void TimePickerRowPattern::CreateAmPmNode()
133 {
134 auto host = GetHost();
135 CHECK_NULL_VOID(host);
136 auto context = host->GetContext();
137 CHECK_NULL_VOID(context);
138 auto pickerTheme = context->GetTheme<PickerTheme>();
139 CHECK_NULL_VOID(pickerTheme);
140 auto heigth = pickerTheme->GetDividerSpacing();
141 if (!GetHour24() && !HasAmPmNode()) {
142 auto amPmColumnNode = FrameNode::GetOrCreateFrameNode(
143 V2::COLUMN_ETS_TAG, GetAmPmId(), []() { return AceType::MakeRefPtr<TimePickerColumnPattern>(); });
144 CHECK_NULL_VOID(amPmColumnNode);
145 for (uint32_t index = 0; index < AM_PM_COUNT; index++) {
146 auto textNode = FrameNode::CreateFrameNode(
147 V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
148 CHECK_NULL_VOID(textNode);
149 textNode->MountToParent(amPmColumnNode);
150 }
151 SetColumn(amPmColumnNode);
152 auto stackAmPmNode = FrameNode::GetOrCreateFrameNode(V2::STACK_ETS_TAG,
153 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<StackPattern>(); });
154 auto buttonNode = FrameNode::GetOrCreateFrameNode(V2::BUTTON_ETS_TAG,
155 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ButtonPattern>(); });
156 buttonNode->MountToParent(stackAmPmNode);
157 auto buttonLayoutProperty = buttonNode->GetLayoutProperty<ButtonLayoutProperty>();
158 amPmColumnNode->MountToParent(stackAmPmNode);
159 auto layoutProperty = stackAmPmNode->GetLayoutProperty<LayoutProperty>();
160 layoutProperty->UpdateAlignment(Alignment::CENTER);
161 layoutProperty->UpdateLayoutWeight(1);
162 stackAmPmNode->MountToParent(host, 0);
163 if (SetAmPmButtonIdeaSize() > 0) {
164 auto buttonLayoutProperty = buttonNode->GetLayoutProperty<ButtonLayoutProperty>();
165 buttonLayoutProperty->UpdateMeasureType(MeasureType::MATCH_PARENT_MAIN_AXIS);
166 buttonLayoutProperty->UpdateType(ButtonType::NORMAL);
167 buttonLayoutProperty->UpdateBorderRadius(BorderRadiusProperty(PRESS_RADIUS));
168 buttonLayoutProperty->UpdateUserDefinedIdealSize(
169 CalcSize(CalcLength(SetAmPmButtonIdeaSize()), CalcLength(heigth - PRESS_INTERVAL)));
170 auto buttonConfirmRenderContext = buttonNode->GetRenderContext();
171 buttonConfirmRenderContext->UpdateBackgroundColor(Color::TRANSPARENT);
172 buttonNode->MarkModifyDone();
173 buttonNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
174 }
175 host->MarkModifyDone();
176 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
177 }
178 }
179
SetAmPmButtonIdeaSize()180 double TimePickerRowPattern::SetAmPmButtonIdeaSize()
181 {
182 auto host = GetHost();
183 CHECK_NULL_RETURN(host, 0);
184 auto children = host->GetChildren();
185 float width = 0.0f;
186 for (const auto& child : children) {
187 auto buttonNode = DynamicCast<FrameNode>(child->GetFirstChild());
188 CHECK_NULL_RETURN(buttonNode, 0);
189 width = buttonNode->GetGeometryNode()->GetFrameSize().Width();
190 }
191 if (width > 0) {
192 return width;
193 }
194 return 0;
195 }
SetEventCallback(EventCallback && value)196 void TimePickerRowPattern::SetEventCallback(EventCallback&& value)
197 {
198 auto host = GetHost();
199 CHECK_NULL_VOID(host);
200 auto children = host->GetChildren();
201 for (const auto& child : children) {
202 auto stackNode = DynamicCast<FrameNode>(child);
203 CHECK_NULL_VOID(stackNode);
204 auto childNode = DynamicCast<FrameNode>(stackNode->GetLastChild());
205 CHECK_NULL_VOID(childNode);
206 auto timePickerColumnPattern = childNode->GetPattern<TimePickerColumnPattern>();
207 CHECK_NULL_VOID(timePickerColumnPattern);
208 timePickerColumnPattern->SetEventCallback(std::move(value));
209 }
210 }
211
FireChangeEvent(bool refresh)212 void TimePickerRowPattern::FireChangeEvent(bool refresh)
213 {
214 if (refresh) {
215 auto timePickerEventHub = GetEventHub<TimePickerEventHub>();
216 CHECK_NULL_VOID(timePickerEventHub);
217 auto str = GetSelectedObject(true);
218 auto info = std::make_shared<DatePickerChangeEvent>(str);
219 timePickerEventHub->FireChangeEvent(info.get());
220 timePickerEventHub->FireDialogChangeEvent(str);
221 }
222 }
223
GetSelectedObject(bool isColumnChange,int32_t status)224 std::string TimePickerRowPattern::GetSelectedObject(bool isColumnChange, int32_t status)
225 {
226 auto time = selectedTime_;
227 if (isColumnChange) {
228 time = GetCurrentTime();
229 }
230 return time.ToString(true, hasSecond_, status);
231 }
232
GetCurrentTime()233 PickerTime TimePickerRowPattern::GetCurrentTime()
234 {
235 PickerTime time;
236 UpdateAllChildNode();
237 auto amPmColumn = allChildNode_["amPm"];
238 auto hourColumn = allChildNode_["hour"];
239 auto minuteColumn = allChildNode_["minute"];
240 CHECK_NULL_RETURN(hourColumn, time);
241 CHECK_NULL_RETURN(minuteColumn, time);
242 auto hourPickerColumnPattern = hourColumn->GetPattern<TimePickerColumnPattern>();
243 CHECK_NULL_RETURN(hourPickerColumnPattern, time);
244 auto minutePickerColumnPattern = minuteColumn->GetPattern<TimePickerColumnPattern>();
245 CHECK_NULL_RETURN(minutePickerColumnPattern, time);
246
247 if (GetHour24()) {
248 time.SetHour(hourPickerColumnPattern->GetCurrentIndex()); // hour from 0 to 23, index from 0 to 23
249 } else if (amPmColumn) {
250 auto amPmPickerColumnPattern = amPmColumn->GetPattern<TimePickerColumnPattern>();
251 CHECK_NULL_RETURN(amPmPickerColumnPattern, time);
252 time.SetHour(GetHourFromAmPm(
253 amPmPickerColumnPattern->GetCurrentIndex() == 0, hourPickerColumnPattern->GetCurrentIndex() + 1));
254 } else {
255 LOGE("AM PM column is null.");
256 }
257
258 time.SetMinute(minutePickerColumnPattern->GetCurrentIndex()); // minute from 0 to 59, index from 0 to 59
259 return time;
260 }
261
GetHourFromAmPm(bool isAm,uint32_t amPmhour) const262 uint32_t TimePickerRowPattern::GetHourFromAmPm(bool isAm, uint32_t amPmhour) const
263 {
264 if (isAm) {
265 if (amPmhour == AM_PM_HOUR_12) { // AM 12:00 means 00:00
266 return 0;
267 }
268 return amPmhour;
269 }
270 if (amPmhour == AM_PM_HOUR_12) { // PM 12 means 12:00
271 return AM_PM_HOUR_12;
272 }
273 return amPmhour + AM_PM_HOUR_12; // need add 12 hour to 24 hours style
274 }
275
HandleColumnChange(const RefPtr<FrameNode> & tag,bool isAdd,uint32_t index,bool needNotify)276 void TimePickerRowPattern::HandleColumnChange(const RefPtr<FrameNode>& tag, bool isAdd, uint32_t index, bool needNotify)
277 {
278 std::vector<RefPtr<FrameNode>> tags;
279 for (const auto& tag : tags) {
280 auto iter = std::find_if(timePickerColumns_.begin(), timePickerColumns_.end(),
281 [&tag](const RefPtr<FrameNode>& column) { return column->GetId() == tag->GetId(); });
282 if (iter != timePickerColumns_.end()) {
283 auto timePickerColumnPattern = (*iter)->GetPattern<TimePickerColumnPattern>();
284 CHECK_NULL_VOID(timePickerColumnPattern);
285 timePickerColumnPattern->FlushCurrentOptions();
286 }
287 }
288 }
289
OnLanguageConfigurationUpdate()290 void TimePickerRowPattern::OnLanguageConfigurationUpdate()
291 {
292 auto buttonConfirmNode = weakButtonConfirm_.Upgrade();
293 CHECK_NULL_VOID(buttonConfirmNode);
294 auto confirmNode = buttonConfirmNode->GetFirstChild();
295 auto confirmNodeLayout = AceType::DynamicCast<FrameNode>(confirmNode)->GetLayoutProperty<TextLayoutProperty>();
296 confirmNodeLayout->UpdateContent(Localization::GetInstance()->GetEntryLetters("common.ok"));
297
298 auto buttonCancelNode = weakButtonCancel_.Upgrade();
299 CHECK_NULL_VOID(buttonCancelNode);
300 auto cancelNode = buttonCancelNode->GetFirstChild();
301 auto cancelNodeLayout = AceType::DynamicCast<FrameNode>(cancelNode)->GetLayoutProperty<TextLayoutProperty>();
302 cancelNodeLayout->UpdateContent(Localization::GetInstance()->GetEntryLetters("common.cancel"));
303 FlushAmPmFormatString();
304 }
305
FlushAmPmFormatString()306 void TimePickerRowPattern::FlushAmPmFormatString()
307 {
308 auto it = std::find(vecAmPm_.begin(), vecAmPm_.end(), "AM");
309 if (it != vecAmPm_.end()) {
310 vecAmPm_.clear();
311 vecAmPm_ = Localization::GetInstance()->GetAmPmStrings();
312 std::string am = vecAmPm_[0];
313 vecAmPm_.emplace_back(am);
314 std::string pm = vecAmPm_[1];
315 vecAmPm_.emplace_back(pm);
316 } else {
317 vecAmPm_.clear();
318 vecAmPm_.emplace_back("AM");
319 vecAmPm_.emplace_back("PM");
320 }
321 }
322
SetChangeCallback(ColumnChangeCallback && value)323 void TimePickerRowPattern::SetChangeCallback(ColumnChangeCallback&& value)
324 {
325 auto host = GetHost();
326 CHECK_NULL_VOID(host);
327 auto children = host->GetChildren();
328 for (const auto& child : children) {
329 auto stackNode = DynamicCast<FrameNode>(child);
330 CHECK_NULL_VOID(stackNode);
331 auto childNode = DynamicCast<FrameNode>(stackNode->GetLastChild());
332 CHECK_NULL_VOID(childNode);
333 auto timePickerColumnPattern = childNode->GetPattern<TimePickerColumnPattern>();
334 CHECK_NULL_VOID(timePickerColumnPattern);
335 timePickerColumnPattern->SetChangeCallback(std::move(value));
336 }
337 }
338
FlushColumn()339 void TimePickerRowPattern::FlushColumn()
340 {
341 UpdateAllChildNode();
342 auto amPmColumn = allChildNode_["amPm"];
343 auto hourColumn = allChildNode_["hour"];
344 if (GetHour24()) {
345 CHECK_NULL_VOID(hourColumn);
346 auto hourColumnPattern = hourColumn->GetPattern<TimePickerColumnPattern>();
347 CHECK_NULL_VOID(hourColumnPattern);
348 hourColumnPattern->SetOptions(GetOptionsCount());
349 hourColumnPattern->SetShowCount(GetShowCount());
350 hourColumnPattern->FlushCurrentOptions();
351 } else if (amPmColumn) {
352 auto amPmColumnPattern = amPmColumn->GetPattern<TimePickerColumnPattern>();
353 CHECK_NULL_VOID(amPmColumnPattern);
354 amPmColumnPattern->SetShowCount(AM_PM_COUNT);
355 amPmColumnPattern->FlushCurrentOptions();
356
357 CHECK_NULL_VOID(hourColumn);
358 auto hourColumnPattern = hourColumn->GetPattern<TimePickerColumnPattern>();
359 CHECK_NULL_VOID(hourColumnPattern);
360 hourColumnPattern->SetOptions(GetOptionsCount());
361 hourColumnPattern->SetShowCount(GetShowCount());
362 hourColumnPattern->FlushCurrentOptions();
363 }
364
365 auto minuteColumn = allChildNode_["minute"];
366 CHECK_NULL_VOID(minuteColumn);
367 auto minuteColumnPattern = minuteColumn->GetPattern<TimePickerColumnPattern>();
368 CHECK_NULL_VOID(minuteColumnPattern);
369 minuteColumnPattern->SetShowCount(GetShowCount());
370 minuteColumnPattern->FlushCurrentOptions();
371 }
372
OnDataLinking(const RefPtr<FrameNode> & tag,bool isAdd,uint32_t index,std::vector<RefPtr<FrameNode>> & resultTags)373 void TimePickerRowPattern::OnDataLinking(
374 const RefPtr<FrameNode>& tag, bool isAdd, uint32_t index, std::vector<RefPtr<FrameNode>>& resultTags)
375 {
376 CHECK_NULL_VOID(tag);
377 auto hourNode = allChildNode_["hour"];
378 CHECK_NULL_VOID(hourNode);
379 if (tag->GetId() != hourNode->GetId()) {
380 return;
381 }
382
383 if (!GetHour24()) {
384 HandleHour12Change(isAdd, index, resultTags);
385 }
386 }
387
GetOptionsValue(const RefPtr<FrameNode> & frameNode,uint32_t optionIndex)388 const std::string& TimePickerRowPattern::GetOptionsValue(const RefPtr<FrameNode>& frameNode, uint32_t optionIndex)
389 {
390 UpdateAllChildNode();
391 if (frameNode == allChildNode_["amPm"]) {
392 return options_[allChildNode_["amPm"]][optionIndex];
393 }
394 bool isHour12 = !GetHour24();
395 auto isHourNode = frameNode == allChildNode_["hour"];
396 if (options_.find(frameNode) == options_.end()) {
397 options_[frameNode] = std::unordered_map<uint32_t, std::string>();
398 }
399 if (options_[frameNode].find(optionIndex) == options_[frameNode].end()) {
400 options_[frameNode][optionIndex] =
401 isHourNode ? GetHourFormatString(optionIndex + isHour12) : GetMinuteFormatString(optionIndex);
402 }
403 return options_[frameNode][optionIndex];
404 }
405
OnColumnsBuilding()406 void TimePickerRowPattern::OnColumnsBuilding()
407 {
408 HandleHourColumnBuilding();
409
410 UpdateAllChildNode();
411 auto minuteColumn = allChildNode_["minute"];
412 CHECK_NULL_VOID(minuteColumn);
413 auto minuteColumnPattern = minuteColumn->GetPattern<TimePickerColumnPattern>();
414 CHECK_NULL_VOID(minuteColumnPattern);
415 optionsTotalCount_[minuteColumn] = 0;
416
417 for (uint32_t minute = 0; minute <= 59; ++minute) { // time's minute from 0 to 59
418 if (minute == selectedTime_.GetMinute()) {
419 minuteColumnPattern->SetCurrentIndex(minute);
420 }
421 optionsTotalCount_[minuteColumn]++;
422 }
423 minuteColumnPattern->SetOptions(GetOptionsCount());
424 }
425
HandleHourColumnBuilding()426 void TimePickerRowPattern::HandleHourColumnBuilding()
427 {
428 UpdateAllChildNode();
429 auto amPmColumn = allChildNode_["amPm"];
430 auto hourColumn = allChildNode_["hour"];
431 optionsTotalCount_[hourColumn] = 0;
432 if (GetHour24()) {
433 CHECK_NULL_VOID(hourColumn);
434 auto hourColumnPattern = hourColumn->GetPattern<TimePickerColumnPattern>();
435 CHECK_NULL_VOID(hourColumnPattern);
436 for (uint32_t hour = 0; hour <= 23; ++hour) { // time's hour from 0 to 23.
437 if (hour == selectedTime_.GetHour()) {
438 hourColumnPattern->SetCurrentIndex(hour);
439 }
440 optionsTotalCount_[hourColumn]++;
441 }
442 hourColumnPattern->SetOptions(GetOptionsCount());
443 hourColumn->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
444 } else if (amPmColumn) {
445 CHECK_NULL_VOID(amPmColumn);
446 CHECK_NULL_VOID(hourColumn);
447 auto amPmColumnPattern = amPmColumn->GetPattern<TimePickerColumnPattern>();
448 CHECK_NULL_VOID(amPmColumnPattern);
449 auto hourColumnPattern = hourColumn->GetPattern<TimePickerColumnPattern>();
450 CHECK_NULL_VOID(hourColumnPattern);
451 options_[amPmColumn][0] = GetAmFormatString();
452 options_[amPmColumn][1] = GetPmFormatString();
453
454 if (IsAmHour(selectedTime_.GetHour())) {
455 amPmColumnPattern->SetCurrentIndex(0); // AM's index
456 } else {
457 amPmColumnPattern->SetCurrentIndex(1); // PM's index
458 }
459 optionsTotalCount_[amPmColumn] = CHILD_WITHOUT_AMPM_SIZE;
460 auto selectedHour = GetAmPmHour(selectedTime_.GetHour());
461 for (uint32_t hour = 1; hour <= AM_PM_HOUR_12; ++hour) { // AM_PM hour start from 1 to 12
462 if (hour == selectedHour) {
463 hourColumnPattern->SetCurrentIndex(hour - 1);
464 }
465 optionsTotalCount_[hourColumn]++;
466 }
467 amPmColumnPattern->SetOptions(GetOptionsCount());
468 hourColumnPattern->SetOptions(GetOptionsCount());
469 } else {
470 LOGE("AM PM column is null.");
471 }
472 }
473
UpdateAllChildNode()474 void TimePickerRowPattern::UpdateAllChildNode()
475 {
476 auto host = GetHost();
477 CHECK_NULL_VOID(host);
478 if (GetHour24() && host->GetChildren().size() == CHILD_WITH_AMPM_SIZE) {
479 host->RemoveChildAtIndex(0);
480 amPmId_.reset();
481 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
482 host->MarkModifyDone();
483 } else if (!GetHour24() && host->GetChildren().size() == CHILD_WITHOUT_AMPM_SIZE) {
484 CreateAmPmNode();
485 }
486 if (GetHour24() && host->GetChildren().size() != CHILD_WITHOUT_AMPM_SIZE) {
487 return;
488 }
489
490 if (!GetHour24() && host->GetChildren().size() != CHILD_WITH_AMPM_SIZE) {
491 return;
492 }
493 auto children = host->GetChildren();
494 auto iter = children.begin();
495 CHECK_NULL_VOID(*iter);
496 if (!GetHour24()) {
497 auto amPm = (*iter);
498 CHECK_NULL_VOID(amPm);
499 iter++;
500 auto hour = *iter;
501 CHECK_NULL_VOID(hour);
502 iter++;
503 auto minute = *iter;
504 CHECK_NULL_VOID(minute);
505 auto amPmStackNode = DynamicCast<FrameNode>(amPm);
506 auto amPmNode = DynamicCast<FrameNode>(amPmStackNode->GetLastChild());
507 auto hourStackNode = DynamicCast<FrameNode>(hour);
508 auto hourNode = DynamicCast<FrameNode>(hourStackNode->GetLastChild());
509 auto minuteStackNode = DynamicCast<FrameNode>(minute);
510 auto minuteNode = DynamicCast<FrameNode>(minuteStackNode->GetLastChild());
511 CHECK_NULL_VOID(amPmNode);
512 CHECK_NULL_VOID(hourNode);
513 CHECK_NULL_VOID(minuteNode);
514 allChildNode_["amPm"] = amPmNode;
515 allChildNode_["hour"] = hourNode;
516 allChildNode_["minute"] = minuteNode;
517 } else {
518 auto hour = *iter;
519 CHECK_NULL_VOID(hour);
520 iter++;
521 auto minute = *iter;
522 CHECK_NULL_VOID(minute);
523 auto hourStackNode = DynamicCast<FrameNode>(hour);
524 auto hourNode = DynamicCast<FrameNode>(hourStackNode->GetLastChild());
525 auto minuteStackNode = DynamicCast<FrameNode>(minute);
526 auto minuteNode = DynamicCast<FrameNode>(minuteStackNode->GetLastChild());
527 CHECK_NULL_VOID(hourNode);
528 CHECK_NULL_VOID(minuteNode);
529 allChildNode_["hour"] = hourNode;
530 allChildNode_["minute"] = minuteNode;
531 }
532 }
533
HandleHour12Change(bool isAdd,uint32_t index,std::vector<RefPtr<FrameNode>> & resultTags)534 void TimePickerRowPattern::HandleHour12Change(bool isAdd, uint32_t index, std::vector<RefPtr<FrameNode>>& resultTags)
535 {
536 UpdateAllChildNode();
537 auto amPm = allChildNode_["amPm"];
538 CHECK_NULL_VOID(amPm);
539 auto amPmPickerColumnPattern = amPm->GetPattern<TimePickerColumnPattern>();
540
541 if (amPmPickerColumnPattern->GetCurrentIndex() == 0 && isAdd && index == 11) { // hour index start from 0 to 11
542 amPmPickerColumnPattern->SetCurrentIndex(1); // add to PM's index
543 resultTags.emplace_back(amPm);
544 return;
545 }
546 if (amPmPickerColumnPattern->GetCurrentIndex() == 1 && !isAdd && index == 10) { // reduce to 11 hour (index is 10)
547 amPmPickerColumnPattern->SetCurrentIndex(0); // change to AM whose index is 0
548 resultTags.emplace_back(amPm);
549 return;
550 }
551 if (amPmPickerColumnPattern->GetCurrentIndex() == 1 && isAdd && index == 11) {
552 amPmPickerColumnPattern->SetCurrentIndex(0); // is PM (index is 1) and last hour (index is 11)
553 resultTags.emplace_back(amPm); // change to PM (index is 0)
554 return;
555 }
556 if (amPmPickerColumnPattern->GetCurrentIndex() == 0 && !isAdd && index == 10) { // reduce to 11 hour(index is 10)
557 amPmPickerColumnPattern->SetCurrentIndex(1); // change to PM
558 resultTags.emplace_back(amPm);
559 return;
560 }
561 }
562
GetAmPmHour(uint32_t hourOf24) const563 uint32_t TimePickerRowPattern::GetAmPmHour(uint32_t hourOf24) const
564 {
565 if (hourOf24 == 0) {
566 return AM_PM_HOUR_12; // AM 12:00 means 00:00 in 24 hour style
567 }
568 if (1 <= hourOf24 && hourOf24 <= AM_PM_HOUR_11) { // 00:00 to 11:00 is the same for any hour style
569 return hourOf24;
570 }
571 if (hourOf24 == AM_PM_HOUR_12) { // 12:00 means PM start hour
572 return AM_PM_HOUR_12; // 12 PM
573 } // hour from 13 to 23
574 return hourOf24 - AM_PM_HOUR_12; // need reduce 12 to 12 hours style
575 }
576
IsAmHour(uint32_t hourOf24) const577 bool TimePickerRowPattern::IsAmHour(uint32_t hourOf24) const
578 {
579 return (0 <= hourOf24 && hourOf24 <= AM_PM_HOUR_11); // 00:00 to 11:00 is AM hour
580 }
581
GetAmFormatString() const582 std::string TimePickerRowPattern::GetAmFormatString() const
583 {
584 if (vecAmPm_.empty()) {
585 return "AM";
586 }
587 return vecAmPm_[0]; // first index is AM
588 }
589
GetPmFormatString() const590 std::string TimePickerRowPattern::GetPmFormatString() const
591 {
592 if (vecAmPm_.size() < 2) { // size need to be 2 for AM and PM
593 return "PM";
594 }
595 return vecAmPm_[1]; // second index is PM
596 }
597
GetHourFormatString(uint32_t hour) const598 std::string TimePickerRowPattern::GetHourFormatString(uint32_t hour) const
599 {
600 DateTime time;
601 time.minute = hour; // minute range [0, 59], hour range [0, 23]; hour range is in minute range.
602 if (Localization::GetInstance()->HasZeroHour()) {
603 return AddZeroPrefix(Localization::GetInstance()->FormatDateTime(time, "m"));
604 }
605
606 return Localization::GetInstance()->FormatDateTime(time, "m");
607 }
608
GetMinuteFormatString(uint32_t minute) const609 std::string TimePickerRowPattern::GetMinuteFormatString(uint32_t minute) const
610 {
611 DateTime time;
612 time.minute = minute;
613 return AddZeroPrefix(Localization::GetInstance()->FormatDateTime(time, "m"));
614 }
615
AddZeroPrefix(const std::string & value) const616 std::string TimePickerRowPattern::AddZeroPrefix(const std::string& value) const
617 {
618 if (value.size() == 1 && '0' <= value[0] && value[0] <= '9') { // value is number in range [0, 9]
619 return std::string("0") + value; // add prefix '0'
620 }
621 return value;
622 }
623
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)624 void TimePickerRowPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
625 {
626 auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
627 auto pattern = wp.Upgrade();
628 if (pattern) {
629 return pattern->OnKeyEvent(event);
630 }
631 return false;
632 };
633 focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
634
635 auto getInnerPaintRectCallback = [wp = WeakClaim(this)](RoundRect& paintRect) {
636 auto pattern = wp.Upgrade();
637 if (pattern) {
638 pattern->GetInnerFocusPaintRect(paintRect);
639 }
640 };
641 focusHub->SetInnerFocusPaintRectCallback(getInnerPaintRectCallback);
642 }
643
PaintFocusState()644 void TimePickerRowPattern::PaintFocusState()
645 {
646 auto host = GetHost();
647 CHECK_NULL_VOID(host);
648
649 RoundRect focusRect;
650 GetInnerFocusPaintRect(focusRect);
651
652 auto focusHub = host->GetFocusHub();
653 CHECK_NULL_VOID(focusHub);
654 focusHub->PaintInnerFocusState(focusRect);
655
656 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
657 }
658
GetInnerFocusPaintRect(RoundRect & paintRect)659 void TimePickerRowPattern::GetInnerFocusPaintRect(RoundRect& paintRect)
660 {
661 auto host = GetHost();
662 CHECK_NULL_VOID(host);
663 auto childSize = host->GetChildren().size();
664 auto stackChild = DynamicCast<FrameNode>(host->GetChildAtIndex(focusKeyID_));
665 CHECK_NULL_VOID(stackChild);
666 auto pickerChild = DynamicCast<FrameNode>(stackChild->GetLastChild());
667 CHECK_NULL_VOID(pickerChild);
668 auto columnWidth = pickerChild->GetGeometryNode()->GetFrameSize().Width();
669 auto pipeline = PipelineBase::GetCurrentContext();
670 CHECK_NULL_VOID(pipeline);
671 auto pickerTheme = pipeline->GetTheme<PickerTheme>();
672 CHECK_NULL_VOID(pickerTheme);
673 auto frameWidth = host->GetGeometryNode()->GetFrameSize().Width();
674 auto dividerSpacing = pipeline->NormalizeToPx(pickerTheme->GetDividerSpacing());
675 auto pickerThemeWidth = dividerSpacing * 2;
676
677 auto centerX = (frameWidth / childSize - pickerThemeWidth) / 2 +
678 pickerChild->GetGeometryNode()->GetFrameRect().Width() * focusKeyID_ +
679 PRESS_INTERVAL.ConvertToPx() * 2;
680 auto centerY =
681 (host->GetGeometryNode()->GetFrameSize().Height() - dividerSpacing) / 2 + PRESS_INTERVAL.ConvertToPx();
682 float piantRectWidth = (dividerSpacing - PRESS_INTERVAL.ConvertToPx()) * 2;
683 float piantRectHeight = dividerSpacing - PRESS_INTERVAL.ConvertToPx() * 2;
684 if (piantRectWidth > columnWidth) {
685 piantRectWidth = columnWidth;
686 centerX = focusKeyID_ * columnWidth;
687 }
688 paintRect.SetRect(RectF(centerX, centerY, piantRectWidth, piantRectHeight));
689 paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_LEFT_POS, static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()),
690 static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()));
691 paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_RIGHT_POS, static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()),
692 static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()));
693 paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_LEFT_POS, static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()),
694 static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()));
695 paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_RIGHT_POS, static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()),
696 static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()));
697 }
698
OnKeyEvent(const KeyEvent & event)699 bool TimePickerRowPattern::OnKeyEvent(const KeyEvent& event)
700 {
701 if (event.action != KeyAction::DOWN) {
702 return false;
703 }
704 if (event.code == KeyCode::KEY_DPAD_UP || event.code == KeyCode::KEY_DPAD_DOWN ||
705 event.code == KeyCode::KEY_DPAD_LEFT || event.code == KeyCode::KEY_DPAD_RIGHT ||
706 event.code == KeyCode::KEY_MOVE_HOME || event.code == KeyCode::KEY_MOVE_END) {
707 return HandleDirectionKey(event.code);
708 }
709 return false;
710 }
711
SetFocusDisable()712 void TimePickerRowPattern::SetFocusDisable()
713 {
714 auto host = GetHost();
715 CHECK_NULL_VOID(host);
716
717 auto focusHub = host->GetFocusHub();
718 CHECK_NULL_VOID(focusHub);
719
720 focusHub->SetFocusable(false);
721 }
722
SetFocusEnable()723 void TimePickerRowPattern::SetFocusEnable()
724 {
725 auto host = GetHost();
726 CHECK_NULL_VOID(host);
727
728 auto focusHub = host->GetFocusHub();
729 CHECK_NULL_VOID(focusHub);
730
731 focusHub->SetFocusable(true);
732 }
733
HandleDirectionKey(KeyCode code)734 bool TimePickerRowPattern::HandleDirectionKey(KeyCode code)
735 {
736 auto host = GetHost();
737 CHECK_NULL_RETURN(host, false);
738 auto stackChild = DynamicCast<FrameNode>(host->GetChildAtIndex(focusKeyID_));
739 auto childSize = host->GetChildren().size();
740 auto pickerChild = DynamicCast<FrameNode>(stackChild->GetLastChild());
741 auto pattern = pickerChild->GetPattern<TimePickerColumnPattern>();
742 auto currernIndex = pattern->GetCurrentIndex();
743 auto totalOptionCount = GetOptionCount(pickerChild);
744 if (totalOptionCount == 0) {
745 return false;
746 }
747 if (code == KeyCode::KEY_DPAD_UP || code == KeyCode::KEY_DPAD_DOWN) {
748 auto index = (code == KeyCode::KEY_DPAD_UP) ? -1 : 1;
749 pattern->SetCurrentIndex((totalOptionCount + currernIndex + index) % totalOptionCount);
750 pattern->FlushCurrentOptions();
751 pattern->HandleChangeCallback((code == KeyCode::KEY_DPAD_UP) ? false : true, true);
752 pattern->HandleEventCallback(true);
753 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
754 return true;
755 }
756 if (code == KeyCode::KEY_MOVE_HOME) {
757 pattern->SetCurrentIndex(1);
758 pattern->InnerHandleScroll(false, false);
759 return true;
760 }
761 if (code == KeyCode::KEY_MOVE_END) {
762 pattern->SetCurrentIndex(totalOptionCount - UNOPTION_COUNT);
763 pattern->InnerHandleScroll(true, false);
764 return true;
765 }
766 if (code == KeyCode::KEY_DPAD_LEFT) {
767 focusKeyID_ -= 1;
768 if (focusKeyID_ < 0) {
769 focusKeyID_ = 0;
770 return false;
771 }
772 PaintFocusState();
773 return true;
774 }
775 if (code == KeyCode::KEY_DPAD_RIGHT) {
776 focusKeyID_ += 1;
777 if (focusKeyID_ > static_cast<int32_t>(childSize) - 1) {
778 focusKeyID_ = static_cast<int32_t>(childSize) - 1;
779 return false;
780 }
781 PaintFocusState();
782 return true;
783 }
784 return false;
785 }
786
OnColorConfigurationUpdate()787 void TimePickerRowPattern::OnColorConfigurationUpdate()
788 {
789 auto host = GetHost();
790 CHECK_NULL_VOID(host);
791 host->SetNeedCallChildrenUpdate(false);
792 auto context = host->GetContext();
793 CHECK_NULL_VOID(context);
794 auto pickerTheme = context->GetTheme<PickerTheme>();
795 CHECK_NULL_VOID(pickerTheme);
796 auto dialogTheme = context->GetTheme<DialogTheme>();
797 CHECK_NULL_VOID(dialogTheme);
798 auto disappearStyle = pickerTheme->GetDisappearOptionStyle();
799 auto normalStyle = pickerTheme->GetOptionStyle(false, false);
800 auto pickerProperty = host->GetLayoutProperty<TimePickerLayoutProperty>();
801 CHECK_NULL_VOID(pickerProperty);
802 pickerProperty->UpdateColor(normalStyle.GetTextColor());
803 pickerProperty->UpdateDisappearColor(disappearStyle.GetTextColor());
804 if (isPicker_) {
805 return;
806 }
807 SetBackgroundColor(dialogTheme->GetBackgroundColor());
808 CHECK_NULL_VOID(buttonTitleNode_);
809 auto buttonTitleRenderContext = buttonTitleNode_->GetRenderContext();
810 CHECK_NULL_VOID(buttonTitleRenderContext);
811 buttonTitleRenderContext->UpdateBackgroundColor(Color::TRANSPARENT);
812 auto childText = buttonTitleNode_->GetFirstChild();
813 CHECK_NULL_VOID(childText);
814 auto textTitleNode = DynamicCast<FrameNode>(childText);
815 CHECK_NULL_VOID(textTitleNode);
816 auto textLayoutProperty = textTitleNode->GetLayoutProperty<TextLayoutProperty>();
817 CHECK_NULL_VOID(textLayoutProperty);
818 textLayoutProperty->UpdateTextColor(pickerTheme->GetTitleStyle().GetTextColor());
819 CHECK_NULL_VOID(contentRowNode_);
820 auto layoutRenderContext = contentRowNode_->GetRenderContext();
821 CHECK_NULL_VOID(layoutRenderContext);
822 layoutRenderContext->UpdateBackgroundColor(dialogTheme->GetButtonBackgroundColor());
823 host->MarkModifyDone();
824 }
825 } // namespace OHOS::Ace::NG
826