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