• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/text_picker/textpicker_column_pattern.h"
17 
18 #include <cstdint>
19 
20 #include "base/geometry/dimension.h"
21 #include "base/geometry/ng/size_t.h"
22 #include "base/utils/measure_util.h"
23 #include "base/utils/utils.h"
24 #include "core/components/picker/picker_theme.h"
25 #include "core/components_ng/pattern/image/image_layout_property.h"
26 #include "core/components_ng/pattern/image/image_pattern.h"
27 #include "core/components_ng/pattern/text/text_pattern.h"
28 #include "core/components_ng/pattern/text_picker/textpicker_event_hub.h"
29 #include "core/components_ng/pattern/text_picker/textpicker_layout_property.h"
30 #include "core/components_ng/pattern/text_picker/textpicker_pattern.h"
31 #include "core/components_ng/pattern/text_picker/toss_animation_controller.h"
32 #include "core/pipeline_ng/ui_task_scheduler.h"
33 
34 namespace OHOS::Ace::NG {
35 namespace {
36 const Dimension FONT_SIZE = Dimension(2.0);
37 const int32_t ANIMATION_ZERO_TO_OUTER = 200; // 200ms for animation that from zero to outer.
38 const int32_t ANIMATION_OUTER_TO_ZERO = 150; // 150ms for animation that from outer to zero.
39 const Dimension FOCUS_SIZE = Dimension(1.0);
40 const float MOVE_DISTANCE = 5.0f;
41 constexpr int32_t HOVER_ANIMATION_DURATION = 40;
42 constexpr int32_t CLICK_ANIMATION_DURATION = 300;
43 constexpr size_t MIXTURE_CHILD_COUNT = 2;
44 const uint32_t OPTION_COUNT_PHONE_LANDSCAPE = 3;
45 const Dimension ICON_SIZE = 24.0_vp;
46 const Dimension ICON_TEXT_SPACE = 8.0_vp;
47 const std::string REGULAR_FONT_FAMILY = "sans-serif";
48 const std::string MEASURE_STRING = "TEST";
49 const int32_t HALF_NUMBER = 2;
50 } // namespace
51 
OnAttachToFrameNode()52 void TextPickerColumnPattern::OnAttachToFrameNode()
53 {
54     auto host = GetHost();
55     CHECK_NULL_VOID(host);
56 
57     auto context = host->GetContext();
58     CHECK_NULL_VOID(context);
59     auto pickerTheme = context->GetTheme<PickerTheme>();
60     CHECK_NULL_VOID(pickerTheme);
61     auto hub = host->GetEventHub<EventHub>();
62     CHECK_NULL_VOID(hub);
63     auto gestureHub = hub->GetOrCreateGestureEventHub();
64     CHECK_NULL_VOID(gestureHub);
65     tossAnimationController_->SetPipelineContext(context);
66     tossAnimationController_->SetColumn(AceType::WeakClaim(this));
67     jumpInterval_ = pickerTheme->GetJumpInterval().ConvertToPx();
68     CreateAnimation();
69     InitPanEvent(gestureHub);
70     host->GetRenderContext()->SetClipToFrame(true);
71 }
72 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)73 bool TextPickerColumnPattern::OnDirtyLayoutWrapperSwap(
74     const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
75 {
76     CHECK_NULL_RETURN_NOLOG(config.frameSizeChange, false);
77     CHECK_NULL_RETURN(dirty, false);
78     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
79     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
80     auto layoutAlgorithm = DynamicCast<TextPickerLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
81     CHECK_NULL_RETURN(layoutAlgorithm, false);
82     halfDisplayCounts_ = layoutAlgorithm->GetHalfDisplayCounts();
83     return true;
84 }
85 
OnModifyDone()86 void TextPickerColumnPattern::OnModifyDone()
87 {
88     auto pipeline = PipelineContext::GetCurrentContext();
89     CHECK_NULL_VOID(pipeline);
90     auto theme = pipeline->GetTheme<PickerTheme>();
91     pressColor_ = theme->GetPressColor();
92     hoverColor_ = theme->GetHoverColor();
93     auto showCount = theme->GetShowOptionCount();
94     InitMouseAndPressEvent();
95     SetAccessibilityAction();
96     if (optionProperties_.size() <= 0) {
97         auto midIndex = showCount / HALF_NUMBER;
98         auto host = GetHost();
99         CHECK_NULL_VOID(host);
100         dividerSpacing_ = pipeline->NormalizeToPx(theme->GetDividerSpacing());
101         gradientHeight_ = static_cast<float>(pipeline->NormalizeToPx(theme->GetGradientHeight()));
102         MeasureContext measureContext;
103         measureContext.textContent = MEASURE_STRING;
104         uint32_t childIndex = 0;
105         TextPickerOptionProperty prop;
106         while (childIndex < showCount) {
107             if (childIndex == midIndex) {
108                 auto selectedOptionSize = theme->GetOptionStyle(true, false).GetFontSize();
109                 measureContext.fontSize = selectedOptionSize;
110                 measureContext.fontFamily = REGULAR_FONT_FAMILY;
111             } else if (childIndex % midIndex == 1 && (childIndex != 0 || childIndex != (showCount - 1))) {
112                 auto focusOptionSize = theme->GetOptionStyle(false, false).GetFontSize() + FONT_SIZE;
113                 measureContext.fontSize = focusOptionSize;
114                 measureContext.fontFamily = REGULAR_FONT_FAMILY;
115             } else {
116                 auto normalOptionSize = theme->GetOptionStyle(false, false).GetFontSize();
117                 measureContext.fontSize = normalOptionSize;
118                 measureContext.fontFamily = REGULAR_FONT_FAMILY;
119             }
120             if (childIndex == showCount / HALF_NUMBER) {
121                 prop.height = dividerSpacing_;
122             } else {
123                 prop.height = gradientHeight_;
124             }
125             Size size = MeasureUtil::MeasureTextSize(measureContext);
126             prop.fontheight = size.Height();
127             optionProperties_.emplace_back(prop);
128             childIndex++;
129         }
130         SetOptionShiftDistance();
131     }
132 }
133 
OnMiddleButtonTouchDown(RefPtr<EventParam> param)134 void TextPickerColumnPattern::OnMiddleButtonTouchDown(RefPtr<EventParam> param)
135 {
136     PlayPressAnimation(pressColor_);
137 }
138 
OnMiddleButtonTouchMove(RefPtr<EventParam> param)139 void TextPickerColumnPattern::OnMiddleButtonTouchMove(RefPtr<EventParam> param)
140 {
141     PlayPressAnimation(Color::TRANSPARENT);
142 }
143 
OnMiddleButtonTouchUp(RefPtr<EventParam> param)144 void TextPickerColumnPattern::OnMiddleButtonTouchUp(RefPtr<EventParam> param)
145 {
146     PlayPressAnimation(Color::TRANSPARENT);
147 }
148 
GetMiddleButtonIndex()149 int32_t TextPickerColumnPattern::GetMiddleButtonIndex()
150 {
151     return GetShowOptionCount() / 2;
152 }
153 
CreateItemTouchEventListener(RefPtr<EventParam> param)154 RefPtr<TouchEventImpl> TextPickerColumnPattern::CreateItemTouchEventListener(RefPtr<EventParam> param)
155 {
156     auto itemCallback = [param, weak = WeakClaim(this)](const TouchEventInfo& info) {
157         auto pattern = weak.Upgrade();
158         CHECK_NULL_VOID(pattern);
159         if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
160             pattern->SetLocalDownDistance(info.GetTouches().front().GetLocalLocation().GetDistance());
161             pattern->OnMiddleButtonTouchDown(param);
162         }
163         if (info.GetTouches().front().GetTouchType() == TouchType::UP) {
164             pattern->OnMiddleButtonTouchUp(param);
165             pattern->SetLocalDownDistance(0.0f);
166         }
167         if (info.GetTouches().front().GetTouchType() == TouchType::MOVE) {
168             if (std::abs(info.GetTouches().front().GetLocalLocation().GetDistance() - pattern->GetLocalDownDistance()) >
169                 MOVE_DISTANCE) {
170                 pattern->OnMiddleButtonTouchMove(param);
171             }
172         }
173     };
174     auto listener = MakeRefPtr<TouchEventImpl>(std::move(itemCallback));
175     return listener;
176 }
177 
CreateItemClickEventListener(RefPtr<EventParam> param)178 RefPtr<ClickEvent> TextPickerColumnPattern::CreateItemClickEventListener(RefPtr<EventParam> param)
179 {
180     auto clickEventHandler = [param, weak = WeakClaim(this)](const GestureEvent& /* info */) {
181         auto pattern = weak.Upgrade();
182         pattern->OnAroundButtonClick(param);
183     };
184 
185     auto listener = AceType::MakeRefPtr<NG::ClickEvent>(clickEventHandler);
186     return listener;
187 }
188 
CreateMouseHoverEventListener(RefPtr<EventParam> param)189 RefPtr<InputEvent> TextPickerColumnPattern::CreateMouseHoverEventListener(RefPtr<EventParam> param)
190 {
191     auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
192         auto pattern = weak.Upgrade();
193         if (pattern) {
194             pattern->HandleMouseEvent(isHover);
195         }
196     };
197     auto hoverEventListener = MakeRefPtr<InputEvent>(std::move(mouseTask));
198     return hoverEventListener;
199 }
200 
InitMouseAndPressEvent()201 void TextPickerColumnPattern::InitMouseAndPressEvent()
202 {
203     if (touchEventInit_) {
204         return;
205     }
206 
207     auto host = GetHost();
208     CHECK_NULL_VOID(host);
209 
210     auto childSize = static_cast<int32_t>(host->GetChildren().size());
211 
212     for (int i = 0; i < childSize; i++) {
213         RefPtr<FrameNode> childNode = DynamicCast<FrameNode>(host->GetChildAtIndex(i));
214         RefPtr<EventParam> param = MakeRefPtr<EventParam>();
215         param->instance = childNode;
216         param->itemIndex = i;
217         param->itemTotalCounts = childSize;
218 
219         auto eventHub = childNode->GetEventHub<EventHub>();
220         CHECK_NULL_VOID(eventHub);
221 
222         if (i != GetMiddleButtonIndex()) {
223             RefPtr<ClickEvent> clickListener = CreateItemClickEventListener(param);
224             CHECK_NULL_VOID(clickListener);
225             auto gesture = eventHub->GetOrCreateGestureEventHub();
226             CHECK_NULL_VOID(gesture);
227             gesture->AddClickEvent(clickListener);
228         } else {
229             auto inputHub = eventHub->GetOrCreateInputEventHub();
230             CHECK_NULL_VOID(inputHub);
231             RefPtr<InputEvent> hoverEventListener = CreateMouseHoverEventListener(param);
232             CHECK_NULL_VOID(hoverEventListener);
233             inputHub->AddOnHoverEvent(hoverEventListener);
234 
235             RefPtr<TouchEventImpl> itemListener = CreateItemTouchEventListener(param);
236             CHECK_NULL_VOID(itemListener);
237             auto gesture = eventHub->GetOrCreateGestureEventHub();
238             CHECK_NULL_VOID(gesture);
239             gesture->AddTouchEvent(itemListener);
240         }
241     }
242 
243     touchEventInit_ = true;
244 }
245 
HandleMouseEvent(bool isHover)246 void TextPickerColumnPattern::HandleMouseEvent(bool isHover)
247 {
248     if (isHover) {
249         PlayPressAnimation(hoverColor_);
250     } else {
251         PlayPressAnimation(Color::TRANSPARENT);
252     }
253 }
254 
SetButtonBackgroundColor(const Color & pressColor)255 void TextPickerColumnPattern::SetButtonBackgroundColor(const Color& pressColor)
256 {
257     auto host = GetHost();
258     CHECK_NULL_VOID(host);
259     auto stack = host->GetParent();
260     CHECK_NULL_VOID(stack);
261     auto buttonNode = DynamicCast<FrameNode>(stack->GetFirstChild());
262     auto renderContext = buttonNode->GetRenderContext();
263     renderContext->UpdateBackgroundColor(pressColor);
264     buttonNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
265 }
266 
PlayPressAnimation(const Color & pressColor)267 void TextPickerColumnPattern::PlayPressAnimation(const Color& pressColor)
268 {
269     AnimationOption option = AnimationOption();
270     option.SetDuration(HOVER_ANIMATION_DURATION);
271     option.SetFillMode(FillMode::FORWARDS);
272     AnimationUtils::Animate(option, [weak = AceType::WeakClaim(this), pressColor]() {
273         auto picker = weak.Upgrade();
274         if (picker) {
275             picker->SetButtonBackgroundColor(pressColor);
276         }
277     });
278 }
279 
GetShowOptionCount() const280 uint32_t TextPickerColumnPattern::GetShowOptionCount() const
281 {
282     auto context = PipelineContext::GetCurrentContext();
283     CHECK_NULL_RETURN(context, 0);
284     auto pickerTheme = context->GetTheme<PickerTheme>();
285     CHECK_NULL_RETURN(pickerTheme, 0);
286     auto showCount = pickerTheme->GetShowOptionCount();
287     return showCount;
288 }
289 
FlushCurrentOptions(bool isDown,bool isUpateTextContentOnly,bool isDirectlyClear)290 void TextPickerColumnPattern::FlushCurrentOptions(bool isDown, bool isUpateTextContentOnly, bool isDirectlyClear)
291 {
292     auto host = GetHost();
293     CHECK_NULL_VOID(host);
294     auto stackNode = DynamicCast<FrameNode>(host->GetParent());
295     CHECK_NULL_VOID(stackNode);
296     auto parentNode = DynamicCast<FrameNode>(stackNode->GetParent());
297     CHECK_NULL_VOID(parentNode);
298     auto textPickerLayoutProperty = parentNode->GetLayoutProperty<TextPickerLayoutProperty>();
299     CHECK_NULL_VOID(textPickerLayoutProperty);
300 
301     if (!isUpateTextContentOnly) {
302         animationProperties_.clear();
303     }
304     if (columnkind_ == TEXT) {
305         FlushCurrentTextOptions(textPickerLayoutProperty, isUpateTextContentOnly, isDirectlyClear);
306     } else if (columnkind_ == ICON) {
307         FlushCurrentImageOptions();
308     } else if (columnkind_ == MIXTURE) {
309         FlushCurrentMixtureOptions(textPickerLayoutProperty, isUpateTextContentOnly);
310     }
311     if (isIndexChanged_) {
312         HandleEventCallback(true);
313     }
314 }
315 
ClearCurrentTextOptions(const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty,bool isUpateTextContentOnly,bool isDirectlyClear)316 void TextPickerColumnPattern::ClearCurrentTextOptions(
317     const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty, bool isUpateTextContentOnly, bool isDirectlyClear)
318 {
319     if (isDirectlyClear) {
320         auto host = GetHost();
321         CHECK_NULL_VOID(host);
322         auto child = host->GetChildren();
323         for (auto iter = child.begin(); iter != child.end(); iter++) {
324             auto textNode = DynamicCast<FrameNode>(*iter);
325             CHECK_NULL_VOID(textNode);
326             auto textPattern = textNode->GetPattern<TextPattern>();
327             CHECK_NULL_VOID(textPattern);
328             auto textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
329             CHECK_NULL_VOID(textLayoutProperty);
330             textLayoutProperty->UpdateContent("");
331             textNode->GetRenderContext()->SetClipToFrame(true);
332             textNode->MarkModifyDone();
333             textNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
334         }
335         selectedIndex_ = 0;
336     }
337 }
338 
FlushCurrentTextOptions(const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty,bool isUpateTextContentOnly,bool isDirectlyClear)339 void TextPickerColumnPattern::FlushCurrentTextOptions(
340     const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty, bool isUpateTextContentOnly, bool isDirectlyClear)
341 {
342     ClearCurrentTextOptions(textPickerLayoutProperty, isUpateTextContentOnly, isDirectlyClear);
343     uint32_t totalOptionCount = GetOptionCount();
344     if (totalOptionCount == 0) {
345         return;
346     }
347     uint32_t currentIndex = GetCurrentIndex();
348     currentIndex = currentIndex % totalOptionCount;
349     uint32_t showCount = GetShowOptionCount();
350     auto middleIndex = showCount / 2; // the center option is selected.
351     auto host = GetHost();
352     CHECK_NULL_VOID(host);
353     auto child = host->GetChildren();
354     auto iter = child.begin();
355     if (child.size() != showCount) {
356         return;
357     }
358     for (uint32_t index = 0; index < showCount; index++) {
359         uint32_t optionIndex = (totalOptionCount + currentIndex + index - middleIndex) % totalOptionCount;
360         RangeContent optionValue = options_[optionIndex];
361         int32_t diffIndex = static_cast<int32_t>(index) - static_cast<int32_t>(middleIndex);
362         int32_t virtualIndex = static_cast<int32_t>(currentIndex) + diffIndex;
363         bool virtualIndexValidate = virtualIndex >= 0 && virtualIndex < static_cast<int32_t>(totalOptionCount);
364         auto textNode = DynamicCast<FrameNode>(*iter);
365         CHECK_NULL_VOID(textNode);
366         auto textPattern = textNode->GetPattern<TextPattern>();
367         CHECK_NULL_VOID(textPattern);
368         auto textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
369         CHECK_NULL_VOID(textLayoutProperty);
370         if (!isUpateTextContentOnly) {
371             UpdatePickerTextProperties(textLayoutProperty, textPickerLayoutProperty, index, middleIndex, showCount);
372         }
373         if (NotLoopOptions() && !virtualIndexValidate) {
374             textLayoutProperty->UpdateContent("");
375         } else {
376             textLayoutProperty->UpdateContent(optionValue.text_);
377             textLayoutProperty->UpdateTextAlign(TextAlign::CENTER);
378         }
379         textNode->GetRenderContext()->SetClipToFrame(true);
380         textNode->MarkModifyDone();
381         textNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
382         iter++;
383     }
384     selectedIndex_ = currentIndex;
385 }
386 
FlushCurrentImageOptions()387 void TextPickerColumnPattern::FlushCurrentImageOptions()
388 {
389     uint32_t totalOptionCount = GetOptionCount();
390     if (totalOptionCount == 0) {
391         return;
392     }
393     uint32_t currentIndex = GetCurrentIndex();
394     currentIndex = currentIndex % totalOptionCount;
395     uint32_t showCount = GetShowOptionCount();
396     auto middleIndex = showCount / 2; // the center option is selected.
397     auto host = GetHost();
398     CHECK_NULL_VOID(host);
399     auto child = host->GetChildren();
400     auto iter = child.begin();
401     if (child.size() != showCount) {
402         return;
403     }
404     for (uint32_t index = 0; index < showCount; index++) {
405         uint32_t optionIndex = (totalOptionCount + currentIndex + index - middleIndex) % totalOptionCount;
406         RangeContent optionValue = options_[optionIndex];
407         int32_t diffIndex = static_cast<int32_t>(index) - static_cast<int32_t>(middleIndex);
408         int32_t virtualIndex = static_cast<int32_t>(currentIndex) + diffIndex;
409         bool virtualIndexValidate = virtualIndex >= 0 && virtualIndex < static_cast<int32_t>(totalOptionCount);
410         auto rangeNode = DynamicCast<FrameNode>(*iter);
411         CHECK_NULL_VOID(rangeNode);
412         auto iconNode = DynamicCast<FrameNode>(rangeNode->GetFirstChild());
413         CHECK_NULL_VOID(iconNode);
414         auto iconPattern = iconNode->GetPattern<ImagePattern>();
415         CHECK_NULL_VOID(iconPattern);
416         auto iconLayoutProperty = iconPattern->GetLayoutProperty<ImageLayoutProperty>();
417         CHECK_NULL_VOID(iconLayoutProperty);
418         CalcSize idealSize = { CalcSize(CalcLength(ICON_SIZE), CalcLength(ICON_SIZE)) };
419         MeasureProperty layoutConstraint;
420         layoutConstraint.selfIdealSize = idealSize;
421         iconLayoutProperty->UpdateCalcLayoutProperty(layoutConstraint);
422         if (NotLoopOptions() && !virtualIndexValidate) {
423             iconLayoutProperty->UpdateVisibility(VisibleType::INVISIBLE);
424         } else {
425             iconLayoutProperty->UpdateVisibility(VisibleType::VISIBLE);
426             iconLayoutProperty->UpdateImageSourceInfo(ImageSourceInfo(optionValue.icon_));
427         }
428         iconNode->MarkModifyDone();
429         iconNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
430 
431         rangeNode->GetRenderContext()->SetClipToFrame(true);
432         rangeNode->MarkModifyDone();
433         rangeNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
434         iter++;
435     }
436     selectedIndex_ = currentIndex;
437 }
438 
FlushCurrentMixtureOptions(const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty,bool isUpateTextContentOnly)439 void TextPickerColumnPattern::FlushCurrentMixtureOptions(
440     const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty, bool isUpateTextContentOnly)
441 {
442     uint32_t totalOptionCount = GetOptionCount();
443     if (totalOptionCount == 0) {
444         return;
445     }
446     uint32_t currentIndex = GetCurrentIndex();
447     currentIndex = currentIndex % totalOptionCount;
448     uint32_t showCount = GetShowOptionCount();
449     auto middleIndex = showCount / 2; // the center option is selected.
450     auto host = GetHost();
451     CHECK_NULL_VOID(host);
452     auto child = host->GetChildren();
453     auto iter = child.begin();
454     if (child.size() != showCount) {
455         return;
456     }
457     for (uint32_t index = 0; index < showCount; index++) {
458         uint32_t optionIndex = (totalOptionCount + currentIndex + index - middleIndex) % totalOptionCount;
459         RangeContent optionValue = options_[optionIndex];
460         int32_t diffIndex = static_cast<int32_t>(index) - static_cast<int32_t>(middleIndex);
461         int32_t virtualIndex = static_cast<int32_t>(currentIndex) + diffIndex;
462         bool virtualIndexValidate = virtualIndex >= 0 && virtualIndex < static_cast<int32_t>(totalOptionCount);
463         auto linearLayoutNode = DynamicCast<FrameNode>(*iter);
464         CHECK_NULL_VOID(linearLayoutNode);
465         auto children = linearLayoutNode->GetChildren();
466         if (children.size() != MIXTURE_CHILD_COUNT) {
467             LOGE("children number is wrong.");
468             continue;
469         }
470         auto iconNode = DynamicCast<FrameNode>(linearLayoutNode->GetFirstChild());
471         auto iconPattern = iconNode->GetPattern<ImagePattern>();
472         CHECK_NULL_VOID(iconPattern);
473         auto iconLayoutProperty = iconPattern->GetLayoutProperty<ImageLayoutProperty>();
474         CHECK_NULL_VOID(iconLayoutProperty);
475         CalcSize idealSize = { CalcSize(CalcLength(ICON_SIZE), CalcLength(ICON_SIZE)) };
476         MeasureProperty layoutConstraint;
477         layoutConstraint.selfIdealSize = idealSize;
478         iconLayoutProperty->UpdateCalcLayoutProperty(layoutConstraint);
479         MarginProperty margin;
480         margin.right = CalcLength(ICON_TEXT_SPACE);
481         iconLayoutProperty->UpdateMargin(margin);
482 
483         auto textNode = DynamicCast<FrameNode>(linearLayoutNode->GetLastChild());
484         auto textPattern = textNode->GetPattern<TextPattern>();
485         CHECK_NULL_VOID(textPattern);
486         auto textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
487         CHECK_NULL_VOID(textLayoutProperty);
488         if (!isUpateTextContentOnly) {
489             UpdatePickerTextProperties(textLayoutProperty, textPickerLayoutProperty, index, middleIndex, showCount);
490         }
491         if (NotLoopOptions() && !virtualIndexValidate) {
492             iconLayoutProperty->UpdateVisibility(VisibleType::INVISIBLE);
493             textLayoutProperty->UpdateContent("");
494         } else {
495             textLayoutProperty->UpdateContent(optionValue.text_);
496             iconLayoutProperty->UpdateVisibility(VisibleType::VISIBLE);
497             iconLayoutProperty->UpdateImageSourceInfo(ImageSourceInfo(optionValue.icon_));
498         }
499         iconNode->MarkModifyDone();
500         iconNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
501         textNode->MarkModifyDone();
502         textNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
503 
504         linearLayoutNode->GetRenderContext()->SetClipToFrame(true);
505         linearLayoutNode->MarkModifyDone();
506         linearLayoutNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
507         iter++;
508     }
509     selectedIndex_ = currentIndex;
510 }
511 
FlushAnimationTextProperties(bool isDown)512 void TextPickerColumnPattern::FlushAnimationTextProperties(bool isDown)
513 {
514     if (!animationProperties_.size()) {
515         return;
516     }
517     if (isDown) {
518         for (size_t i = 0; i < animationProperties_.size(); i++) {
519             if (i > 0) {
520                 animationProperties_[i - 1].upFontSize = animationProperties_[i].upFontSize;
521                 animationProperties_[i - 1].fontSize = animationProperties_[i].fontSize;
522                 animationProperties_[i - 1].downFontSize = animationProperties_[i].downFontSize;
523 
524                 animationProperties_[i - 1].upColor = animationProperties_[i].upColor;
525                 animationProperties_[i - 1].currentColor = animationProperties_[i].currentColor;
526                 animationProperties_[i - 1].downColor = animationProperties_[i].downColor;
527             }
528             if (i == (animationProperties_.size() - 1)) {
529                 animationProperties_[i].upFontSize = animationProperties_[i].fontSize;
530                 animationProperties_[i].fontSize = animationProperties_[i].fontSize * 0.5;
531                 animationProperties_[i].downFontSize = Dimension();
532 
533                 animationProperties_[i].upColor = animationProperties_[i].currentColor;
534                 auto colorEvaluator = AceType::MakeRefPtr<LinearEvaluator<Color>>();
535                 animationProperties_[i].currentColor =
536                     colorEvaluator->Evaluate(Color(), animationProperties_[i].currentColor, 0.5);
537                 animationProperties_[i].downColor = Color();
538             }
539         }
540     } else {
541         for (size_t i = animationProperties_.size() - 1;; i--) {
542             if (i == 0) {
543                 animationProperties_[i].upFontSize = Dimension();
544                 animationProperties_[i].downFontSize = animationProperties_[i].fontSize;
545                 animationProperties_[i].fontSize = animationProperties_[i].fontSize * 0.5;
546 
547                 animationProperties_[i].upColor = Color();
548                 animationProperties_[i].downColor = animationProperties_[i].currentColor;
549                 auto colorEvaluator = AceType::MakeRefPtr<LinearEvaluator<Color>>();
550                 animationProperties_[i].currentColor =
551                     colorEvaluator->Evaluate(Color(), animationProperties_[i].currentColor, 0.5);
552                 break;
553             } else {
554                 animationProperties_[i].upFontSize = animationProperties_[i - 1].upFontSize;
555                 animationProperties_[i].fontSize = animationProperties_[i - 1].fontSize;
556                 animationProperties_[i].downFontSize = animationProperties_[i - 1].downFontSize;
557 
558                 animationProperties_[i].upColor = animationProperties_[i - 1].upColor;
559                 animationProperties_[i].currentColor = animationProperties_[i - 1].currentColor;
560                 animationProperties_[i].downColor = animationProperties_[i - 1].downColor;
561             }
562         }
563     }
564 }
565 
UpdateDisappearTextProperties(const RefPtr<PickerTheme> & pickerTheme,const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty)566 void TextPickerColumnPattern::UpdateDisappearTextProperties(const RefPtr<PickerTheme>& pickerTheme,
567     const RefPtr<TextLayoutProperty>& textLayoutProperty,
568     const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty)
569 {
570     auto normalOptionSize = pickerTheme->GetOptionStyle(false, false).GetFontSize();
571     textLayoutProperty->UpdateTextColor(textPickerLayoutProperty->GetDisappearColor().value_or(
572         pickerTheme->GetOptionStyle(false, false).GetTextColor()));
573     if (textPickerLayoutProperty->HasDisappearFontSize()) {
574         textLayoutProperty->UpdateFontSize(textPickerLayoutProperty->GetDisappearFontSize().value());
575     } else {
576         textLayoutProperty->UpdateAdaptMaxFontSize(normalOptionSize);
577         textLayoutProperty->UpdateAdaptMinFontSize(pickerTheme->GetOptionStyle(false, false).GetAdaptMinFontSize());
578     }
579     textLayoutProperty->UpdateFontWeight(textPickerLayoutProperty->GetDisappearWeight().value_or(
580         pickerTheme->GetOptionStyle(false, false).GetFontWeight()));
581     textLayoutProperty->UpdateFontFamily(textPickerLayoutProperty->GetDisappearFontFamily().value_or(
582         pickerTheme->GetOptionStyle(false, false).GetFontFamilies()));
583     textLayoutProperty->UpdateItalicFontStyle(textPickerLayoutProperty->GetDisappearFontStyle().value_or(
584         pickerTheme->GetOptionStyle(false, false).GetFontStyle()));
585 }
586 
UpdateCandidateTextProperties(const RefPtr<PickerTheme> & pickerTheme,const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty)587 void TextPickerColumnPattern::UpdateCandidateTextProperties(const RefPtr<PickerTheme>& pickerTheme,
588     const RefPtr<TextLayoutProperty>& textLayoutProperty,
589     const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty)
590 {
591     auto focusOptionSize = pickerTheme->GetOptionStyle(false, false).GetFontSize() + FONT_SIZE;
592     textLayoutProperty->UpdateTextColor(textPickerLayoutProperty->GetColor().value_or(
593         pickerTheme->GetOptionStyle(false, false).GetTextColor()));
594     if (textPickerLayoutProperty->HasFontSize()) {
595         textLayoutProperty->UpdateFontSize(textPickerLayoutProperty->GetFontSize().value());
596     } else {
597         textLayoutProperty->UpdateAdaptMaxFontSize(focusOptionSize);
598         textLayoutProperty->UpdateAdaptMinFontSize(
599             pickerTheme->GetOptionStyle(true, false).GetAdaptMinFontSize() - FOCUS_SIZE);
600     }
601     textLayoutProperty->UpdateFontWeight(textPickerLayoutProperty->GetWeight().value_or(
602         pickerTheme->GetOptionStyle(false, false).GetFontWeight()));
603     textLayoutProperty->UpdateFontFamily(textPickerLayoutProperty->GetFontFamily().value_or(
604         pickerTheme->GetOptionStyle(false, false).GetFontFamilies()));
605     textLayoutProperty->UpdateItalicFontStyle(textPickerLayoutProperty->GetFontStyle().value_or(
606         pickerTheme->GetOptionStyle(false, false).GetFontStyle()));
607 }
608 
UpdateSelectedTextProperties(const RefPtr<PickerTheme> & pickerTheme,const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty)609 void TextPickerColumnPattern::UpdateSelectedTextProperties(const RefPtr<PickerTheme>& pickerTheme,
610     const RefPtr<TextLayoutProperty>& textLayoutProperty,
611     const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty)
612 {
613     auto selectedOptionSize = pickerTheme->GetOptionStyle(true, false).GetFontSize();
614     textLayoutProperty->UpdateTextColor(textPickerLayoutProperty->GetSelectedColor().value_or(
615         pickerTheme->GetOptionStyle(true, false).GetTextColor()));
616     if (textPickerLayoutProperty->HasSelectedFontSize()) {
617         textLayoutProperty->UpdateFontSize(textPickerLayoutProperty->GetSelectedFontSize().value());
618     } else {
619         textLayoutProperty->UpdateAdaptMaxFontSize(selectedOptionSize);
620         textLayoutProperty->UpdateAdaptMinFontSize(pickerTheme->GetOptionStyle(true, false).GetAdaptMinFontSize());
621     }
622     textLayoutProperty->UpdateFontWeight(textPickerLayoutProperty->GetSelectedWeight().value_or(
623         pickerTheme->GetOptionStyle(true, false).GetFontWeight()));
624     textLayoutProperty->UpdateFontFamily(textPickerLayoutProperty->GetSelectedFontFamily().value_or(
625         pickerTheme->GetOptionStyle(true, false).GetFontFamilies()));
626     textLayoutProperty->UpdateItalicFontStyle(textPickerLayoutProperty->GetSelectedFontStyle().value_or(
627         pickerTheme->GetOptionStyle(true, false).GetFontStyle()));
628 }
629 
AddAnimationTextProperties(uint32_t currentIndex,const RefPtr<TextLayoutProperty> & textLayoutProperty)630 void TextPickerColumnPattern::AddAnimationTextProperties(
631     uint32_t currentIndex, const RefPtr<TextLayoutProperty>& textLayoutProperty)
632 {
633     TextProperties properties;
634     if (textLayoutProperty->HasFontSize()) {
635         properties.fontSize = Dimension(textLayoutProperty->GetFontSize().value().ConvertToPx());
636     }
637     if (textLayoutProperty->HasTextColor()) {
638         properties.currentColor = textLayoutProperty->GetTextColor().value();
639     }
640     if (currentIndex > 0) {
641         properties.upFontSize = animationProperties_[currentIndex - 1].fontSize;
642         animationProperties_[currentIndex - 1].downFontSize = properties.fontSize;
643 
644         properties.upColor = animationProperties_[currentIndex - 1].currentColor;
645         animationProperties_[currentIndex - 1].downColor = properties.currentColor;
646     }
647     animationProperties_.emplace_back(properties);
648 }
649 
UpdatePickerTextProperties(const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty,uint32_t currentIndex,uint32_t middleIndex,uint32_t showCount)650 void TextPickerColumnPattern::UpdatePickerTextProperties(const RefPtr<TextLayoutProperty>& textLayoutProperty,
651     const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty, uint32_t currentIndex, uint32_t middleIndex,
652     uint32_t showCount)
653 {
654     auto host = GetHost();
655     CHECK_NULL_VOID(host);
656     auto context = host->GetContext();
657     CHECK_NULL_VOID(context);
658     auto pickerTheme = context->GetTheme<PickerTheme>();
659     CHECK_NULL_VOID(pickerTheme);
660     if (currentIndex < middleIndex) {
661         if (currentIndex == 0) {
662             UpdateDisappearTextProperties(pickerTheme, textLayoutProperty, textPickerLayoutProperty);
663         } else {
664             UpdateCandidateTextProperties(pickerTheme, textLayoutProperty, textPickerLayoutProperty);
665         }
666         textLayoutProperty->UpdateAlignment(Alignment::TOP_CENTER);
667     }
668     if (currentIndex == middleIndex) {
669         UpdateSelectedTextProperties(pickerTheme, textLayoutProperty, textPickerLayoutProperty);
670         textLayoutProperty->UpdateAlignment(Alignment::CENTER);
671     }
672     if (currentIndex > middleIndex) {
673         if (currentIndex == showCount - 1) {
674             UpdateDisappearTextProperties(pickerTheme, textLayoutProperty, textPickerLayoutProperty);
675         } else {
676             UpdateCandidateTextProperties(pickerTheme, textLayoutProperty, textPickerLayoutProperty);
677         }
678         textLayoutProperty->UpdateAlignment(Alignment::BOTTOM_CENTER);
679     }
680     textLayoutProperty->UpdateMaxLines(1);
681     AddAnimationTextProperties(currentIndex, textLayoutProperty);
682 }
683 
TextPropertiesLinearAnimation(const RefPtr<TextLayoutProperty> & textLayoutProperty,uint32_t index,uint32_t showCount,bool isDown,double scale)684 void TextPickerColumnPattern::TextPropertiesLinearAnimation(
685     const RefPtr<TextLayoutProperty>& textLayoutProperty, uint32_t index, uint32_t showCount, bool isDown, double scale)
686 {
687     if (index >= animationProperties_.size()) {
688         LOGE("Animation Properties vactor is break.");
689         return;
690     }
691     if ((!index && isDown) || ((index == (showCount - 1)) && !isDown)) {
692         return;
693     }
694     Dimension startFontSize = animationProperties_[index].fontSize;
695     Color startColor = animationProperties_[index].currentColor;
696     Dimension endFontSize;
697     Color endColor;
698     if (!isDown) {
699         endFontSize = animationProperties_[index].downFontSize;
700         endColor = animationProperties_[index].downColor;
701     } else {
702         endFontSize = animationProperties_[index].upFontSize;
703         endColor = animationProperties_[index].upColor;
704     }
705     Dimension updateSize = LinearFontSize(startFontSize, endFontSize, scale);
706     textLayoutProperty->UpdateFontSize(updateSize);
707     auto colorEvaluator = AceType::MakeRefPtr<LinearEvaluator<Color>>();
708     Color updateColor = colorEvaluator->Evaluate(startColor, endColor, scale);
709     textLayoutProperty->UpdateTextColor(updateColor);
710 }
711 
UpdateTextPropertiesLinear(bool isDown,double scale)712 void TextPickerColumnPattern::UpdateTextPropertiesLinear(bool isDown, double scale)
713 {
714     if (scale > 1.0) {
715         return;
716     }
717     if (columnkind_ == ICON) {
718         return;
719     }
720     auto host = GetHost();
721     CHECK_NULL_VOID(host);
722     uint32_t showCount = GetShowOptionCount();
723     auto child = host->GetChildren();
724     auto iter = child.begin();
725     if (child.size() != showCount) {
726         return;
727     }
728     for (uint32_t index = 0; index < showCount; index++) {
729         auto rangeNode = DynamicCast<FrameNode>(*iter);
730         CHECK_NULL_VOID(rangeNode);
731         RefPtr<TextLayoutProperty> textLayoutProperty;
732         if (columnkind_ == TEXT) {
733             auto textPattern = rangeNode->GetPattern<TextPattern>();
734             CHECK_NULL_VOID(textPattern);
735             textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
736             CHECK_NULL_VOID(textLayoutProperty);
737             TextPropertiesLinearAnimation(textLayoutProperty, index, showCount, isDown, scale);
738         } else if (columnkind_ == MIXTURE) {
739             auto children = rangeNode->GetChildren();
740             if (children.size() != MIXTURE_CHILD_COUNT) {
741                 LOGE("children number is wrong.");
742                 continue;
743             }
744             auto textNode = DynamicCast<FrameNode>(rangeNode->GetLastChild());
745             auto textPattern = textNode->GetPattern<TextPattern>();
746             textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
747             CHECK_NULL_VOID(textLayoutProperty);
748             TextPropertiesLinearAnimation(textLayoutProperty, index, showCount, isDown, scale);
749             textNode->MarkModifyDone();
750             textNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
751         }
752         rangeNode->MarkModifyDone();
753         rangeNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
754         iter++;
755     }
756 }
757 
LinearFontSize(const Dimension & startFontSize,const Dimension & endFontSize,double percent)758 Dimension TextPickerColumnPattern::LinearFontSize(
759     const Dimension& startFontSize, const Dimension& endFontSize, double percent)
760 {
761     return startFontSize + (endFontSize - startFontSize) * percent;
762 }
763 
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)764 void TextPickerColumnPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
765 {
766     CHECK_NULL_VOID_NOLOG(!panEvent_);
767     auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& event) {
768         LOGI("Pan event start");
769         auto pattern = weak.Upgrade();
770         CHECK_NULL_VOID_NOLOG(pattern);
771         pattern->HandleDragStart(event);
772     };
773     auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& event) {
774         auto pattern = weak.Upgrade();
775         CHECK_NULL_VOID_NOLOG(pattern);
776         pattern->HandleDragMove(event);
777     };
778     auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
779         LOGI("Pan event end mainVelocity: %{public}lf", info.GetMainVelocity());
780         auto pattern = weak.Upgrade();
781         CHECK_NULL_VOID_NOLOG(pattern);
782         if (info.GetInputEventType() == InputEventType::AXIS) {
783             return;
784         }
785         pattern->HandleDragEnd();
786     };
787     auto actionCancelTask = [weak = WeakClaim(this)]() {
788         LOGI("Pan event cancel");
789         auto pattern = weak.Upgrade();
790         CHECK_NULL_VOID_NOLOG(pattern);
791         pattern->HandleDragEnd();
792     };
793     PanDirection panDirection;
794     panDirection.type = PanDirection::VERTICAL;
795     panEvent_ = MakeRefPtr<PanEvent>(
796         std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
797     gestureHub->AddPanEvent(panEvent_, panDirection, DEFAULT_PAN_FINGER, DEFAULT_PAN_DISTANCE);
798 }
799 
GetParentLayout() const800 RefPtr<TextPickerLayoutProperty> TextPickerColumnPattern::GetParentLayout() const
801 {
802     auto host = GetHost();
803     auto stackNode = DynamicCast<FrameNode>(host->GetParent());
804     auto parentNode = DynamicCast<FrameNode>(stackNode->GetParent());
805 
806     auto property = parentNode->GetLayoutProperty<TextPickerLayoutProperty>();
807     return property;
808 }
809 
810 
HandleDragStart(const GestureEvent & event)811 void TextPickerColumnPattern::HandleDragStart(const GestureEvent& event)
812 {
813     CHECK_NULL_VOID_NOLOG(GetHost());
814     CHECK_NULL_VOID_NOLOG(GetToss());
815     auto toss = GetToss();
816     yOffset_ = event.GetGlobalPoint().GetY();
817     toss->SetStart(yOffset_);
818     yLast_ = yOffset_;
819     pressed_ = true;
820     auto frameNode = GetHost();
821     CHECK_NULL_VOID(frameNode);
822     frameNode->OnAccessibilityEvent(AccessibilityEventType::SCROLL_START);
823 }
824 
HandleDragMove(const GestureEvent & event)825 void TextPickerColumnPattern::HandleDragMove(const GestureEvent& event)
826 {
827     if (event.GetFingerList().size() > 1) {
828         return;
829     }
830     if (event.GetInputEventType() == InputEventType::AXIS) {
831         int32_t step = LessNotEqual(event.GetDelta().GetY(), 0.0) ? 1 : -1;
832         InnerHandleScroll(step);
833 
834         return;
835     }
836 
837     CHECK_NULL_VOID_NOLOG(pressed_);
838     CHECK_NULL_VOID_NOLOG(GetHost());
839     CHECK_NULL_VOID_NOLOG(GetToss());
840     auto toss = GetToss();
841     double offsetY = event.GetGlobalPoint().GetY();
842     if (NearEqual(offsetY, yLast_, 1.0)) { // if changing less than 1.0, no need to handle
843         return;
844     }
845 
846     toss->SetEnd(offsetY);
847     UpdateColumnChildPosition(offsetY, true);
848 }
849 
HandleDragEnd()850 void TextPickerColumnPattern::HandleDragEnd()
851 {
852     pressed_ = false;
853     CHECK_NULL_VOID_NOLOG(GetHost());
854     CHECK_NULL_VOID_NOLOG(GetToss());
855     auto toss = GetToss();
856     auto frameNode = GetHost();
857     CHECK_NULL_VOID(frameNode);
858     if (!NotLoopOptions() && toss->Play()) {
859         frameNode->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
860         return;
861     }
862     yOffset_ = 0.0;
863     yLast_ = 0.0;
864     if (!animationCreated_) {
865         ScrollOption(0.0);
866         return;
867     }
868     ScrollDirection dir = scrollDelta_ > 0.0 ? ScrollDirection::DOWN : ScrollDirection::UP;
869     int32_t middleIndex = GetShowOptionCount() / HALF_NUMBER;
870     double shiftDistance = (dir == ScrollDirection::UP) ? optionProperties_[middleIndex].prevDistance
871                                                         : optionProperties_[middleIndex].nextDistance;
872     double shiftThreshold = shiftDistance / HALF_NUMBER;
873     if (std::abs(scrollDelta_) >= std::abs(shiftThreshold)) {
874         InnerHandleScroll(LessNotEqual(scrollDelta_, 0.0) ? 1 : -1, true);
875         scrollDelta_ = scrollDelta_ - std::abs(shiftDistance) * (dir == ScrollDirection::UP ? -1 : 1);
876     }
877     auto curve = CreateAnimation(scrollDelta_, 0.0);
878     fromController_->ClearInterpolators();
879     fromController_->AddInterpolator(curve);
880     fromController_->Play();
881     frameNode->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
882 }
883 
CreateAnimation()884 void TextPickerColumnPattern::CreateAnimation()
885 {
886     CHECK_NULL_VOID_NOLOG(!animationCreated_);
887     toController_ = CREATE_ANIMATOR(PipelineContext::GetCurrentContext());
888     toController_->SetDuration(ANIMATION_ZERO_TO_OUTER); // 200ms for animation that from zero to outer.
889     auto weak = AceType::WeakClaim(this);
890     toController_->AddStopListener([weak]() {
891         auto column = weak.Upgrade();
892         if (column) {
893             column->HandleCurveStopped();
894         } else {
895             LOGE("timepicker column is null.");
896         }
897     });
898     fromBottomCurve_ = CreateAnimation(jumpInterval_, 0.0);
899     fromTopCurve_ = CreateAnimation(0.0 - jumpInterval_, 0.0);
900     fromController_ = CREATE_ANIMATOR(PipelineContext::GetCurrentContext());
901     fromController_->SetDuration(ANIMATION_OUTER_TO_ZERO);
902     animationCreated_ = true;
903 }
904 
CreateAnimation(double from,double to)905 RefPtr<CurveAnimation<double>> TextPickerColumnPattern::CreateAnimation(double from, double to)
906 {
907     auto weak = AceType::WeakClaim(this);
908     auto curve = AceType::MakeRefPtr<CurveAnimation<double>>(from, to, Curves::FRICTION);
909     curve->AddListener(Animation<double>::ValueCallback([weak](double value) {
910         auto column = weak.Upgrade();
911         CHECK_NULL_VOID(column);
912         column->ScrollOption(value);
913     }));
914     return curve;
915 }
916 
HandleCurveStopped()917 void TextPickerColumnPattern::HandleCurveStopped()
918 {
919     CHECK_NULL_VOID_NOLOG(animationCreated_);
920     if (NearZero(scrollDelta_)) {
921         return;
922     }
923     ScrollOption(0.0 - scrollDelta_);
924     int32_t step = GreatNotEqual(scrollDelta_, 0.0) ? 1 : -1;
925     InnerHandleScroll(step);
926     fromController_->ClearInterpolators();
927     if (LessNotEqual(scrollDelta_, 0.0)) {
928         fromController_->AddInterpolator(fromTopCurve_);
929     } else {
930         fromController_->AddInterpolator(fromBottomCurve_);
931     }
932     fromController_->Play();
933 }
934 
ScrollOption(double delta)935 void TextPickerColumnPattern::ScrollOption(double delta)
936 {
937     scrollDelta_ = delta;
938     auto midIndex = GetShowOptionCount() / HALF_NUMBER;
939     ScrollDirection dir = delta > 0.0 ? ScrollDirection::DOWN : ScrollDirection::UP;
940     double shiftDistance = (dir == ScrollDirection::UP) ? optionProperties_[midIndex].prevDistance
941                                                         : optionProperties_[midIndex].nextDistance;
942     double distancePercent = delta / shiftDistance;
943     double textThresHold = optionProperties_[midIndex].height / 4;
944     double textLinearPercent = 0.0;
945     if (std::abs(delta) > textThresHold) {
946         textLinearPercent = (std::abs(delta) - textThresHold) / (std::abs(shiftDistance) - textThresHold);
947     }
948     UpdateTextPropertiesLinear(LessNotEqual(delta, 0.0), textLinearPercent);
949     CalcAlgorithmOffset(dir, distancePercent);
950 
951     auto host = GetHost();
952     CHECK_NULL_VOID(host);
953     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
954 }
955 
ResetAlgorithmOffset()956 void TextPickerColumnPattern::ResetAlgorithmOffset()
957 {
958     algorithmOffset_.clear();
959 
960     uint32_t counts = GetShowOptionCount();
961     for (uint32_t i = 0; i < counts; i++) {
962         algorithmOffset_.emplace_back(0.0);
963     }
964 }
965 
UpdateScrollDelta(double delta)966 void TextPickerColumnPattern::UpdateScrollDelta(double delta)
967 {
968     SetCurrentOffset(delta);
969     auto host = GetHost();
970     CHECK_NULL_VOID(host);
971     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
972 }
973 
CalcAlgorithmOffset(ScrollDirection dir,double distancePercent)974 void TextPickerColumnPattern::CalcAlgorithmOffset(ScrollDirection dir, double distancePercent)
975 {
976     algorithmOffset_.clear();
977 
978     uint32_t counts = GetShowOptionCount();
979 
980     for (uint32_t i = 0; i < counts; i++) {
981         double distance =
982             (dir == ScrollDirection::UP) ? optionProperties_[i].prevDistance : optionProperties_[i].nextDistance;
983         algorithmOffset_.emplace_back(distance * distancePercent);
984     }
985 }
986 
GetShiftDistance(int32_t index,ScrollDirection dir)987 double TextPickerColumnPattern::GetShiftDistance(int32_t index, ScrollDirection dir)
988 {
989     auto pipeline = PipelineBase::GetCurrentContext();
990     CHECK_NULL_RETURN(pipeline, 0.0);
991     auto theme = pipeline->GetTheme<PickerTheme>();
992     CHECK_NULL_RETURN(theme, 0.0);
993     int32_t optionCounts = theme->GetShowOptionCount();
994     if (optionCounts == 0) {
995         return 0.0;
996     }
997     int32_t nextIndex = 0;
998     auto isDown = dir == ScrollDirection::DOWN;
999     nextIndex = isDown ? (optionCounts + index + 1) % optionCounts : (optionCounts + index - 1) % optionCounts;
1000     double distance = 0.0;
1001     double val = 0.0;
1002     switch (static_cast<OptionIndex>(index)) {
1003         case OptionIndex::COLUMN_INDEX_0: // first
1004             distance = (dir == ScrollDirection::DOWN) ? optionProperties_[index].height
1005                                                       : (0.0 - optionProperties_[index].height);
1006             break;
1007         case OptionIndex::COLUMN_INDEX_1:
1008             if (dir == ScrollDirection::UP) {
1009                 distance = -optionProperties_[nextIndex].height;
1010             } else {
1011                 distance = optionProperties_[index].height +
1012                            (optionProperties_[nextIndex].height - optionProperties_[nextIndex].fontheight) /
1013                            HALF_NUMBER;
1014             }
1015             break;
1016 
1017         case OptionIndex::COLUMN_INDEX_2:
1018             val = optionProperties_[index].height / HALF_NUMBER + optionProperties_[nextIndex].height -
1019                   optionProperties_[nextIndex].fontheight / HALF_NUMBER;
1020             distance = (dir == ScrollDirection::DOWN) ? val : (0.0 - val);
1021             break;
1022         case OptionIndex::COLUMN_INDEX_3:
1023             if (dir == ScrollDirection::DOWN) {
1024                 distance = optionProperties_[nextIndex].height;
1025             } else {
1026                 val = optionProperties_[index].height +
1027                       (optionProperties_[nextIndex].height - optionProperties_[nextIndex].fontheight) / HALF_NUMBER;
1028                 distance = 0.0 - val;
1029             }
1030             break;
1031         case OptionIndex::COLUMN_INDEX_4: // last
1032             distance = (dir == ScrollDirection::DOWN) ? optionProperties_[index].height
1033                                                       : (0.0 - optionProperties_[index].height);
1034             break;
1035         default:
1036             break;
1037     }
1038 
1039     return distance;
1040 }
1041 
GetShiftDistanceForLandscape(int32_t index,ScrollDirection dir)1042 double TextPickerColumnPattern::GetShiftDistanceForLandscape(int32_t index, ScrollDirection dir)
1043 {
1044     auto pipeline = PipelineBase::GetCurrentContext();
1045     CHECK_NULL_RETURN(pipeline, 0.0);
1046     auto theme = pipeline->GetTheme<PickerTheme>();
1047     CHECK_NULL_RETURN(theme, 0.0);
1048     int32_t optionCounts = theme->GetShowOptionCount();
1049     if (optionCounts == 0) {
1050         return 0.0;
1051     }
1052     int32_t nextIndex = 0;
1053     auto isDown = dir == ScrollDirection::DOWN;
1054     nextIndex = isDown ? (optionCounts + index + 1) % optionCounts : (optionCounts + index - 1) % optionCounts;
1055     double distance = 0.0;
1056     double val = 0.0;
1057     switch (static_cast<OptionIndex>(index)) {
1058         case OptionIndex::COLUMN_INDEX_0:
1059             if (dir == ScrollDirection::UP) {
1060                 distance = 0.0 - optionProperties_[index].height;
1061             } else {
1062                 distance = optionProperties_[index].height +
1063                            (optionProperties_[nextIndex].height - optionProperties_[nextIndex].fontheight) /
1064                            HALF_NUMBER;
1065             }
1066             break;
1067 
1068         case OptionIndex::COLUMN_INDEX_1:
1069             val = optionProperties_[index].height / HALF_NUMBER + optionProperties_[nextIndex].height -
1070                   optionProperties_[nextIndex].fontheight / HALF_NUMBER;
1071             distance = (dir == ScrollDirection::DOWN) ? val : (0.0 - val);
1072             break;
1073 
1074         case OptionIndex::COLUMN_INDEX_2:
1075             if (dir == ScrollDirection::DOWN) {
1076                 distance = optionProperties_[index].height;
1077             } else {
1078                 val = optionProperties_[index].height +
1079                       (optionProperties_[nextIndex].height - optionProperties_[nextIndex].fontheight) / HALF_NUMBER;
1080                 distance = 0.0 - val;
1081             }
1082             break;
1083         default:
1084             break;
1085     }
1086 
1087     return distance;
1088 }
1089 
SetOptionShiftDistance()1090 void TextPickerColumnPattern::SetOptionShiftDistance()
1091 {
1092     auto pipeline = PipelineBase::GetCurrentContext();
1093     CHECK_NULL_VOID(pipeline);
1094     auto theme = pipeline->GetTheme<PickerTheme>();
1095     CHECK_NULL_VOID(theme);
1096     int32_t itemCounts = theme->GetShowOptionCount();
1097     bool isLanscape = itemCounts == OPTION_COUNT_PHONE_LANDSCAPE;
1098     for (int32_t i = 0; i < itemCounts; i++) {
1099         TextPickerOptionProperty& prop = optionProperties_[i];
1100         if (isLanscape) {
1101             prop.prevDistance = GetShiftDistanceForLandscape(i, ScrollDirection::UP);
1102             prop.nextDistance = GetShiftDistanceForLandscape(i, ScrollDirection::DOWN);
1103         } else {
1104             prop.prevDistance = GetShiftDistance(i, ScrollDirection::UP);
1105             prop.nextDistance = GetShiftDistance(i, ScrollDirection::DOWN);
1106         }
1107     }
1108 }
1109 
UpdateToss(double offsetY)1110 void TextPickerColumnPattern::UpdateToss(double offsetY)
1111 {
1112     UpdateColumnChildPosition(offsetY);
1113 }
1114 
TossStoped()1115 void TextPickerColumnPattern::TossStoped()
1116 {
1117     yOffset_ = 0.0;
1118     yLast_ = 0.0;
1119     ScrollOption(0.0);
1120 }
1121 
GetSelectedObject(bool isColumnChange,int32_t status) const1122 std::string TextPickerColumnPattern::GetSelectedObject(bool isColumnChange, int32_t status) const
1123 {
1124     auto host = GetHost();
1125     CHECK_NULL_RETURN(host, "");
1126     auto value = GetOption(GetSelected());
1127     auto index = GetSelected();
1128     if (isColumnChange) {
1129         value = GetCurrentText();
1130         index = GetCurrentIndex();
1131     }
1132 
1133     auto context = host->GetContext();
1134     CHECK_NULL_RETURN(context, "");
1135 
1136     if (context->GetIsDeclarative()) {
1137         return std::string("{\"value\":") + "\"" + value + "\"" + ",\"index\":" + std::to_string(index) +
1138                ",\"status\":" + std::to_string(status) + "}";
1139     } else {
1140         return std::string("{\"newValue\":") + "\"" + value + "\"" + ",\"newSelected\":" + std::to_string(index) +
1141                ",\"status\":" + std::to_string(status) + "}";
1142     }
1143 }
1144 
UpdateColumnChildPosition(double offsetY,bool isUpatePropertiesOnly)1145 void TextPickerColumnPattern::UpdateColumnChildPosition(double offsetY, bool isUpatePropertiesOnly)
1146 {
1147     yLast_ = offsetY;
1148     double dragDelta = yLast_ - yOffset_;
1149     if (!CanMove(LessNotEqual(dragDelta, 0))) {
1150         return;
1151     }
1152     auto midIndex = GetShowOptionCount() / HALF_NUMBER;
1153     ScrollDirection dir = dragDelta > 0.0 ? ScrollDirection::DOWN : ScrollDirection::UP;
1154     double shiftDistance = (dir == ScrollDirection::UP) ? optionProperties_[midIndex].prevDistance
1155                                                         : optionProperties_[midIndex].nextDistance;
1156     if (GreatOrEqual(std::abs(dragDelta), std::abs(shiftDistance))) {
1157         int32_t step = LessNotEqual(scrollDelta_, 0.0) ? 1 : -1;
1158         InnerHandleScroll(step, true);
1159         dragDelta = dragDelta - (abs(shiftDistance) * (LessNotEqual(dragDelta, 0.0) ? -1 : 1));
1160         yOffset_ = offsetY;
1161     }
1162     ScrollOption(dragDelta);
1163 }
1164 
CanMove(bool isDown) const1165 bool TextPickerColumnPattern::CanMove(bool isDown) const
1166 {
1167     if (!NotLoopOptions()) {
1168         return true;
1169     }
1170     auto host = GetHost();
1171     CHECK_NULL_RETURN(host, false);
1172     int totalOptionCount = static_cast<int>(GetOptionCount());
1173     int currentIndex = static_cast<int>(GetCurrentIndex());
1174     int nextVirtualIndex = isDown ? currentIndex + 1 : currentIndex - 1;
1175     return nextVirtualIndex >= 0 && nextVirtualIndex < totalOptionCount;
1176 }
1177 
NotLoopOptions() const1178 bool TextPickerColumnPattern::NotLoopOptions() const
1179 {
1180     RefPtr<TextPickerLayoutProperty> layout = GetParentLayout();
1181     bool canLoop = layout->GetCanLoop().value();
1182     return !canLoop;
1183 }
1184 
InnerHandleScroll(int32_t step,bool isUpatePropertiesOnly)1185 bool TextPickerColumnPattern::InnerHandleScroll(int32_t step, bool isUpatePropertiesOnly)
1186 {
1187     auto host = GetHost();
1188     CHECK_NULL_RETURN(host, false);
1189     auto totalOptionCount = static_cast<int32_t>(GetOptionCount());
1190 
1191     CHECK_NULL_RETURN(host, false);
1192     if (totalOptionCount == 0) {
1193         return false;
1194     }
1195 
1196     int32_t currentIndex = GetCurrentIndex();
1197     int32_t prevIndex = currentIndex;
1198     RefPtr<TextPickerLayoutProperty> layout = GetParentLayout();
1199     CHECK_NULL_RETURN(host, false);
1200 
1201     bool canLoop = layout->GetCanLoop().value_or(true);
1202     if (!canLoop) {
1203         // scroll down
1204         if (step > 0) {
1205             currentIndex = (currentIndex + step) > (totalOptionCount - 1) ?
1206                 totalOptionCount - 1 : currentIndex + step;
1207         // scroll up
1208         } else if (step < 0) {
1209             currentIndex = currentIndex + step < 0 ? 0 : currentIndex + step;
1210         }
1211     } else {
1212         currentIndex = (totalOptionCount + currentIndex + step) % totalOptionCount;
1213     }
1214 
1215     if (currentIndex != prevIndex) {
1216         SetCurrentIndex(currentIndex);
1217         bool isDown = step > 0;
1218         HandleChangeCallback(isDown, true);
1219         FlushCurrentOptions(isDown, isUpatePropertiesOnly);
1220     }
1221 
1222     return true;
1223 }
1224 
OnKeyEvent(const KeyEvent & event)1225 bool TextPickerColumnPattern::OnKeyEvent(const KeyEvent& event)
1226 {
1227     if (event.action != KeyAction::DOWN) {
1228         return false;
1229     }
1230     if (event.code == KeyCode::KEY_DPAD_UP || event.code == KeyCode::KEY_DPAD_DOWN ||
1231         event.code == KeyCode::KEY_DPAD_LEFT || event.code == KeyCode::KEY_DPAD_RIGHT) {
1232         HandleDirectionKey(event.code);
1233         return true;
1234     }
1235     return false;
1236 }
1237 
HandleDirectionKey(KeyCode code)1238 bool TextPickerColumnPattern::HandleDirectionKey(KeyCode code)
1239 {
1240     auto host = GetHost();
1241     CHECK_NULL_RETURN(host, false);
1242     auto currernIndex = GetCurrentIndex();
1243     auto totalOptionCount = GetOptionCount();
1244     if (totalOptionCount == 0) {
1245         return false;
1246     }
1247     if (code == KeyCode::KEY_DPAD_UP) {
1248         SetCurrentIndex((totalOptionCount + currernIndex - 1) % totalOptionCount);
1249         FlushCurrentOptions();
1250         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1251         return true;
1252     }
1253     if (code == KeyCode::KEY_DPAD_DOWN) {
1254         SetCurrentIndex((totalOptionCount + currernIndex + 1) % totalOptionCount);
1255         FlushCurrentOptions();
1256         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1257         return true;
1258     }
1259     return false;
1260 }
SetAccessibilityAction()1261 void TextPickerColumnPattern::SetAccessibilityAction()
1262 {
1263     auto host = GetHost();
1264     CHECK_NULL_VOID(host);
1265     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1266     CHECK_NULL_VOID(accessibilityProperty);
1267     accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
1268         const auto& pattern = weakPtr.Upgrade();
1269         CHECK_NULL_VOID(pattern);
1270         CHECK_NULL_VOID(pattern->animationCreated_);
1271         if (!pattern->CanMove(true)) {
1272             return;
1273         }
1274         pattern->InnerHandleScroll(1);
1275         CHECK_NULL_VOID(pattern->fromController_);
1276         pattern->fromController_->ClearInterpolators();
1277         pattern->fromController_->AddInterpolator(pattern->fromTopCurve_);
1278         pattern->fromController_->Play();
1279         auto frameNode = pattern->GetHost();
1280         CHECK_NULL_VOID(frameNode);
1281         frameNode->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
1282     });
1283 
1284     accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)]() {
1285         const auto& pattern = weakPtr.Upgrade();
1286         CHECK_NULL_VOID(pattern);
1287         CHECK_NULL_VOID(pattern->animationCreated_);
1288         if (!pattern->CanMove(false)) {
1289             return;
1290         }
1291         pattern->InnerHandleScroll(-1);
1292         CHECK_NULL_VOID(pattern->fromController_);
1293         pattern->fromController_->ClearInterpolators();
1294         pattern->fromController_->AddInterpolator(pattern->fromBottomCurve_);
1295         pattern->fromController_->Play();
1296         auto frameNode = pattern->GetHost();
1297         CHECK_NULL_VOID(frameNode);
1298         frameNode->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
1299     });
1300 }
1301 
OnAroundButtonClick(RefPtr<EventParam> param)1302 void TextPickerColumnPattern::OnAroundButtonClick(RefPtr<EventParam> param)
1303 {
1304     int32_t middleIndex = GetShowOptionCount() / HALF_NUMBER;
1305     int32_t step = param->itemIndex - middleIndex;
1306     if (step != 0) {
1307         if (fromController_->IsRunning()) {
1308             fromController_->Finish();
1309         }
1310         InnerHandleScroll(step);
1311         double distance =
1312             (step > 0 ? optionProperties_[middleIndex].prevDistance : optionProperties_[middleIndex].nextDistance) *
1313             std::abs(step);
1314         auto curveTop = CreateAnimation(abs(distance), 0.0);
1315         auto curveBottom = CreateAnimation(0 - abs(distance), 0.0);
1316         fromController_->ClearInterpolators();
1317 
1318         fromController_->AddInterpolator(step > 0 ? curveTop : curveBottom);
1319         fromController_->SetDuration(CLICK_ANIMATION_DURATION);
1320         fromController_->Play();
1321     }
1322 }
1323 } // namespace OHOS::Ace::NG
1324