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/time_picker/timepicker_column_pattern.h"
17 
18 #include <cstdint>
19 #include <cstdlib>
20 #include <iterator>
21 #include <list>
22 
23 #include "core/components_ng/pattern/time_picker/timepicker_haptic_factory.h"
24 #include "base/utils/measure_util.h"
25 #include "base/utils/utils.h"
26 #include "bridge/common/utils/utils.h"
27 #include "core/common/font_manager.h"
28 #include "core/components/common/layout/constants.h"
29 #include "core/components/common/properties/color.h"
30 #include "core/components/picker/picker_base_component.h"
31 #include "core/components_ng/base/frame_scene_status.h"
32 #include "core/components_ng/layout/layout_wrapper.h"
33 #include "core/components_ng/pattern/button/button_pattern.h"
34 #include "core/components_ng/pattern/text/text_pattern.h"
35 #include "core/components_ng/pattern/time_picker/timepicker_row_pattern.h"
36 #include "core/pipeline_ng/ui_task_scheduler.h"
37 
38 namespace OHOS::Ace::NG {
39 namespace {
40 // timepicker style modification
41 constexpr Dimension PADDING_WEIGHT = 10.0_vp;
42 const Dimension FONT_SIZE = Dimension(2.0);
43 const uint32_t OPTION_COUNT_PHONE_LANDSCAPE = 3;
44 const int32_t CHILDREN_SIZE = 3;
45 const float TEXT_HEIGHT_NUMBER = 3.0f;
46 const float TEXT_HOUR24_HEIGHT_NUMBER = 9.0f;
47 const float TEXT_WEIGHT_NUMBER = 6.0f;
48 const Dimension FOCUS_SIZE = Dimension(1.0);
49 const int32_t MIDDLE_CHILD_INDEX = 2;
50 const float MOVE_DISTANCE = 5.0f;
51 constexpr int32_t HOVER_ANIMATION_DURATION = 250;
52 constexpr int32_t PRESS_ANIMATION_DURATION = 100;
53 constexpr int32_t CLICK_ANIMATION_DURATION = 300;
54 constexpr float FONTWEIGHT = 0.5f;
55 constexpr float FONT_SIZE_PERCENT = 1.0f;
56 constexpr char MEASURE_SIZE_STRING[] = "TEST";
57 constexpr int32_t HOT_ZONE_HEIGHT_CANDIDATE = 2;
58 constexpr int32_t HOT_ZONE_HEIGHT_DISAPPEAR = 4;
59 constexpr char PICKER_DRAG_SCENE[] = "picker_drag_scene";
60 } // namespace
61 
OnAttachToFrameNode()62 void TimePickerColumnPattern::OnAttachToFrameNode()
63 {
64     auto host = GetHost();
65     CHECK_NULL_VOID(host);
66     auto context = host->GetContextRefPtr();
67     CHECK_NULL_VOID(context);
68     auto pickerTheme = context->GetTheme<PickerTheme>();
69     CHECK_NULL_VOID(pickerTheme);
70     auto hub = host->GetEventHub<EventHub>();
71     CHECK_NULL_VOID(hub);
72     auto gestureHub = hub->GetOrCreateGestureEventHub();
73     CHECK_NULL_VOID(gestureHub);
74     tossAnimationController_->SetPipelineContext(context);
75     tossAnimationController_->SetColumn(AceType::WeakClaim(this));
76     jumpInterval_ = pickerTheme->GetJumpInterval().ConvertToPx();
77     CreateAnimation();
78     InitPanEvent(gestureHub);
79     host->GetRenderContext()->SetClipToFrame(true);
80     InitHapticController(host);
81     RegisterWindowStateChangedCallback();
82 }
83 
OnDetachFromFrameNode(FrameNode * frameNode)84 void TimePickerColumnPattern::OnDetachFromFrameNode(FrameNode* frameNode)
85 {
86     if (hapticController_) {
87         hapticController_->Stop();
88     }
89     UnregisterWindowStateChangedCallback();
90 }
91 
OnModifyDone()92 void TimePickerColumnPattern::OnModifyDone()
93 {
94     auto host = GetHost();
95     auto focusHub = host->GetFocusHub();
96     CHECK_NULL_VOID(focusHub);
97     auto pipeline = GetContext();
98     CHECK_NULL_VOID(pipeline);
99     auto theme = pipeline->GetTheme<PickerTheme>();
100     pressColor_ = theme->GetPressColor();
101     hoverColor_ = theme->GetHoverColor();
102     auto showCount = GetShowCount();
103     InitOnKeyEvent(focusHub);
104     InitMouseAndPressEvent();
105     SetAccessibilityAction();
106     if (optionProperties_.empty()) {
107         auto midIndex = showCount / 2;
108         auto host = GetHost();
109         CHECK_NULL_VOID(host);
110         dividerSpacing_ = pipeline->NormalizeToPx(theme->GetDividerSpacing());
111         gradientHeight_ = static_cast<float>(pipeline->NormalizeToPx(theme->GetGradientHeight()));
112         MeasureContext measureContext;
113         measureContext.textContent = MEASURE_SIZE_STRING;
114         uint32_t childIndex = 0;
115         TimePickerOptionProperty prop;
116         while (childIndex < showCount) {
117             if (childIndex == midIndex) { // selected
118                 auto selectedOptionSize = theme->GetOptionStyle(true, false).GetFontSize();
119                 measureContext.fontSize = selectedOptionSize;
120             } else if ((childIndex == (midIndex + 1)) || (childIndex == (midIndex - 1))) {
121                 auto focusOptionSize = theme->GetOptionStyle(false, false).GetFontSize() + FONT_SIZE;
122                 measureContext.fontSize = focusOptionSize;
123             } else {
124                 auto normalOptionSize = theme->GetOptionStyle(false, false).GetFontSize();
125                 measureContext.fontSize = normalOptionSize;
126             }
127             if (childIndex == midIndex) {
128                 prop.height = dividerSpacing_;
129             } else {
130                 prop.height = gradientHeight_;
131             }
132             Size size = MeasureUtil::MeasureTextSize(measureContext);
133             prop.fontheight = size.Height();
134             optionProperties_.emplace_back(prop);
135             childIndex++;
136         }
137         SetOptionShiftDistance();
138     }
139     InitHapticController(host);
140 }
141 
InitHapticController(const RefPtr<FrameNode> & host)142 void TimePickerColumnPattern::InitHapticController(const RefPtr<FrameNode>& host)
143 {
144     CHECK_NULL_VOID(host);
145     auto blendNode = DynamicCast<FrameNode>(host->GetParent());
146     CHECK_NULL_VOID(blendNode);
147     auto stackNode = DynamicCast<FrameNode>(blendNode->GetParent());
148     CHECK_NULL_VOID(stackNode);
149     auto parentNode = DynamicCast<FrameNode>(stackNode->GetParent());
150     CHECK_NULL_VOID(parentNode);
151     auto timePickerRowPattern = parentNode->GetPattern<TimePickerRowPattern>();
152     CHECK_NULL_VOID(timePickerRowPattern);
153     if (timePickerRowPattern->GetIsEnableHaptic()) {
154         isEnableHaptic_ = true;
155         if (!hapticController_) {
156             auto context = parentNode->GetContext();
157             CHECK_NULL_VOID(context);
158             context->AddAfterLayoutTask([weak = WeakClaim(this)]() {
159                 auto pattern = weak.Upgrade();
160                 CHECK_NULL_VOID(pattern);
161                 pattern->hapticController_ = TimepickerAudioHapticFactory::GetInstance();
162             });
163         }
164     } else {
165         isEnableHaptic_ = false;
166         if (hapticController_) {
167             hapticController_->Stop();
168         }
169     }
170 }
171 
RegisterWindowStateChangedCallback()172 void TimePickerColumnPattern::RegisterWindowStateChangedCallback()
173 {
174     auto host = GetHost();
175     CHECK_NULL_VOID(host);
176     auto pipeline = host->GetContext();
177     CHECK_NULL_VOID(pipeline);
178     pipeline->AddWindowStateChangedCallback(host->GetId());
179 }
180 
UnregisterWindowStateChangedCallback()181 void TimePickerColumnPattern::UnregisterWindowStateChangedCallback()
182 {
183     auto host = GetHost();
184     CHECK_NULL_VOID(host);
185     auto pipeline = host->GetContext();
186     CHECK_NULL_VOID(pipeline);
187     pipeline->RemoveWindowStateChangedCallback(host->GetId());
188 }
189 
OnWindowHide()190 void TimePickerColumnPattern::OnWindowHide()
191 {
192     isShow_ = false;
193     if (hapticController_) {
194         hapticController_->Stop();
195     }
196 }
197 
OnWindowShow()198 void TimePickerColumnPattern::OnWindowShow()
199 {
200     isShow_ = true;
201 }
202 
ParseTouchListener()203 void TimePickerColumnPattern::ParseTouchListener()
204 {
205     auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
206         auto pattern = weak.Upgrade();
207         CHECK_NULL_VOID(pattern);
208         if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
209             pattern->OnTouchDown();
210             pattern->SetLocalDownDistance(info.GetTouches().front().GetLocalLocation().GetDistance());
211         }
212         if (info.GetTouches().front().GetTouchType() == TouchType::UP ||
213             info.GetTouches().front().GetTouchType() == TouchType::CANCEL) {
214             pattern->OnTouchUp();
215             pattern->SetLocalDownDistance(0.0f);
216         }
217         if (info.GetTouches().front().GetTouchType() == TouchType::MOVE) {
218             if (std::abs(info.GetTouches().front().GetLocalLocation().GetDistance() - pattern->GetLocalDownDistance()) >
219                 MOVE_DISTANCE) {
220                 pattern->OnTouchUp();
221             }
222         }
223     };
224     touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
225 }
226 
ParseMouseEvent()227 void TimePickerColumnPattern::ParseMouseEvent()
228 {
229     auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
230         auto pattern = weak.Upgrade();
231         CHECK_NULL_VOID(pattern);
232         if (pattern) {
233             pattern->HandleMouseEvent(isHover);
234         }
235     };
236     mouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
237 }
238 
InitMouseAndPressEvent()239 void TimePickerColumnPattern::InitMouseAndPressEvent()
240 {
241     if (mouseEvent_ || touchListener_) {
242         return;
243     }
244     auto host = GetHost();
245     CHECK_NULL_VOID(host);
246     auto columnEventHub = host->GetEventHub<EventHub>();
247     CHECK_NULL_VOID(columnEventHub);
248     RefPtr<TouchEventImpl> touchListener = CreateItemTouchEventListener();
249     CHECK_NULL_VOID(touchListener);
250     auto columnGesture = columnEventHub->GetOrCreateGestureEventHub();
251     CHECK_NULL_VOID(columnGesture);
252     columnGesture->AddTouchEvent(touchListener);
253     RefPtr<FrameNode> middleChild = nullptr;
254     auto childSize = static_cast<int32_t>(host->GetChildren().size());
255     auto midSize = childSize / 2;
256     middleChild = DynamicCast<FrameNode>(host->GetChildAtIndex(midSize));
257     CHECK_NULL_VOID(middleChild);
258     auto eventHub = middleChild->GetEventHub<EventHub>();
259     CHECK_NULL_VOID(eventHub);
260     auto inputHub = eventHub->GetOrCreateInputEventHub();
261     ParseMouseEvent();
262     inputHub->AddOnHoverEvent(mouseEvent_);
263     auto gesture = middleChild->GetOrCreateGestureEventHub();
264     CHECK_NULL_VOID(gesture);
265     ParseTouchListener();
266     gesture->AddTouchEvent(touchListener_);
267     for (int32_t i = 0; i < childSize; i++) {
268         RefPtr<FrameNode> childNode = DynamicCast<FrameNode>(host->GetChildAtIndex(i));
269         CHECK_NULL_VOID(childNode);
270         RefPtr<TimePickerEventParam> param = MakeRefPtr<TimePickerEventParam>();
271         param->instance_ = childNode;
272         param->itemIndex_ = i;
273         param->itemTotalCounts_ = childSize;
274 
275         auto eventHub = childNode->GetEventHub<EventHub>();
276         CHECK_NULL_VOID(eventHub);
277         if (i != midSize) {
278             RefPtr<ClickEvent> clickListener = CreateItemClickEventListener(param);
279             CHECK_NULL_VOID(clickListener);
280             auto gesture = eventHub->GetOrCreateGestureEventHub();
281             CHECK_NULL_VOID(gesture);
282             gesture->AddClickEvent(clickListener);
283         }
284     }
285 }
286 
CreateItemTouchEventListener()287 RefPtr<TouchEventImpl> TimePickerColumnPattern::CreateItemTouchEventListener()
288 {
289     auto toss = GetToss();
290     CHECK_NULL_RETURN(toss, nullptr);
291     auto touchCallback = [weak = WeakClaim(this), toss](const TouchEventInfo& info) {
292         auto pattern = weak.Upgrade();
293         CHECK_NULL_VOID(pattern);
294         auto isToss = pattern->GetTossStatus();
295         if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
296             if (isToss == true) {
297                 pattern->touchBreak_ = true;
298                 pattern->animationBreak_ = true;
299                 pattern->clickBreak_ = true;
300                 auto TossEndPosition = toss->GetTossEndPosition();
301                 pattern->SetYLast(TossEndPosition);
302                 toss->StopTossAnimation();
303             } else {
304                 pattern->animationBreak_ = false;
305                 pattern->clickBreak_ = false;
306             }
307         }
308         if (info.GetTouches().front().GetTouchType() == TouchType::UP) {
309             pattern->touchBreak_ = false;
310             if (pattern->animationBreak_ == true) {
311                 pattern->PlayRestAnimation();
312                 pattern->yOffset_ = 0.0;
313             }
314         }
315     };
316     auto listener = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
317     return listener;
318 }
319 
HandleMouseEvent(bool isHover)320 void TimePickerColumnPattern::HandleMouseEvent(bool isHover)
321 {
322     if (isHover) {
323         hoverd_ = true;
324         PlayHoverAnimation(hoverColor_);
325     } else {
326         hoverd_ = false;
327         PlayHoverAnimation(Color::TRANSPARENT);
328     }
329 }
330 
OnTouchDown()331 void TimePickerColumnPattern::OnTouchDown()
332 {
333     PlayPressAnimation(pressColor_);
334 }
335 
OnTouchUp()336 void TimePickerColumnPattern::OnTouchUp()
337 {
338     if (hoverd_) {
339         PlayPressAnimation(hoverColor_);
340     } else {
341         PlayPressAnimation(Color::TRANSPARENT);
342     }
343 }
344 
SetButtonBackgroundColor(const Color & pressColor)345 void TimePickerColumnPattern::SetButtonBackgroundColor(const Color& pressColor)
346 {
347     auto host = GetHost();
348     CHECK_NULL_VOID(host);
349     auto blendNode = host->GetParent();
350     CHECK_NULL_VOID(blendNode);
351     auto stack = blendNode->GetParent();
352     CHECK_NULL_VOID(stack);
353     auto buttonNode = DynamicCast<FrameNode>(stack->GetFirstChild());
354     auto renderContext = buttonNode->GetRenderContext();
355     renderContext->UpdateBackgroundColor(pressColor);
356     buttonNode->MarkModifyDone();
357     buttonNode->MarkDirtyNode();
358 }
359 
PlayPressAnimation(const Color & pressColor)360 void TimePickerColumnPattern::PlayPressAnimation(const Color& pressColor)
361 {
362     AnimationOption option = AnimationOption();
363     option.SetDuration(PRESS_ANIMATION_DURATION);
364     option.SetCurve(Curves::SHARP);
365     option.SetFillMode(FillMode::FORWARDS);
366     AnimationUtils::Animate(option, [weak = AceType::WeakClaim(this), pressColor]() {
367         auto picker = weak.Upgrade();
368         if (picker) {
369             picker->SetButtonBackgroundColor(pressColor);
370         }
371     });
372 }
373 
PlayHoverAnimation(const Color & color)374 void TimePickerColumnPattern::PlayHoverAnimation(const Color& color)
375 {
376     AnimationOption option = AnimationOption();
377     option.SetDuration(HOVER_ANIMATION_DURATION);
378     option.SetCurve(Curves::FRICTION);
379     option.SetFillMode(FillMode::FORWARDS);
380     AnimationUtils::Animate(option, [weak = AceType::WeakClaim(this), color]() {
381         auto picker = weak.Upgrade();
382         if (picker) {
383             picker->SetButtonBackgroundColor(color);
384         }
385     });
386 }
387 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)388 bool TimePickerColumnPattern::OnDirtyLayoutWrapperSwap(
389     const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
390 {
391     bool isChange =
392         config.frameSizeChange || config.frameOffsetChange || config.contentSizeChange || config.contentOffsetChange;
393 
394     CHECK_NULL_RETURN(isChange, false);
395     CHECK_NULL_RETURN(dirty, false);
396     auto geometryNode = dirty->GetGeometryNode();
397     CHECK_NULL_RETURN(geometryNode, false);
398     auto offset = geometryNode->GetFrameOffset();
399     auto size = geometryNode->GetFrameSize();
400     if (!NearEqual(offset, offset_) || !NearEqual(size, size_)) {
401         offset_ = offset;
402         size_ = size;
403         AddHotZoneRectToText();
404     }
405     return true;
406 }
407 
InitTextFontFamily()408 void TimePickerColumnPattern::InitTextFontFamily()
409 {
410     auto host = GetHost();
411     CHECK_NULL_VOID(host);
412     auto blendNode = DynamicCast<FrameNode>(host->GetParent());
413     CHECK_NULL_VOID(blendNode);
414     auto stackNode = DynamicCast<FrameNode>(blendNode->GetParent());
415     CHECK_NULL_VOID(stackNode);
416     auto parentNode = DynamicCast<FrameNode>(stackNode->GetParent());
417     CHECK_NULL_VOID(parentNode);
418     auto pipeline = parentNode->GetContext();
419     CHECK_NULL_VOID(pipeline);
420     auto timePickerRowPattern = parentNode->GetPattern<TimePickerRowPattern>();
421     CHECK_NULL_VOID(timePickerRowPattern);
422     auto timePickerLayoutProperty = parentNode->GetLayoutProperty<TimePickerLayoutProperty>();
423     CHECK_NULL_VOID(timePickerLayoutProperty);
424     hasUserDefinedDisappearFontFamily_ = timePickerRowPattern->GetHasUserDefinedDisappearFontFamily();
425     hasUserDefinedNormalFontFamily_ = timePickerRowPattern->GetHasUserDefinedNormalFontFamily();
426     hasUserDefinedSelectedFontFamily_ = timePickerRowPattern->GetHasUserDefinedSelectedFontFamily();
427     auto fontManager = pipeline->GetFontManager();
428     CHECK_NULL_VOID(fontManager);
429     if (!(fontManager->GetAppCustomFont().empty())) {
430         hasAppCustomFont_ = true;
431     }
432     auto appCustomFontFamily = Framework::ConvertStrToFontFamilies(fontManager->GetAppCustomFont());
433     if (hasAppCustomFont_ && !hasUserDefinedDisappearFontFamily_) {
434         timePickerLayoutProperty->UpdateDisappearFontFamily(appCustomFontFamily);
435     }
436     if (hasAppCustomFont_ && !hasUserDefinedNormalFontFamily_) {
437         timePickerLayoutProperty->UpdateFontFamily(appCustomFontFamily);
438     }
439     if (hasAppCustomFont_ && !hasUserDefinedSelectedFontFamily_) {
440         timePickerLayoutProperty->UpdateSelectedFontFamily(appCustomFontFamily);
441     }
442 }
443 
FlushCurrentOptions(bool isDown,bool isUpateTextContentOnly)444 void TimePickerColumnPattern::FlushCurrentOptions(bool isDown, bool isUpateTextContentOnly)
445 {
446     auto host = GetHost();
447     CHECK_NULL_VOID(host);
448     auto blendNode = DynamicCast<FrameNode>(host->GetParent());
449     CHECK_NULL_VOID(blendNode);
450     auto stackNode = DynamicCast<FrameNode>(blendNode->GetParent());
451     CHECK_NULL_VOID(stackNode);
452     auto parentNode = DynamicCast<FrameNode>(stackNode->GetParent());
453     CHECK_NULL_VOID(parentNode);
454     auto dataPickerLayoutProperty = host->GetLayoutProperty<LinearLayoutProperty>();
455     CHECK_NULL_VOID(dataPickerLayoutProperty);
456     dataPickerLayoutProperty->UpdatePadding(
457         PaddingProperty { CalcLength(static_cast<float>(PADDING_WEIGHT.ConvertToPx()), DimensionUnit::PX) });
458     dataPickerLayoutProperty->UpdateAlignSelf(FlexAlign::CENTER);
459     auto timePickerRowPattern = parentNode->GetPattern<TimePickerRowPattern>();
460     CHECK_NULL_VOID(timePickerRowPattern);
461     auto showOptionCount = GetShowCount();
462     uint32_t totalOptionCount = timePickerRowPattern->GetOptionCount(host);
463     auto timePickerLayoutProperty = parentNode->GetLayoutProperty<TimePickerLayoutProperty>();
464     CHECK_NULL_VOID(timePickerLayoutProperty);
465     uint32_t currentIndex = host->GetPattern<TimePickerColumnPattern>()->GetCurrentIndex();
466     CHECK_EQUAL_VOID(totalOptionCount, 0);
467     currentIndex = currentIndex % totalOptionCount;
468     uint32_t selectedIndex = showOptionCount / 2; // the center option is selected.
469     auto child = host->GetChildren();
470     auto iter = child.begin();
471     InitTextFontFamily();
472 
473     if (!isUpateTextContentOnly) {
474         animationProperties_.clear();
475     }
476     auto actualOptionCount = showOptionCount < child.size() ? showOptionCount : child.size();
477     for (uint32_t index = 0; index < actualOptionCount; index++) {
478         uint32_t optionIndex = (totalOptionCount + currentIndex + index - selectedIndex) % totalOptionCount;
479         auto textNode = DynamicCast<FrameNode>(*iter);
480         CHECK_NULL_VOID(textNode);
481         auto textPattern = textNode->GetPattern<TextPattern>();
482         CHECK_NULL_VOID(textPattern);
483         auto textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
484         CHECK_NULL_VOID(textLayoutProperty);
485         if (!isUpateTextContentOnly) {
486             ChangeTextStyle(index, showOptionCount, textLayoutProperty, timePickerLayoutProperty);
487             ChangeAmPmTextStyle(index, showOptionCount, textLayoutProperty, timePickerLayoutProperty);
488             AddAnimationTextProperties(index, textLayoutProperty);
489         }
490         iter++;
491         int32_t diffIndex = static_cast<int32_t>(index) - static_cast<int32_t>(selectedIndex);
492         int32_t virtualIndex = static_cast<int32_t>(currentIndex) + diffIndex;
493         bool virtualIndexValidate = virtualIndex >= 0 && virtualIndex < static_cast<int32_t>(totalOptionCount);
494         if ((NotLoopOptions() || !wheelModeEnabled_) && !virtualIndexValidate) {
495             textLayoutProperty->UpdateContent("");
496         } else {
497             auto optionValue = timePickerRowPattern->GetOptionsValue(host, optionIndex);
498             textLayoutProperty->UpdateContent(optionValue);
499             textLayoutProperty->UpdateTextAlign(TextAlign::CENTER);
500         }
501         textNode->MarkModifyDone();
502         textNode->MarkDirtyNode();
503     }
504 }
505 
UpdateDisappearTextProperties(const RefPtr<PickerTheme> & pickerTheme,const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<TimePickerLayoutProperty> & timePickerLayoutProperty)506 void TimePickerColumnPattern::UpdateDisappearTextProperties(const RefPtr<PickerTheme>& pickerTheme,
507     const RefPtr<TextLayoutProperty>& textLayoutProperty,
508     const RefPtr<TimePickerLayoutProperty>& timePickerLayoutProperty)
509 {
510     auto normalOptionSize = pickerTheme->GetOptionStyle(false, false).GetFontSize();
511     textLayoutProperty->UpdateTextColor(timePickerLayoutProperty->GetDisappearColor().value_or(
512         pickerTheme->GetOptionStyle(false, false).GetTextColor()));
513     if (timePickerLayoutProperty->HasDisappearFontSize()) {
514         textLayoutProperty->UpdateFontSize(timePickerLayoutProperty->GetDisappearFontSize().value());
515     } else {
516         textLayoutProperty->UpdateAdaptMaxFontSize(normalOptionSize);
517         textLayoutProperty->UpdateAdaptMinFontSize(pickerTheme->GetOptionStyle(false, false).GetAdaptMinFontSize());
518     }
519     textLayoutProperty->UpdateFontWeight(timePickerLayoutProperty->GetDisappearWeight().value_or(
520         pickerTheme->GetOptionStyle(false, false).GetFontWeight()));
521     DisappearWeight_ = timePickerLayoutProperty->GetDisappearWeight().value_or(
522         pickerTheme->GetOptionStyle(false, false).GetFontWeight());
523     textLayoutProperty->UpdateFontFamily(timePickerLayoutProperty->GetDisappearFontFamily().value_or(
524         pickerTheme->GetOptionStyle(false, false).GetFontFamilies()));
525     textLayoutProperty->UpdateItalicFontStyle(timePickerLayoutProperty->GetDisappearFontStyle().value_or(
526         pickerTheme->GetOptionStyle(false, false).GetFontStyle()));
527 }
528 
UpdateCandidateTextProperties(const RefPtr<PickerTheme> & pickerTheme,const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<TimePickerLayoutProperty> & timePickerLayoutProperty)529 void TimePickerColumnPattern::UpdateCandidateTextProperties(const RefPtr<PickerTheme>& pickerTheme,
530     const RefPtr<TextLayoutProperty>& textLayoutProperty,
531     const RefPtr<TimePickerLayoutProperty>& timePickerLayoutProperty)
532 {
533     auto focusOptionSize = pickerTheme->GetOptionStyle(false, false).GetFontSize() + FONT_SIZE;
534     textLayoutProperty->UpdateTextColor(
535         timePickerLayoutProperty->GetColor().value_or(pickerTheme->GetOptionStyle(false, false).GetTextColor()));
536     if (timePickerLayoutProperty->HasFontSize()) {
537         textLayoutProperty->UpdateFontSize(timePickerLayoutProperty->GetFontSize().value());
538     } else {
539         textLayoutProperty->UpdateAdaptMaxFontSize(focusOptionSize);
540         textLayoutProperty->UpdateAdaptMinFontSize(
541             pickerTheme->GetOptionStyle(true, false).GetAdaptMinFontSize() - FOCUS_SIZE);
542     }
543     textLayoutProperty->UpdateFontWeight(
544         timePickerLayoutProperty->GetWeight().value_or(pickerTheme->GetOptionStyle(false, false).GetFontWeight()));
545     textLayoutProperty->UpdateFontFamily(timePickerLayoutProperty->GetFontFamily().value_or(
546         pickerTheme->GetOptionStyle(false, false).GetFontFamilies()));
547     textLayoutProperty->UpdateItalicFontStyle(
548         timePickerLayoutProperty->GetFontStyle().value_or(pickerTheme->GetOptionStyle(false, false).GetFontStyle()));
549 }
550 
UpdateSelectedTextProperties(const RefPtr<PickerTheme> & pickerTheme,const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<TimePickerLayoutProperty> & timePickerLayoutProperty)551 void TimePickerColumnPattern::UpdateSelectedTextProperties(const RefPtr<PickerTheme>& pickerTheme,
552     const RefPtr<TextLayoutProperty>& textLayoutProperty,
553     const RefPtr<TimePickerLayoutProperty>& timePickerLayoutProperty)
554 {
555     auto selectedOptionSize = pickerTheme->GetOptionStyle(true, false).GetFontSize();
556     textLayoutProperty->UpdateTextColor(
557         timePickerLayoutProperty->GetSelectedColor().value_or(pickerTheme->GetOptionStyle(true, false).GetTextColor()));
558     if (timePickerLayoutProperty->HasSelectedFontSize()) {
559         textLayoutProperty->UpdateFontSize(timePickerLayoutProperty->GetSelectedFontSize().value());
560     } else {
561         textLayoutProperty->UpdateAdaptMaxFontSize(selectedOptionSize);
562         textLayoutProperty->UpdateAdaptMinFontSize(pickerTheme->GetOptionStyle(true, false).GetAdaptMinFontSize());
563     }
564     textLayoutProperty->UpdateFontWeight(timePickerLayoutProperty->GetSelectedWeight().value_or(
565         pickerTheme->GetOptionStyle(true, false).GetFontWeight()));
566     SelectedWeight_ = timePickerLayoutProperty->GetSelectedWeight().value_or(
567         pickerTheme->GetOptionStyle(true, false).GetFontWeight());
568     textLayoutProperty->UpdateFontFamily(timePickerLayoutProperty->GetSelectedFontFamily().value_or(
569         pickerTheme->GetOptionStyle(true, false).GetFontFamilies()));
570     textLayoutProperty->UpdateItalicFontStyle(timePickerLayoutProperty->GetSelectedFontStyle().value_or(
571         pickerTheme->GetOptionStyle(true, false).GetFontStyle()));
572 }
573 
ChangeAmPmTextStyle(uint32_t index,uint32_t showOptionCount,const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<TimePickerLayoutProperty> & timePickerLayoutProperty)574 void TimePickerColumnPattern::ChangeAmPmTextStyle(uint32_t index, uint32_t showOptionCount,
575     const RefPtr<TextLayoutProperty>& textLayoutProperty,
576     const RefPtr<TimePickerLayoutProperty>& timePickerLayoutProperty)
577 {
578     if (showOptionCount != CHILDREN_SIZE) {
579         return;
580     }
581     auto pipeline = GetContext();
582     CHECK_NULL_VOID(pipeline);
583     auto pickerTheme = pipeline->GetTheme<PickerTheme>();
584     CHECK_NULL_VOID(pickerTheme);
585     uint32_t selectedIndex = showOptionCount / 2; // the center option is selected.
586     if (index == selectedIndex) {
587         UpdateSelectedTextProperties(pickerTheme, textLayoutProperty, timePickerLayoutProperty);
588         textLayoutProperty->UpdateAlignment(Alignment::CENTER);
589     }
590     if ((index == 0) || (index == showOptionCount - 1)) {
591         UpdateDisappearTextProperties(pickerTheme, textLayoutProperty, timePickerLayoutProperty);
592         if (index == 0) {
593             textLayoutProperty->UpdateAlignment(Alignment::TOP_CENTER);
594         } else {
595             textLayoutProperty->UpdateAlignment(Alignment::BOTTOM_CENTER);
596         }
597     }
598     textLayoutProperty->UpdateMaxLines(1);
599 }
600 
ChangeTextStyle(uint32_t index,uint32_t showOptionCount,const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<TimePickerLayoutProperty> & timePickerLayoutProperty)601 void TimePickerColumnPattern::ChangeTextStyle(uint32_t index, uint32_t showOptionCount,
602     const RefPtr<TextLayoutProperty>& textLayoutProperty,
603     const RefPtr<TimePickerLayoutProperty>& timePickerLayoutProperty)
604 {
605     if (showOptionCount == CHILDREN_SIZE) {
606         return;
607     }
608     auto pipeline = GetContext();
609     CHECK_NULL_VOID(pipeline);
610     auto pickerTheme = pipeline->GetTheme<PickerTheme>();
611     CHECK_NULL_VOID(pickerTheme);
612     uint32_t selectedIndex = showOptionCount / 2; // the center option is selected.
613     uint32_t val = selectedIndex > 0 ? selectedIndex - 1 : 0;
614     if (index != selectedIndex) {
615         if ((index == selectedIndex + 1) || (index == val)) {
616             UpdateCandidateTextProperties(pickerTheme, textLayoutProperty, timePickerLayoutProperty);
617         } else {
618             UpdateDisappearTextProperties(pickerTheme, textLayoutProperty, timePickerLayoutProperty);
619         }
620         if (index < selectedIndex) {
621             textLayoutProperty->UpdateAlignment(Alignment::TOP_CENTER);
622         } else {
623             textLayoutProperty->UpdateAlignment(Alignment::BOTTOM_CENTER);
624         }
625     }
626     if (index == selectedIndex) {
627         UpdateSelectedTextProperties(pickerTheme, textLayoutProperty, timePickerLayoutProperty);
628         textLayoutProperty->UpdateAlignment(Alignment::CENTER);
629     }
630     textLayoutProperty->UpdateMaxLines(1);
631 }
632 
AddAnimationTextProperties(uint32_t currentIndex,const RefPtr<TextLayoutProperty> & textLayoutProperty)633 void TimePickerColumnPattern::AddAnimationTextProperties(
634     uint32_t currentIndex, const RefPtr<TextLayoutProperty>& textLayoutProperty)
635 {
636     TimeTextProperties properties;
637     if (textLayoutProperty->HasFontSize()) {
638         MeasureContext measureContext;
639         measureContext.textContent = MEASURE_SIZE_STRING;
640         measureContext.fontSize = textLayoutProperty->GetFontSize().value();
641         auto size = MeasureUtil::MeasureTextSize(measureContext);
642         if (!optionProperties_.empty()) {
643             optionProperties_[currentIndex].fontheight = size.Height();
644             if (optionProperties_[currentIndex].fontheight > optionProperties_[currentIndex].height) {
645                 optionProperties_[currentIndex].fontheight = optionProperties_[currentIndex].height;
646             }
647         }
648         SetOptionShiftDistance();
649         properties.fontSize = Dimension(textLayoutProperty->GetFontSize().value().ConvertToPx());
650     }
651     if (textLayoutProperty->HasFontWeight()) {
652         properties.fontWeight = textLayoutProperty->GetFontWeight().value();
653     }
654     if (textLayoutProperty->HasTextColor()) {
655         properties.currentColor = textLayoutProperty->GetTextColor().value();
656     }
657     if (currentIndex > 0) {
658         properties.upFontSize = animationProperties_[currentIndex - 1].fontSize;
659         animationProperties_[currentIndex - 1].downFontSize = properties.fontSize;
660 
661         properties.upColor = animationProperties_[currentIndex - 1].currentColor;
662         animationProperties_[currentIndex - 1].downColor = properties.currentColor;
663 
664         properties.upFontWeight = animationProperties_[currentIndex - 1].fontWeight;
665         animationProperties_[currentIndex - 1].downFontWeight = properties.fontWeight;
666     }
667     animationProperties_.emplace_back(properties);
668 }
669 
FlushAnimationTextProperties(bool isDown)670 void TimePickerColumnPattern::FlushAnimationTextProperties(bool isDown)
671 {
672     if (!animationProperties_.size()) {
673         return;
674     }
675     if (isDown) {
676         for (size_t i = 0; i < animationProperties_.size(); i++) {
677             if (i > 0) {
678                 animationProperties_[i - 1].upFontSize = animationProperties_[i].upFontSize;
679                 animationProperties_[i - 1].fontSize = animationProperties_[i].fontSize;
680                 animationProperties_[i - 1].downFontSize = animationProperties_[i].downFontSize;
681 
682                 animationProperties_[i - 1].upColor = animationProperties_[i].upColor;
683                 animationProperties_[i - 1].currentColor = animationProperties_[i].currentColor;
684                 animationProperties_[i - 1].downColor = animationProperties_[i].downColor;
685             }
686             if (i == (animationProperties_.size() - 1)) {
687                 animationProperties_[i].upFontSize = animationProperties_[i].fontSize;
688                 animationProperties_[i].fontSize = animationProperties_[i].fontSize * 0.5;
689                 animationProperties_[i].downFontSize = Dimension();
690 
691                 animationProperties_[i].upColor = animationProperties_[i].currentColor;
692                 auto colorEvaluator = AceType::MakeRefPtr<LinearEvaluator<Color>>();
693                 animationProperties_[i].currentColor =
694                     colorEvaluator->Evaluate(Color(), animationProperties_[i].currentColor, 0.5);
695                 animationProperties_[i].downColor = Color();
696             }
697         }
698     } else {
699         for (size_t i = animationProperties_.size() - 1;; i--) {
700             if (i == 0) {
701                 animationProperties_[i].upFontSize = Dimension();
702                 animationProperties_[i].downFontSize = animationProperties_[i].fontSize;
703                 animationProperties_[i].fontSize = animationProperties_[i].fontSize * 0.5;
704 
705                 animationProperties_[i].upColor = Color();
706                 animationProperties_[i].downColor = animationProperties_[i].currentColor;
707                 auto colorEvaluator = AceType::MakeRefPtr<LinearEvaluator<Color>>();
708                 animationProperties_[i].currentColor =
709                     colorEvaluator->Evaluate(Color(), animationProperties_[i].currentColor, 0.5);
710                 break;
711             } else {
712                 animationProperties_[i].upFontSize = animationProperties_[i - 1].upFontSize;
713                 animationProperties_[i].fontSize = animationProperties_[i - 1].fontSize;
714                 animationProperties_[i].downFontSize = animationProperties_[i - 1].downFontSize;
715 
716                 animationProperties_[i].upColor = animationProperties_[i - 1].upColor;
717                 animationProperties_[i].currentColor = animationProperties_[i - 1].currentColor;
718                 animationProperties_[i].downColor = animationProperties_[i - 1].downColor;
719             }
720         }
721     }
722 }
723 
TextPropertiesLinearAnimation(const RefPtr<TextLayoutProperty> & textLayoutProperty,uint32_t index,uint32_t showCount,bool isDown,double scale)724 void TimePickerColumnPattern::TextPropertiesLinearAnimation(
725     const RefPtr<TextLayoutProperty>& textLayoutProperty, uint32_t index, uint32_t showCount, bool isDown, double scale)
726 {
727     if (index >= animationProperties_.size()) {
728         return;
729     }
730     Dimension startFontSize = animationProperties_[index].fontSize;
731     Color startColor = animationProperties_[index].currentColor;
732     if ((!index && isDown) || ((index == (showCount - 1)) && !isDown && scale)) {
733         textLayoutProperty->UpdateFontSize(startFontSize);
734         textLayoutProperty->UpdateTextColor(startColor);
735         return;
736     }
737     Dimension endFontSize;
738     Color endColor;
739     if (!isDown) {
740         endFontSize = animationProperties_[index].downFontSize;
741         endColor = animationProperties_[index].downColor;
742 
743         if (scale >= FONTWEIGHT) {
744             textLayoutProperty->UpdateFontWeight(animationProperties_[index].downFontWeight);
745         }
746     } else {
747         endFontSize = animationProperties_[index].upFontSize;
748         endColor = animationProperties_[index].upColor;
749 
750         if (scale >= FONTWEIGHT) {
751             textLayoutProperty->UpdateFontWeight(animationProperties_[index].upFontWeight);
752         }
753     }
754     Dimension updateSize = LinearFontSize(startFontSize, endFontSize, distancePercent_);
755     textLayoutProperty->UpdateFontSize(updateSize);
756     auto colorEvaluator = AceType::MakeRefPtr<LinearEvaluator<Color>>();
757     Color updateColor = colorEvaluator->Evaluate(startColor, endColor, distancePercent_);
758     textLayoutProperty->UpdateTextColor(updateColor);
759     if (scale < FONTWEIGHT) {
760         textLayoutProperty->UpdateFontWeight(animationProperties_[index].fontWeight);
761     }
762 }
763 
UpdateTextPropertiesLinear(bool isDown,double scale)764 void TimePickerColumnPattern::UpdateTextPropertiesLinear(bool isDown, double scale)
765 {
766     auto host = GetHost();
767     CHECK_NULL_VOID(host);
768     uint32_t showCount = GetShowCount();
769     auto child = host->GetChildren();
770     auto iter = child.begin();
771     if (child.size() != showCount) {
772         return;
773     }
774     for (uint32_t index = 0; index < showCount; index++) {
775         auto textNode = DynamicCast<FrameNode>(*iter);
776         CHECK_NULL_VOID(textNode);
777         auto textPattern = textNode->GetPattern<TextPattern>();
778         CHECK_NULL_VOID(textPattern);
779         RefPtr<TextLayoutProperty> textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
780         CHECK_NULL_VOID(textLayoutProperty);
781         TextPropertiesLinearAnimation(textLayoutProperty, index, showCount, isDown, scale);
782         iter++;
783     }
784 }
785 
LinearFontSize(const Dimension & startFontSize,const Dimension & endFontSize,double percent)786 Dimension TimePickerColumnPattern::LinearFontSize(
787     const Dimension& startFontSize, const Dimension& endFontSize, double percent)
788 {
789     if (percent > FONT_SIZE_PERCENT) {
790         return startFontSize + (endFontSize - startFontSize);
791     } else {
792         return startFontSize + (endFontSize - startFontSize) * percent;
793     }
794 }
795 
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)796 void TimePickerColumnPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
797 {
798     CHECK_NULL_VOID(!panEvent_);
799     auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& event) {
800         auto pattern = weak.Upgrade();
801         CHECK_NULL_VOID(pattern);
802         if (event.GetInputEventType() == InputEventType::AXIS && event.GetSourceTool() == SourceTool::MOUSE) {
803             return;
804         }
805         pattern->HandleDragStart(event);
806     };
807     auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& event) {
808         auto pattern = weak.Upgrade();
809         CHECK_NULL_VOID(pattern);
810         pattern->SetMainVelocity(event.GetMainVelocity());
811         pattern->HandleDragMove(event);
812     };
813     auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
814         auto pattern = weak.Upgrade();
815         CHECK_NULL_VOID(pattern);
816         if (info.GetInputEventType() == InputEventType::AXIS && info.GetSourceTool() == SourceTool::MOUSE) {
817             return;
818         }
819         pattern->SetMainVelocity(info.GetMainVelocity());
820         pattern->HandleDragEnd();
821     };
822     auto actionCancelTask = [weak = WeakClaim(this)]() {
823         auto pattern = weak.Upgrade();
824         CHECK_NULL_VOID(pattern);
825         pattern->HandleDragEnd();
826     };
827     PanDirection panDirection;
828     panDirection.type = PanDirection::VERTICAL;
829     panEvent_ = MakeRefPtr<PanEvent>(
830         std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
831     gestureHub->AddPanEvent(panEvent_, panDirection, DEFAULT_PAN_FINGER, DEFAULT_PAN_DISTANCE);
832 }
833 
HandleDragStart(const GestureEvent & event)834 void TimePickerColumnPattern::HandleDragStart(const GestureEvent& event)
835 {
836     CHECK_NULL_VOID(GetHost());
837     CHECK_NULL_VOID(GetToss());
838     auto toss = GetToss();
839     auto offsetY = event.GetGlobalPoint().GetY();
840     toss->SetStart(offsetY);
841     yLast_ = offsetY;
842     pressed_ = true;
843     auto frameNode = GetHost();
844     CHECK_NULL_VOID(frameNode);
845     frameNode->AddFRCSceneInfo(PICKER_DRAG_SCENE, event.GetMainVelocity(), SceneStatus::START);
846     // AccessibilityEventType::SCROLL_START
847 }
848 
HandleDragMove(const GestureEvent & event)849 void TimePickerColumnPattern::HandleDragMove(const GestureEvent& event)
850 {
851     if (event.GetInputEventType() == InputEventType::AXIS && event.GetSourceTool() == SourceTool::MOUSE) {
852         InnerHandleScroll(LessNotEqual(event.GetDelta().GetY(), 0.0), true);
853         return;
854     }
855     animationBreak_ = false;
856     CHECK_NULL_VOID(pressed_);
857     CHECK_NULL_VOID(GetHost());
858     CHECK_NULL_VOID(GetToss());
859     auto toss = GetToss();
860     auto offsetY =
861         event.GetGlobalPoint().GetY() + (event.GetInputEventType() == InputEventType::AXIS ? event.GetOffsetY() : 0.0);
862     if (NearEqual(offsetY, yLast_, 1.0)) { // if changing less than 1.0, no need to handle
863         return;
864     }
865     toss->SetEnd(offsetY);
866     UpdateColumnChildPosition(offsetY);
867     auto frameNode = GetHost();
868     CHECK_NULL_VOID(frameNode);
869     frameNode->AddFRCSceneInfo(PICKER_DRAG_SCENE, event.GetMainVelocity(), SceneStatus::RUNNING);
870 }
871 
HandleDragEnd()872 void TimePickerColumnPattern::HandleDragEnd()
873 {
874     if (hapticController_) {
875         hapticController_->Stop();
876     }
877     pressed_ = false;
878     CHECK_NULL_VOID(GetHost());
879     CHECK_NULL_VOID(GetToss());
880     auto toss = GetToss();
881     auto frameNode = GetHost();
882     CHECK_NULL_VOID(frameNode);
883     if (!NotLoopOptions() && toss->Play()) {
884         frameNode->AddFRCSceneInfo(PICKER_DRAG_SCENE, mainVelocity_, SceneStatus::END);
885         // AccessibilityEventType::SCROLL_END
886         return;
887     }
888     yOffset_ = 0.0;
889     yLast_ = 0.0;
890     if (!animationCreated_) {
891         ScrollOption(0.0);
892         return;
893     }
894 
895     TimePickerScrollDirection dir =
896         scrollDelta_ > 0.0 ? TimePickerScrollDirection::DOWN : TimePickerScrollDirection::UP;
897     int32_t middleIndex = static_cast<int32_t>(GetShowCount()) / MIDDLE_CHILD_INDEX;
898     auto shiftDistance = (dir == TimePickerScrollDirection::UP) ? optionProperties_[middleIndex].prevDistance
899                                                                 : optionProperties_[middleIndex].nextDistance;
900     auto shiftThreshold = shiftDistance / MIDDLE_CHILD_INDEX;
901     if (std::abs(scrollDelta_) >= std::abs(shiftThreshold)) {
902         InnerHandleScroll(LessNotEqual(scrollDelta_, 0.0), true);
903         scrollDelta_ = scrollDelta_ - std::abs(shiftDistance) * (dir == TimePickerScrollDirection::UP ? -1 : 1);
904     }
905     CreateAnimation(scrollDelta_, 0.0);
906     frameNode->AddFRCSceneInfo(PICKER_DRAG_SCENE, mainVelocity_, SceneStatus::END);
907     // AccessibilityEventType::SCROLL_END
908 }
909 
CreateAnimation()910 void TimePickerColumnPattern::CreateAnimation()
911 {
912     CHECK_NULL_VOID(!animationCreated_);
913     auto host = GetHost();
914     CHECK_NULL_VOID(host);
915     auto renderContext = host->GetRenderContext();
916     CHECK_NULL_VOID(renderContext);
917     auto propertyCallback = [weak = AceType::WeakClaim(this)](float value) {
918         auto column = weak.Upgrade();
919         CHECK_NULL_VOID(column);
920         column->ScrollOption(value);
921     };
922     scrollProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
923     renderContext->AttachNodeAnimatableProperty(scrollProperty_);
924 
925     auto aroundClickCallback = [weak = AceType::WeakClaim(this)](float value) {
926         auto column = weak.Upgrade();
927         CHECK_NULL_VOID(column);
928         if (value > 0) {
929             column->UpdateColumnChildPosition(std::ceil(value));
930         } else {
931             column->UpdateColumnChildPosition(std::floor(value));
932         }
933     };
934     aroundClickProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(aroundClickCallback));
935     renderContext->AttachNodeAnimatableProperty(aroundClickProperty_);
936     animationCreated_ = true;
937 }
938 
CreateAnimation(double from,double to)939 void TimePickerColumnPattern::CreateAnimation(double from, double to)
940 {
941     AnimationOption option;
942     option.SetCurve(Curves::FAST_OUT_SLOW_IN);
943     option.SetDuration(CLICK_ANIMATION_DURATION);
944     scrollProperty_->Set(from);
945     AnimationUtils::Animate(option, [weak = AceType::WeakClaim(this), to]() {
946         auto column = weak.Upgrade();
947         CHECK_NULL_VOID(column);
948         column->scrollProperty_->Set(to);
949     });
950 }
951 
ScrollOption(double delta,bool isJump)952 void TimePickerColumnPattern::ScrollOption(double delta, bool isJump)
953 {
954     scrollDelta_ = delta;
955     auto midIndex = GetShowCount() / 2;
956     TimePickerScrollDirection dir = delta > 0.0 ? TimePickerScrollDirection::DOWN : TimePickerScrollDirection::UP;
957     auto shiftDistance = (dir == TimePickerScrollDirection::UP) ? optionProperties_[midIndex].prevDistance
958                                                                 : optionProperties_[midIndex].nextDistance;
959     distancePercent_ = delta / shiftDistance;
960     auto textLinearPercent = 0.0;
961     textLinearPercent = (std::abs(delta)) / (optionProperties_[midIndex].height);
962     UpdateTextPropertiesLinear(LessNotEqual(delta, 0.0), textLinearPercent);
963     CalcAlgorithmOffset(dir, distancePercent_);
964     auto host = GetHost();
965     CHECK_NULL_VOID(host);
966     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
967 }
968 
ResetAlgorithmOffset()969 void TimePickerColumnPattern::ResetAlgorithmOffset()
970 {
971     algorithmOffset_.clear();
972     uint32_t counts = GetShowCount();
973     for (uint32_t i = 0; i < counts; i++) {
974         algorithmOffset_.emplace_back(0.0f);
975     }
976 }
977 
UpdateScrollDelta(double delta)978 void TimePickerColumnPattern::UpdateScrollDelta(double delta)
979 {
980     SetCurrentOffset(delta);
981     auto host = GetHost();
982     CHECK_NULL_VOID(host);
983     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
984 }
985 
CalcAlgorithmOffset(TimePickerScrollDirection dir,double distancePercent)986 void TimePickerColumnPattern::CalcAlgorithmOffset(TimePickerScrollDirection dir, double distancePercent)
987 {
988     algorithmOffset_.clear();
989     uint32_t counts = GetShowCount();
990     for (uint32_t i = 0; i < counts; i++) {
991         auto distance = (dir == TimePickerScrollDirection::UP) ? optionProperties_[i].prevDistance
992                                                                : optionProperties_[i].nextDistance;
993         algorithmOffset_.emplace_back(static_cast<int32_t>(distance * distancePercent));
994     }
995 }
996 
GetShiftDistance(uint32_t index,TimePickerScrollDirection dir)997 float TimePickerColumnPattern::GetShiftDistance(uint32_t index, TimePickerScrollDirection dir)
998 {
999     auto pipeline = GetContext();
1000     CHECK_NULL_RETURN(pipeline, 0.0f);
1001     auto theme = pipeline->GetTheme<PickerTheme>();
1002     CHECK_NULL_RETURN(theme, 0.0f);
1003     const uint32_t optionCounts = GetShowCount();
1004     uint32_t nextIndex = 0;
1005     float distance = 0.0f;
1006     float val = 0.0f;
1007     auto isDown = dir == TimePickerScrollDirection::DOWN;
1008     if (optionCounts == 0) {
1009         return distance;
1010     }
1011     if (isDown) {
1012         nextIndex = (optionCounts + index + 1) % optionCounts; // index add one
1013     } else {
1014         nextIndex = (optionCounts + index - 1) % optionCounts; // index reduce one
1015     }
1016     switch (static_cast<TimePickerOptionIndex>(index)) {
1017         case TimePickerOptionIndex::COLUMN_INDEX_0: // first
1018             distance = (dir == TimePickerScrollDirection::DOWN) ? optionProperties_[index].height
1019                                                                 : (0.0f - optionProperties_[index].height);
1020             break;
1021         case TimePickerOptionIndex::COLUMN_INDEX_1:
1022             distance = (dir == TimePickerScrollDirection::DOWN) ? optionProperties_[index].height
1023                                                                 : (0.0f - optionProperties_[index].height);
1024             break;
1025         case TimePickerOptionIndex::COLUMN_INDEX_2:
1026             if (dir == TimePickerScrollDirection::UP) {
1027                 distance = -optionProperties_[nextIndex].height;
1028             } else {
1029                 val = optionProperties_[index].height +
1030                       (optionProperties_[nextIndex].height - optionProperties_[nextIndex].fontheight) /
1031                           MIDDLE_CHILD_INDEX;
1032                 distance = std::ceil(val);
1033             }
1034             break;
1035         case TimePickerOptionIndex::COLUMN_INDEX_3:
1036             val = (optionProperties_[index].height - optionProperties_[nextIndex].fontheight) / MIDDLE_CHILD_INDEX +
1037                   optionProperties_[nextIndex].height;
1038             distance = (dir == TimePickerScrollDirection::DOWN) ? val : (0.0f - val);
1039             distance = std::floor(distance);
1040             break;
1041         case TimePickerOptionIndex::COLUMN_INDEX_4:
1042             if (dir == TimePickerScrollDirection::DOWN) {
1043                 distance = optionProperties_[nextIndex].height;
1044             } else {
1045                 val = optionProperties_[index].height +
1046                       (optionProperties_[nextIndex].height - optionProperties_[nextIndex].fontheight) /
1047                           MIDDLE_CHILD_INDEX;
1048                 distance = std::ceil(0.0f - val);
1049             }
1050             break;
1051         case TimePickerOptionIndex::COLUMN_INDEX_5:
1052             distance = (dir == TimePickerScrollDirection::DOWN) ? optionProperties_[index].height
1053                                                                 : (0.0f - optionProperties_[index].height);
1054             break;
1055         case TimePickerOptionIndex::COLUMN_INDEX_6: // last
1056             distance = (dir == TimePickerScrollDirection::DOWN) ? optionProperties_[index].height
1057                                                                 : (0.0f - optionProperties_[index].height);
1058             break;
1059         default:
1060             break;
1061     }
1062     return distance;
1063 }
1064 
GetShiftDistanceForLandscape(uint32_t index,TimePickerScrollDirection dir)1065 float TimePickerColumnPattern::GetShiftDistanceForLandscape(uint32_t index, TimePickerScrollDirection dir)
1066 {
1067     auto pipeline = GetContext();
1068     CHECK_NULL_RETURN(pipeline, 0.0f);
1069     auto theme = pipeline->GetTheme<PickerTheme>();
1070     CHECK_NULL_RETURN(theme, 0.0f);
1071     uint32_t optionCounts = GetShowCount();
1072     uint32_t nextIndex = 0;
1073     float distance = 0.0f;
1074     float val = 0.0f;
1075     auto isDown = dir == TimePickerScrollDirection::DOWN;
1076     if (optionCounts == 0) {
1077         return distance;
1078     }
1079     if (isDown) {
1080         nextIndex = (optionCounts + index + 1) % optionCounts; // index add one
1081     } else {
1082         nextIndex = (optionCounts + index - 1) % optionCounts; // index reduce one
1083     }
1084     switch (static_cast<TimePickerOptionIndex>(index)) {
1085         case TimePickerOptionIndex::COLUMN_INDEX_0: // first
1086             if (dir == TimePickerScrollDirection::UP) {
1087                 distance = 0.0f - optionProperties_[index].height;
1088             } else {
1089                 distance = optionProperties_[index].height +
1090                            (optionProperties_[nextIndex].height - optionProperties_[nextIndex].fontheight) /
1091                                MIDDLE_CHILD_INDEX;
1092             }
1093             break;
1094         case TimePickerOptionIndex::COLUMN_INDEX_1:
1095             val = (optionProperties_[index].height - optionProperties_[nextIndex].fontheight) / MIDDLE_CHILD_INDEX +
1096                   optionProperties_[nextIndex].height;
1097             distance = (dir == TimePickerScrollDirection::DOWN) ? val : (0.0f - val);
1098             distance = std::floor(distance);
1099             break;
1100         case TimePickerOptionIndex::COLUMN_INDEX_2: // last
1101             if (dir == TimePickerScrollDirection::DOWN) {
1102                 distance = optionProperties_[index].height;
1103             } else {
1104                 val = optionProperties_[index].height +
1105                       (optionProperties_[nextIndex].height - optionProperties_[nextIndex].fontheight) /
1106                           MIDDLE_CHILD_INDEX;
1107                 distance = 0.0f - val;
1108             }
1109             break;
1110         default:
1111             break;
1112     }
1113     return distance;
1114 }
1115 
SetOptionShiftDistance()1116 void TimePickerColumnPattern::SetOptionShiftDistance()
1117 {
1118     auto pipeline = PipelineBase::GetCurrentContext();
1119     CHECK_NULL_VOID(pipeline);
1120     auto theme = pipeline->GetTheme<PickerTheme>();
1121     CHECK_NULL_VOID(theme);
1122     uint32_t itemCounts = GetShowCount();
1123     bool isLanscape = itemCounts == OPTION_COUNT_PHONE_LANDSCAPE;
1124     for (uint32_t i = 0; i < itemCounts; i++) {
1125         TimePickerOptionProperty& prop = optionProperties_[i];
1126         if (isLanscape) {
1127             prop.prevDistance = GetShiftDistanceForLandscape(i, TimePickerScrollDirection::UP);
1128             prop.nextDistance = GetShiftDistanceForLandscape(i, TimePickerScrollDirection::DOWN);
1129         } else {
1130             prop.prevDistance = GetShiftDistance(i, TimePickerScrollDirection::UP);
1131             prop.nextDistance = GetShiftDistance(i, TimePickerScrollDirection::DOWN);
1132         }
1133     }
1134 }
1135 
UpdateToss(double offsetY)1136 void TimePickerColumnPattern::UpdateToss(double offsetY)
1137 {
1138     UpdateColumnChildPosition(offsetY);
1139 }
1140 
UpdateFinishToss(double offsetY)1141 void TimePickerColumnPattern::UpdateFinishToss(double offsetY)
1142 {
1143     int32_t dragDelta = offsetY - yLast_;
1144     if (!CanMove(LessNotEqual(dragDelta, 0))) {
1145         return;
1146     }
1147     auto midIndex = GetShowCount() / 2;
1148     TimePickerScrollDirection dir = dragDelta > 0.0 ? TimePickerScrollDirection::DOWN : TimePickerScrollDirection::UP;
1149     auto shiftDistance = (dir == TimePickerScrollDirection::UP) ? optionProperties_[midIndex].prevDistance
1150                                                                 : optionProperties_[midIndex].nextDistance;
1151     ScrollOption(shiftDistance);
1152 }
1153 
TossStoped()1154 void TimePickerColumnPattern::TossStoped()
1155 {
1156     yOffset_ = 0.0;
1157     yLast_ = 0.0;
1158     ScrollOption(0.0);
1159 }
1160 
SetDividerHeight(uint32_t showOptionCount)1161 void TimePickerColumnPattern::SetDividerHeight(uint32_t showOptionCount)
1162 {
1163     auto host = GetHost();
1164     CHECK_NULL_VOID(host);
1165     auto pipeline = GetContext();
1166     CHECK_NULL_VOID(pipeline);
1167     auto pickerTheme = pipeline->GetTheme<PickerTheme>();
1168     auto childSize = host->GetChildren().size();
1169     if (childSize != CHILDREN_SIZE) {
1170         gradientHeight_ = static_cast<float>(pickerTheme->GetGradientHeight().Value() * TEXT_HEIGHT_NUMBER);
1171     } else {
1172         gradientHeight_ = static_cast<float>(pickerTheme->GetGradientHeight().Value() - TEXT_HOUR24_HEIGHT_NUMBER);
1173     }
1174     dividerHeight_ = static_cast<float>(
1175         gradientHeight_ + pickerTheme->GetDividerSpacing().Value() + pickerTheme->GetGradientHeight().Value());
1176     dividerSpacingWidth_ = static_cast<float>(pickerTheme->GetDividerSpacing().Value() * TEXT_WEIGHT_NUMBER);
1177 }
1178 
NotLoopOptions() const1179 bool TimePickerColumnPattern::NotLoopOptions() const
1180 {
1181     auto host = GetHost();
1182     CHECK_NULL_RETURN(host, false);
1183     auto showOptionCount = GetShowCount();
1184     auto options = GetOptions();
1185     uint32_t totalOptionCount = options[host];
1186     return totalOptionCount <= showOptionCount / 2 + 1; // the critical value of loop condition.
1187 }
1188 
InnerHandleScroll(bool isDown,bool isUpatePropertiesOnly)1189 bool TimePickerColumnPattern::InnerHandleScroll(bool isDown, bool isUpatePropertiesOnly)
1190 {
1191     auto host = GetHost();
1192     CHECK_NULL_RETURN(host, false);
1193     auto options = GetOptions();
1194     auto totalOptionCount = options[host];
1195 
1196     CHECK_NULL_RETURN(host, false);
1197     CHECK_NULL_RETURN(totalOptionCount, false);
1198 
1199     uint32_t currentIndex = GetCurrentIndex();
1200     if (isDown) {
1201         currentIndex = (totalOptionCount + currentIndex + 1) % totalOptionCount; // index add one
1202     } else {
1203         auto totalCountAndIndex = totalOptionCount + currentIndex;
1204         currentIndex = (totalCountAndIndex ? totalCountAndIndex - 1 : 0) % totalOptionCount; // index reduce one
1205     }
1206     SetCurrentIndex(currentIndex);
1207     if (hapticController_ && isEnableHaptic_) {
1208         hapticController_->PlayOnce();
1209     }
1210     FlushCurrentOptions(isDown, isUpatePropertiesOnly);
1211     HandleChangeCallback(isDown, true);
1212     HandleEventCallback(true);
1213 
1214     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
1215     host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE);
1216     return true;
1217 }
1218 
UpdateColumnChildPosition(double offsetY)1219 void TimePickerColumnPattern::UpdateColumnChildPosition(double offsetY)
1220 {
1221     int32_t dragDelta = offsetY - yLast_;
1222     if (hapticController_ && isShow_) {
1223         if (isEnableHaptic_) {
1224             hapticController_->HandleDelta(dragDelta);
1225         }
1226     }
1227     yLast_ = offsetY;
1228     if (!CanMove(LessNotEqual(dragDelta, 0))) {
1229         return;
1230     }
1231     offsetCurSet_ = 0.0;
1232     auto midIndex = GetShowCount() / 2;
1233     TimePickerScrollDirection dir = dragDelta > 0.0 ? TimePickerScrollDirection::DOWN : TimePickerScrollDirection::UP;
1234     auto shiftDistance = (dir == TimePickerScrollDirection::UP) ? optionProperties_[midIndex].prevDistance
1235                                                                 : optionProperties_[midIndex].nextDistance;
1236     // the abs of drag delta is less than jump interval.
1237     dragDelta = dragDelta + yOffset_;
1238     if (GreatOrEqual(std::abs(dragDelta), std::abs(shiftDistance))) {
1239         InnerHandleScroll(LessNotEqual(dragDelta, 0.0), true);
1240         dragDelta = dragDelta % static_cast<int>(std::abs(shiftDistance));
1241         if (!NearZero(dragDelta) && !CanMove(LessNotEqual(dragDelta, 0))) {
1242             dragDelta = 0.0;
1243             auto toss = GetToss();
1244             CHECK_NULL_VOID(toss);
1245             toss->StopTossAnimation();
1246             if (hapticController_) {
1247                 hapticController_->Stop();
1248             }
1249         }
1250         ScrollOption(dragDelta, true);
1251         offsetCurSet_ = dragDelta;
1252         yOffset_ = dragDelta;
1253         return;
1254     }
1255     // update selected option
1256     ScrollOption(dragDelta);
1257     offsetCurSet_ = dragDelta;
1258     yOffset_ = dragDelta;
1259 }
1260 
ShiftOptionProp(RefPtr<FrameNode> curNode,RefPtr<FrameNode> shiftNode)1261 void TimePickerColumnPattern::ShiftOptionProp(RefPtr<FrameNode> curNode, RefPtr<FrameNode> shiftNode)
1262 {
1263     RefPtr<TextPattern> curPattern = curNode->GetPattern<TextPattern>();
1264     CHECK_NULL_VOID(curPattern);
1265     RefPtr<TextLayoutProperty> curLayoutProperty = curPattern->GetLayoutProperty<TextLayoutProperty>();
1266     CHECK_NULL_VOID(curLayoutProperty);
1267 
1268     RefPtr<TextPattern> shiftPattern = shiftNode->GetPattern<TextPattern>();
1269     CHECK_NULL_VOID(shiftPattern);
1270     RefPtr<TextLayoutProperty> shiftLayoutProperty = shiftPattern->GetLayoutProperty<TextLayoutProperty>();
1271     CHECK_NULL_VOID(shiftLayoutProperty);
1272     curLayoutProperty->UpdateFontWeight(shiftLayoutProperty->GetFontWeight().value_or(FontWeight::W100));
1273 }
1274 
CanMove(bool isDown) const1275 bool TimePickerColumnPattern::CanMove(bool isDown) const
1276 {
1277     if (wheelModeEnabled_) {
1278         CHECK_NULL_RETURN(NotLoopOptions(), true);
1279     }
1280     auto host = GetHost();
1281     CHECK_NULL_RETURN(host, false);
1282     auto options = GetOptions();
1283     int totalOptionCount = static_cast<int>(options[host]);
1284     auto timePickerColumnPattern = host->GetPattern<TimePickerColumnPattern>();
1285     CHECK_NULL_RETURN(timePickerColumnPattern, false);
1286     int currentIndex = static_cast<int>(timePickerColumnPattern->GetCurrentIndex());
1287     int nextVirtualIndex = isDown ? currentIndex + 1 : currentIndex - 1;
1288     return nextVirtualIndex >= 0 && nextVirtualIndex < totalOptionCount;
1289 }
1290 
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)1291 void TimePickerColumnPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
1292 {
1293     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
1294         auto pattern = wp.Upgrade();
1295         CHECK_NULL_RETURN(pattern, false);
1296         return pattern->OnKeyEvent(event);
1297     };
1298     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
1299 }
1300 
OnKeyEvent(const KeyEvent & event)1301 bool TimePickerColumnPattern::OnKeyEvent(const KeyEvent& event)
1302 {
1303     if (event.action != KeyAction::DOWN) {
1304         return false;
1305     }
1306     if (event.code == KeyCode::KEY_DPAD_UP || event.code == KeyCode::KEY_DPAD_DOWN) {
1307         HandleDirectionKey(event.code);
1308         return true;
1309     }
1310     return false;
1311 }
1312 
HandleDirectionKey(KeyCode code)1313 bool TimePickerColumnPattern::HandleDirectionKey(KeyCode code)
1314 {
1315     if (code == KeyCode::KEY_DPAD_UP) {
1316         // Need to update: current selection
1317         return true;
1318     }
1319     if (code == KeyCode::KEY_DPAD_DOWN) {
1320         // Need to update: current selection
1321         return true;
1322     }
1323     return false;
1324 }
1325 
SetAccessibilityAction()1326 void TimePickerColumnPattern::SetAccessibilityAction()
1327 {
1328     auto host = GetHost();
1329     CHECK_NULL_VOID(host);
1330     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1331     CHECK_NULL_VOID(accessibilityProperty);
1332     accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
1333         const auto& pattern = weakPtr.Upgrade();
1334         CHECK_NULL_VOID(pattern);
1335         if (!pattern->CanMove(true)) {
1336             return;
1337         }
1338         CHECK_NULL_VOID(pattern->animationCreated_);
1339         pattern->InnerHandleScroll(true);
1340         pattern->CreateAnimation(0.0 - pattern->jumpInterval_, 0.0);
1341         // AccessibilityEventType::SCROLL_END
1342     });
1343 
1344     accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)]() {
1345         const auto& pattern = weakPtr.Upgrade();
1346         CHECK_NULL_VOID(pattern);
1347         if (!pattern->CanMove(false)) {
1348             return;
1349         }
1350         CHECK_NULL_VOID(pattern->animationCreated_);
1351         pattern->InnerHandleScroll(false);
1352         pattern->CreateAnimation(pattern->jumpInterval_, 0.0);
1353         // AccessibilityEventType::SCROLL_END
1354     });
1355 }
1356 
CreateItemClickEventListener(RefPtr<TimePickerEventParam> param)1357 RefPtr<ClickEvent> TimePickerColumnPattern::CreateItemClickEventListener(RefPtr<TimePickerEventParam> param)
1358 {
1359     auto clickEventHandler = [param, weak = WeakClaim(this)](const GestureEvent& /* info */) {
1360         auto pattern = weak.Upgrade();
1361         pattern->OnAroundButtonClick(param);
1362     };
1363     auto listener = AceType::MakeRefPtr<NG::ClickEvent>(clickEventHandler);
1364     return listener;
1365 }
1366 
OnAroundButtonClick(RefPtr<TimePickerEventParam> param)1367 void TimePickerColumnPattern::OnAroundButtonClick(RefPtr<TimePickerEventParam> param)
1368 {
1369     if (clickBreak_) {
1370         return;
1371     }
1372     int32_t middleIndex = static_cast<int32_t>(GetShowCount()) / 2;
1373     int32_t step = param->itemIndex_ - middleIndex;
1374     if (step != 0) {
1375         if (animation_) {
1376             AnimationUtils::StopAnimation(animation_);
1377             yLast_ = 0.0;
1378             yOffset_ = 0.0;
1379         }
1380         auto distance =
1381             (step > 0 ? optionProperties_[middleIndex].prevDistance : optionProperties_[middleIndex].nextDistance) *
1382             std::abs(step);
1383         AnimationOption option;
1384         option.SetCurve(Curves::FAST_OUT_SLOW_IN);
1385         option.SetDuration(CLICK_ANIMATION_DURATION);
1386         aroundClickProperty_->Set(0.0);
1387         animation_ = AnimationUtils::StartAnimation(option, [weak = AceType::WeakClaim(this), step, distance]() {
1388             auto column = weak.Upgrade();
1389             CHECK_NULL_VOID(column);
1390             column->aroundClickProperty_->Set(step > 0 ? 0.0 - std::abs(distance) : std::abs(distance));
1391         });
1392         auto host = GetHost();
1393         CHECK_NULL_VOID(host);
1394         auto pipeline = host->GetContext();
1395         CHECK_NULL_VOID(pipeline);
1396         pipeline->RequestFrame();
1397     }
1398 }
TossAnimationStoped()1399 void TimePickerColumnPattern::TossAnimationStoped()
1400 {
1401     if (hapticController_) {
1402         hapticController_->Stop();
1403     }
1404     yLast_ = 0.0;
1405 }
1406 
PlayRestAnimation()1407 void TimePickerColumnPattern::PlayRestAnimation()
1408 {
1409     TimePickerScrollDirection dir =
1410         scrollDelta_ > 0.0 ? TimePickerScrollDirection::DOWN : TimePickerScrollDirection::UP;
1411     int32_t middleIndex = static_cast<int32_t>(GetShowCount()) / 2;
1412     double shiftDistance = (dir == TimePickerScrollDirection::UP) ? optionProperties_[middleIndex].prevDistance
1413                                                                   : optionProperties_[middleIndex].nextDistance;
1414     double shiftThreshold = shiftDistance / 2;
1415     if (std::abs(scrollDelta_) >= std::abs(shiftThreshold)) {
1416         InnerHandleScroll(LessNotEqual(scrollDelta_, 0.0), true);
1417         scrollDelta_ = scrollDelta_ - std::abs(shiftDistance) * (dir == TimePickerScrollDirection::UP ? -1 : 1);
1418     }
1419 
1420     CreateAnimation(scrollDelta_, 0.0);
1421 }
1422 
CalculateHotZone(int32_t index,int32_t midSize,float middleChildHeight,float otherChildHeight)1423 DimensionRect TimePickerColumnPattern::CalculateHotZone(
1424     int32_t index, int32_t midSize, float middleChildHeight, float otherChildHeight)
1425 {
1426     float hotZoneHeight = 0.0f;
1427     float hotZoneOffsetY = 0.0f;
1428     if (index == midSize) {
1429         hotZoneHeight = middleChildHeight;
1430     }
1431     if (size_.Height() <= middleChildHeight) {
1432         hotZoneHeight = index == midSize ? size_.Height() : 0;
1433     } else if (size_.Height() <= (middleChildHeight + HOT_ZONE_HEIGHT_CANDIDATE * otherChildHeight)) {
1434         if ((index == midSize + 1) || (index == midSize - 1)) {
1435             hotZoneHeight = (size_.Height() - middleChildHeight) / MIDDLE_CHILD_INDEX;
1436             hotZoneOffsetY = (index == midSize - 1) ? (otherChildHeight - hotZoneHeight) : 0;
1437         }
1438     } else if (size_.Height() <= (middleChildHeight + HOT_ZONE_HEIGHT_DISAPPEAR * otherChildHeight)) {
1439         if ((index == midSize + 1) || (index == midSize - 1)) {
1440             hotZoneHeight = otherChildHeight;
1441         } else if ((index == midSize + HOT_ZONE_HEIGHT_CANDIDATE) || (index == midSize - HOT_ZONE_HEIGHT_CANDIDATE)) {
1442             hotZoneHeight = (size_.Height() - middleChildHeight - HOT_ZONE_HEIGHT_CANDIDATE * otherChildHeight) /
1443                             MIDDLE_CHILD_INDEX;
1444             hotZoneOffsetY = (index == midSize - HOT_ZONE_HEIGHT_CANDIDATE) ? (otherChildHeight - hotZoneHeight) : 0;
1445         }
1446     } else {
1447         if ((index == midSize + 1) || (index == midSize - 1)) {
1448             hotZoneHeight = otherChildHeight;
1449         } else if ((index == midSize + HOT_ZONE_HEIGHT_CANDIDATE) || (index == midSize - HOT_ZONE_HEIGHT_CANDIDATE)) {
1450             hotZoneHeight = otherChildHeight;
1451         }
1452     }
1453     OffsetF hotZoneOffset;
1454     SizeF hotZoneSize;
1455     hotZoneOffset.SetX(0.0f);
1456     hotZoneOffset.SetY(hotZoneOffsetY);
1457     hotZoneSize.SetWidth(size_.Width());
1458     hotZoneSize.SetHeight(hotZoneHeight);
1459     DimensionRect hotZoneRegion;
1460     hotZoneRegion.SetSize(DimensionSize(Dimension(hotZoneSize.Width()), Dimension(hotZoneSize.Height())));
1461     hotZoneRegion.SetOffset(DimensionOffset(Dimension(hotZoneOffset.GetX()), Dimension(hotZoneOffset.GetY())));
1462     return hotZoneRegion;
1463 }
1464 
AddHotZoneRectToText()1465 void TimePickerColumnPattern::AddHotZoneRectToText()
1466 {
1467     auto host = GetHost();
1468     CHECK_NULL_VOID(host);
1469     auto childSize = static_cast<int32_t>(host->GetChildren().size());
1470     auto midSize = childSize / MIDDLE_CHILD_INDEX;
1471     auto middleChildHeight = optionProperties_[midSize].height;
1472     auto otherChildHeight = optionProperties_[midSize - 1].height;
1473     for (int32_t i = 0; i < childSize; i++) {
1474         RefPtr<FrameNode> childNode = DynamicCast<FrameNode>(host->GetChildAtIndex(i));
1475         CHECK_NULL_VOID(childNode);
1476         DimensionRect hotZoneRegion = CalculateHotZone(i, midSize, middleChildHeight, otherChildHeight);
1477         childNode->AddHotZoneRect(hotZoneRegion);
1478     }
1479 }
1480 } // namespace OHOS::Ace::NG
1481