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/text_picker/textpicker_column_pattern.h"
17 
18 #include <cstdint>
19 #include <cstdlib>
20 
21 #include "base/geometry/dimension.h"
22 #include "base/geometry/ng/size_t.h"
23 #include "base/utils/measure_util.h"
24 #include "base/utils/utils.h"
25 #include "bridge/common/utils/utils.h"
26 #include "core/common/container.h"
27 #include "core/common/font_manager.h"
28 #include "core/components/picker/picker_theme.h"
29 #include "core/components_ng/base/frame_scene_status.h"
30 #include "core/components_ng/pattern/image/image_layout_property.h"
31 #include "core/components_ng/pattern/image/image_pattern.h"
32 #include "core/components_ng/pattern/text/text_pattern.h"
33 #include "core/components_ng/pattern/text_picker/textpicker_event_hub.h"
34 #include "core/components_ng/pattern/text_picker/textpicker_layout_property.h"
35 #include "core/components_ng/pattern/text_picker/textpicker_pattern.h"
36 #include "core/components_ng/pattern/text_picker/toss_animation_controller.h"
37 #include "core/pipeline_ng/ui_task_scheduler.h"
38 
39 namespace OHOS::Ace::NG {
40 namespace {
41 const Dimension FONT_SIZE = Dimension(2.0);
42 const Dimension FOCUS_SIZE = Dimension(1.0);
43 const float MOVE_DISTANCE = 5.0f;
44 const double MOVE_THRESHOLD = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ? 2.0 : 1.0;
45 constexpr float FONTWEIGHT = 0.5f;
46 constexpr float FONT_SIZE_PERCENT = 1.0f;
47 constexpr int32_t HOVER_ANIMATION_DURATION = 40;
48 constexpr int32_t CLICK_ANIMATION_DURATION = 300;
49 constexpr size_t MIXTURE_CHILD_COUNT = 2;
50 const int32_t OPTION_COUNT_PHONE_LANDSCAPE = 3;
51 const Dimension ICON_SIZE = 24.0_vp;
52 const Dimension ICON_TEXT_SPACE = 8.0_vp;
53 const std::vector<std::string> FONT_FAMILY_DEFAULT = { "sans-serif" };
54 const std::string MEASURE_STRING = "TEST";
55 const int32_t HALF_NUMBER = 2;
56 const int32_t BUFFER_NODE_NUMBER = 2;
57 const double CURVE_MOVE_THRESHOLD = 0.5;
58 constexpr char PICKER_DRAG_SCENE[] = "picker_drag_scene";
59 } // namespace
60 
OnAttachToFrameNode()61 void TextPickerColumnPattern::OnAttachToFrameNode()
62 {
63     auto host = GetHost();
64     CHECK_NULL_VOID(host);
65 
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     overscroller_.SetColumn(AceType::WeakClaim(this));
77     jumpInterval_ = pickerTheme->GetJumpInterval().ConvertToPx();
78     CreateAnimation();
79     InitPanEvent(gestureHub);
80     host->GetRenderContext()->SetClipToFrame(true);
81 }
82 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)83 bool TextPickerColumnPattern::OnDirtyLayoutWrapperSwap(
84     const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
85 {
86     bool isChange =
87         config.frameSizeChange || config.frameOffsetChange || config.contentSizeChange || config.contentOffsetChange;
88 
89     CHECK_NULL_RETURN(isChange, false);
90     CHECK_NULL_RETURN(dirty, false);
91     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
92     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
93     auto layoutAlgorithm = DynamicCast<TextPickerLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
94     CHECK_NULL_RETURN(layoutAlgorithm, false);
95     halfDisplayCounts_ = layoutAlgorithm->GetHalfDisplayCounts();
96     return true;
97 }
98 
OnModifyDone()99 void TextPickerColumnPattern::OnModifyDone()
100 {
101     auto pipeline = PipelineContext::GetCurrentContext();
102     CHECK_NULL_VOID(pipeline);
103     auto theme = pipeline->GetTheme<PickerTheme>();
104     pressColor_ = theme->GetPressColor();
105     hoverColor_ = theme->GetHoverColor();
106     auto showCount = GetShowOptionCount();
107     InitMouseAndPressEvent();
108     SetAccessibilityAction();
109     if (optionProperties_.size() <= 0) {
110         auto midIndex = showCount / HALF_NUMBER;
111         auto host = GetHost();
112         CHECK_NULL_VOID(host);
113         dividerSpacing_ = pipeline->NormalizeToPx(theme->GetDividerSpacing());
114         gradientHeight_ = static_cast<float>(pipeline->NormalizeToPx(theme->GetGradientHeight()));
115         MeasureContext measureContext;
116         measureContext.textContent = MEASURE_STRING;
117         uint32_t childIndex = 0;
118         TextPickerOptionProperty prop;
119         while (childIndex < showCount) {
120             if (childIndex == midIndex) {
121                 auto selectedOptionSize = theme->GetOptionStyle(true, false).GetFontSize();
122                 measureContext.fontSize = selectedOptionSize;
123                 measureContext.fontFamily = FONT_FAMILY_DEFAULT[0];
124             } else if ((childIndex == (midIndex + 1)) || (childIndex == (midIndex - 1))) {
125                 auto focusOptionSize = theme->GetOptionStyle(false, false).GetFontSize() + FONT_SIZE;
126                 measureContext.fontSize = focusOptionSize;
127                 measureContext.fontFamily = FONT_FAMILY_DEFAULT[0];
128             } else {
129                 auto normalOptionSize = theme->GetOptionStyle(false, false).GetFontSize();
130                 measureContext.fontSize = normalOptionSize;
131                 measureContext.fontFamily = FONT_FAMILY_DEFAULT[0];
132             }
133             if (childIndex == midIndex) {
134                 prop.height = dividerSpacing_;
135             } else {
136                 prop.height = gradientHeight_;
137             }
138             Size size = MeasureUtil::MeasureTextSize(measureContext);
139             prop.fontheight = size.Height();
140             optionProperties_.emplace_back(prop);
141             childIndex++;
142         }
143         SetOptionShiftDistance();
144     }
145 }
146 
OnMiddleButtonTouchDown()147 void TextPickerColumnPattern::OnMiddleButtonTouchDown()
148 {
149     PlayPressAnimation(pressColor_);
150 }
151 
OnMiddleButtonTouchMove()152 void TextPickerColumnPattern::OnMiddleButtonTouchMove()
153 {
154     PlayPressAnimation(Color::TRANSPARENT);
155 }
156 
OnMiddleButtonTouchUp()157 void TextPickerColumnPattern::OnMiddleButtonTouchUp()
158 {
159     PlayPressAnimation(Color::TRANSPARENT);
160 }
161 
GetMiddleButtonIndex()162 int32_t TextPickerColumnPattern::GetMiddleButtonIndex()
163 {
164     return GetShowOptionCount() / 2;
165 }
166 
CreateItemTouchEventListener()167 RefPtr<TouchEventImpl> TextPickerColumnPattern::CreateItemTouchEventListener()
168 {
169     auto toss = GetToss();
170     CHECK_NULL_RETURN(toss, nullptr);
171     auto touchCallback = [weak = WeakClaim(this), toss](const TouchEventInfo& info) {
172         auto pattern = weak.Upgrade();
173         CHECK_NULL_VOID(pattern);
174         auto isToss = pattern->GetTossStatus();
175         if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
176             if (isToss) {
177                 pattern->touchBreak_ = true;
178                 pattern->animationBreak_ = true;
179                 pattern->clickBreak_ = true;
180                 auto TossEndPosition = toss->GetTossEndPosition();
181                 pattern->SetYLast(TossEndPosition);
182                 toss->StopTossAnimation();
183             } else {
184                 pattern->animationBreak_ = false;
185                 pattern->clickBreak_ = false;
186             }
187         }
188         if (info.GetTouches().front().GetTouchType() == TouchType::UP) {
189             pattern->touchBreak_ = false;
190             if (pattern->animationBreak_) {
191                 pattern->PlayResetAnimation();
192                 pattern->yOffset_ = 0.0;
193             }
194         }
195     };
196     auto listener = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
197     return listener;
198 }
199 
CreateItemClickEventListener(RefPtr<EventParam> param)200 RefPtr<ClickEvent> TextPickerColumnPattern::CreateItemClickEventListener(RefPtr<EventParam> param)
201 {
202     auto clickEventHandler = [param, weak = WeakClaim(this)](const GestureEvent& /* info */) {
203         auto pattern = weak.Upgrade();
204         pattern->OnAroundButtonClick(param);
205     };
206 
207     auto listener = AceType::MakeRefPtr<NG::ClickEvent>(clickEventHandler);
208     return listener;
209 }
210 
CreateMouseHoverEventListener(RefPtr<EventParam> param)211 RefPtr<InputEvent> TextPickerColumnPattern::CreateMouseHoverEventListener(RefPtr<EventParam> param)
212 {
213     auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
214         auto pattern = weak.Upgrade();
215         if (pattern) {
216             pattern->HandleMouseEvent(isHover);
217         }
218     };
219     auto hoverEventListener = MakeRefPtr<InputEvent>(std::move(mouseTask));
220     return hoverEventListener;
221 }
222 
ParseTouchListener()223 void TextPickerColumnPattern::ParseTouchListener()
224 {
225     auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
226         auto pattern = weak.Upgrade();
227         CHECK_NULL_VOID(pattern);
228         if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
229             pattern->SetLocalDownDistance(info.GetTouches().front().GetLocalLocation().GetDistance());
230             pattern->OnMiddleButtonTouchDown();
231             return;
232         }
233         if (info.GetTouches().front().GetTouchType() == TouchType::UP) {
234             pattern->OnMiddleButtonTouchUp();
235             pattern->SetLocalDownDistance(0.0f);
236             return;
237         }
238         if (info.GetTouches().front().GetTouchType() == TouchType::MOVE) {
239             if (std::abs(info.GetTouches().front().GetLocalLocation().GetDistance() - pattern->GetLocalDownDistance()) >
240                 MOVE_DISTANCE) {
241                 pattern->OnMiddleButtonTouchUp();
242             }
243         }
244     };
245     touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
246 }
247 
ParseMouseEvent()248 void TextPickerColumnPattern::ParseMouseEvent()
249 {
250     auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
251         auto pattern = weak.Upgrade();
252         CHECK_NULL_VOID(pattern);
253         pattern->HandleMouseEvent(isHover);
254     };
255     mouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
256 }
257 
InitMouseAndPressEvent()258 void TextPickerColumnPattern::InitMouseAndPressEvent()
259 {
260     if (mouseEvent_ || touchListener_) {
261         return;
262     }
263     auto host = GetHost();
264     CHECK_NULL_VOID(host);
265     auto columnEventHub = host->GetEventHub<EventHub>();
266     CHECK_NULL_VOID(columnEventHub);
267     RefPtr<TouchEventImpl> touchListener = CreateItemTouchEventListener();
268     CHECK_NULL_VOID(touchListener);
269     auto columnGesture = columnEventHub->GetOrCreateGestureEventHub();
270     CHECK_NULL_VOID(columnGesture);
271     columnGesture->AddTouchEvent(touchListener);
272     auto childSize = static_cast<int32_t>(host->GetChildren().size());
273     RefPtr<FrameNode> middleChild = nullptr;
274     auto midSize = childSize / 2;
275     middleChild = DynamicCast<FrameNode>(host->GetChildAtIndex(midSize));
276     CHECK_NULL_VOID(middleChild);
277     auto eventHub = middleChild->GetEventHub<EventHub>();
278     CHECK_NULL_VOID(eventHub);
279     auto inputHub = eventHub->GetOrCreateInputEventHub();
280     ParseMouseEvent();
281     inputHub->AddOnHoverEvent(mouseEvent_);
282     auto gesture = middleChild->GetOrCreateGestureEventHub();
283     CHECK_NULL_VOID(gesture);
284     ParseTouchListener();
285     gesture->AddTouchEvent(touchListener_);
286     int32_t i = 0;
287     for (const auto& child : host->GetChildren()) {
288         RefPtr<FrameNode> childNode = DynamicCast<FrameNode>(child);
289         CHECK_NULL_VOID(childNode);
290         RefPtr<EventParam> param = MakeRefPtr<EventParam>();
291         param->instance = childNode;
292         param->itemIndex = i;
293         param->itemTotalCounts = childSize;
294         auto eventHub = childNode->GetEventHub<EventHub>();
295         CHECK_NULL_VOID(eventHub);
296         if (i != midSize) {
297             RefPtr<ClickEvent> clickListener = CreateItemClickEventListener(param);
298             CHECK_NULL_VOID(clickListener);
299             auto gesture = eventHub->GetOrCreateGestureEventHub();
300             CHECK_NULL_VOID(gesture);
301             gesture->AddClickEvent(clickListener);
302         }
303         i++;
304     }
305 }
306 
HandleMouseEvent(bool isHover)307 void TextPickerColumnPattern::HandleMouseEvent(bool isHover)
308 {
309     if (isHover) {
310         PlayPressAnimation(hoverColor_);
311     } else {
312         PlayPressAnimation(Color::TRANSPARENT);
313     }
314     isHover_ = isHover;
315 }
316 
SetButtonBackgroundColor(const Color & pressColor)317 void TextPickerColumnPattern::SetButtonBackgroundColor(const Color& pressColor)
318 {
319     auto host = GetHost();
320     CHECK_NULL_VOID(host);
321     auto blend = host->GetParent();
322     CHECK_NULL_VOID(blend);
323     auto stack = blend->GetParent();
324     CHECK_NULL_VOID(stack);
325     auto buttonNode = DynamicCast<FrameNode>(stack->GetFirstChild());
326     auto renderContext = buttonNode->GetRenderContext();
327     renderContext->UpdateBackgroundColor(pressColor);
328     buttonNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
329 }
330 
PlayPressAnimation(const Color & pressColor)331 void TextPickerColumnPattern::PlayPressAnimation(const Color& pressColor)
332 {
333     AnimationOption option = AnimationOption();
334     option.SetDuration(HOVER_ANIMATION_DURATION);
335     option.SetFillMode(FillMode::FORWARDS);
336     AnimationUtils::Animate(option, [weak = AceType::WeakClaim(this), pressColor]() {
337         auto picker = weak.Upgrade();
338         if (picker) {
339             picker->SetButtonBackgroundColor(pressColor);
340         }
341     });
342 }
343 
GetShowOptionCount() const344 uint32_t TextPickerColumnPattern::GetShowOptionCount() const
345 {
346     auto context = PipelineContext::GetCurrentContext();
347     CHECK_NULL_RETURN(context, 0);
348     auto pickerTheme = context->GetTheme<PickerTheme>();
349     CHECK_NULL_RETURN(pickerTheme, 0);
350     auto showCount = pickerTheme->GetShowOptionCount() + BUFFER_NODE_NUMBER;
351     return showCount;
352 }
353 
ResetOptionPropertyHeight()354 void TextPickerColumnPattern::ResetOptionPropertyHeight()
355 {
356     if (needOptionPropertyHeightReset_) {
357         auto host = GetHost();
358         CHECK_NULL_VOID(host);
359         auto blendNode = DynamicCast<FrameNode>(host->GetParent());
360         CHECK_NULL_VOID(blendNode);
361         auto stackNode = DynamicCast<FrameNode>(blendNode->GetParent());
362         CHECK_NULL_VOID(stackNode);
363         auto parentNode = DynamicCast<FrameNode>(stackNode->GetParent());
364         CHECK_NULL_VOID(parentNode);
365         auto textPickerLayoutProperty = parentNode->GetLayoutProperty<TextPickerLayoutProperty>();
366         CHECK_NULL_VOID(textPickerLayoutProperty);
367         bool isDefaultPickerItemHeight_ = false;
368         if (textPickerLayoutProperty->HasDefaultPickerItemHeight()) {
369             auto defaultPickerItemHeightValue = textPickerLayoutProperty->GetDefaultPickerItemHeightValue();
370             isDefaultPickerItemHeight_ = LessOrEqual(defaultPickerItemHeightValue.Value(), 0.0) ? false : true;
371         }
372         if (isDefaultPickerItemHeight_) {
373             auto pickerItemHeight = 0.0;
374             auto pattern = parentNode->GetPattern<TextPickerPattern>();
375             CHECK_NULL_VOID(pattern);
376             pickerItemHeight = pattern->GetResizeFlag() ? pattern->GetResizePickerItemHeight()
377                                                         : pattern->GetDefaultPickerItemHeight();
378             int32_t itemCounts = static_cast<int32_t>(GetShowOptionCount());
379             for (int32_t i = 0; i < itemCounts; i++) {
380                 TextPickerOptionProperty& prop = optionProperties_[i];
381                 prop.height = pickerItemHeight;
382             }
383             SetOptionShiftDistance();
384         }
385         needOptionPropertyHeightReset_ = false;
386     }
387 }
388 
InitTextFontFamily()389 void TextPickerColumnPattern::InitTextFontFamily()
390 {
391     auto host = GetHost();
392     CHECK_NULL_VOID(host);
393     auto blendNode = DynamicCast<FrameNode>(host->GetParent());
394     CHECK_NULL_VOID(blendNode);
395     auto stackNode = DynamicCast<FrameNode>(blendNode->GetParent());
396     CHECK_NULL_VOID(stackNode);
397     auto parentNode = DynamicCast<FrameNode>(stackNode->GetParent());
398     CHECK_NULL_VOID(parentNode);
399     auto pipeline = parentNode->GetContext();
400     CHECK_NULL_VOID(pipeline);
401     auto pattern = parentNode->GetPattern<TextPickerPattern>();
402     CHECK_NULL_VOID(pattern);
403     auto textPickerLayoutProperty = parentNode->GetLayoutProperty<TextPickerLayoutProperty>();
404     CHECK_NULL_VOID(textPickerLayoutProperty);
405     hasUserDefinedDisappearFontFamily_ = pattern->GetHasUserDefinedDisappearFontFamily();
406     hasUserDefinedNormalFontFamily_ = pattern->GetHasUserDefinedNormalFontFamily();
407     hasUserDefinedSelectedFontFamily_ = pattern->GetHasUserDefinedSelectedFontFamily();
408     auto fontManager = pipeline->GetFontManager();
409     CHECK_NULL_VOID(fontManager);
410     if (!(fontManager->GetAppCustomFont().empty())) {
411         hasAppCustomFont_ = true;
412     }
413     auto appCustomFontFamily = Framework::ConvertStrToFontFamilies(fontManager->GetAppCustomFont());
414     if (hasAppCustomFont_ && !hasUserDefinedDisappearFontFamily_) {
415         textPickerLayoutProperty->UpdateDisappearFontFamily(appCustomFontFamily);
416     }
417     if (hasAppCustomFont_ && !hasUserDefinedNormalFontFamily_) {
418         textPickerLayoutProperty->UpdateFontFamily(appCustomFontFamily);
419     }
420     if (hasAppCustomFont_ && !hasUserDefinedSelectedFontFamily_) {
421         textPickerLayoutProperty->UpdateSelectedFontFamily(appCustomFontFamily);
422     }
423 }
424 
FlushCurrentOptions(bool isDown,bool isUpateTextContentOnly,bool isDirectlyClear,bool isUpdateAnimationProperties)425 void TextPickerColumnPattern::FlushCurrentOptions(
426     bool isDown, bool isUpateTextContentOnly, bool isDirectlyClear, bool isUpdateAnimationProperties)
427 {
428     ResetOptionPropertyHeight();
429 
430     auto host = GetHost();
431     CHECK_NULL_VOID(host);
432     auto blendNode = DynamicCast<FrameNode>(host->GetParent());
433     CHECK_NULL_VOID(blendNode);
434     auto stackNode = DynamicCast<FrameNode>(blendNode->GetParent());
435     CHECK_NULL_VOID(stackNode);
436     auto parentNode = DynamicCast<FrameNode>(stackNode->GetParent());
437     CHECK_NULL_VOID(parentNode);
438     auto textPickerLayoutProperty = parentNode->GetLayoutProperty<TextPickerLayoutProperty>();
439     CHECK_NULL_VOID(textPickerLayoutProperty);
440 
441     InitTextFontFamily();
442 
443     if (!isUpateTextContentOnly) {
444         animationProperties_.clear();
445     }
446     if (columnkind_ == TEXT) {
447         FlushCurrentTextOptions(textPickerLayoutProperty, isUpateTextContentOnly, isDirectlyClear);
448     } else if (columnkind_ == ICON) {
449         FlushCurrentImageOptions();
450     } else if (columnkind_ == MIXTURE) {
451         FlushCurrentMixtureOptions(textPickerLayoutProperty, isUpateTextContentOnly);
452     }
453     if (isUpateTextContentOnly && isUpdateAnimationProperties) {
454         FlushAnimationTextProperties(isDown);
455     }
456 }
457 
ClearCurrentTextOptions(const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty,bool isUpateTextContentOnly,bool isDirectlyClear)458 void TextPickerColumnPattern::ClearCurrentTextOptions(
459     const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty, bool isUpateTextContentOnly, bool isDirectlyClear)
460 {
461     if (isDirectlyClear) {
462         auto host = GetHost();
463         CHECK_NULL_VOID(host);
464         auto child = host->GetChildren();
465         for (auto iter = child.begin(); iter != child.end(); iter++) {
466             auto textNode = DynamicCast<FrameNode>(*iter);
467             CHECK_NULL_VOID(textNode);
468             auto textPattern = textNode->GetPattern<TextPattern>();
469             CHECK_NULL_VOID(textPattern);
470             auto textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
471             CHECK_NULL_VOID(textLayoutProperty);
472             textLayoutProperty->UpdateContent("");
473             textNode->GetRenderContext()->SetClipToFrame(true);
474             textNode->MarkModifyDone();
475             textNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
476         }
477         selectedIndex_ = 0;
478     }
479 }
480 
FlushCurrentTextOptions(const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty,bool isUpateTextContentOnly,bool isDirectlyClear)481 void TextPickerColumnPattern::FlushCurrentTextOptions(
482     const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty, bool isUpateTextContentOnly, bool isDirectlyClear)
483 {
484     ClearCurrentTextOptions(textPickerLayoutProperty, isUpateTextContentOnly, isDirectlyClear);
485     uint32_t totalOptionCount = GetOptionCount();
486     if (totalOptionCount == 0) {
487         return;
488     }
489     uint32_t currentIndex = GetCurrentIndex();
490     currentIndex = currentIndex % totalOptionCount;
491     uint32_t showCount = GetShowOptionCount();
492     auto middleIndex = showCount / 2; // the center option is selected.
493     auto host = GetHost();
494     CHECK_NULL_VOID(host);
495     auto child = host->GetChildren();
496     auto iter = child.begin();
497     if (child.size() != showCount) {
498         return;
499     }
500     for (uint32_t index = 0; index < showCount; index++) {
501         uint32_t optionIndex = (totalOptionCount + currentIndex + index - middleIndex) % totalOptionCount;
502         RangeContent optionValue = options_[optionIndex];
503         int32_t diffIndex = static_cast<int32_t>(index) - static_cast<int32_t>(middleIndex);
504         int32_t virtualIndex = static_cast<int32_t>(currentIndex) + diffIndex;
505         bool virtualIndexValidate = virtualIndex >= 0 && virtualIndex < static_cast<int32_t>(totalOptionCount);
506         auto textNode = DynamicCast<FrameNode>(*iter);
507         CHECK_NULL_VOID(textNode);
508         auto textPattern = textNode->GetPattern<TextPattern>();
509         CHECK_NULL_VOID(textPattern);
510         auto textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
511         CHECK_NULL_VOID(textLayoutProperty);
512         if (!isUpateTextContentOnly) {
513             UpdatePickerTextProperties(textLayoutProperty, textPickerLayoutProperty, index, middleIndex, showCount);
514         }
515         if (NotLoopOptions() && !virtualIndexValidate) {
516             textLayoutProperty->UpdateContent("");
517         } else {
518             textLayoutProperty->UpdateContent(optionValue.text_);
519             textLayoutProperty->UpdateTextAlign(TextAlign::CENTER);
520         }
521         UpdateTextAccessibilityProperty(textNode, virtualIndex, iter, virtualIndexValidate);
522         textNode->GetRenderContext()->SetClipToFrame(true);
523         textNode->MarkModifyDone();
524         textNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
525         iter++;
526     }
527     selectedIndex_ = currentIndex;
528 }
529 
UpdateTextAccessibilityProperty(RefPtr<FrameNode> & textNode,int32_t virtualIndex,std::list<RefPtr<UINode>>::iterator & iter,bool virtualIndexValidate)530 void TextPickerColumnPattern::UpdateTextAccessibilityProperty(RefPtr<FrameNode>& textNode, int32_t virtualIndex,
531     std::list<RefPtr<UINode>>::iterator& iter, bool virtualIndexValidate)
532 {
533     auto accessibilityProperty = textNode->GetAccessibilityProperty<AccessibilityProperty>();
534     CHECK_NULL_VOID(accessibilityProperty);
535     if (!NotLoopOptions() || virtualIndexValidate) {
536         accessibilityProperty->SetAccessibilityLevel(AccessibilityProperty::Level::AUTO);
537         return;
538     }
539     accessibilityProperty->SetAccessibilityLevel(AccessibilityProperty::Level::NO_STR);
540     auto isFocus = accessibilityProperty->GetAccessibilityFocusState();
541     if (virtualIndex == -1 && isFocus) {
542         auto nextTextNode = DynamicCast<FrameNode>(*(++iter));
543         CHECK_NULL_VOID(nextTextNode);
544         nextTextNode->OnAccessibilityEvent(AccessibilityEventType::REQUEST_FOCUS);
545         --iter;
546     } else if (virtualIndex == static_cast<int32_t>(GetOptionCount()) && isFocus) {
547         auto preTextNode = DynamicCast<FrameNode>(*(--iter));
548         CHECK_NULL_VOID(preTextNode);
549         preTextNode->OnAccessibilityEvent(AccessibilityEventType::REQUEST_FOCUS);
550         ++iter;
551     }
552 }
553 
FlushCurrentImageOptions()554 void TextPickerColumnPattern::FlushCurrentImageOptions()
555 {
556     uint32_t totalOptionCount = GetOptionCount();
557     if (totalOptionCount == 0) {
558         return;
559     }
560     uint32_t currentIndex = GetCurrentIndex();
561     currentIndex = currentIndex % totalOptionCount;
562     uint32_t showCount = GetShowOptionCount();
563     auto middleIndex = showCount / 2; // the center option is selected.
564     auto host = GetHost();
565     CHECK_NULL_VOID(host);
566     auto child = host->GetChildren();
567     auto iter = child.begin();
568     if (child.size() != showCount) {
569         return;
570     }
571     for (uint32_t index = 0; index < showCount; index++) {
572         uint32_t optionIndex = (totalOptionCount + currentIndex + index - middleIndex) % totalOptionCount;
573         RangeContent optionValue = options_[optionIndex];
574         int32_t diffIndex = static_cast<int32_t>(index) - static_cast<int32_t>(middleIndex);
575         int32_t virtualIndex = static_cast<int32_t>(currentIndex) + diffIndex;
576         bool virtualIndexValidate = virtualIndex >= 0 && virtualIndex < static_cast<int32_t>(totalOptionCount);
577         auto rangeNode = DynamicCast<FrameNode>(*iter);
578         CHECK_NULL_VOID(rangeNode);
579         auto iconNode = DynamicCast<FrameNode>(rangeNode->GetFirstChild());
580         CHECK_NULL_VOID(iconNode);
581         auto iconPattern = iconNode->GetPattern<ImagePattern>();
582         CHECK_NULL_VOID(iconPattern);
583         auto iconLayoutProperty = iconPattern->GetLayoutProperty<ImageLayoutProperty>();
584         CHECK_NULL_VOID(iconLayoutProperty);
585         CalcSize idealSize = { CalcSize(CalcLength(ICON_SIZE), CalcLength(ICON_SIZE)) };
586         MeasureProperty layoutConstraint;
587         layoutConstraint.selfIdealSize = idealSize;
588         iconLayoutProperty->UpdateCalcLayoutProperty(layoutConstraint);
589         if (NotLoopOptions() && !virtualIndexValidate) {
590             iconLayoutProperty->UpdateVisibility(VisibleType::INVISIBLE);
591         } else {
592             iconLayoutProperty->UpdateVisibility(VisibleType::VISIBLE);
593             iconLayoutProperty->UpdateImageSourceInfo(ImageSourceInfo(optionValue.icon_));
594         }
595         UpdateTextAccessibilityProperty(rangeNode, virtualIndex, iter, virtualIndexValidate);
596         iconNode->MarkModifyDone();
597         iconNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
598 
599         rangeNode->GetRenderContext()->SetClipToFrame(true);
600         rangeNode->MarkModifyDone();
601         rangeNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
602         iter++;
603     }
604     selectedIndex_ = currentIndex;
605 }
606 
FlushCurrentMixtureOptions(const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty,bool isUpateTextContentOnly)607 void TextPickerColumnPattern::FlushCurrentMixtureOptions(
608     const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty, bool isUpateTextContentOnly)
609 {
610     uint32_t totalOptionCount = GetOptionCount();
611     if (totalOptionCount == 0) {
612         return;
613     }
614     uint32_t currentIndex = GetCurrentIndex();
615     currentIndex = currentIndex % totalOptionCount;
616     uint32_t showCount = GetShowOptionCount();
617     auto middleIndex = showCount / 2; // the center option is selected.
618     auto host = GetHost();
619     CHECK_NULL_VOID(host);
620     auto child = host->GetChildren();
621     auto iter = child.begin();
622     if (child.size() != showCount) {
623         return;
624     }
625     for (uint32_t index = 0; index < showCount; index++) {
626         uint32_t optionIndex = (totalOptionCount + currentIndex + index - middleIndex) % totalOptionCount;
627         RangeContent optionValue = options_[optionIndex];
628         int32_t diffIndex = static_cast<int32_t>(index) - static_cast<int32_t>(middleIndex);
629         int32_t virtualIndex = static_cast<int32_t>(currentIndex) + diffIndex;
630         bool virtualIndexValidate = virtualIndex >= 0 && virtualIndex < static_cast<int32_t>(totalOptionCount);
631         auto linearLayoutNode = DynamicCast<FrameNode>(*iter);
632         CHECK_NULL_VOID(linearLayoutNode);
633         auto children = linearLayoutNode->GetChildren();
634         if (children.size() != MIXTURE_CHILD_COUNT) {
635             continue;
636         }
637         auto iconNode = DynamicCast<FrameNode>(linearLayoutNode->GetFirstChild());
638         auto iconPattern = iconNode->GetPattern<ImagePattern>();
639         CHECK_NULL_VOID(iconPattern);
640         auto iconLayoutProperty = iconPattern->GetLayoutProperty<ImageLayoutProperty>();
641         CHECK_NULL_VOID(iconLayoutProperty);
642         auto iconLayoutDirection = iconLayoutProperty->GetNonAutoLayoutDirection();
643         CalcSize idealSize = { CalcSize(CalcLength(ICON_SIZE), CalcLength(ICON_SIZE)) };
644         MeasureProperty layoutConstraint;
645         layoutConstraint.selfIdealSize = idealSize;
646         iconLayoutProperty->UpdateCalcLayoutProperty(layoutConstraint);
647         MarginProperty margin;
648         margin.right = CalcLength(ICON_TEXT_SPACE);
649         bool isRtl = AceApplicationInfo::GetInstance().IsRightToLeft();
650         if (isRtl || iconLayoutDirection == TextDirection::RTL) {
651             margin.left = CalcLength(ICON_TEXT_SPACE);
652         }
653         iconLayoutProperty->UpdateMargin(margin);
654 
655         auto textNode = DynamicCast<FrameNode>(linearLayoutNode->GetLastChild());
656         auto textPattern = textNode->GetPattern<TextPattern>();
657         CHECK_NULL_VOID(textPattern);
658         auto textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
659         CHECK_NULL_VOID(textLayoutProperty);
660         if (!isUpateTextContentOnly) {
661             UpdatePickerTextProperties(textLayoutProperty, textPickerLayoutProperty, index, middleIndex, showCount);
662         }
663         if (NotLoopOptions() && !virtualIndexValidate) {
664             iconLayoutProperty->UpdateVisibility(VisibleType::INVISIBLE);
665             textLayoutProperty->UpdateContent("");
666         } else {
667             textLayoutProperty->UpdateContent(optionValue.text_);
668             iconLayoutProperty->UpdateVisibility(VisibleType::VISIBLE);
669             iconLayoutProperty->UpdateImageSourceInfo(ImageSourceInfo(optionValue.icon_));
670         }
671         UpdateTextAccessibilityProperty(linearLayoutNode, virtualIndex, iter, virtualIndexValidate);
672         iconNode->MarkModifyDone();
673         iconNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
674         textNode->MarkModifyDone();
675         textNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
676 
677         linearLayoutNode->GetRenderContext()->SetClipToFrame(true);
678         linearLayoutNode->MarkModifyDone();
679         linearLayoutNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
680         iter++;
681     }
682     selectedIndex_ = currentIndex;
683 }
684 
FlushAnimationTextProperties(bool isDown)685 void TextPickerColumnPattern::FlushAnimationTextProperties(bool isDown)
686 {
687     const size_t size = animationProperties_.size();
688     if (size == 0) {
689         return;
690     }
691     if (isDown) {
692         for (size_t i = 0; i < size; i++) {
693             if (i > 0) {
694                 animationProperties_[i - 1].upFontSize = animationProperties_[i].upFontSize;
695                 animationProperties_[i - 1].fontSize = animationProperties_[i].fontSize;
696                 animationProperties_[i - 1].downFontSize = animationProperties_[i].downFontSize;
697                 animationProperties_[i - 1].upColor = animationProperties_[i].upColor;
698                 animationProperties_[i - 1].currentColor = animationProperties_[i].currentColor;
699                 animationProperties_[i - 1].downColor = animationProperties_[i].downColor;
700             }
701             if (i + 1 == size) {
702                 animationProperties_[i].upFontSize = animationProperties_[i].fontSize;
703                 animationProperties_[i].fontSize = animationProperties_[i].fontSize * 0.5;
704                 animationProperties_[i].downFontSize = Dimension();
705                 animationProperties_[i].upColor = animationProperties_[i].currentColor;
706                 auto colorEvaluator = AceType::MakeRefPtr<LinearEvaluator<Color>>();
707                 animationProperties_[i].currentColor =
708                     colorEvaluator->Evaluate(Color(), animationProperties_[i].currentColor, 0.5);
709                 animationProperties_[i].downColor = Color();
710             }
711         }
712     } else {
713         for (size_t i = size - 1;; i--) {
714             if (i == 0) {
715                 animationProperties_[i].upFontSize = Dimension();
716                 animationProperties_[i].downFontSize = animationProperties_[i].fontSize;
717                 animationProperties_[i].fontSize = animationProperties_[i].fontSize * 0.5;
718                 animationProperties_[i].upColor = Color();
719                 animationProperties_[i].downColor = animationProperties_[i].currentColor;
720                 auto colorEvaluator = AceType::MakeRefPtr<LinearEvaluator<Color>>();
721                 animationProperties_[i].currentColor =
722                     colorEvaluator->Evaluate(Color(), animationProperties_[i].currentColor, 0.5);
723                 break;
724             } else {
725                 animationProperties_[i].upFontSize = animationProperties_[i - 1].upFontSize;
726                 animationProperties_[i].fontSize = animationProperties_[i - 1].fontSize;
727                 animationProperties_[i].downFontSize = animationProperties_[i - 1].downFontSize;
728                 animationProperties_[i].upColor = animationProperties_[i - 1].upColor;
729                 animationProperties_[i].currentColor = animationProperties_[i - 1].currentColor;
730                 animationProperties_[i].downColor = animationProperties_[i - 1].downColor;
731             }
732         }
733     }
734 }
735 
UpdateDisappearTextProperties(const RefPtr<PickerTheme> & pickerTheme,const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty)736 void TextPickerColumnPattern::UpdateDisappearTextProperties(const RefPtr<PickerTheme>& pickerTheme,
737     const RefPtr<TextLayoutProperty>& textLayoutProperty,
738     const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty)
739 {
740     auto normalOptionSize = pickerTheme->GetOptionStyle(false, false).GetFontSize();
741     textLayoutProperty->UpdateTextColor(textPickerLayoutProperty->GetDisappearColor().value_or(
742         pickerTheme->GetOptionStyle(false, false).GetTextColor()));
743     if (textPickerLayoutProperty->HasDisappearFontSize()) {
744         textLayoutProperty->UpdateFontSize(textPickerLayoutProperty->GetDisappearFontSize().value());
745     } else {
746         textLayoutProperty->UpdateAdaptMaxFontSize(normalOptionSize);
747         textLayoutProperty->UpdateAdaptMinFontSize(pickerTheme->GetOptionStyle(false, false).GetAdaptMinFontSize());
748     }
749     textLayoutProperty->UpdateFontWeight(textPickerLayoutProperty->GetDisappearWeight().value_or(
750         pickerTheme->GetOptionStyle(false, false).GetFontWeight()));
751     auto fontFamilyVector = textPickerLayoutProperty->GetDisappearFontFamily().value_or(
752         pickerTheme->GetOptionStyle(false, false).GetFontFamilies());
753     textLayoutProperty->UpdateFontFamily(fontFamilyVector.empty() ? FONT_FAMILY_DEFAULT : fontFamilyVector);
754     textLayoutProperty->UpdateItalicFontStyle(textPickerLayoutProperty->GetDisappearFontStyle().value_or(
755         pickerTheme->GetOptionStyle(false, false).GetFontStyle()));
756 }
757 
UpdateCandidateTextProperties(const RefPtr<PickerTheme> & pickerTheme,const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty)758 void TextPickerColumnPattern::UpdateCandidateTextProperties(const RefPtr<PickerTheme>& pickerTheme,
759     const RefPtr<TextLayoutProperty>& textLayoutProperty,
760     const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty)
761 {
762     auto focusOptionSize = pickerTheme->GetOptionStyle(false, false).GetFontSize() + FONT_SIZE;
763     textLayoutProperty->UpdateTextColor(
764         textPickerLayoutProperty->GetColor().value_or(pickerTheme->GetOptionStyle(false, false).GetTextColor()));
765     if (textPickerLayoutProperty->HasFontSize()) {
766         textLayoutProperty->UpdateFontSize(textPickerLayoutProperty->GetFontSize().value());
767     } else {
768         textLayoutProperty->UpdateAdaptMaxFontSize(focusOptionSize);
769         textLayoutProperty->UpdateAdaptMinFontSize(
770             pickerTheme->GetOptionStyle(true, false).GetAdaptMinFontSize() - FOCUS_SIZE);
771     }
772     textLayoutProperty->UpdateFontWeight(
773         textPickerLayoutProperty->GetWeight().value_or(pickerTheme->GetOptionStyle(false, false).GetFontWeight()));
774     auto fontFamilyVector = textPickerLayoutProperty->GetFontFamily().value_or(
775         pickerTheme->GetOptionStyle(false, false).GetFontFamilies());
776     textLayoutProperty->UpdateFontFamily(fontFamilyVector.empty() ? FONT_FAMILY_DEFAULT : fontFamilyVector);
777     textLayoutProperty->UpdateItalicFontStyle(
778         textPickerLayoutProperty->GetFontStyle().value_or(pickerTheme->GetOptionStyle(false, false).GetFontStyle()));
779 }
780 
UpdateSelectedTextProperties(const RefPtr<PickerTheme> & pickerTheme,const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty)781 void TextPickerColumnPattern::UpdateSelectedTextProperties(const RefPtr<PickerTheme>& pickerTheme,
782     const RefPtr<TextLayoutProperty>& textLayoutProperty,
783     const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty)
784 {
785     auto selectedOptionSize = pickerTheme->GetOptionStyle(true, false).GetFontSize();
786     textLayoutProperty->UpdateTextColor(
787         textPickerLayoutProperty->GetSelectedColor().value_or(pickerTheme->GetOptionStyle(true, false).GetTextColor()));
788     if (textPickerLayoutProperty->HasSelectedFontSize()) {
789         textLayoutProperty->UpdateFontSize(textPickerLayoutProperty->GetSelectedFontSize().value());
790     } else {
791         textLayoutProperty->UpdateAdaptMaxFontSize(selectedOptionSize);
792         textLayoutProperty->UpdateAdaptMinFontSize(pickerTheme->GetOptionStyle(true, false).GetAdaptMinFontSize());
793     }
794     textLayoutProperty->UpdateFontWeight(textPickerLayoutProperty->GetSelectedWeight().value_or(
795         pickerTheme->GetOptionStyle(true, false).GetFontWeight()));
796     auto fontFamilyVector = textPickerLayoutProperty->GetSelectedFontFamily().value_or(
797         pickerTheme->GetOptionStyle(true, false).GetFontFamilies());
798     textLayoutProperty->UpdateFontFamily(fontFamilyVector.empty() ? FONT_FAMILY_DEFAULT : fontFamilyVector);
799     textLayoutProperty->UpdateItalicFontStyle(textPickerLayoutProperty->GetSelectedFontStyle().value_or(
800         pickerTheme->GetOptionStyle(true, false).GetFontStyle()));
801 }
802 
AddAnimationTextProperties(uint32_t currentIndex,const RefPtr<TextLayoutProperty> & textLayoutProperty)803 void TextPickerColumnPattern::AddAnimationTextProperties(
804     uint32_t currentIndex, const RefPtr<TextLayoutProperty>& textLayoutProperty)
805 {
806     TextProperties properties{};
807     if (textLayoutProperty->HasFontSize()) {
808         MeasureContext measureContext;
809         measureContext.textContent = MEASURE_STRING;
810         measureContext.fontSize = textLayoutProperty->GetFontSize().value();
811         if (textLayoutProperty->HasFontFamily()) {
812             auto fontFamilyVector = textLayoutProperty->GetFontFamily().value();
813             if (fontFamilyVector.empty()) {
814                 measureContext.fontFamily = FONT_FAMILY_DEFAULT[0];
815             } else {
816                 measureContext.fontFamily = fontFamilyVector[0];
817             }
818         } else {
819             measureContext.fontFamily = FONT_FAMILY_DEFAULT[0];
820         }
821         auto size = MeasureUtil::MeasureTextSize(measureContext);
822         if (!optionProperties_.empty()) {
823             optionProperties_[currentIndex].fontheight = size.Height();
824             if (optionProperties_[currentIndex].fontheight > optionProperties_[currentIndex].height) {
825                 optionProperties_[currentIndex].fontheight = optionProperties_[currentIndex].height;
826             }
827         }
828         SetOptionShiftDistance();
829         properties.fontSize = Dimension(textLayoutProperty->GetFontSize().value().ConvertToPx());
830     }
831     if (textLayoutProperty->HasTextColor()) {
832         properties.currentColor = textLayoutProperty->GetTextColor().value();
833     }
834     if (textLayoutProperty->HasFontWeight()) {
835         properties.fontWeight = textLayoutProperty->GetFontWeight().value();
836     }
837     if (currentIndex > 0) {
838         properties.upFontSize = animationProperties_[currentIndex - 1].fontSize;
839         animationProperties_[currentIndex - 1].downFontSize = properties.fontSize;
840 
841         properties.upColor = animationProperties_[currentIndex - 1].currentColor;
842         animationProperties_[currentIndex - 1].downColor = properties.currentColor;
843 
844         properties.upFontWeight = animationProperties_[currentIndex - 1].fontWeight;
845         animationProperties_[currentIndex - 1].downFontWeight = properties.fontWeight;
846     }
847     animationProperties_.emplace_back(properties);
848 }
849 
UpdatePickerTextProperties(const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty,uint32_t currentIndex,uint32_t middleIndex,uint32_t showCount)850 void TextPickerColumnPattern::UpdatePickerTextProperties(const RefPtr<TextLayoutProperty>& textLayoutProperty,
851     const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty, uint32_t currentIndex, uint32_t middleIndex,
852     uint32_t showCount)
853 {
854     auto host = GetHost();
855     CHECK_NULL_VOID(host);
856     auto context = host->GetContext();
857     CHECK_NULL_VOID(context);
858     auto pickerTheme = context->GetTheme<PickerTheme>();
859     CHECK_NULL_VOID(pickerTheme);
860     if (currentIndex == middleIndex) {
861         UpdateSelectedTextProperties(pickerTheme, textLayoutProperty, textPickerLayoutProperty);
862         textLayoutProperty->UpdateAlignment(Alignment::CENTER);
863     } else if ((currentIndex == middleIndex + 1) || (currentIndex == middleIndex - 1)) {
864         UpdateCandidateTextProperties(pickerTheme, textLayoutProperty, textPickerLayoutProperty);
865     } else {
866         UpdateDisappearTextProperties(pickerTheme, textLayoutProperty, textPickerLayoutProperty);
867     }
868     if (currentIndex < middleIndex) {
869         textLayoutProperty->UpdateAlignment(Alignment::TOP_CENTER);
870     } else if (currentIndex > middleIndex) {
871         textLayoutProperty->UpdateAlignment(Alignment::BOTTOM_CENTER);
872     }
873     textLayoutProperty->UpdateMaxLines(1);
874     AddAnimationTextProperties(currentIndex, textLayoutProperty);
875 }
876 
TextPropertiesLinearAnimation(const RefPtr<TextLayoutProperty> & textLayoutProperty,uint32_t idx,uint32_t showCount,bool isDown,double scaleSize)877 void TextPickerColumnPattern::TextPropertiesLinearAnimation(const RefPtr<TextLayoutProperty>& textLayoutProperty,
878     uint32_t idx, uint32_t showCount, bool isDown, double scaleSize)
879 {
880     uint32_t deltaIdx = static_cast<uint32_t>(GetOverScrollDeltaIndex());
881     auto index = idx;
882     if (GreatNotEqual(scrollDelta_, 0.0f)) {
883         index = index + deltaIdx;
884     } else {
885         if (index < deltaIdx) {
886             return;
887         }
888         index = index - deltaIdx;
889     }
890 
891     auto percent = distancePercent_ - deltaIdx;
892     auto scale = scaleSize - deltaIdx;
893 
894     if (index >= animationProperties_.size()) {
895         return;
896     }
897     Dimension startFontSize = animationProperties_[index].fontSize;
898     Color startColor = animationProperties_[index].currentColor;
899     if ((!index && isDown) || ((index == (showCount - 1)) && !isDown)) {
900         textLayoutProperty->UpdateFontSize(startFontSize);
901         textLayoutProperty->UpdateTextColor(startColor);
902         return;
903     }
904     Dimension endFontSize;
905     Color endColor;
906     if (GreatNotEqual(scrollDelta_, 0.0)) {
907         endFontSize = animationProperties_[index].downFontSize;
908         endColor = animationProperties_[index].downColor;
909         if (GreatOrEqual(scale, FONTWEIGHT)) {
910             textLayoutProperty->UpdateFontWeight(animationProperties_[index].downFontWeight);
911         }
912     } else {
913         endFontSize = animationProperties_[index].upFontSize;
914         endColor = animationProperties_[index].upColor;
915         if (GreatOrEqual(scale, FONTWEIGHT)) {
916             textLayoutProperty->UpdateFontWeight(animationProperties_[index].upFontWeight);
917         }
918     }
919     Dimension updateSize = LinearFontSize(startFontSize, endFontSize, percent);
920     textLayoutProperty->UpdateFontSize(updateSize);
921     auto colorEvaluator = AceType::MakeRefPtr<LinearEvaluator<Color>>();
922     Color updateColor = colorEvaluator->Evaluate(startColor, endColor, std::abs(percent));
923     textLayoutProperty->UpdateTextColor(updateColor);
924     if (scale < FONTWEIGHT) {
925         textLayoutProperty->UpdateFontWeight(animationProperties_[index].fontWeight);
926     }
927 }
928 
GetOverScrollDeltaIndex() const929 int32_t TextPickerColumnPattern::GetOverScrollDeltaIndex() const
930 {
931     auto deltaIdx = 0;
932     if (NotLoopOptions() && overscroller_.IsOverScroll()) {
933         auto midIndex = GetShowOptionCount() / HALF_NUMBER;
934         auto shiftDistance = optionProperties_[midIndex].nextDistance;
935         for (auto idx = midIndex; idx < GetShowOptionCount(); idx++) {
936             if (shiftDistance > std::abs(scrollDelta_)) {
937                 break;
938             }
939             shiftDistance += optionProperties_[idx].nextDistance;
940             deltaIdx++;
941         }
942     }
943     return deltaIdx;
944 }
945 
UpdateTextPropertiesLinear(bool isDown,double scale)946 void TextPickerColumnPattern::UpdateTextPropertiesLinear(bool isDown, double scale)
947 {
948     if (columnkind_ == ICON) {
949         return;
950     }
951     auto host = GetHost();
952     CHECK_NULL_VOID(host);
953     uint32_t showCount = GetShowOptionCount();
954     auto child = host->GetChildren();
955     auto iter = child.begin();
956     if (child.size() != showCount) {
957         return;
958     }
959     for (uint32_t index = 0; index < showCount; index++) {
960         auto rangeNode = DynamicCast<FrameNode>(*iter);
961         CHECK_NULL_VOID(rangeNode);
962         RefPtr<TextLayoutProperty> textLayoutProperty;
963         if (columnkind_ == TEXT) {
964             auto textPattern = rangeNode->GetPattern<TextPattern>();
965             CHECK_NULL_VOID(textPattern);
966             textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
967             CHECK_NULL_VOID(textLayoutProperty);
968             TextPropertiesLinearAnimation(textLayoutProperty, index, showCount, isDown, scale);
969         } else if (columnkind_ == MIXTURE) {
970             auto children = rangeNode->GetChildren();
971             if (children.size() != MIXTURE_CHILD_COUNT) {
972                 continue;
973             }
974             auto textNode = DynamicCast<FrameNode>(rangeNode->GetLastChild());
975             auto textPattern = textNode->GetPattern<TextPattern>();
976             textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
977             CHECK_NULL_VOID(textLayoutProperty);
978             TextPropertiesLinearAnimation(textLayoutProperty, index, showCount, isDown, scale);
979             textNode->MarkModifyDone();
980             textNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
981         }
982         rangeNode->MarkModifyDone();
983         rangeNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
984         iter++;
985     }
986 }
987 
LinearFontSize(const Dimension & startFontSize,const Dimension & endFontSize,double percent)988 Dimension TextPickerColumnPattern::LinearFontSize(
989     const Dimension& startFontSize, const Dimension& endFontSize, double percent)
990 {
991     if (percent > FONT_SIZE_PERCENT) {
992         return startFontSize + (endFontSize - startFontSize);
993     } else {
994         return startFontSize + (endFontSize - startFontSize) * std::abs(percent);
995     }
996 }
997 
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)998 void TextPickerColumnPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
999 {
1000     CHECK_NULL_VOID(!panEvent_);
1001     auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& event) {
1002         auto pattern = weak.Upgrade();
1003         CHECK_NULL_VOID(pattern);
1004         if (event.GetInputEventType() == InputEventType::AXIS && event.GetSourceTool() == SourceTool::MOUSE) {
1005             return;
1006         }
1007         pattern->HandleDragStart(event);
1008     };
1009     auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& event) {
1010         auto pattern = weak.Upgrade();
1011         CHECK_NULL_VOID(pattern);
1012         pattern->SetMainVelocity(event.GetMainVelocity());
1013         pattern->HandleDragMove(event);
1014     };
1015     auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1016         auto pattern = weak.Upgrade();
1017         CHECK_NULL_VOID(pattern);
1018         if (info.GetInputEventType() == InputEventType::AXIS && info.GetSourceTool() == SourceTool::MOUSE) {
1019             return;
1020         }
1021         pattern->SetMainVelocity(info.GetMainVelocity());
1022         pattern->HandleDragEnd();
1023     };
1024     auto actionCancelTask = [weak = WeakClaim(this)]() {
1025         auto pattern = weak.Upgrade();
1026         CHECK_NULL_VOID(pattern);
1027         pattern->HandleDragEnd();
1028     };
1029     PanDirection panDirection;
1030     panDirection.type = PanDirection::VERTICAL;
1031     panEvent_ = MakeRefPtr<PanEvent>(
1032         std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
1033     gestureHub->AddPanEvent(panEvent_, panDirection, DEFAULT_PAN_FINGER, DEFAULT_PAN_DISTANCE);
1034 }
1035 
GetParentLayout() const1036 RefPtr<TextPickerLayoutProperty> TextPickerColumnPattern::GetParentLayout() const
1037 {
1038     auto host = GetHost();
1039     CHECK_NULL_RETURN(host, nullptr);
1040     auto blendNode = DynamicCast<FrameNode>(host->GetParent());
1041     CHECK_NULL_RETURN(blendNode, nullptr);
1042     auto stackNode = DynamicCast<FrameNode>(blendNode->GetParent());
1043     CHECK_NULL_RETURN(stackNode, nullptr);
1044     auto parentNode = DynamicCast<FrameNode>(stackNode->GetParent());
1045     CHECK_NULL_RETURN(parentNode, nullptr);
1046 
1047     auto property = parentNode->GetLayoutProperty<TextPickerLayoutProperty>();
1048     return property;
1049 }
1050 
HandleDragStart(const GestureEvent & event)1051 void TextPickerColumnPattern::HandleDragStart(const GestureEvent& event)
1052 {
1053     CHECK_NULL_VOID(GetToss());
1054     auto toss = GetToss();
1055     auto offsetY = event.GetGlobalPoint().GetY();
1056     toss->SetStart(offsetY);
1057     yLast_ = offsetY;
1058     overscroller_.SetStart(offsetY);
1059     pressed_ = true;
1060     auto frameNode = GetHost();
1061     CHECK_NULL_VOID(frameNode);
1062     frameNode->AddFRCSceneInfo(PICKER_DRAG_SCENE, event.GetMainVelocity(), SceneStatus::START);
1063     // AccessibilityEventType::SCROLL_START
1064 
1065     if (animation_) {
1066         AnimationUtils::StopAnimation(animation_);
1067     }
1068 
1069     if (NotLoopOptions() && reboundAnimation_) {
1070         AnimationUtils::StopAnimation(reboundAnimation_);
1071         isReboundInProgress_ = false;
1072         overscroller_.ResetVelocity();
1073         overscroller_.SetOverScroll(scrollDelta_);
1074     }
1075 }
1076 
HandleDragMove(const GestureEvent & event)1077 void TextPickerColumnPattern::HandleDragMove(const GestureEvent& event)
1078 {
1079     if (event.GetFingerList().size() > 1) {
1080         return;
1081     }
1082     if (event.GetInputEventType() == InputEventType::AXIS && event.GetSourceTool() == SourceTool::MOUSE) {
1083         SetScrollDirection(LessNotEqual(event.GetDelta().GetY(), 0.0));
1084         if (InnerHandleScroll(isDownScroll_, true)) {
1085             HandleScrollStopEventCallback(true);
1086         }
1087         return;
1088     }
1089     animationBreak_ = false;
1090     CHECK_NULL_VOID(pressed_);
1091     CHECK_NULL_VOID(GetHost());
1092     CHECK_NULL_VOID(GetToss());
1093     auto toss = GetToss();
1094     auto offsetY =
1095         event.GetGlobalPoint().GetY() + (event.GetInputEventType() == InputEventType::AXIS ? event.GetOffsetY() : 0.0);
1096     if (NearEqual(offsetY, yLast_, MOVE_THRESHOLD)) { // if changing less than MOVE_THRESHOLD, no need to handle
1097         return;
1098     }
1099     toss->SetEnd(offsetY);
1100     SetScrollDirection(GreatNotEqual(GetMainVelocity(), 0.0));
1101     UpdateColumnChildPosition(offsetY);
1102     auto frameNode = GetHost();
1103     CHECK_NULL_VOID(frameNode);
1104     frameNode->AddFRCSceneInfo(PICKER_DRAG_SCENE, event.GetMainVelocity(), SceneStatus::RUNNING);
1105 }
1106 
HandleDragEnd()1107 void TextPickerColumnPattern::HandleDragEnd()
1108 {
1109     pressed_ = false;
1110     CHECK_NULL_VOID(GetToss());
1111     auto toss = GetToss();
1112     auto frameNode = GetHost();
1113     CHECK_NULL_VOID(frameNode);
1114     if (NotLoopOptions()) {
1115         frameNode->AddFRCSceneInfo(PICKER_DRAG_SCENE, mainVelocity_, SceneStatus::END);
1116         if (overscroller_.IsOverScroll()) { // Start rebound animation. Higher priority than fling
1117             CreateReboundAnimation(overscroller_.GetOverScroll(), 0.0);
1118             HandleScrollStopEventCallback(true);
1119             return;
1120         }
1121     }
1122     if (toss->Play()) { // Start fling animation
1123         frameNode->AddFRCSceneInfo(PICKER_DRAG_SCENE, mainVelocity_, SceneStatus::END);
1124         // AccessibilityEventType::SCROLL_END
1125         return;
1126     }
1127     yOffset_ = 0.0;
1128     yLast_ = 0.0;
1129     if (!animationCreated_) {
1130         ScrollOption(0.0);
1131         return;
1132     }
1133     int32_t middleIndex = static_cast<int32_t>(GetShowOptionCount()) / HALF_NUMBER;
1134     auto shiftDistance = isDownScroll_ ? optionProperties_[middleIndex].nextDistance
1135                                        : optionProperties_[middleIndex].prevDistance;
1136     auto shiftThreshold = shiftDistance / HALF_NUMBER;
1137     if (std::abs(scrollDelta_) >= std::abs(shiftThreshold)) {
1138         InnerHandleScroll(!isDownScroll_, true, false);
1139         scrollDelta_ = scrollDelta_ - std::abs(shiftDistance) * (isDownScroll_ ? 1 : -1);
1140         if (NearZero(scrollDelta_)) {
1141             HandleScrollStopEventCallback(true);
1142         }
1143     }
1144     SetScrollDirection(GreatNotEqual(scrollDelta_, 0.0));
1145     CreateAnimation(scrollDelta_, 0.0);
1146     frameNode->AddFRCSceneInfo(PICKER_DRAG_SCENE, mainVelocity_, SceneStatus::END);
1147     if (!NearZero(scrollDelta_)) {
1148         HandleScrollStopEventCallback(true);
1149     }
1150     // AccessibilityEventType::SCROLL_END
1151 }
1152 
CreateAnimation()1153 void TextPickerColumnPattern::CreateAnimation()
1154 {
1155     CHECK_NULL_VOID(!animationCreated_);
1156     auto host = GetHost();
1157     CHECK_NULL_VOID(host);
1158     auto renderContext = host->GetRenderContext();
1159     CHECK_NULL_VOID(renderContext);
1160     auto propertyCallback = [weak = AceType::WeakClaim(this)](float value) {
1161         auto column = weak.Upgrade();
1162         CHECK_NULL_VOID(column);
1163         column->ScrollOption(value);
1164     };
1165     scrollProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1166     renderContext->AttachNodeAnimatableProperty(scrollProperty_);
1167 
1168     auto aroundClickCallback = [weak = AceType::WeakClaim(this)](float value) {
1169         auto column = weak.Upgrade();
1170         CHECK_NULL_VOID(column);
1171         column->UpdateColumnChildPosition(value);
1172     };
1173     aroundClickProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(aroundClickCallback));
1174     renderContext->AttachNodeAnimatableProperty(aroundClickProperty_);
1175     animationCreated_ = true;
1176 }
1177 
CreateAnimation(double from,double to)1178 void TextPickerColumnPattern::CreateAnimation(double from, double to)
1179 {
1180     AnimationOption option;
1181     option.SetCurve(Curves::FAST_OUT_SLOW_IN);
1182     option.SetDuration(CLICK_ANIMATION_DURATION);
1183     scrollProperty_->Set(from);
1184     AnimationUtils::Animate(option, [weak = AceType::WeakClaim(this), to]() {
1185         auto column = weak.Upgrade();
1186         CHECK_NULL_VOID(column);
1187         column->scrollProperty_->Set(to);
1188     });
1189 }
1190 
CreateReboundAnimation(double from,double to)1191 void TextPickerColumnPattern::CreateReboundAnimation(double from, double to)
1192 {
1193     isReboundInProgress_ = true;
1194     AnimationOption option;
1195     option.SetCurve(overscroller_.GetReboundCurve());
1196     option.SetDuration(CLICK_ANIMATION_DURATION);
1197     scrollProperty_->Set(from);
1198     reboundAnimation_ = AnimationUtils::StartAnimation(option, [weak = AceType::WeakClaim(this), to]() {
1199         auto column = weak.Upgrade();
1200         CHECK_NULL_VOID(column);
1201         column->scrollProperty_->Set(to);
1202     }, [weak = AceType::WeakClaim(this)]() { // On Finish
1203         auto column = weak.Upgrade();
1204         CHECK_NULL_VOID(column);
1205         if (column->isReboundInProgress_) {
1206             column->isReboundInProgress_ = false;
1207             column->overscroller_.Reset();
1208             column->yLast_ = 0.0;
1209             column->yOffset_ = 0.0;
1210         }
1211     });
1212 }
1213 
ScrollOption(double delta)1214 void TextPickerColumnPattern::ScrollOption(double delta)
1215 {
1216     scrollDelta_ = delta;
1217     auto midIndex = GetShowOptionCount() / HALF_NUMBER;
1218     auto shiftDistance = isDownScroll_ ? optionProperties_[midIndex].nextDistance
1219                                        : optionProperties_[midIndex].prevDistance;
1220     distancePercent_ = delta / shiftDistance;
1221     auto textLinearPercent = 0.0;
1222     textLinearPercent = (std::abs(delta)) / (optionProperties_[midIndex].height);
1223     UpdateTextPropertiesLinear(isDownScroll_, textLinearPercent);
1224     CalcAlgorithmOffset(distancePercent_);
1225     auto host = GetHost();
1226     CHECK_NULL_VOID(host);
1227     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
1228 }
1229 
ResetAlgorithmOffset()1230 void TextPickerColumnPattern::ResetAlgorithmOffset()
1231 {
1232     algorithmOffset_.clear();
1233 
1234     uint32_t counts = GetShowOptionCount();
1235     for (uint32_t i = 0; i < counts; i++) {
1236         algorithmOffset_.emplace_back(0.0);
1237     }
1238 }
1239 
UpdateScrollDelta(double delta)1240 void TextPickerColumnPattern::UpdateScrollDelta(double delta)
1241 {
1242     SetCurrentOffset(delta);
1243     auto host = GetHost();
1244     CHECK_NULL_VOID(host);
1245     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1246 }
1247 
CalcAlgorithmOffset(double distancePercent)1248 void TextPickerColumnPattern::CalcAlgorithmOffset(double distancePercent)
1249 {
1250     algorithmOffset_.clear();
1251 
1252     uint32_t counts = GetShowOptionCount();
1253 
1254     for (uint32_t i = 0; i < counts; i++) {
1255         double distance = isDownScroll_ ? optionProperties_[i].nextDistance
1256                                         : optionProperties_[i].prevDistance;
1257         auto val  = std::trunc(distance * distancePercent);
1258         algorithmOffset_.emplace_back(static_cast<int32_t>(val));
1259     }
1260 }
1261 
GetShiftDistance(int32_t index,ScrollDirection dir)1262 double TextPickerColumnPattern::GetShiftDistance(int32_t index, ScrollDirection dir)
1263 {
1264     if (optionProperties_.empty()) {
1265         return 0.0;
1266     }
1267     int32_t optionCounts = static_cast<int32_t>(GetShowOptionCount());
1268     if (optionCounts == 0) {
1269         return 0.0;
1270     }
1271     int32_t nextIndex = 0;
1272     auto isDown = dir == ScrollDirection::DOWN;
1273     nextIndex = isDown ? (optionCounts + index + 1) % optionCounts : (optionCounts + index - 1) % optionCounts;
1274     double distance = 0.0;
1275     switch (static_cast<OptionIndex>(index)) {
1276         case OptionIndex::COLUMN_INDEX_0: // first
1277             distance = (dir == ScrollDirection::DOWN) ? optionProperties_[index].height
1278                                                       : (0.0 - optionProperties_[index].height);
1279             break;
1280         case OptionIndex::COLUMN_INDEX_1:
1281             distance = (dir == ScrollDirection::DOWN) ? optionProperties_[index].height
1282                                                       : (0.0 - optionProperties_[nextIndex].height);
1283             break;
1284         case OptionIndex::COLUMN_INDEX_2:
1285             distance = GetUpCandidateDistance(index, nextIndex, dir);
1286             break;
1287 
1288         case OptionIndex::COLUMN_INDEX_3:
1289             distance = GetSelectedDistance(index, nextIndex, dir);
1290             break;
1291 
1292         case OptionIndex::COLUMN_INDEX_4:
1293             distance = GetDownCandidateDistance(index, nextIndex, dir);
1294             break;
1295         case OptionIndex::COLUMN_INDEX_5:
1296             distance = (dir == ScrollDirection::DOWN) ? optionProperties_[index].height
1297                                                       : (0.0 - optionProperties_[nextIndex].height);
1298             break;
1299         case OptionIndex::COLUMN_INDEX_6: // last
1300             distance = (dir == ScrollDirection::DOWN) ? optionProperties_[index].height
1301                                                       : (0.0 - optionProperties_[nextIndex].height);
1302             break;
1303         default:
1304             break;
1305     }
1306 
1307     return distance;
1308 }
1309 
GetSelectedDistance(int32_t index,int32_t nextIndex,ScrollDirection dir)1310 double TextPickerColumnPattern::GetSelectedDistance(int32_t index, int32_t nextIndex, ScrollDirection dir)
1311 {
1312     double distance = 0.0;
1313     double val = 0.0;
1314     if (columnkind_ == TEXT) {
1315         if (GreatOrEqual(optionProperties_[nextIndex].fontheight, optionProperties_[nextIndex].height)) {
1316             distance = (dir == ScrollDirection::UP) ?
1317                 - optionProperties_[nextIndex].height : optionProperties_[index].height;
1318         } else {
1319             val = optionProperties_[index].height / HALF_NUMBER + optionProperties_[nextIndex].height -
1320                   optionProperties_[nextIndex].fontheight / HALF_NUMBER;
1321             val = std::round(val);
1322             distance = (dir == ScrollDirection::DOWN) ? val : (0.0 - val);
1323         }
1324     } else {
1325         val = std::round((optionProperties_[index].height + optionProperties_[nextIndex].height) / HALF_NUMBER);
1326         distance = (dir == ScrollDirection::DOWN) ? val : (0.0 - val);
1327     }
1328     return distance;
1329 }
1330 
GetUpCandidateDistance(int32_t index,int32_t nextIndex,ScrollDirection dir)1331 double TextPickerColumnPattern::GetUpCandidateDistance(int32_t index, int32_t nextIndex, ScrollDirection dir)
1332 {
1333     double distance = 0.0;
1334     double val = 0.0;
1335     // the index of last element in optionProperties_. return -1 while the arraySize equals 0.
1336     auto maxIndex = static_cast<int32_t>(optionProperties_.size()) - 1;
1337     auto minIndex = 0;
1338     if (index > maxIndex || index < minIndex || nextIndex > maxIndex || nextIndex < minIndex) {
1339         return distance;
1340     }
1341     if (columnkind_ == TEXT) {
1342         if (dir == ScrollDirection::UP) {
1343             distance = -optionProperties_[nextIndex].height;
1344         } else {
1345             val = optionProperties_[index].height +
1346                   (optionProperties_[nextIndex].height - optionProperties_[nextIndex].fontheight) / HALF_NUMBER;
1347             distance = std::round(val);
1348         }
1349     } else {
1350         val = std::round((optionProperties_[index].height + optionProperties_[nextIndex].height) / HALF_NUMBER);
1351         distance = (dir == ScrollDirection::DOWN) ? val : (0.0 - val);
1352     }
1353     return distance;
1354 }
1355 
GetDownCandidateDistance(int32_t index,int32_t nextIndex,ScrollDirection dir)1356 double TextPickerColumnPattern::GetDownCandidateDistance(int32_t index, int32_t nextIndex, ScrollDirection dir)
1357 {
1358     double distance = 0.0;
1359     double val = 0.0;
1360     if (columnkind_ == TEXT) {
1361         if (dir == ScrollDirection::DOWN) {
1362             distance = optionProperties_[index].height;
1363         } else {
1364             val = optionProperties_[index].height +
1365                   (optionProperties_[nextIndex].height - optionProperties_[nextIndex].fontheight) / HALF_NUMBER;
1366             if (GreatNotEqual(optionProperties_[nextIndex].fontheight, optionProperties_[index].height)) {
1367                 val = val + (optionProperties_[nextIndex].fontheight - optionProperties_[index].height);
1368             }
1369             distance = - std::round(val);
1370         }
1371     } else {
1372         val = std::round((optionProperties_[index].height + optionProperties_[nextIndex].height) / HALF_NUMBER);
1373         distance = (dir == ScrollDirection::DOWN) ? val : (0.0 - val);
1374     }
1375     return distance;
1376 }
1377 
GetShiftDistanceForLandscape(int32_t index,ScrollDirection dir)1378 double TextPickerColumnPattern::GetShiftDistanceForLandscape(int32_t index, ScrollDirection dir)
1379 {
1380     int32_t optionCounts = static_cast<int32_t>(GetShowOptionCount());
1381     if (optionCounts == 0) {
1382         return 0.0;
1383     }
1384     int32_t nextIndex = 0;
1385     auto isDown = dir == ScrollDirection::DOWN;
1386     nextIndex = isDown ? (optionCounts + index + 1) % optionCounts : (optionCounts + index - 1) % optionCounts;
1387     double distance = 0.0;
1388     switch (static_cast<OptionIndex>(index)) {
1389         case OptionIndex::COLUMN_INDEX_0:
1390             distance = GetUpCandidateDistance(index, nextIndex, dir);
1391             break;
1392 
1393         case OptionIndex::COLUMN_INDEX_1:
1394             distance = GetSelectedDistance(index, nextIndex, dir);
1395             break;
1396 
1397         case OptionIndex::COLUMN_INDEX_2:
1398             distance = GetDownCandidateDistance(index, nextIndex, dir);
1399             break;
1400         default:
1401             break;
1402     }
1403 
1404     return distance;
1405 }
1406 
SetOptionShiftDistance()1407 void TextPickerColumnPattern::SetOptionShiftDistance()
1408 {
1409     CHECK_EQUAL_VOID(optionProperties_.empty(), true);
1410     int32_t itemCounts = static_cast<int32_t>(GetShowOptionCount());
1411     bool isLanscape = (itemCounts == OPTION_COUNT_PHONE_LANDSCAPE + BUFFER_NODE_NUMBER);
1412     for (int32_t i = 0; i < static_cast<int32_t>(itemCounts); i++) {
1413         TextPickerOptionProperty& prop = optionProperties_[i];
1414         if (isLanscape) {
1415             prop.prevDistance = GetShiftDistanceForLandscape(i, ScrollDirection::UP);
1416             prop.nextDistance = GetShiftDistanceForLandscape(i, ScrollDirection::DOWN);
1417         } else {
1418             prop.prevDistance = GetShiftDistance(i, ScrollDirection::UP);
1419             prop.nextDistance = GetShiftDistance(i, ScrollDirection::DOWN);
1420         }
1421     }
1422 }
1423 
UpdateToss(double offsetY)1424 void TextPickerColumnPattern::UpdateToss(double offsetY)
1425 {
1426     UpdateColumnChildPosition(offsetY);
1427 }
1428 
TossStoped()1429 void TextPickerColumnPattern::TossStoped()
1430 {
1431     yOffset_ = 0.0;
1432     yLast_ = 0.0;
1433     ScrollOption(0.0);
1434 }
1435 
TossAnimationStoped()1436 void TextPickerColumnPattern::TossAnimationStoped()
1437 {
1438     yLast_ = 0.0;
1439 }
1440 
GetSelectedObject(bool isColumnChange,int32_t status) const1441 std::string TextPickerColumnPattern::GetSelectedObject(bool isColumnChange, int32_t status) const
1442 {
1443     auto host = GetHost();
1444     CHECK_NULL_RETURN(host, "");
1445     auto value = GetOption(GetSelected());
1446     auto index = GetSelected();
1447     if (isColumnChange) {
1448         value = GetCurrentText();
1449         index = GetCurrentIndex();
1450     }
1451 
1452     auto context = host->GetContext();
1453     CHECK_NULL_RETURN(context, "");
1454 
1455     if (context->GetIsDeclarative()) {
1456         return std::string("{\"value\":") + "\"" + value + "\"" + ",\"index\":" + std::to_string(index) +
1457                ",\"status\":" + std::to_string(status) + "}";
1458     } else {
1459         return std::string("{\"newValue\":") + "\"" + value + "\"" + ",\"newSelected\":" + std::to_string(index) +
1460                ",\"status\":" + std::to_string(status) + "}";
1461     }
1462 }
1463 
ResetTotalDelta()1464 void TextPickerColumnPattern::ResetTotalDelta()
1465 {
1466     totalDragDelta_ = 0.0;
1467 }
1468 
SpringCurveTailMoveProcess(bool useRebound,double & dragDelta)1469 bool TextPickerColumnPattern::SpringCurveTailMoveProcess(bool useRebound, double& dragDelta)
1470 {
1471     if (useRebound) {
1472         return false;
1473     }
1474     auto toss = GetToss();
1475     if (toss && toss->GetTossPlaying()) {
1476         if (std::abs(dragDelta) < CURVE_MOVE_THRESHOLD) {
1477             dragDelta = dragDelta > 0 ? CURVE_MOVE_THRESHOLD : -CURVE_MOVE_THRESHOLD;
1478         }
1479         totalDragDelta_ += dragDelta;
1480         if (std::abs(totalDragDelta_) >= std::abs(toss->GetTossEndPosition())) {
1481             dragDelta -= (totalDragDelta_ - toss->GetTossEndPosition());
1482             ResetTotalDelta();
1483             return true;
1484         }
1485     }
1486     return false;
1487 }
1488 
SpringCurveTailEndProcess(bool useRebound,bool stopMove)1489 void TextPickerColumnPattern::SpringCurveTailEndProcess(bool useRebound, bool stopMove)
1490 {
1491     if (useRebound || !stopMove) {
1492         return;
1493     }
1494     auto toss = GetToss();
1495     if (toss) {
1496         toss->SetTossPlaying(false);
1497         toss->StopTossAnimation();
1498     }
1499 }
1500 
UpdateColumnChildPosition(double offsetY)1501 void TextPickerColumnPattern::UpdateColumnChildPosition(double offsetY)
1502 {
1503     double dragDelta = offsetY - yLast_;
1504     auto midIndex = GetShowOptionCount() / HALF_NUMBER;
1505     auto shiftDistance = isDownScroll_ ? optionProperties_[midIndex].nextDistance
1506                                        : optionProperties_[midIndex].prevDistance;
1507     auto useRebound = NotLoopOptions();
1508     auto stopMove = SpringCurveTailMoveProcess(useRebound, dragDelta);
1509     offsetCurSet_ = 0.0;
1510 
1511     // the abs of drag delta is less than jump interval.
1512     dragDelta = dragDelta + yOffset_;
1513     auto isOverScroll = useRebound && overscroller_.IsOverScroll();
1514     if ((std::abs(dragDelta) >= std::abs(shiftDistance)) && !isOverScroll) {
1515         int32_t shiftDistanceCount = static_cast<int>(std::abs(dragDelta) / std::abs(shiftDistance));
1516         double additionalShift = dragDelta - shiftDistanceCount * shiftDistance;
1517         if (GreatNotEqual(std::abs(additionalShift), std::abs(dragDelta))) {
1518             additionalShift = dragDelta + shiftDistanceCount * shiftDistance;
1519         }
1520         for (int32_t i = 0; i < shiftDistanceCount; i++) {
1521             ScrollOption(shiftDistance);
1522             InnerHandleScroll(dragDelta < 0, true, false);
1523         }
1524         dragDelta = additionalShift;
1525         if (NearZero(dragDelta)) {
1526             HandleScrollStopEventCallback(true);
1527         }
1528     }
1529     if (useRebound && !isReboundInProgress_) {
1530         if (overscroller_.ApplyCurrentOffset(yLast_, offsetY, dragDelta)) {
1531             dragDelta =
1532                 overscroller_.IsBackOverScroll() ? overscroller_.GetBackScroll() : overscroller_.GetOverScroll();
1533         }
1534     }
1535 
1536     // Set options position
1537     ScrollOption(dragDelta);
1538     offsetCurSet_ = dragDelta;
1539     yOffset_ = dragDelta;
1540     yLast_ = offsetY;
1541 
1542     if (useRebound && !pressed_ && isTossStatus_ && !isReboundInProgress_ && overscroller_.IsOverScroll()) {
1543         overscroller_.UpdateTossSpring(offsetY);
1544         if (overscroller_.ShouldStartRebound()) {
1545             auto toss = GetToss();
1546             CHECK_NULL_VOID(toss);
1547             toss->StopTossAnimation(); // Stop fling animation and start rebound animation implicitly
1548         }
1549     }
1550     SpringCurveTailEndProcess(useRebound, stopMove);
1551 }
1552 
CanMove(bool isDown) const1553 bool TextPickerColumnPattern::CanMove(bool isDown) const
1554 {
1555     if (!NotLoopOptions()) {
1556         return true;
1557     }
1558     auto host = GetHost();
1559     CHECK_NULL_RETURN(host, false);
1560     int totalOptionCount = static_cast<int>(GetOptionCount());
1561     int currentIndex = static_cast<int>(GetCurrentIndex());
1562     int nextVirtualIndex = isDown ? currentIndex + 1 : currentIndex - 1;
1563     return nextVirtualIndex >= 0 && nextVirtualIndex < totalOptionCount;
1564 }
1565 
NotLoopOptions() const1566 bool TextPickerColumnPattern::NotLoopOptions() const
1567 {
1568     RefPtr<TextPickerLayoutProperty> layout = GetParentLayout();
1569     CHECK_NULL_RETURN(layout, false);
1570     bool canLoop = layout->GetCanLoop().value_or(true);
1571     return !canLoop;
1572 }
1573 
InnerHandleScroll(bool isDown,bool isUpatePropertiesOnly,bool isUpdateAnimationProperties)1574 bool TextPickerColumnPattern::InnerHandleScroll(
1575     bool isDown, bool isUpatePropertiesOnly, bool isUpdateAnimationProperties)
1576 {
1577     auto host = GetHost();
1578     CHECK_NULL_RETURN(host, false);
1579     auto totalOptionCount = GetOptionCount();
1580     if (totalOptionCount == 0) {
1581         return false;
1582     }
1583 
1584     if (NotLoopOptions() && ((isDown && currentIndex_ == totalOptionCount - 1) || (!isDown && currentIndex_ == 0))) {
1585         return false;
1586     }
1587 
1588     uint32_t currentIndex = GetCurrentIndex();
1589     if (isDown) {
1590         currentIndex = (totalOptionCount + currentIndex + 1) % totalOptionCount; // index add one
1591     } else {
1592         auto totalCountAndIndex = totalOptionCount + currentIndex;
1593         currentIndex = (totalCountAndIndex ? totalCountAndIndex - 1 : 0) % totalOptionCount; // index reduce one
1594     }
1595     SetCurrentIndex(currentIndex);
1596     FlushCurrentOptions(isDown, isUpatePropertiesOnly, isUpdateAnimationProperties);
1597     HandleChangeCallback(isDown, true);
1598     HandleEventCallback(true);
1599 
1600     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
1601     host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE);
1602     return true;
1603 }
1604 
OnKeyEvent(const KeyEvent & event)1605 bool TextPickerColumnPattern::OnKeyEvent(const KeyEvent& event)
1606 {
1607     if (event.action != KeyAction::DOWN) {
1608         return false;
1609     }
1610     if (event.code == KeyCode::KEY_DPAD_UP || event.code == KeyCode::KEY_DPAD_DOWN ||
1611         event.code == KeyCode::KEY_DPAD_LEFT || event.code == KeyCode::KEY_DPAD_RIGHT) {
1612         HandleDirectionKey(event.code);
1613         return true;
1614     }
1615     return false;
1616 }
1617 
HandleDirectionKey(KeyCode code)1618 bool TextPickerColumnPattern::HandleDirectionKey(KeyCode code)
1619 {
1620     auto host = GetHost();
1621     CHECK_NULL_RETURN(host, false);
1622     auto currernIndex = GetCurrentIndex();
1623     auto totalOptionCount = GetOptionCount();
1624     if (totalOptionCount == 0) {
1625         return false;
1626     }
1627     if (code == KeyCode::KEY_DPAD_UP) {
1628         auto totalCountAndIndex = totalOptionCount + currernIndex;
1629         SetCurrentIndex((totalCountAndIndex ? totalCountAndIndex - 1 : 0) % totalOptionCount);
1630         SetScrollDirection(false);
1631         FlushCurrentOptions();
1632         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1633         return true;
1634     }
1635     if (code == KeyCode::KEY_DPAD_DOWN) {
1636         SetCurrentIndex((totalOptionCount + currernIndex + 1) % totalOptionCount);
1637         SetScrollDirection(true);
1638         FlushCurrentOptions(isDownScroll_);
1639         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1640         return true;
1641     }
1642     return false;
1643 }
SetAccessibilityAction()1644 void TextPickerColumnPattern::SetAccessibilityAction()
1645 {
1646     auto host = GetHost();
1647     CHECK_NULL_VOID(host);
1648     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1649     CHECK_NULL_VOID(accessibilityProperty);
1650     accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
1651         const auto& pattern = weakPtr.Upgrade();
1652         CHECK_NULL_VOID(pattern);
1653         CHECK_NULL_VOID(pattern->animationCreated_);
1654         if (!pattern->CanMove(true)) {
1655             return;
1656         }
1657         pattern->SetScrollDirection(true);
1658         pattern->InnerHandleScroll(true);
1659         pattern->CreateAnimation(0.0 - pattern->jumpInterval_, 0.0);
1660         pattern->HandleScrollStopEventCallback(true);
1661         // AccessibilityEventType::SCROLL_END
1662     });
1663 
1664     accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)]() {
1665         const auto& pattern = weakPtr.Upgrade();
1666         CHECK_NULL_VOID(pattern);
1667         CHECK_NULL_VOID(pattern->animationCreated_);
1668         if (!pattern->CanMove(false)) {
1669             return;
1670         }
1671         pattern->SetScrollDirection(false);
1672         pattern->InnerHandleScroll(false);
1673         pattern->CreateAnimation(pattern->jumpInterval_, 0.0);
1674         pattern->HandleScrollStopEventCallback(true);
1675         // AccessibilityEventType::SCROLL_END
1676     });
1677 }
1678 
OnAroundButtonClick(RefPtr<EventParam> param)1679 void TextPickerColumnPattern::OnAroundButtonClick(RefPtr<EventParam> param)
1680 {
1681     if (clickBreak_) {
1682         return;
1683     }
1684     int32_t middleIndex = static_cast<int32_t>(GetShowOptionCount()) / HALF_NUMBER;
1685     int32_t step = param->itemIndex - middleIndex;
1686     auto overFirst = static_cast<int32_t>(currentIndex_) + step < 0 && step < 0;
1687     auto overLast =
1688         (static_cast<int32_t>(currentIndex_) + step > static_cast<int32_t>(GetOptionCount()) - 1) && step > 0;
1689     if (NotLoopOptions() && (overscroller_.IsOverScroll() || overFirst || overLast)) {
1690         return;
1691     }
1692     if (step != 0) {
1693         if (animation_) {
1694             AnimationUtils::StopAnimation(animation_);
1695             yOffset_ = 0.0;
1696         }
1697         double distance =
1698             (step > 0 ? optionProperties_[middleIndex].prevDistance : optionProperties_[middleIndex].nextDistance) *
1699             std::abs(step);
1700         AnimationOption option;
1701         option.SetCurve(Curves::FAST_OUT_SLOW_IN);
1702         option.SetDuration(CLICK_ANIMATION_DURATION);
1703         yLast_ = 0.0;
1704         aroundClickProperty_->Set(0.0);
1705         animation_ = AnimationUtils::StartAnimation(option, [weak = AceType::WeakClaim(this), step, distance]() {
1706             auto column = weak.Upgrade();
1707             CHECK_NULL_VOID(column);
1708             auto isDown = step < 0;
1709             column->SetScrollDirection(isDown);
1710             column->aroundClickProperty_->Set(step > 0 ? 0.0 - std::abs(distance) : std::abs(distance));
1711         });
1712         auto host = GetHost();
1713         CHECK_NULL_VOID(host);
1714         auto pipeline = host->GetContext();
1715         CHECK_NULL_VOID(pipeline);
1716         pipeline->RequestFrame();
1717     }
1718 }
1719 
PlayResetAnimation()1720 void TextPickerColumnPattern::PlayResetAnimation()
1721 {
1722     int32_t middleIndex = static_cast<int32_t>(GetShowOptionCount()) / HALF_NUMBER;
1723     double shiftDistance = isDownScroll_ ? optionProperties_[middleIndex].nextDistance
1724                                          : optionProperties_[middleIndex].prevDistance;
1725 
1726     double shiftThreshold = shiftDistance / HALF_NUMBER;
1727     if (std::abs(scrollDelta_) >= std::abs(shiftThreshold)) {
1728         InnerHandleScroll(!isDownScroll_, true, false);
1729         scrollDelta_ = scrollDelta_ - std::abs(shiftDistance) * (isDownScroll_ ? 1 : -1);
1730         if (NearZero(scrollDelta_)) {
1731             HandleScrollStopEventCallback(true);
1732         }
1733     }
1734 
1735     SetScrollDirection(GreatNotEqual(scrollDelta_, 0.0));
1736     CreateAnimation(scrollDelta_, 0.0);
1737     if (!NearZero(scrollDelta_)) {
1738         HandleScrollStopEventCallback(true);
1739     }
1740 }
1741 
SetCanLoop(bool isLoop)1742 void TextPickerColumnPattern::SetCanLoop(bool isLoop)
1743 {
1744     if (isLoop_ == isLoop) {
1745         return;
1746     }
1747 
1748     isLoop_ = isLoop;
1749     if (overscroller_.IsOverScroll()) {
1750         overscroller_.Reset();
1751         isReboundInProgress_ = false;
1752         yOffset_ = 0.0;
1753         ScrollOption(0.0);
1754     }
1755 
1756     if (!isLoop && isTossStatus_) {
1757         auto toss = GetToss();
1758         CHECK_NULL_VOID(toss);
1759         overscroller_.SetLoopTossOffset(toss->GetTossOffset());
1760     }
1761 }
1762 } // namespace OHOS::Ace::NG
1763