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 constexpr int32_t CHILD_WITH_AMPM_SIZE = 3;
32 constexpr int32_t CHILD_WITHOUT_AMPM_SIZE = 2;
33 constexpr uint32_t AM_PM_HOUR_12 = 12;
34 constexpr uint32_t AM_PM_HOUR_11 = 11;
35 const int32_t AM_PM_COUNT = 3;
36 const Dimension PRESS_INTERVAL = 4.0_vp;
37 const Dimension PRESS_RADIUS = 8.0_vp;
38 const int32_t UNOPTION_COUNT = 2;
39 const int32_t AMPMDEFAULTPOSITION = 0;
40 const int32_t AMPM_FORWARD_WITHSECOND = 3;
41 const int32_t AMPM_FORWARD_WITHOUTSECOND = 2;
42 const int32_t AMPM_BACKWARD_WITHSECOND = -3;
43 const int32_t AMPM_BACKWARD_WITHOUTSECOND = -2;
44 const int32_t CHILD_INDEX_FIRST = 0;
45 const int32_t CHILD_INDEX_SECOND = 1;
46 const int32_t CHILD_INDEX_THIRD = 2;
47 const int32_t CHILD_INDEX_FOURTH = 3;
48 constexpr float DISABLE_ALPHA = 0.6f;
49 const Dimension FOCUS_OFFSET = 2.0_vp;
50 const int32_t RATE = 2;
51 } // namespace
52 
OnAttachToFrameNode()53 void TimePickerRowPattern::OnAttachToFrameNode()
54 {
55     auto host = GetHost();
56     CHECK_NULL_VOID(host);
57     host->GetRenderContext()->SetClipToFrame(true);
58     host->GetRenderContext()->UpdateClipEdge(true);
59 }
60 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)61 bool TimePickerRowPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
62 {
63     CHECK_NULL_RETURN(dirty, false);
64     SetButtonIdeaSize();
65     return true;
66 }
67 
SetButtonIdeaSize()68 void TimePickerRowPattern::SetButtonIdeaSize()
69 {
70     auto host = GetHost();
71     CHECK_NULL_VOID(host);
72     auto context = host->GetContext();
73     CHECK_NULL_VOID(context);
74     auto pickerTheme = context->GetTheme<PickerTheme>();
75     CHECK_NULL_VOID(pickerTheme);
76     auto children = host->GetChildren();
77     auto height = pickerTheme->GetDividerSpacing();
78     for (const auto& child : children) {
79         auto childNode = DynamicCast<FrameNode>(child);
80         CHECK_NULL_VOID(childNode);
81         auto width = childNode->GetGeometryNode()->GetFrameSize().Width();
82         auto defaultWidth = height.ConvertToPx() * 2;
83         if (width > defaultWidth) {
84             width = static_cast<float>(defaultWidth);
85         }
86         auto buttonNode = DynamicCast<FrameNode>(child->GetFirstChild());
87         auto buttonLayoutProperty = buttonNode->GetLayoutProperty<ButtonLayoutProperty>();
88         buttonLayoutProperty->UpdateMeasureType(MeasureType::MATCH_PARENT_MAIN_AXIS);
89         buttonLayoutProperty->UpdateType(ButtonType::NORMAL);
90         buttonLayoutProperty->UpdateBorderRadius(BorderRadiusProperty(PRESS_RADIUS));
91         buttonLayoutProperty->UpdateUserDefinedIdealSize(
92             CalcSize(CalcLength(width - PRESS_INTERVAL.ConvertToPx()), CalcLength(height - PRESS_INTERVAL)));
93         auto buttonConfirmRenderContext = buttonNode->GetRenderContext();
94         buttonConfirmRenderContext->UpdateBackgroundColor(Color::TRANSPARENT);
95         buttonNode->MarkModifyDone();
96         buttonNode->MarkDirtyNode();
97     }
98 }
99 
ColumnPatternInitHapticController()100 void TimePickerRowPattern::ColumnPatternInitHapticController()
101 {
102     if (!isHapticChanged_) {
103         return;
104     }
105 
106     isHapticChanged_ = false;
107     for (auto iter = allChildNode_.begin(); iter != allChildNode_.end(); iter++) {
108         auto columnNode = iter->second.Upgrade();
109         if (!columnNode) {
110             continue;
111         }
112         auto pattern = columnNode->GetPattern<TimePickerColumnPattern>();
113         if (!pattern) {
114             continue;
115         }
116         pattern->InitHapticController(columnNode);
117     }
118 }
119 
OnModifyDone()120 void TimePickerRowPattern::OnModifyDone()
121 {
122     if (isFiredTimeChange_ && !isForceUpdate_ && !isDateTimeOptionUpdate_) {
123         isFiredTimeChange_ = false;
124         ColumnPatternInitHapticController();
125         return;
126     }
127 
128     isHapticChanged_ = false;
129     isForceUpdate_ = false;
130     isDateTimeOptionUpdate_ = false;
131     auto host = GetHost();
132     CHECK_NULL_VOID(host);
133     auto pickerProperty = host->GetLayoutProperty<TimePickerLayoutProperty>();
134     CHECK_NULL_VOID(pickerProperty);
135     wheelModeEnabled_ = pickerProperty->GetLoopValue(true);
136     UpdateLanguageAndAmPmTimeOrder();
137     CreateOrDeleteSecondNode();
138     CreateAmPmNode();
139     OnColumnsBuilding();
140     FlushColumn();
141     InitDisabled();
142     SetChangeCallback([weak = WeakClaim(this)](const RefPtr<FrameNode>& tag, bool add, uint32_t index, bool notify) {
143         auto refPtr = weak.Upgrade();
144         CHECK_NULL_VOID(refPtr);
145         refPtr->HandleColumnChange(tag, add, index, notify);
146     });
147     SetEventCallback([weak = WeakClaim(this)](bool refresh) {
148         auto refPtr = weak.Upgrade();
149         CHECK_NULL_VOID(refPtr);
150         refPtr->FireChangeEvent(refresh);
151     });
152     auto focusHub = host->GetFocusHub();
153     if (focusHub) {
154         InitOnKeyEvent(focusHub);
155     }
156     if (HasTitleNode()) {
157         auto textTitleNode = FrameNode::GetOrCreateFrameNode(
158             V2::TEXT_ETS_TAG, GetTitleId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
159         auto str = GetDialogTitleDate();
160         CHECK_NULL_VOID(textTitleNode);
161         auto textLayoutProperty = textTitleNode->GetLayoutProperty<TextLayoutProperty>();
162         CHECK_NULL_VOID(textLayoutProperty);
163         textLayoutProperty->UpdateContent(str.ToString(false));
164     }
165     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
166 }
167 
InitDisabled()168 void TimePickerRowPattern::InitDisabled()
169 {
170     auto host = GetHost();
171     CHECK_NULL_VOID(host);
172     auto eventHub = host->GetEventHub<EventHub>();
173     CHECK_NULL_VOID(eventHub);
174     enabled_ = eventHub->IsEnabled();
175     auto renderContext = host->GetRenderContext();
176     CHECK_NULL_VOID(renderContext);
177     if (!enabled_) {
178         renderContext->UpdateOpacity(curOpacity_ * DISABLE_ALPHA);
179     }
180     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
181 }
182 
CreateAmPmNode()183 void TimePickerRowPattern::CreateAmPmNode()
184 {
185     auto host = GetHost();
186     CHECK_NULL_VOID(host);
187     auto context = host->GetContext();
188     CHECK_NULL_VOID(context);
189     auto pickerTheme = context->GetTheme<PickerTheme>();
190     CHECK_NULL_VOID(pickerTheme);
191     auto height = pickerTheme->GetDividerSpacing();
192     if (!GetHour24() && !HasAmPmNode()) {
193         auto amPmColumnNode = FrameNode::GetOrCreateFrameNode(
194             V2::COLUMN_ETS_TAG, GetAmPmId(), []() { return AceType::MakeRefPtr<TimePickerColumnPattern>(); });
195         CHECK_NULL_VOID(amPmColumnNode);
196         for (uint32_t index = 0; index < AM_PM_COUNT; index++) {
197             auto textNode = FrameNode::CreateFrameNode(
198                 V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
199             CHECK_NULL_VOID(textNode);
200             textNode->MountToParent(amPmColumnNode);
201         }
202         SetColumn(amPmColumnNode);
203         auto stackAmPmNode = FrameNode::GetOrCreateFrameNode(V2::STACK_ETS_TAG,
204             ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<StackPattern>(); });
205         auto buttonNode = FrameNode::GetOrCreateFrameNode(V2::BUTTON_ETS_TAG,
206             ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ButtonPattern>(); });
207         auto blendNodeId = ElementRegister::GetInstance()->MakeUniqueId();
208         auto columnBlendNode = FrameNode::GetOrCreateFrameNode(
209             V2::COLUMN_ETS_TAG, blendNodeId, []() { return AceType::MakeRefPtr<LinearLayoutPattern>(true); });
210         buttonNode->MountToParent(stackAmPmNode);
211         auto buttonLayoutProperty = buttonNode->GetLayoutProperty<ButtonLayoutProperty>();
212         amPmColumnNode->MountToParent(columnBlendNode);
213         columnBlendNode->MountToParent(stackAmPmNode);
214         auto layoutProperty = stackAmPmNode->GetLayoutProperty<LayoutProperty>();
215         layoutProperty->UpdateAlignment(Alignment::CENTER);
216         layoutProperty->UpdateLayoutWeight(1);
217         amPmTimeOrder_ == "01" ? stackAmPmNode->MountToParent(host) : stackAmPmNode->MountToParent(host, 0);
218         if (SetAmPmButtonIdeaSize() > 0) {
219             auto buttonLayoutProperty = buttonNode->GetLayoutProperty<ButtonLayoutProperty>();
220             buttonLayoutProperty->UpdateMeasureType(MeasureType::MATCH_PARENT_MAIN_AXIS);
221             buttonLayoutProperty->UpdateType(ButtonType::NORMAL);
222             buttonLayoutProperty->UpdateBorderRadius(BorderRadiusProperty(PRESS_RADIUS));
223             buttonLayoutProperty->UpdateUserDefinedIdealSize(
224                 CalcSize(CalcLength(SetAmPmButtonIdeaSize()), CalcLength(height - PRESS_INTERVAL)));
225             buttonNode->GetRenderContext()->UpdateBackgroundColor(Color::TRANSPARENT);
226             buttonNode->MarkModifyDone();
227             buttonNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
228         }
229         host->MarkModifyDone();
230         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
231     }
232 }
233 
CreateOrDeleteSecondNode()234 void TimePickerRowPattern::CreateOrDeleteSecondNode()
235 {
236     auto host = GetHost();
237     CHECK_NULL_VOID(host);
238     if (!HasSecondNode()) {
239         if (hasSecond_) {
240             auto secondColumnNode = FrameNode::GetOrCreateFrameNode(
241                 V2::COLUMN_ETS_TAG, GetSecondId(), []() { return AceType::MakeRefPtr<TimePickerColumnPattern>(); });
242             CHECK_NULL_VOID(secondColumnNode);
243             for (uint32_t index = 0; index < GetShowCount(); index++) {
244                 auto textNode = FrameNode::CreateFrameNode(V2::TEXT_ETS_TAG,
245                     ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
246                 CHECK_NULL_VOID(textNode);
247                 textNode->MarkModifyDone();
248                 textNode->MountToParent(secondColumnNode);
249             }
250             SetColumn(secondColumnNode);
251             auto stackSecondNode = FrameNode::GetOrCreateFrameNode(V2::STACK_ETS_TAG,
252                 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<StackPattern>(); });
253             auto buttonSecondNode = FrameNode::GetOrCreateFrameNode(V2::BUTTON_ETS_TAG,
254                 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ButtonPattern>(); });
255             auto blendNodeId = ElementRegister::GetInstance()->MakeUniqueId();
256             auto columnBlendNode = FrameNode::GetOrCreateFrameNode(
257                 V2::COLUMN_ETS_TAG, blendNodeId, []() { return AceType::MakeRefPtr<LinearLayoutPattern>(true); });
258             buttonSecondNode->MarkModifyDone();
259             buttonSecondNode->MountToParent(stackSecondNode);
260             secondColumnNode->MarkModifyDone();
261             secondColumnNode->MountToParent(columnBlendNode);
262             columnBlendNode->MarkModifyDone();
263             columnBlendNode->MountToParent(stackSecondNode);
264             auto layoutProperty = stackSecondNode->GetLayoutProperty<LayoutProperty>();
265             layoutProperty->UpdateAlignment(Alignment::CENTER);
266             layoutProperty->UpdateLayoutWeight(1);
267             stackSecondNode->MarkModifyDone();
268             MountSecondNode(stackSecondNode);
269             host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
270         }
271     } else {
272         if (!hasSecond_) {
273             RemoveSecondNode();
274             secondId_.reset();
275             host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
276         }
277     }
278 }
279 
MountSecondNode(const RefPtr<FrameNode> & stackSecondNode)280 void TimePickerRowPattern::MountSecondNode(const RefPtr<FrameNode>& stackSecondNode)
281 {
282     auto host = GetHost();
283     CHECK_NULL_VOID(host);
284     int32_t secondNodePosition = static_cast<int32_t>(host->GetChildren().size()) - 1;
285     if (!HasAmPmNode()) {
286         if (language_ == "ug") {
287             secondNodePosition = CHILD_INDEX_FIRST;
288         }
289     } else {
290         if (amPmTimeOrder_ == "01") {
291             secondNodePosition = CHILD_INDEX_THIRD;
292         } else if (language_ == "ug") {
293             secondNodePosition = CHILD_INDEX_SECOND;
294         }
295     }
296     stackSecondNode->MountToParent(host, secondNodePosition);
297 }
298 
RemoveSecondNode()299 void TimePickerRowPattern::RemoveSecondNode()
300 {
301     auto host = GetHost();
302     CHECK_NULL_VOID(host);
303     int32_t secondNodePosition = static_cast<int32_t>(host->GetChildren().size()) - 1;
304     if (!HasAmPmNode()) {
305         if (language_ == "ug") {
306             secondNodePosition = CHILD_INDEX_FIRST;
307         }
308     } else {
309         if (amPmTimeOrder_ == "01") {
310             secondNodePosition = CHILD_INDEX_THIRD;
311         } else if (language_ == "ug") {
312             secondNodePosition = CHILD_INDEX_SECOND;
313         }
314     }
315     host->RemoveChildAtIndex(secondNodePosition);
316 }
317 
SetAmPmButtonIdeaSize()318 double TimePickerRowPattern::SetAmPmButtonIdeaSize()
319 {
320     auto host = GetHost();
321     CHECK_NULL_RETURN(host, 0);
322     auto children = host->GetChildren();
323     float width = 0.0f;
324     for (const auto& child : children) {
325         auto buttonNode = DynamicCast<FrameNode>(child->GetFirstChild());
326         CHECK_NULL_RETURN(buttonNode, 0);
327         width = buttonNode->GetGeometryNode()->GetFrameSize().Width();
328     }
329     if (width > 0) {
330         return width;
331     }
332     return 0;
333 }
SetEventCallback(EventCallback && value)334 void TimePickerRowPattern::SetEventCallback(EventCallback&& value)
335 {
336     auto host = GetHost();
337     CHECK_NULL_VOID(host);
338     auto children = host->GetChildren();
339     for (const auto& child : children) {
340         auto stackNode = DynamicCast<FrameNode>(child);
341         CHECK_NULL_VOID(stackNode);
342         auto columnBlendNode = DynamicCast<FrameNode>(stackNode->GetLastChild());
343         CHECK_NULL_VOID(columnBlendNode);
344         auto childNode = DynamicCast<FrameNode>(columnBlendNode->GetLastChild());
345         CHECK_NULL_VOID(childNode);
346         auto timePickerColumnPattern = childNode->GetPattern<TimePickerColumnPattern>();
347         CHECK_NULL_VOID(timePickerColumnPattern);
348         timePickerColumnPattern->SetEventCallback(std::move(value));
349     }
350 }
351 
FireChangeEvent(bool refresh)352 void TimePickerRowPattern::FireChangeEvent(bool refresh)
353 {
354     if (refresh) {
355         auto timePickerEventHub = GetEventHub<TimePickerEventHub>();
356         CHECK_NULL_VOID(timePickerEventHub);
357         auto str = GetSelectedObject(true);
358         auto info = std::make_shared<DatePickerChangeEvent>(str);
359         timePickerEventHub->FireChangeEvent(info.get());
360         timePickerEventHub->FireDialogChangeEvent(str);
361         firedTimeStr_ = str;
362     }
363 }
364 
GetSelectedObject(bool isColumnChange,int32_t status)365 std::string TimePickerRowPattern::GetSelectedObject(bool isColumnChange, int32_t status)
366 {
367     auto time = selectedTime_;
368     if (isColumnChange) {
369         time = GetCurrentTime();
370     }
371     return time.ToString(true, hasSecond_, status);
372 }
373 
GetCurrentTime()374 PickerTime TimePickerRowPattern::GetCurrentTime()
375 {
376     PickerTime time;
377     UpdateAllChildNode();
378     auto amPmColumn = allChildNode_["amPm"].Upgrade();
379     auto hourColumn = allChildNode_["hour"].Upgrade();
380     auto minuteColumn = allChildNode_["minute"].Upgrade();
381     CHECK_NULL_RETURN(hourColumn, time);
382     CHECK_NULL_RETURN(minuteColumn, time);
383     auto hourPickerColumnPattern = hourColumn->GetPattern<TimePickerColumnPattern>();
384     CHECK_NULL_RETURN(hourPickerColumnPattern, time);
385     auto minutePickerColumnPattern = minuteColumn->GetPattern<TimePickerColumnPattern>();
386     CHECK_NULL_RETURN(minutePickerColumnPattern, time);
387 
388     if (GetHour24()) {
389         time.SetHour(hourPickerColumnPattern->GetCurrentIndex()); // hour from 0 to 23, index from 0 to 23
390     } else if (amPmColumn) {
391         auto amPmPickerColumnPattern = amPmColumn->GetPattern<TimePickerColumnPattern>();
392         CHECK_NULL_RETURN(amPmPickerColumnPattern, time);
393         time.SetHour(GetHourFromAmPm(
394             amPmPickerColumnPattern->GetCurrentIndex() == 0, hourPickerColumnPattern->GetCurrentIndex() + 1));
395     }
396 
397     time.SetMinute(minutePickerColumnPattern->GetCurrentIndex()); // minute from 0 to 59, index from 0 to 59
398     if (hasSecond_) {
399         auto secondColumn = allChildNode_["second"].Upgrade();
400         CHECK_NULL_RETURN(secondColumn, time);
401         auto secondPickerColumnPattern = secondColumn->GetPattern<TimePickerColumnPattern>();
402         CHECK_NULL_RETURN(secondPickerColumnPattern, time);
403         time.SetSecond(secondPickerColumnPattern->GetCurrentIndex()); // second from 0 to 59, index from 0 to 59
404     }
405     return time;
406 }
407 
GetHourFromAmPm(bool isAm,uint32_t amPmhour) const408 uint32_t TimePickerRowPattern::GetHourFromAmPm(bool isAm, uint32_t amPmhour) const
409 {
410     if (isAm) {
411         if (amPmhour == AM_PM_HOUR_12) { // AM 12:00 means 00:00
412             return 0;
413         }
414         return amPmhour;
415     }
416     if (amPmhour == AM_PM_HOUR_12) { // PM 12 means 12:00
417         return AM_PM_HOUR_12;
418     }
419     return amPmhour + AM_PM_HOUR_12; // need add 12 hour to 24 hours style
420 }
421 
HandleColumnChange(const RefPtr<FrameNode> & tag,bool isAdd,uint32_t index,bool needNotify)422 void TimePickerRowPattern::HandleColumnChange(const RefPtr<FrameNode>& tag, bool isAdd, uint32_t index, bool needNotify)
423 {
424     std::vector<RefPtr<FrameNode>> tags;
425     for (const auto& tag : tags) {
426         auto iter = std::find_if(timePickerColumns_.begin(), timePickerColumns_.end(), [&tag](const auto& c) {
427                 auto column = c.Upgrade();
428                 return column && column->GetId() == tag->GetId();
429             });
430         if (iter != timePickerColumns_.end()) {
431             auto timePickerColumn = (*iter).Upgrade();
432             CHECK_NULL_VOID(timePickerColumn);
433             auto timePickerColumnPattern = timePickerColumn->GetPattern<TimePickerColumnPattern>();
434             CHECK_NULL_VOID(timePickerColumnPattern);
435             timePickerColumnPattern->FlushCurrentOptions();
436         }
437     }
438 }
439 
OnFontConfigurationUpdate()440 void TimePickerRowPattern::OnFontConfigurationUpdate()
441 {
442     CHECK_NULL_VOID(closeDialogEvent_);
443     closeDialogEvent_();
444 }
OnLanguageConfigurationUpdate()445 void TimePickerRowPattern::OnLanguageConfigurationUpdate()
446 {
447     FlushAmPmFormatString();
448     UpdateLanguageAndAmPmTimeOrder();
449     if (!GetHour24()) {
450         auto host = GetHost();
451         CHECK_NULL_VOID(host);
452         auto children = host->GetChildren();
453         auto iter = children.begin();
454         CHECK_NULL_VOID(*iter);
455         auto amPmNode = *iter;
456         CHECK_NULL_VOID(amPmNode);
457         if (amPmTimeOrder_ == "01" && isAmPmTimeOrderUpdate_) {
458             // if hasSecond_ is true, then amPmNode should be moved from slot 0 to 3, otherwise from slot 0 to 2
459             hasSecond_ ? amPmNode->MovePosition(3) : amPmNode->MovePosition(2);
460         } else if (amPmTimeOrder_ == "10" && isAmPmTimeOrderUpdate_) {
461             // if hasSecond_ is true, then amPmNode should be moved from slot 3 to 0, otherwise form slot 2 to 0
462             hasSecond_ ? std::advance(iter, 3) : std::advance(iter, 2);
463             amPmNode = *iter;
464             CHECK_NULL_VOID(amPmNode);
465             amPmNode->MovePosition(0);
466         }
467         UpdateNodePositionForUg();
468         auto layoutProperty = AceType::DynamicCast<FrameNode>(amPmNode)->GetLayoutProperty<LayoutProperty>();
469         layoutProperty->UpdateAlignment(Alignment::CENTER);
470         layoutProperty->UpdateLayoutWeight(1);
471         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
472     }
473     auto buttonConfirmNode = weakButtonConfirm_.Upgrade();
474     CHECK_NULL_VOID(buttonConfirmNode);
475     auto confirmNode = AceType::DynamicCast<FrameNode>(buttonConfirmNode->GetFirstChild());
476     CHECK_NULL_VOID(confirmNode);
477     auto confirmNodeLayout = confirmNode->GetLayoutProperty<TextLayoutProperty>();
478     CHECK_NULL_VOID(confirmNodeLayout);
479     confirmNodeLayout->UpdateContent(Localization::GetInstance()->GetEntryLetters("common.ok"));
480     confirmNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
481 
482     auto buttonCancelNode = weakButtonCancel_.Upgrade();
483     CHECK_NULL_VOID(buttonCancelNode);
484     auto cancelNode = AceType::DynamicCast<FrameNode>(buttonCancelNode->GetFirstChild());
485     CHECK_NULL_VOID(cancelNode);
486     auto cancelNodeLayout = cancelNode->GetLayoutProperty<TextLayoutProperty>();
487     CHECK_NULL_VOID(cancelNodeLayout);
488     cancelNodeLayout->UpdateContent(Localization::GetInstance()->GetEntryLetters("common.cancel"));
489     cancelNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
490 }
491 
UpdateNodePositionForUg()492 void TimePickerRowPattern::UpdateNodePositionForUg()
493 {
494     if (!isPreLanguageUg_ && language_ != "ug") {
495         return;
496     }
497 
498     auto host = GetHost();
499     CHECK_NULL_VOID(host);
500     // ug's node order is S:M:H, need to change it to H:M:S
501     if (isPreLanguageUg_ && isAmPmTimeOrderUpdate_) {
502         if (hasSecond_) {
503             auto secondNode = host->GetChildAtIndex(CHILD_INDEX_FIRST);
504             CHECK_NULL_VOID(secondNode);
505             secondNode->MovePosition(CHILD_INDEX_THIRD);
506 
507             auto minuteNode = host->GetChildAtIndex(CHILD_INDEX_FIRST);
508             CHECK_NULL_VOID(minuteNode);
509             minuteNode->MovePosition(CHILD_INDEX_SECOND);
510         } else {
511             auto minuteNode = host->GetChildAtIndex(CHILD_INDEX_FIRST);
512             CHECK_NULL_VOID(minuteNode);
513             minuteNode->MovePosition(CHILD_INDEX_SECOND);
514         }
515     } else if ((isPreLanguageUg_ && !isAmPmTimeOrderUpdate_) || (language_ == "ug")) {
516         if (hasSecond_) {
517             auto hourNode = host->GetChildAtIndex(CHILD_INDEX_FOURTH);
518             CHECK_NULL_VOID(hourNode);
519             hourNode->MovePosition(CHILD_INDEX_SECOND);
520             auto minuteNode = host->GetChildAtIndex(CHILD_INDEX_FOURTH);
521             CHECK_NULL_VOID(minuteNode);
522             minuteNode->MovePosition(CHILD_INDEX_THIRD);
523         } else {
524             auto hourNode = host->GetChildAtIndex(CHILD_INDEX_THIRD);
525             CHECK_NULL_VOID(hourNode);
526             hourNode->MovePosition(CHILD_INDEX_SECOND);
527         }
528     }
529     if (isPreLanguageUg_) {
530         isPreLanguageUg_ = false;
531     }
532 }
533 
FlushAmPmFormatString()534 void TimePickerRowPattern::FlushAmPmFormatString()
535 {
536     auto amPmStrings = Localization::GetInstance()->GetAmPmStrings();
537     if (amPmStrings.size() > 1) {
538         vecAmPm_.clear();
539         std::string am = amPmStrings[0];
540         vecAmPm_.emplace_back(am);
541         std::string pm = amPmStrings[1];
542         vecAmPm_.emplace_back(pm);
543     }
544 }
545 
SetChangeCallback(ColumnChangeCallback && value)546 void TimePickerRowPattern::SetChangeCallback(ColumnChangeCallback&& value)
547 {
548     auto host = GetHost();
549     CHECK_NULL_VOID(host);
550     auto children = host->GetChildren();
551     for (const auto& child : children) {
552         auto stackNode = DynamicCast<FrameNode>(child);
553         CHECK_NULL_VOID(stackNode);
554         auto columnBlendNode = DynamicCast<FrameNode>(stackNode->GetLastChild());
555         CHECK_NULL_VOID(columnBlendNode);
556         auto childNode = DynamicCast<FrameNode>(columnBlendNode->GetLastChild());
557         CHECK_NULL_VOID(childNode);
558         auto timePickerColumnPattern = childNode->GetPattern<TimePickerColumnPattern>();
559         CHECK_NULL_VOID(timePickerColumnPattern);
560         timePickerColumnPattern->SetChangeCallback(std::move(value));
561     }
562 }
563 
FlushColumn()564 void TimePickerRowPattern::FlushColumn()
565 {
566     UpdateAllChildNode();
567     auto amPmColumn = allChildNode_["amPm"].Upgrade();
568     auto hourColumn = allChildNode_["hour"].Upgrade();
569     if (GetHour24()) {
570         CHECK_NULL_VOID(hourColumn);
571         auto hourColumnPattern = hourColumn->GetPattern<TimePickerColumnPattern>();
572         CHECK_NULL_VOID(hourColumnPattern);
573         hourColumnPattern->SetOptions(GetOptionsCount());
574         hourColumnPattern->SetShowCount(GetShowCount());
575         hourColumnPattern->FlushCurrentOptions();
576     } else if (amPmColumn) {
577         auto amPmColumnPattern = amPmColumn->GetPattern<TimePickerColumnPattern>();
578         CHECK_NULL_VOID(amPmColumnPattern);
579         amPmColumnPattern->SetShowCount(AM_PM_COUNT);
580         amPmColumnPattern->FlushCurrentOptions();
581 
582         CHECK_NULL_VOID(hourColumn);
583         auto hourColumnPattern = hourColumn->GetPattern<TimePickerColumnPattern>();
584         CHECK_NULL_VOID(hourColumnPattern);
585         hourColumnPattern->SetOptions(GetOptionsCount());
586         hourColumnPattern->SetShowCount(GetShowCount());
587         hourColumnPattern->FlushCurrentOptions();
588     }
589 
590     auto minuteColumn = allChildNode_["minute"].Upgrade();
591     CHECK_NULL_VOID(minuteColumn);
592     auto minuteColumnPattern = minuteColumn->GetPattern<TimePickerColumnPattern>();
593     CHECK_NULL_VOID(minuteColumnPattern);
594     minuteColumnPattern->SetShowCount(GetShowCount());
595     minuteColumnPattern->FlushCurrentOptions();
596     if (hasSecond_) {
597         auto secondColumn = allChildNode_["second"].Upgrade();
598         CHECK_NULL_VOID(secondColumn);
599         auto secondColumnPattern = secondColumn->GetPattern<TimePickerColumnPattern>();
600         CHECK_NULL_VOID(secondColumnPattern);
601         secondColumnPattern->SetOptions(GetOptionsCount());
602         secondColumnPattern->SetShowCount(GetShowCount());
603         secondColumnPattern->FlushCurrentOptions();
604     }
605 }
606 
OnDataLinking(const RefPtr<FrameNode> & tag,bool isAdd,uint32_t index,std::vector<RefPtr<FrameNode>> & resultTags)607 void TimePickerRowPattern::OnDataLinking(
608     const RefPtr<FrameNode>& tag, bool isAdd, uint32_t index, std::vector<RefPtr<FrameNode>>& resultTags)
609 {
610     CHECK_NULL_VOID(tag);
611     auto hourNode = allChildNode_["hour"].Upgrade();
612     CHECK_NULL_VOID(hourNode);
613     if (tag->GetId() != hourNode->GetId()) {
614         return;
615     }
616 
617     if (!GetHour24()) {
618         HandleHour12Change(isAdd, index, resultTags);
619     }
620 }
621 
GetOptionsValue(const RefPtr<FrameNode> & frameNode,uint32_t optionIndex)622 const std::string& TimePickerRowPattern::GetOptionsValue(const RefPtr<FrameNode>& frameNode, uint32_t optionIndex)
623 {
624     UpdateAllChildNode();
625     if (frameNode == allChildNode_["amPm"]) {
626         return options_[allChildNode_["amPm"]][optionIndex];
627     }
628     bool isHour12 = !GetHour24();
629     auto isHourNode = frameNode == allChildNode_["hour"];
630     if (options_.find(frameNode) == options_.end()) {
631         options_[frameNode] = std::unordered_map<uint32_t, std::string>();
632     }
633     if (options_[frameNode].find(optionIndex) == options_[frameNode].end()) {
634         options_[frameNode][optionIndex] =
635             isHourNode ? GetHourFormatString(optionIndex + isHour12) : GetMinuteFormatString(optionIndex);
636     }
637     return options_[frameNode][optionIndex];
638 }
639 
OnColumnsBuilding()640 void TimePickerRowPattern::OnColumnsBuilding()
641 {
642     HandleHourColumnBuilding();
643     HandleMinAndSecColumnBuilding();
644 }
645 
HandleHourColumnBuilding()646 void TimePickerRowPattern::HandleHourColumnBuilding()
647 {
648     UpdateAllChildNode();
649     auto amPmColumn = allChildNode_["amPm"].Upgrade();
650     auto hourColumn = allChildNode_["hour"].Upgrade();
651     optionsTotalCount_[hourColumn] = 0;
652     if (GetHour24()) {
653         CHECK_NULL_VOID(hourColumn);
654         auto hourColumnPattern = hourColumn->GetPattern<TimePickerColumnPattern>();
655         CHECK_NULL_VOID(hourColumnPattern);
656         for (uint32_t hour = 0; hour <= 23; ++hour) { // time's hour from 0 to 23.
657             if (hour == selectedTime_.GetHour()) {
658                 hourColumnPattern->SetCurrentIndex(hour);
659             }
660             optionsTotalCount_[hourColumn]++;
661         }
662         hourColumnPattern->SetOptions(GetOptionsCount());
663         hourColumnPattern->SetWheelModeEnabled(wheelModeEnabled_);
664         hourColumn->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
665     } else if (amPmColumn) {
666         CHECK_NULL_VOID(amPmColumn);
667         CHECK_NULL_VOID(hourColumn);
668         auto amPmColumnPattern = amPmColumn->GetPattern<TimePickerColumnPattern>();
669         CHECK_NULL_VOID(amPmColumnPattern);
670         auto hourColumnPattern = hourColumn->GetPattern<TimePickerColumnPattern>();
671         CHECK_NULL_VOID(hourColumnPattern);
672         options_[amPmColumn][0] = GetAmFormatString();
673         options_[amPmColumn][1] = GetPmFormatString();
674 
675         if (IsAmHour(selectedTime_.GetHour())) {
676             amPmColumnPattern->SetCurrentIndex(0); // AM's index
677         } else {
678             amPmColumnPattern->SetCurrentIndex(1); // PM's index
679         }
680         optionsTotalCount_[amPmColumn] = CHILD_WITHOUT_AMPM_SIZE;
681         auto selectedHour = GetAmPmHour(selectedTime_.GetHour());
682         for (uint32_t hour = 1; hour <= AM_PM_HOUR_12; ++hour) { // AM_PM hour start from 1 to 12
683             if (hour == selectedHour) {
684                 hourColumnPattern->SetCurrentIndex(hour - 1);
685             }
686             optionsTotalCount_[hourColumn]++;
687         }
688         amPmColumnPattern->SetOptions(GetOptionsCount());
689         hourColumnPattern->SetOptions(GetOptionsCount());
690         amPmColumnPattern->SetWheelModeEnabled(wheelModeEnabled_);
691         hourColumnPattern->SetWheelModeEnabled(wheelModeEnabled_);
692     }
693 }
694 
HandleMinAndSecColumnBuilding()695 void TimePickerRowPattern::HandleMinAndSecColumnBuilding()
696 {
697     UpdateAllChildNode();
698     auto minuteColumn = allChildNode_["minute"].Upgrade();
699     CHECK_NULL_VOID(minuteColumn);
700     auto minuteColumnPattern = minuteColumn->GetPattern<TimePickerColumnPattern>();
701     CHECK_NULL_VOID(minuteColumnPattern);
702     optionsTotalCount_[minuteColumn] = 0;
703 
704     for (uint32_t minute = 0; minute <= 59; ++minute) { // time's minute from 0 to 59
705         if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) &&
706             GetPrefixMinute() == ZeroPrefixType::HIDE) {
707             options_[minuteColumn][minute] = std::to_string(minute);
708         } else {
709             if (minute < 10) { // time's minute less than 10
710                 options_[minuteColumn][minute] = std::string("0") + std::to_string(minute);
711             }
712         }
713         if (minute == selectedTime_.GetMinute()) {
714             minuteColumnPattern->SetCurrentIndex(minute);
715         }
716         optionsTotalCount_[minuteColumn]++;
717     }
718     minuteColumnPattern->SetOptions(GetOptionsCount());
719     minuteColumnPattern->SetWheelModeEnabled(wheelModeEnabled_);
720 
721     auto secondColumn = allChildNode_["second"].Upgrade();
722     CHECK_NULL_VOID(secondColumn);
723     auto secondColumnPattern = secondColumn->GetPattern<TimePickerColumnPattern>();
724     CHECK_NULL_VOID(secondColumnPattern);
725     optionsTotalCount_[secondColumn] = 0;
726 
727     for (uint32_t second = 0; second <= 59; ++second) { // time's second from 0 to 59
728         if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) &&
729             GetPrefixSecond() == ZeroPrefixType::HIDE) {
730             options_[secondColumn][second] = std::to_string(second);
731         } else {
732             if (second < 10) { // time's second less than 10
733                 options_[secondColumn][second] = std::string("0") + std::to_string(second);
734             }
735         }
736         if (second == selectedTime_.GetSecond()) {
737             secondColumnPattern->SetCurrentIndex(second);
738         }
739         optionsTotalCount_[secondColumn]++;
740     }
741     secondColumnPattern->SetOptions(GetOptionsCount());
742     secondColumnPattern->SetWheelModeEnabled(wheelModeEnabled_);
743 }
744 
UpdateAllChildNode()745 void TimePickerRowPattern::UpdateAllChildNode()
746 {
747     if (hasSecond_) {
748         GetAllChildNodeWithSecond();
749         return;
750     }
751     auto host = GetHost();
752     CHECK_NULL_VOID(host);
753     if (GetHour24() && host->GetChildren().size() == CHILD_WITH_AMPM_SIZE) {
754         // if amPmTimeOrder is "10", amPm node is in slot 0, otherwise in slot 2
755         host->RemoveChildAtIndex(amPmTimeOrder_ == "10" ? AMPMDEFAULTPOSITION : AMPM_FORWARD_WITHOUTSECOND);
756         amPmId_.reset();
757         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
758         host->MarkModifyDone();
759     } else if (!GetHour24() && host->GetChildren().size() == CHILD_WITHOUT_AMPM_SIZE) {
760         CreateAmPmNode();
761     }
762     if (GetHour24() && host->GetChildren().size() != CHILD_WITHOUT_AMPM_SIZE) {
763         return;
764     }
765 
766     if (!GetHour24() && host->GetChildren().size() != CHILD_WITH_AMPM_SIZE) {
767         return;
768     }
769     if (language_ == "ug") {
770         UpdateAllChildNodeForUg();
771         return;
772     }
773     auto children = host->GetChildren();
774     auto iter = children.begin();
775     CHECK_NULL_VOID(*iter);
776     auto amPmNode = GetAmPmNode(iter);
777     auto hourNode = GetHourNode(iter);
778     auto minuteNode = GetMinuteNode(iter);
779     allChildNode_["amPm"] = amPmNode;
780     allChildNode_["hour"] = hourNode;
781     allChildNode_["minute"] = minuteNode;
782 }
783 
GetAllChildNodeWithSecond()784 void TimePickerRowPattern::GetAllChildNodeWithSecond()
785 {
786     auto host = GetHost();
787     CHECK_NULL_VOID(host);
788     if (GetHour24() && host->GetChildren().size() == CHILD_WITH_AMPM_SIZE + 1) {
789         host->RemoveChildAtIndex(amPmTimeOrder_ == "10" ? AMPMDEFAULTPOSITION : AMPM_FORWARD_WITHSECOND);
790         amPmId_.reset();
791         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
792         host->MarkModifyDone();
793     } else if (!GetHour24() && host->GetChildren().size() == CHILD_WITHOUT_AMPM_SIZE + 1) {
794         CreateAmPmNode();
795     }
796     if ((GetHour24() && host->GetChildren().size() != CHILD_WITHOUT_AMPM_SIZE + 1) ||
797         (!GetHour24() && host->GetChildren().size() != CHILD_WITH_AMPM_SIZE + 1)) {
798         return;
799     }
800     if (language_ == "ug") {
801         UpdateAllChildNodeForUg();
802         return;
803     }
804     auto children = host->GetChildren();
805     auto iter = children.begin();
806     CHECK_NULL_VOID(*iter);
807     auto amPmNode = GetAmPmNode(iter);
808     auto hourNode = GetHourNode(iter);
809     auto minuteNode = GetMinuteNode(iter);
810     auto secondNode = GetSecondNode(iter);
811     allChildNode_["amPm"] = amPmNode;
812     allChildNode_["hour"] = hourNode;
813     allChildNode_["minute"] = minuteNode;
814     allChildNode_["second"] = secondNode;
815 }
816 
GetAmPmNode(std::list<RefPtr<UINode>>::iterator & iter)817 RefPtr<FrameNode> TimePickerRowPattern::GetAmPmNode(std::list<RefPtr<UINode>>::iterator& iter)
818 {
819     if (GetHour24()) {
820         return nullptr;
821     }
822     auto amPm = (*iter);
823     if (amPmTimeOrder_ == "01") {
824         if (!hasSecond_) {
825             std::advance(iter, AMPM_FORWARD_WITHOUTSECOND);
826             amPm = (*iter);
827             std::advance(iter, AMPM_BACKWARD_WITHOUTSECOND);
828         } else {
829             std::advance(iter, AMPM_FORWARD_WITHSECOND);
830             amPm = (*iter);
831             std::advance(iter, AMPM_BACKWARD_WITHSECOND);
832         }
833     } else {
834         iter++;
835     }
836     CHECK_NULL_RETURN(amPm, nullptr);
837     auto amPmStackNode = DynamicCast<FrameNode>(amPm);
838     auto amPmBlendNode = DynamicCast<FrameNode>(amPmStackNode->GetLastChild());
839     CHECK_NULL_RETURN(amPmBlendNode, nullptr);
840     auto amPmNode = DynamicCast<FrameNode>(amPmBlendNode->GetLastChild());
841     CHECK_NULL_RETURN(amPmNode, nullptr);
842     return amPmNode;
843 }
844 
GetHourNode(std::list<RefPtr<UINode>>::iterator & iter)845 RefPtr<FrameNode> TimePickerRowPattern::GetHourNode(std::list<RefPtr<UINode>>::iterator& iter)
846 {
847     auto hour = *iter;
848     CHECK_NULL_RETURN(hour, nullptr);
849     auto hourStackNode = DynamicCast<FrameNode>(hour);
850     auto hourBlendNode = DynamicCast<FrameNode>(hourStackNode->GetLastChild());
851     CHECK_NULL_RETURN(hourBlendNode, nullptr);
852     auto hourNode = DynamicCast<FrameNode>(hourBlendNode->GetLastChild());
853     CHECK_NULL_RETURN(hourNode, nullptr);
854     iter++;
855     return hourNode;
856 }
857 
GetMinuteNode(std::list<RefPtr<UINode>>::iterator & iter)858 RefPtr<FrameNode> TimePickerRowPattern::GetMinuteNode(std::list<RefPtr<UINode>>::iterator& iter)
859 {
860     auto minute = *iter;
861     CHECK_NULL_RETURN(minute, nullptr);
862     auto minuteStackNode = DynamicCast<FrameNode>(minute);
863     auto minuteBlendNode = DynamicCast<FrameNode>(minuteStackNode->GetLastChild());
864     CHECK_NULL_RETURN(minuteBlendNode, nullptr);
865     auto minuteNode = DynamicCast<FrameNode>(minuteBlendNode->GetLastChild());
866     CHECK_NULL_RETURN(minuteNode, nullptr);
867     iter++;
868     return minuteNode;
869 }
870 
GetSecondNode(std::list<RefPtr<UINode>>::iterator & iter)871 RefPtr<FrameNode> TimePickerRowPattern::GetSecondNode(std::list<RefPtr<UINode>>::iterator& iter)
872 {
873     if (!hasSecond_) {
874         return nullptr;
875     }
876     auto second = *iter;
877     CHECK_NULL_RETURN(second, nullptr);
878     auto secondStackNode = DynamicCast<FrameNode>(second);
879     auto secondBlendNode = DynamicCast<FrameNode>(secondStackNode->GetLastChild());
880     CHECK_NULL_RETURN(secondBlendNode, nullptr);
881     auto secondNode = DynamicCast<FrameNode>(secondBlendNode->GetLastChild());
882     CHECK_NULL_RETURN(secondNode, nullptr);
883     if (language_ == "ug") {
884         iter++;
885     }
886     return secondNode;
887 }
888 
UpdateAllChildNodeForUg()889 void TimePickerRowPattern::UpdateAllChildNodeForUg()
890 {
891     auto host = GetHost();
892     CHECK_NULL_VOID(host);
893     auto children = host->GetChildren();
894     auto iter = children.begin();
895     CHECK_NULL_VOID(*iter);
896     RefPtr<FrameNode> amPmNode;
897     if (!GetHour24()) {
898         auto amPm = (*iter);
899         CHECK_NULL_VOID(amPm);
900         auto amPmStackNode = DynamicCast<FrameNode>(amPm);
901         amPmNode = DynamicCast<FrameNode>(amPmStackNode->GetLastChild()->GetLastChild());
902         CHECK_NULL_VOID(amPmNode);
903         iter++;
904     }
905     auto secondNode = GetSecondNode(iter);
906     auto minuteNode = GetMinuteNode(iter);
907     auto hourNode = GetHourNode(iter);
908     allChildNode_["amPm"] = amPmNode;
909     allChildNode_["hour"] = hourNode;
910     allChildNode_["minute"] = minuteNode;
911     allChildNode_["second"] = secondNode;
912 }
913 
HandleHour12Change(bool isAdd,uint32_t index,std::vector<RefPtr<FrameNode>> & resultTags)914 void TimePickerRowPattern::HandleHour12Change(bool isAdd, uint32_t index, std::vector<RefPtr<FrameNode>>& resultTags)
915 {
916     UpdateAllChildNode();
917     auto amPm = allChildNode_["amPm"].Upgrade();
918     CHECK_NULL_VOID(amPm);
919     auto amPmPickerColumnPattern = amPm->GetPattern<TimePickerColumnPattern>();
920 
921     if (amPmPickerColumnPattern->GetCurrentIndex() == 0 && isAdd && index == 11) { // hour index start from 0 to 11
922         amPmPickerColumnPattern->SetCurrentIndex(1);                               // add to PM's index
923         resultTags.emplace_back(amPm);
924         return;
925     }
926     if (amPmPickerColumnPattern->GetCurrentIndex() == 1 && !isAdd && index == 10) { // reduce to 11 hour (index is 10)
927         amPmPickerColumnPattern->SetCurrentIndex(0);                                // change to AM whose index is 0
928         resultTags.emplace_back(amPm);
929         return;
930     }
931     if (amPmPickerColumnPattern->GetCurrentIndex() == 1 && isAdd && index == 11) {
932         amPmPickerColumnPattern->SetCurrentIndex(0); // is PM (index is 1) and last hour (index is 11)
933         resultTags.emplace_back(amPm);               // change to PM (index is 0)
934         return;
935     }
936     if (amPmPickerColumnPattern->GetCurrentIndex() == 0 && !isAdd && index == 10) { // reduce to 11 hour(index is 10)
937         amPmPickerColumnPattern->SetCurrentIndex(1);                                // change to PM
938         resultTags.emplace_back(amPm);
939         return;
940     }
941 }
942 
GetAmPmHour(uint32_t hourOf24) const943 uint32_t TimePickerRowPattern::GetAmPmHour(uint32_t hourOf24) const
944 {
945     if (hourOf24 == 0) {
946         return AM_PM_HOUR_12;                         // AM 12:00 means 00:00 in 24 hour style
947     }
948     if (1 <= hourOf24 && hourOf24 <= AM_PM_HOUR_11) { // 00:00 to 11:00 is the same for any hour style
949         return hourOf24;
950     }
951     if (hourOf24 == AM_PM_HOUR_12) { // 12:00 means PM start hour
952         return AM_PM_HOUR_12;        // 12 PM
953     }                                // hour from 13 to 23
954     return hourOf24 - AM_PM_HOUR_12; // need reduce 12 to 12 hours style
955 }
956 
IsAmHour(uint32_t hourOf24) const957 bool TimePickerRowPattern::IsAmHour(uint32_t hourOf24) const
958 {
959     return (0 <= hourOf24 && hourOf24 <= AM_PM_HOUR_11); // 00:00 to 11:00 is AM hour
960 }
961 
GetAmFormatString() const962 std::string TimePickerRowPattern::GetAmFormatString() const
963 {
964     if (vecAmPm_.empty()) {
965         return "AM";
966     }
967     return vecAmPm_[0]; // first index is AM
968 }
969 
GetPmFormatString() const970 std::string TimePickerRowPattern::GetPmFormatString() const
971 {
972     if (vecAmPm_.size() < 2) { // size need to be 2 for AM and PM
973         return "PM";
974     }
975     return vecAmPm_[1]; // second index is PM
976 }
977 
GetHourFormatString(uint32_t hour) const978 std::string TimePickerRowPattern::GetHourFormatString(uint32_t hour) const
979 {
980     DateTime time;
981     time.minute = hour; // minute range [0, 59], hour range [0, 23]; hour range is in minute range.
982     if (!Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
983         if (Localization::GetInstance()->HasZeroHour()) {
984             return AddZeroPrefix(Localization::GetInstance()->FormatDateTime(time, "m"));
985         }
986     } else {
987         if (((GetPrefixHour() == ZeroPrefixType::AUTO) && GetHour24()) ||
988             GetPrefixHour() == ZeroPrefixType::SHOW) {
989             return AddZeroPrefix(Localization::GetInstance()->FormatDateTime(time, "m"));
990         }
991     }
992     return Localization::GetInstance()->FormatDateTime(time, "m");
993 }
994 
GetMinuteFormatString(uint32_t minute) const995 std::string TimePickerRowPattern::GetMinuteFormatString(uint32_t minute) const
996 {
997     DateTime time;
998     time.minute = minute;
999     if (!Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1000         return AddZeroPrefix(Localization::GetInstance()->FormatDateTime(time, "m"));
1001     } else {
1002         return Localization::GetInstance()->FormatDateTime(time, "m");
1003     }
1004 }
1005 
GetSecondFormatString(uint32_t second) const1006 std::string TimePickerRowPattern::GetSecondFormatString(uint32_t second) const
1007 {
1008     DateTime time;
1009     time.second = second;
1010     if (!Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1011         return AddZeroPrefix(Localization::GetInstance()->FormatDateTime(time, "s"));
1012     } else {
1013         return Localization::GetInstance()->FormatDateTime(time, "s");
1014     }
1015 }
1016 
AddZeroPrefix(const std::string & value) const1017 std::string TimePickerRowPattern::AddZeroPrefix(const std::string& value) const
1018 {
1019     if (value.size() == 1 && '0' <= value[0] && value[0] <= '9') { // value is number in range [0, 9]
1020         return std::string("0") + value;                           // add prefix '0'
1021     }
1022     return value;
1023 }
1024 
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)1025 void TimePickerRowPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
1026 {
1027     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
1028         auto pattern = wp.Upgrade();
1029         if (pattern) {
1030             return pattern->OnKeyEvent(event);
1031         }
1032         return false;
1033     };
1034     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
1035 
1036     auto getInnerPaintRectCallback = [wp = WeakClaim(this)](RoundRect& paintRect) {
1037         auto pattern = wp.Upgrade();
1038         if (pattern) {
1039             pattern->GetInnerFocusPaintRect(paintRect);
1040         }
1041     };
1042     focusHub->SetInnerFocusPaintRectCallback(getInnerPaintRectCallback);
1043 }
1044 
PaintFocusState()1045 void TimePickerRowPattern::PaintFocusState()
1046 {
1047     auto host = GetHost();
1048     CHECK_NULL_VOID(host);
1049 
1050     RoundRect focusRect;
1051     GetInnerFocusPaintRect(focusRect);
1052 
1053     auto focusHub = host->GetFocusHub();
1054     CHECK_NULL_VOID(focusHub);
1055     focusHub->PaintInnerFocusState(focusRect);
1056 
1057     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1058 }
1059 
GetInnerFocusPaintRect(RoundRect & paintRect)1060 void TimePickerRowPattern::GetInnerFocusPaintRect(RoundRect& paintRect)
1061 {
1062     auto host = GetHost();
1063     CHECK_NULL_VOID(host);
1064     auto childSize = host->GetChildren().size();
1065     auto stackChild = DynamicCast<FrameNode>(host->GetChildAtIndex(focusKeyID_));
1066     CHECK_NULL_VOID(stackChild);
1067     auto columnBlendChild = DynamicCast<FrameNode>(stackChild->GetLastChild());
1068     CHECK_NULL_VOID(columnBlendChild);
1069     auto pickerChild = DynamicCast<FrameNode>(columnBlendChild->GetLastChild());
1070     CHECK_NULL_VOID(pickerChild);
1071     auto columnWidth = pickerChild->GetGeometryNode()->GetFrameSize().Width();
1072     auto pipeline = PipelineBase::GetCurrentContext();
1073     CHECK_NULL_VOID(pipeline);
1074     auto pickerTheme = pipeline->GetTheme<PickerTheme>();
1075     CHECK_NULL_VOID(pickerTheme);
1076     auto frameWidth = host->GetGeometryNode()->GetFrameSize().Width();
1077     auto dividerSpacing = pipeline->NormalizeToPx(pickerTheme->GetDividerSpacing());
1078     auto pickerThemeWidth = dividerSpacing * RATE;
1079 
1080     CHECK_EQUAL_VOID(childSize, 0);
1081     auto centerX = (frameWidth / childSize - pickerThemeWidth) / RATE +
1082                    pickerChild->GetGeometryNode()->GetFrameRect().Width() * focusKeyID_ +
1083                    PRESS_INTERVAL.ConvertToPx() * RATE;
1084     CHECK_NULL_VOID(host->GetGeometryNode());
1085     auto centerY =
1086         (host->GetGeometryNode()->GetFrameSize().Height() - dividerSpacing) / RATE + PRESS_INTERVAL.ConvertToPx();
1087     float piantRectWidth = (dividerSpacing - PRESS_INTERVAL.ConvertToPx()) * RATE;
1088     float piantRectHeight = dividerSpacing - PRESS_INTERVAL.ConvertToPx() * RATE;
1089     if (piantRectWidth > columnWidth) {
1090         piantRectWidth = columnWidth - FOCUS_OFFSET.ConvertToPx() * RATE;
1091         centerX = focusKeyID_ * columnWidth + FOCUS_OFFSET.ConvertToPx();
1092     }
1093     paintRect.SetRect(RectF(centerX, centerY, piantRectWidth, piantRectHeight));
1094     paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_LEFT_POS, static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()),
1095         static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()));
1096     paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_RIGHT_POS, static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()),
1097         static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()));
1098     paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_LEFT_POS, static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()),
1099         static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()));
1100     paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_RIGHT_POS, static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()),
1101         static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()));
1102 }
1103 
OnKeyEvent(const KeyEvent & event)1104 bool TimePickerRowPattern::OnKeyEvent(const KeyEvent& event)
1105 {
1106     if (event.action != KeyAction::DOWN) {
1107         return false;
1108     }
1109     if (event.code == KeyCode::KEY_DPAD_UP || event.code == KeyCode::KEY_DPAD_DOWN ||
1110         event.code == KeyCode::KEY_DPAD_LEFT || event.code == KeyCode::KEY_DPAD_RIGHT ||
1111         event.code == KeyCode::KEY_MOVE_HOME || event.code == KeyCode::KEY_MOVE_END) {
1112         return HandleDirectionKey(event.code);
1113     }
1114     return false;
1115 }
1116 
SetFocusDisable()1117 void TimePickerRowPattern::SetFocusDisable()
1118 {
1119     auto host = GetHost();
1120     CHECK_NULL_VOID(host);
1121 
1122     auto focusHub = host->GetFocusHub();
1123     CHECK_NULL_VOID(focusHub);
1124 
1125     focusHub->SetFocusable(false);
1126 }
1127 
SetFocusEnable()1128 void TimePickerRowPattern::SetFocusEnable()
1129 {
1130     auto host = GetHost();
1131     CHECK_NULL_VOID(host);
1132 
1133     auto focusHub = host->GetFocusHub();
1134     CHECK_NULL_VOID(focusHub);
1135 
1136     focusHub->SetFocusable(true);
1137 }
1138 
HandleDirectionKey(KeyCode code)1139 bool TimePickerRowPattern::HandleDirectionKey(KeyCode code)
1140 {
1141     auto host = GetHost();
1142     CHECK_NULL_RETURN(host, false);
1143     auto stackChild = DynamicCast<FrameNode>(host->GetChildAtIndex(focusKeyID_));
1144     auto childSize = host->GetChildren().size();
1145     auto pickerChild = DynamicCast<FrameNode>(stackChild->GetLastChild()->GetLastChild());
1146     auto pattern = pickerChild->GetPattern<TimePickerColumnPattern>();
1147     auto currentIndex = pattern->GetCurrentIndex();
1148     auto totalOptionCount = GetOptionCount(pickerChild);
1149     if (totalOptionCount == 0) {
1150         return false;
1151     }
1152     if (code == KeyCode::KEY_DPAD_UP || code == KeyCode::KEY_DPAD_DOWN) {
1153         auto index = (code == KeyCode::KEY_DPAD_UP) ? -1 : 1;
1154         pattern->SetCurrentIndex((totalOptionCount + currentIndex + index) % totalOptionCount);
1155         pattern->FlushCurrentOptions();
1156         pattern->HandleChangeCallback((code == KeyCode::KEY_DPAD_UP) ? false : true, true);
1157         pattern->HandleEventCallback(true);
1158         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1159         return true;
1160     }
1161     if (code == KeyCode::KEY_MOVE_HOME) {
1162         pattern->SetCurrentIndex(1);
1163         pattern->InnerHandleScroll(false, false);
1164         return true;
1165     }
1166     if (code == KeyCode::KEY_MOVE_END) {
1167         pattern->SetCurrentIndex(totalOptionCount - UNOPTION_COUNT);
1168         pattern->InnerHandleScroll(true, false);
1169         return true;
1170     }
1171     if (code == KeyCode::KEY_DPAD_LEFT) {
1172         focusKeyID_ -= 1;
1173         if (focusKeyID_ < 0) {
1174             focusKeyID_ = 0;
1175             return false;
1176         }
1177         PaintFocusState();
1178         return true;
1179     }
1180     if (code == KeyCode::KEY_DPAD_RIGHT) {
1181         focusKeyID_ += 1;
1182         if (focusKeyID_ > static_cast<int32_t>(childSize) - 1) {
1183             focusKeyID_ = static_cast<int32_t>(childSize) - 1;
1184             return false;
1185         }
1186         PaintFocusState();
1187         return true;
1188     }
1189     return false;
1190 }
1191 
OnColorConfigurationUpdate()1192 void TimePickerRowPattern::OnColorConfigurationUpdate()
1193 {
1194     auto host = GetHost();
1195     CHECK_NULL_VOID(host);
1196     host->SetNeedCallChildrenUpdate(false);
1197     auto context = host->GetContext();
1198     CHECK_NULL_VOID(context);
1199     auto pickerTheme = context->GetTheme<PickerTheme>();
1200     CHECK_NULL_VOID(pickerTheme);
1201     auto dialogTheme = context->GetTheme<DialogTheme>();
1202     CHECK_NULL_VOID(dialogTheme);
1203     auto disappearStyle = pickerTheme->GetDisappearOptionStyle();
1204     auto normalStyle = pickerTheme->GetOptionStyle(false, false);
1205     auto pickerProperty = host->GetLayoutProperty<TimePickerLayoutProperty>();
1206     CHECK_NULL_VOID(pickerProperty);
1207     pickerProperty->UpdateColor(GetTextProperties().normalTextStyle_.textColor.value_or(normalStyle.GetTextColor()));
1208     pickerProperty->UpdateDisappearColor(
1209         GetTextProperties().disappearTextStyle_.textColor.value_or(disappearStyle.GetTextColor()));
1210     if (isPicker_) {
1211         return;
1212     }
1213     SetBackgroundColor(dialogTheme->GetBackgroundColor());
1214     auto buttonTitleNode = buttonTitleNode_.Upgrade();
1215     CHECK_NULL_VOID(buttonTitleNode);
1216     auto buttonTitleRenderContext = buttonTitleNode->GetRenderContext();
1217     CHECK_NULL_VOID(buttonTitleRenderContext);
1218     buttonTitleRenderContext->UpdateBackgroundColor(Color::TRANSPARENT);
1219     auto childText = buttonTitleNode->GetFirstChild();
1220     CHECK_NULL_VOID(childText);
1221     auto textTitleNode = DynamicCast<FrameNode>(childText);
1222     CHECK_NULL_VOID(textTitleNode);
1223     auto textLayoutProperty = textTitleNode->GetLayoutProperty<TextLayoutProperty>();
1224     CHECK_NULL_VOID(textLayoutProperty);
1225     textLayoutProperty->UpdateTextColor(pickerTheme->GetTitleStyle().GetTextColor());
1226     auto contentRowNode = contentRowNode_.Upgrade();
1227     CHECK_NULL_VOID(contentRowNode);
1228     auto layoutRenderContext = contentRowNode->GetRenderContext();
1229     CHECK_NULL_VOID(layoutRenderContext);
1230     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN) || !layoutRenderContext->IsUniRenderEnabled()) {
1231         layoutRenderContext->UpdateBackgroundColor(dialogTheme->GetButtonBackgroundColor());
1232     }
1233     host->MarkModifyDone();
1234 }
1235 } // namespace OHOS::Ace::NG
1236