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