• 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/picker/datepicker_column_pattern.h"
17 
18 #include <cstdint>
19 #include <iterator>
20 #include <list>
21 #include <stdint.h>
22 
23 #include "base/utils/measure_util.h"
24 #include "base/utils/utils.h"
25 #include "core/components/common/layout/constants.h"
26 #include "core/components/common/properties/color.h"
27 #include "core/components/picker/picker_base_component.h"
28 #include "core/components_ng/layout/layout_wrapper.h"
29 #include "core/components_ng/pattern/button/button_layout_property.h"
30 #include "core/components_ng/pattern/picker/datepicker_event_hub.h"
31 #include "core/components_ng/pattern/picker/datepicker_pattern.h"
32 #include "core/components_ng/pattern/picker/toss_animation_controller.h"
33 #include "core/components_ng/pattern/text/text_layout_property.h"
34 #include "core/components_ng/pattern/text/text_pattern.h"
35 #include "core/components_ng/property/calc_length.h"
36 #include "core/pipeline_ng/pipeline_context.h"
37 #include "core/pipeline_ng/ui_task_scheduler.h"
38 
39 namespace OHOS::Ace::NG {
40 
41 namespace {
42 
43 // TODO datepicker style modification
44 constexpr float PADDING_WEIGHT = 10.0f;
45 const Dimension FONT_SIZE = Dimension(2.0);
46 const float TEXT_HEIGHT_NUMBER = 3.0f;
47 const float TEXT_WEIGHT_NUMBER = 6.0f;
48 const int32_t ANIMATION_ZERO_TO_OUTER = 200;
49 const int32_t OPTION_COUNT_PHONE_LANDSCAPE = 3;
50 const Dimension FOCUS_SIZE = Dimension(1.0);
51 const float MOVE_DISTANCE = 5.0f;
52 constexpr int32_t HOVER_ANIMATION_DURATION = 250;
53 constexpr int32_t PRESS_ANIMATION_DURATION = 100;
54 constexpr int32_t CLICK_ANIMATION_DURATION = 300;
55 constexpr int32_t MINDDLE_CHILD_INDEX = 2;
56 constexpr char MEASURE_SIZE_STRING[] = "TEST";
57 constexpr float FONTWEIGHT = 0.33f;
58 } // namespace
59 
OnAttachToFrameNode()60 void DatePickerColumnPattern::OnAttachToFrameNode()
61 {
62     auto host = GetHost();
63     CHECK_NULL_VOID(host);
64     auto context = host->GetContext();
65     CHECK_NULL_VOID(context);
66     auto pickerTheme = context->GetTheme<PickerTheme>();
67     CHECK_NULL_VOID(pickerTheme);
68     auto hub = host->GetEventHub<EventHub>();
69     CHECK_NULL_VOID(hub);
70     auto gestureHub = hub->GetOrCreateGestureEventHub();
71     CHECK_NULL_VOID(gestureHub);
72     tossAnimationController_->SetPipelineContext(context);
73     tossAnimationController_->SetColumn(AceType::WeakClaim(this));
74     jumpInterval_ = pickerTheme->GetJumpInterval().ConvertToPx();
75     CreateAnimation();
76     InitPanEvent(gestureHub);
77     host->GetRenderContext()->SetClipToFrame(true);
78 }
79 
OnModifyDone()80 void DatePickerColumnPattern::OnModifyDone()
81 {
82     auto pipeline = PipelineBase::GetCurrentContext();
83     CHECK_NULL_VOID(pipeline);
84     auto theme = pipeline->GetTheme<PickerTheme>();
85     CHECK_NULL_VOID(theme);
86     pressColor_ = theme->GetPressColor();
87     hoverColor_ = theme->GetHoverColor();
88     auto showCount = theme->GetShowOptionCount();
89     InitMouseAndPressEvent();
90     SetAccessibilityAction();
91     if (optionProperties_.empty()) {
92         auto midIndex = showCount / 2;
93         auto host = GetHost();
94         CHECK_NULL_VOID(host);
95         dividerSpacing_ = pipeline->NormalizeToPx(theme->GetDividerSpacing());
96         gradientHeight_ = static_cast<float>(pipeline->NormalizeToPx(theme->GetGradientHeight()));
97         MeasureContext measureContext;
98         measureContext.textContent = MEASURE_SIZE_STRING;
99         uint32_t childIndex = 0;
100         DatePickerOptionProperty prop;
101         while (childIndex < showCount) {
102             if (childIndex == midIndex) { // selected
103                 auto selectedOptionSize = theme->GetOptionStyle(true, false).GetFontSize();
104                 measureContext.fontSize = selectedOptionSize;
105             } else if (childIndex % midIndex == 1 && (childIndex != 0 || childIndex != (showCount - 1))) {
106                 auto focusOptionSize = theme->GetOptionStyle(false, false).GetFontSize() + FONT_SIZE;
107                 measureContext.fontSize = focusOptionSize;
108             } else {
109                 auto normalOptionSize = theme->GetOptionStyle(false, false).GetFontSize();
110                 measureContext.fontSize = normalOptionSize;
111             }
112             if (childIndex == showCount / MINDDLE_CHILD_INDEX) {
113                 prop.height = dividerSpacing_;
114             } else {
115                 prop.height = gradientHeight_;
116             }
117             Size size = MeasureUtil::MeasureTextSize(measureContext);
118             prop.fontheight = size.Height();
119             optionProperties_.emplace_back(prop);
120             childIndex++;
121         }
122         SetOptionShiftDistance();
123     }
124 }
125 
InitMouseAndPressEvent()126 void DatePickerColumnPattern::InitMouseAndPressEvent()
127 {
128     if (mouseEvent_ || touchListener_) {
129         return;
130     }
131     auto host = GetHost();
132     CHECK_NULL_VOID(host);
133     auto childSize = static_cast<int32_t>(host->GetChildren().size());
134     RefPtr<FrameNode> middleChild = nullptr;
135     middleChild = DynamicCast<FrameNode>(host->GetChildAtIndex(MINDDLE_CHILD_INDEX));
136     CHECK_NULL_VOID(middleChild);
137     auto eventHub = middleChild->GetEventHub<EventHub>();
138     CHECK_NULL_VOID(eventHub);
139     auto inputHub = eventHub->GetOrCreateInputEventHub();
140     auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
141         auto pattern = weak.Upgrade();
142         CHECK_NULL_VOID(pattern);
143         pattern->HandleMouseEvent(isHover);
144     };
145     mouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
146     inputHub->AddOnHoverEvent(mouseEvent_);
147     auto gesture = middleChild->GetOrCreateGestureEventHub();
148     CHECK_NULL_VOID(gesture);
149     auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
150         auto pattern = weak.Upgrade();
151         CHECK_NULL_VOID(pattern);
152         if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
153             pattern->SetLocalDownDistance(info.GetTouches().front().GetLocalLocation().GetDistance());
154             pattern->OnTouchDown();
155         }
156         if (info.GetTouches().front().GetTouchType() == TouchType::UP) {
157             pattern->OnTouchUp();
158             pattern->SetLocalDownDistance(0.0f);
159         }
160         if (info.GetTouches().front().GetTouchType() == TouchType::MOVE) {
161             if (std::abs(info.GetTouches().front().GetLocalLocation().GetDistance() - pattern->GetLocalDownDistance()) >
162                 MOVE_DISTANCE) {
163                 pattern->OnTouchUp();
164             }
165         }
166     };
167     touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
168     gesture->AddTouchEvent(touchListener_);
169     for (int32_t i = 0; i < childSize; i++) {
170         RefPtr<FrameNode> childNode = DynamicCast<FrameNode>(host->GetChildAtIndex(i));
171         CHECK_NULL_VOID(childNode);
172         RefPtr<DatePickerEventParam> param = MakeRefPtr<DatePickerEventParam>();
173         param->instance_ = childNode;
174         param->itemIndex_ = i;
175         param->itemTotalCounts_ = childSize;
176 
177         auto eventHub = childNode->GetEventHub<EventHub>();
178         CHECK_NULL_VOID(eventHub);
179         if (i != childSize / MINDDLE_CHILD_INDEX) {
180             RefPtr<ClickEvent> clickListener = CreateItemClickEventListener(param);
181             CHECK_NULL_VOID(clickListener);
182             auto gesture = eventHub->GetOrCreateGestureEventHub();
183             CHECK_NULL_VOID(gesture);
184             gesture->AddClickEvent(clickListener);
185         }
186     }
187 }
188 
HandleMouseEvent(bool isHover)189 void DatePickerColumnPattern::HandleMouseEvent(bool isHover)
190 {
191     if (isHover) {
192         hoverd_ = true;
193         PlayHoverAnimation(hoverColor_);
194     } else {
195         hoverd_ = false;
196         PlayHoverAnimation(Color::TRANSPARENT);
197     }
198 }
199 
OnTouchDown()200 void DatePickerColumnPattern::OnTouchDown()
201 {
202     PlayPressAnimation(pressColor_);
203 }
204 
OnTouchUp()205 void DatePickerColumnPattern::OnTouchUp()
206 {
207     if (hoverd_) {
208         PlayPressAnimation(hoverColor_);
209     } else {
210         PlayPressAnimation(Color::TRANSPARENT);
211     }
212 }
213 
SetButtonBackgroundColor(const Color & pressColor)214 void DatePickerColumnPattern::SetButtonBackgroundColor(const Color& pressColor)
215 {
216     auto host = GetHost();
217     CHECK_NULL_VOID(host);
218     auto stack = host->GetParent();
219     CHECK_NULL_VOID(stack);
220     auto buttonNode = DynamicCast<FrameNode>(stack->GetFirstChild());
221     auto renderContext = buttonNode->GetRenderContext();
222     renderContext->UpdateBackgroundColor(pressColor);
223     buttonNode->MarkModifyDone();
224     buttonNode->MarkDirtyNode();
225 }
226 
PlayPressAnimation(const Color & pressColor)227 void DatePickerColumnPattern::PlayPressAnimation(const Color& pressColor)
228 {
229     AnimationOption option = AnimationOption();
230     option.SetDuration(PRESS_ANIMATION_DURATION);
231     option.SetCurve(Curves::SHARP);
232     option.SetFillMode(FillMode::FORWARDS);
233     AnimationUtils::Animate(option, [weak = AceType::WeakClaim(this), pressColor]() {
234         auto picker = weak.Upgrade();
235         CHECK_NULL_VOID(picker);
236         picker->SetButtonBackgroundColor(pressColor);
237     });
238 }
239 
PlayHoverAnimation(const Color & color)240 void DatePickerColumnPattern::PlayHoverAnimation(const Color& color)
241 {
242     AnimationOption option = AnimationOption();
243     option.SetDuration(HOVER_ANIMATION_DURATION);
244     option.SetCurve(Curves::FRICTION);
245     option.SetFillMode(FillMode::FORWARDS);
246     AnimationUtils::Animate(option, [weak = AceType::WeakClaim(this), color]() {
247         auto picker = weak.Upgrade();
248         CHECK_NULL_VOID(picker);
249         picker->SetButtonBackgroundColor(color);
250     });
251 }
252 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)253 bool DatePickerColumnPattern::OnDirtyLayoutWrapperSwap(
254     const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
255 {
256     CHECK_NULL_RETURN_NOLOG(config.frameSizeChange, false);
257     CHECK_NULL_RETURN(dirty, false);
258     return true;
259 }
260 
FlushCurrentOptions(bool isDown,bool isUpateTextContentOnly,bool isUpdateAnimationProperties)261 void DatePickerColumnPattern::FlushCurrentOptions(
262     bool isDown, bool isUpateTextContentOnly, bool isUpdateAnimationProperties)
263 {
264     auto host = GetHost();
265     CHECK_NULL_VOID(host);
266     auto stackNode = DynamicCast<FrameNode>(host->GetParent());
267     CHECK_NULL_VOID(stackNode);
268     auto parentNode = DynamicCast<FrameNode>(stackNode->GetParent());
269     CHECK_NULL_VOID(parentNode);
270 
271     auto dataPickerLayoutProperty = host->GetLayoutProperty<DataPickerLayoutProperty>();
272     CHECK_NULL_VOID(dataPickerLayoutProperty);
273     dataPickerLayoutProperty->UpdatePadding(PaddingProperty { CalcLength(PADDING_WEIGHT, DimensionUnit::PX) });
274     dataPickerLayoutProperty->UpdateAlignSelf(FlexAlign::CENTER);
275 
276     auto datePickerPattern = parentNode->GetPattern<DatePickerPattern>();
277     CHECK_NULL_VOID(datePickerPattern);
278     auto dataPickerRowLayoutProperty = parentNode->GetLayoutProperty<DataPickerRowLayoutProperty>();
279     CHECK_NULL_VOID(dataPickerRowLayoutProperty);
280     auto showOptionCount = datePickerPattern->GetShowCount();
281     uint32_t totalOptionCount = datePickerPattern->GetOptionCount(host);
282     uint32_t currentIndex = host->GetPattern<DatePickerColumnPattern>()->GetCurrentIndex();
283     currentIndex = currentIndex % totalOptionCount;
284     uint32_t selectedIndex = showOptionCount / 2; // the center option is selected.
285 
286     auto child = host->GetChildren();
287     auto iter = child.begin();
288     if (child.size() != showOptionCount) {
289         return;
290     }
291     SetCurrentIndex(currentIndex);
292     SetDividerHeight(showOptionCount);
293     if (!isUpateTextContentOnly) {
294         animationProperties_.clear();
295     }
296     for (uint32_t index = 0; index < showOptionCount; index++) {
297         currentChildIndex_ = index;
298         uint32_t optionIndex = (totalOptionCount + currentIndex + index - selectedIndex) % totalOptionCount;
299 
300         auto textNode = DynamicCast<FrameNode>(*iter);
301         CHECK_NULL_VOID(textNode);
302         auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
303         CHECK_NULL_VOID(textLayoutProperty);
304 
305         if (!isUpateTextContentOnly) {
306             UpdatePickerTextProperties(index, showOptionCount, textLayoutProperty, dataPickerRowLayoutProperty);
307         }
308         iter++;
309         int32_t diffIndex = static_cast<int32_t>(index) - static_cast<int32_t>(selectedIndex);
310         int32_t virtualIndex = static_cast<int32_t>(currentIndex) + diffIndex;
311         bool virtualIndexValidate = virtualIndex >= 0 && virtualIndex < static_cast<int32_t>(totalOptionCount);
312         if (NotLoopOptions() && !virtualIndexValidate) {
313             textLayoutProperty->UpdateContent("");
314             textNode->MarkModifyDone();
315             textNode->MarkDirtyNode();
316             continue;
317         }
318         auto optionValue = datePickerPattern->GetAllOptions(host)[optionIndex];
319         textLayoutProperty->UpdateContent(optionValue);
320         textLayoutProperty->UpdateTextAlign(TextAlign::CENTER);
321         textNode->MarkModifyDone();
322         textNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
323     }
324     if (isUpateTextContentOnly && isUpdateAnimationProperties) {
325         FlushAnimationTextProperties(isDown);
326     }
327 }
328 
UpdatePickerTextProperties(uint32_t index,uint32_t showOptionCount,const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<DataPickerRowLayoutProperty> & dataPickerRowLayoutProperty)329 void DatePickerColumnPattern::UpdatePickerTextProperties(uint32_t index, uint32_t showOptionCount,
330     const RefPtr<TextLayoutProperty>& textLayoutProperty,
331     const RefPtr<DataPickerRowLayoutProperty>& dataPickerRowLayoutProperty)
332 {
333     auto pipeline = PipelineBase::GetCurrentContext();
334     CHECK_NULL_VOID(pipeline);
335     auto pickerTheme = pipeline->GetTheme<PickerTheme>();
336     CHECK_NULL_VOID(pickerTheme);
337     uint32_t selectedIndex = showOptionCount / 2; // the center option is selected.
338     if (index == selectedIndex) {
339         UpdateSelectedTextProperties(pickerTheme, textLayoutProperty, dataPickerRowLayoutProperty);
340         textLayoutProperty->UpdateAlignment(Alignment::CENTER);
341     } else if ((index == 0) || (index == showOptionCount - 1)) {
342         UpdateDisappearTextProperties(pickerTheme, textLayoutProperty, dataPickerRowLayoutProperty);
343     } else {
344         UpdateCandidateTextProperties(pickerTheme, textLayoutProperty, dataPickerRowLayoutProperty);
345     }
346     if (index < selectedIndex) {
347         textLayoutProperty->UpdateAlignment(Alignment::TOP_CENTER);
348     } else if (index > selectedIndex) {
349         textLayoutProperty->UpdateAlignment(Alignment::BOTTOM_CENTER);
350     }
351     textLayoutProperty->UpdateMaxLines(1);
352     AddAnimationTextProperties(index, textLayoutProperty);
353 }
354 
UpdateDisappearTextProperties(const RefPtr<PickerTheme> & pickerTheme,const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<DataPickerRowLayoutProperty> & dataPickerRowLayoutProperty)355 void DatePickerColumnPattern::UpdateDisappearTextProperties(const RefPtr<PickerTheme>& pickerTheme,
356     const RefPtr<TextLayoutProperty>& textLayoutProperty,
357     const RefPtr<DataPickerRowLayoutProperty>& dataPickerRowLayoutProperty)
358 {
359     auto normalOptionSize = pickerTheme->GetOptionStyle(false, false).GetFontSize();
360     textLayoutProperty->UpdateTextColor(dataPickerRowLayoutProperty->GetDisappearColor().value_or(
361         pickerTheme->GetOptionStyle(false, false).GetTextColor()));
362     if (dataPickerRowLayoutProperty->HasDisappearFontSize()) {
363         textLayoutProperty->UpdateFontSize(dataPickerRowLayoutProperty->GetDisappearFontSize().value());
364     } else {
365         textLayoutProperty->UpdateAdaptMaxFontSize(normalOptionSize);
366         textLayoutProperty->UpdateAdaptMinFontSize(pickerTheme->GetOptionStyle(false, false).GetAdaptMinFontSize());
367     }
368     textLayoutProperty->UpdateFontWeight(dataPickerRowLayoutProperty->GetDisappearWeight().value_or(
369         pickerTheme->GetOptionStyle(false, false).GetFontWeight()));
370     textLayoutProperty->UpdateFontFamily(dataPickerRowLayoutProperty->GetDisappearFontFamily().value_or(
371         pickerTheme->GetOptionStyle(false, false).GetFontFamilies()));
372     textLayoutProperty->UpdateItalicFontStyle(dataPickerRowLayoutProperty->GetDisappearFontStyle().value_or(
373         pickerTheme->GetOptionStyle(false, false).GetFontStyle()));
374 }
375 
UpdateCandidateTextProperties(const RefPtr<PickerTheme> & pickerTheme,const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<DataPickerRowLayoutProperty> & dataPickerRowLayoutProperty)376 void DatePickerColumnPattern::UpdateCandidateTextProperties(const RefPtr<PickerTheme>& pickerTheme,
377     const RefPtr<TextLayoutProperty>& textLayoutProperty,
378     const RefPtr<DataPickerRowLayoutProperty>& dataPickerRowLayoutProperty)
379 {
380     auto focusOptionSize = pickerTheme->GetOptionStyle(false, false).GetFontSize() + FONT_SIZE;
381     textLayoutProperty->UpdateTextColor(
382         dataPickerRowLayoutProperty->GetColor().value_or(pickerTheme->GetOptionStyle(false, false).GetTextColor()));
383     if (dataPickerRowLayoutProperty->HasFontSize()) {
384         textLayoutProperty->UpdateFontSize(dataPickerRowLayoutProperty->GetFontSize().value());
385     } else {
386         textLayoutProperty->UpdateAdaptMaxFontSize(focusOptionSize);
387         textLayoutProperty->UpdateAdaptMinFontSize(
388             pickerTheme->GetOptionStyle(true, false).GetAdaptMinFontSize() - FOCUS_SIZE);
389     }
390     textLayoutProperty->UpdateFontWeight(
391         dataPickerRowLayoutProperty->GetWeight().value_or(pickerTheme->GetOptionStyle(false, false).GetFontWeight()));
392     CandidateWeight_ =
393         dataPickerRowLayoutProperty->GetWeight().value_or(pickerTheme->GetOptionStyle(false, false).GetFontWeight());
394     textLayoutProperty->UpdateFontFamily(dataPickerRowLayoutProperty->GetFontFamily().value_or(
395         pickerTheme->GetOptionStyle(false, false).GetFontFamilies()));
396     textLayoutProperty->UpdateItalicFontStyle(
397         dataPickerRowLayoutProperty->GetFontStyle().value_or(pickerTheme->GetOptionStyle(false, false).GetFontStyle()));
398 }
399 
UpdateSelectedTextProperties(const RefPtr<PickerTheme> & pickerTheme,const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<DataPickerRowLayoutProperty> & dataPickerRowLayoutProperty)400 void DatePickerColumnPattern::UpdateSelectedTextProperties(const RefPtr<PickerTheme>& pickerTheme,
401     const RefPtr<TextLayoutProperty>& textLayoutProperty,
402     const RefPtr<DataPickerRowLayoutProperty>& dataPickerRowLayoutProperty)
403 {
404     auto selectedOptionSize = pickerTheme->GetOptionStyle(true, false).GetFontSize();
405     textLayoutProperty->UpdateTextColor(dataPickerRowLayoutProperty->GetSelectedColor().value_or(
406         pickerTheme->GetOptionStyle(true, false).GetTextColor()));
407     if (dataPickerRowLayoutProperty->HasSelectedFontSize()) {
408         textLayoutProperty->UpdateFontSize(dataPickerRowLayoutProperty->GetSelectedFontSize().value());
409     } else {
410         textLayoutProperty->UpdateAdaptMaxFontSize(selectedOptionSize);
411         textLayoutProperty->UpdateAdaptMinFontSize(pickerTheme->GetOptionStyle(true, false).GetAdaptMinFontSize());
412     }
413     textLayoutProperty->UpdateFontWeight(dataPickerRowLayoutProperty->GetSelectedWeight().value_or(
414         pickerTheme->GetOptionStyle(true, false).GetFontWeight()));
415     SelectedWeight_ = dataPickerRowLayoutProperty->GetSelectedWeight().value_or(
416         pickerTheme->GetOptionStyle(true, false).GetFontWeight());
417     textLayoutProperty->UpdateFontFamily(dataPickerRowLayoutProperty->GetSelectedFontFamily().value_or(
418         pickerTheme->GetOptionStyle(true, false).GetFontFamilies()));
419     textLayoutProperty->UpdateItalicFontStyle(dataPickerRowLayoutProperty->GetSelectedFontStyle().value_or(
420         pickerTheme->GetOptionStyle(true, false).GetFontStyle()));
421 }
422 
SetDividerHeight(uint32_t showOptionCount)423 void DatePickerColumnPattern::SetDividerHeight(uint32_t showOptionCount)
424 {
425     auto pipeline = PipelineBase::GetCurrentContext();
426     CHECK_NULL_VOID(pipeline);
427     auto pickerTheme = pipeline->GetTheme<PickerTheme>();
428     CHECK_NULL_VOID(pickerTheme);
429     gradientHeight_ = static_cast<float>(pickerTheme->GetGradientHeight().Value() * TEXT_HEIGHT_NUMBER);
430     dividerHeight_ = static_cast<float>(
431         gradientHeight_ + pickerTheme->GetDividerSpacing().Value() + pickerTheme->GetGradientHeight().Value());
432     dividerSpacingWidth_ = static_cast<float>(pickerTheme->GetDividerSpacing().Value() * TEXT_WEIGHT_NUMBER);
433 }
434 
NotLoopOptions() const435 bool DatePickerColumnPattern::NotLoopOptions() const
436 {
437     auto host = GetHost();
438     CHECK_NULL_RETURN(host, false);
439     auto showOptionCount = GetShowCount();
440     auto options = GetOptions();
441     uint32_t totalOptionCount = options[host].size();
442     return totalOptionCount <= showOptionCount / 2 + 1; // the critical value of loop condition.
443 }
444 
AddAnimationTextProperties(uint32_t currentIndex,const RefPtr<TextLayoutProperty> & textLayoutProperty)445 void DatePickerColumnPattern::AddAnimationTextProperties(
446     uint32_t currentIndex, const RefPtr<TextLayoutProperty>& textLayoutProperty)
447 {
448     DateTextProperties properties;
449     if (textLayoutProperty->HasFontSize()) {
450         properties.fontSize = Dimension(textLayoutProperty->GetFontSize().value().ConvertToPx());
451     }
452     if (textLayoutProperty->HasTextColor()) {
453         properties.currentColor = textLayoutProperty->GetTextColor().value();
454     }
455     if (currentIndex > 0) {
456         properties.upFontSize = animationProperties_[currentIndex - 1].fontSize;
457         animationProperties_[currentIndex - 1].downFontSize = properties.fontSize;
458 
459         properties.upColor = animationProperties_[currentIndex - 1].currentColor;
460         animationProperties_[currentIndex - 1].downColor = properties.currentColor;
461     }
462     animationProperties_.emplace_back(properties);
463 }
464 
FlushAnimationTextProperties(bool isDown)465 void DatePickerColumnPattern::FlushAnimationTextProperties(bool isDown)
466 {
467     if (!animationProperties_.size()) {
468         return;
469     }
470     if (isDown) {
471         for (size_t i = 0; i < animationProperties_.size(); i++) {
472             if (i > 0) {
473                 animationProperties_[i - 1].upFontSize = animationProperties_[i].upFontSize;
474                 animationProperties_[i - 1].fontSize = animationProperties_[i].fontSize;
475                 animationProperties_[i - 1].downFontSize = animationProperties_[i].downFontSize;
476 
477                 animationProperties_[i - 1].upColor = animationProperties_[i].upColor;
478                 animationProperties_[i - 1].currentColor = animationProperties_[i].currentColor;
479                 animationProperties_[i - 1].downColor = animationProperties_[i].downColor;
480             }
481             if (i == (animationProperties_.size() - 1)) {
482                 animationProperties_[i].upFontSize = animationProperties_[i].fontSize;
483                 animationProperties_[i].fontSize = animationProperties_[i].fontSize * 0.5;
484                 animationProperties_[i].downFontSize = Dimension();
485 
486                 animationProperties_[i].upColor = animationProperties_[i].currentColor;
487                 auto colorEvaluator = AceType::MakeRefPtr<LinearEvaluator<Color>>();
488                 animationProperties_[i].currentColor =
489                     colorEvaluator->Evaluate(Color(), animationProperties_[i].currentColor, 0.5);
490                 animationProperties_[i].downColor = Color();
491             }
492         }
493     } else {
494         for (size_t i = animationProperties_.size() - 1;; i--) {
495             if (i == 0) {
496                 animationProperties_[i].upFontSize = Dimension();
497                 animationProperties_[i].downFontSize = animationProperties_[i].fontSize;
498                 animationProperties_[i].fontSize = animationProperties_[i].fontSize * 0.5;
499 
500                 animationProperties_[i].upColor = Color();
501                 animationProperties_[i].downColor = animationProperties_[i].currentColor;
502                 auto colorEvaluator = AceType::MakeRefPtr<LinearEvaluator<Color>>();
503                 animationProperties_[i].currentColor =
504                     colorEvaluator->Evaluate(Color(), animationProperties_[i].currentColor, 0.5);
505                 break;
506             } else {
507                 animationProperties_[i].upFontSize = animationProperties_[i - 1].upFontSize;
508                 animationProperties_[i].fontSize = animationProperties_[i - 1].fontSize;
509                 animationProperties_[i].downFontSize = animationProperties_[i - 1].downFontSize;
510 
511                 animationProperties_[i].upColor = animationProperties_[i - 1].upColor;
512                 animationProperties_[i].currentColor = animationProperties_[i - 1].currentColor;
513                 animationProperties_[i].downColor = animationProperties_[i - 1].downColor;
514             }
515         }
516     }
517 }
518 
TextPropertiesLinearAnimation(const RefPtr<TextLayoutProperty> & textLayoutProperty,uint32_t index,uint32_t showCount,bool isDown,double scale)519 void DatePickerColumnPattern::TextPropertiesLinearAnimation(
520     const RefPtr<TextLayoutProperty>& textLayoutProperty, uint32_t index, uint32_t showCount, bool isDown, double scale)
521 {
522     if (index >= animationProperties_.size()) {
523         LOGE("Animation Properties vactor is break.");
524         return;
525     }
526     Dimension startFontSize = animationProperties_[index].fontSize;
527     Color startColor = animationProperties_[index].currentColor;
528     if ((!index && isDown) || ((index == (showCount - 1)) && !isDown)) {
529         textLayoutProperty->UpdateFontSize(startFontSize);
530         textLayoutProperty->UpdateTextColor(startColor);
531         return;
532     }
533     Dimension endFontSize;
534     Color endColor;
535     auto midIndex = showCount / 2;
536     if (!isDown) {
537         endFontSize = animationProperties_[index].downFontSize;
538         endColor = animationProperties_[index].downColor;
539         if ((index == midIndex - 1) && (scale >= FONTWEIGHT)) {
540             textLayoutProperty->UpdateFontWeight(SelectedWeight_);
541         }
542         if ((index == midIndex) && (scale >= FONTWEIGHT)) {
543             textLayoutProperty->UpdateFontWeight(CandidateWeight_);
544         }
545 
546     } else {
547         endFontSize = animationProperties_[index].upFontSize;
548         endColor = animationProperties_[index].upColor;
549 
550         if ((index == midIndex + 1) && (scale >= FONTWEIGHT)) {
551             textLayoutProperty->UpdateFontWeight(SelectedWeight_);
552         }
553         if ((index == midIndex) && (scale >= FONTWEIGHT)) {
554             textLayoutProperty->UpdateFontWeight(CandidateWeight_);
555         }
556     }
557     Dimension updateSize = LinearFontSize(startFontSize, endFontSize, scale);
558     textLayoutProperty->UpdateFontSize(updateSize);
559     auto colorEvaluator = AceType::MakeRefPtr<LinearEvaluator<Color>>();
560     Color updateColor = colorEvaluator->Evaluate(startColor, endColor, scale);
561     textLayoutProperty->UpdateTextColor(updateColor);
562     if (scale < FONTWEIGHT) {
563         if (index == midIndex) {
564             textLayoutProperty->UpdateFontWeight(SelectedWeight_);
565         } else {
566             textLayoutProperty->UpdateFontWeight(CandidateWeight_);
567         }
568     }
569 }
570 
UpdateTextPropertiesLinear(bool isDown,double scale)571 void DatePickerColumnPattern::UpdateTextPropertiesLinear(bool isDown, double scale)
572 {
573     if (scale > 1) {
574         return;
575     }
576     auto host = GetHost();
577     CHECK_NULL_VOID(host);
578     uint32_t showCount = GetShowCount();
579     auto child = host->GetChildren();
580     auto iter = child.begin();
581     if (child.size() != showCount) {
582         return;
583     }
584     for (uint32_t index = 0; index < showCount; index++) {
585         auto textNode = DynamicCast<FrameNode>(*iter);
586         CHECK_NULL_VOID(textNode);
587         auto textPattern = textNode->GetPattern<TextPattern>();
588         CHECK_NULL_VOID(textPattern);
589         RefPtr<TextLayoutProperty> textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
590         CHECK_NULL_VOID(textLayoutProperty);
591         TextPropertiesLinearAnimation(textLayoutProperty, index, showCount, isDown, scale);
592         iter++;
593     }
594 }
595 
LinearFontSize(const Dimension & startFontSize,const Dimension & endFontSize,double percent)596 Dimension DatePickerColumnPattern::LinearFontSize(
597     const Dimension& startFontSize, const Dimension& endFontSize, double percent)
598 {
599     return startFontSize + (endFontSize - startFontSize) * percent;
600 }
601 
InnerHandleScroll(bool isDown,bool isUpatePropertiesOnly,bool isUpdateAnimationProperties)602 bool DatePickerColumnPattern::InnerHandleScroll(
603     bool isDown, bool isUpatePropertiesOnly, bool isUpdateAnimationProperties)
604 {
605     auto host = GetHost();
606     CHECK_NULL_RETURN(host, false);
607     auto options = GetOptions();
608     auto totalOptionCount = options[host].size();
609 
610     CHECK_NULL_RETURN(host, false);
611     CHECK_NULL_RETURN(totalOptionCount, false);
612 
613     uint32_t currentIndex = GetCurrentIndex();
614     if (isDown) {
615         currentIndex = (totalOptionCount + currentIndex + 1) % totalOptionCount; // index add one
616     } else {
617         currentIndex = (totalOptionCount + currentIndex - 1) % totalOptionCount; // index reduce one
618     }
619     SetCurrentIndex(currentIndex);
620     FlushCurrentOptions(isDown, isUpatePropertiesOnly, isUpdateAnimationProperties);
621     HandleChangeCallback(isDown, true);
622     HandleEventCallback(true);
623 
624     auto textNodes = host->GetChildren();
625     DatePickerScrollDirection dir = isDown ? DatePickerScrollDirection::DOWN : DatePickerScrollDirection::UP;
626     if (dir == DatePickerScrollDirection::UP) {
627         for (auto iter = textNodes.begin(); iter != (--textNodes.end()); iter++) {
628             auto curNode = DynamicCast<FrameNode>(*iter);
629             auto shiftIter = std::next(iter, 1);
630             auto shiftNode = DynamicCast<FrameNode>(*shiftIter);
631             ShiftOptionProp(curNode, shiftNode);
632         }
633     } else {
634         for (auto iter = textNodes.rbegin(); iter != (--textNodes.rend()); iter++) {
635             auto curNode = DynamicCast<FrameNode>(*iter);
636             auto shiftIter = std::next(iter, 1);
637             auto shiftNode = DynamicCast<FrameNode>(*shiftIter);
638             ShiftOptionProp(curNode, shiftNode);
639         }
640     }
641     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
642     return true;
643 }
644 
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)645 void DatePickerColumnPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
646 {
647     CHECK_NULL_VOID_NOLOG(!panEvent_);
648     auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& event) {
649         LOGI("Pan event start");
650         auto pattern = weak.Upgrade();
651         CHECK_NULL_VOID_NOLOG(pattern);
652         pattern->HandleDragStart(event);
653     };
654     auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& event) {
655         auto pattern = weak.Upgrade();
656         CHECK_NULL_VOID_NOLOG(pattern);
657         pattern->HandleDragMove(event);
658     };
659     auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
660         LOGI("Pan event end mainVelocity: %{public}lf", info.GetMainVelocity());
661         auto pattern = weak.Upgrade();
662         CHECK_NULL_VOID_NOLOG(pattern);
663         if (info.GetInputEventType() == InputEventType::AXIS && info.GetSourceTool() == SourceTool::MOUSE) {
664             return;
665         }
666         pattern->HandleDragEnd();
667     };
668     auto actionCancelTask = [weak = WeakClaim(this)]() {
669         LOGI("Pan event cancel");
670         auto pattern = weak.Upgrade();
671         CHECK_NULL_VOID_NOLOG(pattern);
672         pattern->HandleDragEnd();
673     };
674     PanDirection panDirection;
675     panDirection.type = PanDirection::VERTICAL;
676     panEvent_ = MakeRefPtr<PanEvent>(
677         std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
678     gestureHub->AddPanEvent(panEvent_, panDirection, DEFAULT_PAN_FINGER, DEFAULT_PAN_DISTANCE);
679 }
680 
HandleDragStart(const GestureEvent & event)681 void DatePickerColumnPattern::HandleDragStart(const GestureEvent& event)
682 {
683     CHECK_NULL_VOID_NOLOG(GetHost());
684     CHECK_NULL_VOID_NOLOG(GetToss());
685     auto toss = GetToss();
686     yOffset_ = event.GetGlobalPoint().GetY();
687     toss->SetStart(yOffset_);
688     yLast_ = yOffset_;
689     pressed_ = true;
690     auto frameNode = GetHost();
691     CHECK_NULL_VOID(frameNode);
692     frameNode->OnAccessibilityEvent(AccessibilityEventType::SCROLL_START);
693 }
694 
HandleDragMove(const GestureEvent & event)695 void DatePickerColumnPattern::HandleDragMove(const GestureEvent& event)
696 {
697     if (event.GetInputEventType() == InputEventType::AXIS && event.GetSourceTool() == SourceTool::MOUSE) {
698         InnerHandleScroll(LessNotEqual(event.GetDelta().GetY(), 0.0));
699         return;
700     }
701     CHECK_NULL_VOID_NOLOG(pressed_);
702     CHECK_NULL_VOID_NOLOG(GetHost());
703     CHECK_NULL_VOID_NOLOG(GetToss());
704     auto toss = GetToss();
705     auto offsetY =
706         event.GetGlobalPoint().GetY() + (event.GetInputEventType() == InputEventType::AXIS ? event.GetOffsetY() : 0.0);
707     if (NearEqual(offsetY, yLast_, 1.0)) { // if changing less than 1.0, no need to handle
708         return;
709     }
710     toss->SetEnd(offsetY);
711     UpdateColumnChildPosition(offsetY);
712 }
713 
HandleDragEnd()714 void DatePickerColumnPattern::HandleDragEnd()
715 {
716     pressed_ = false;
717     CHECK_NULL_VOID_NOLOG(GetHost());
718     CHECK_NULL_VOID_NOLOG(GetToss());
719     auto toss = GetToss();
720     auto frameNode = GetHost();
721     CHECK_NULL_VOID(frameNode);
722     if (!NotLoopOptions() && toss->Play()) {
723         frameNode->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
724         return;
725     }
726     yOffset_ = 0.0;
727     yLast_ = 0.0;
728     if (!animationCreated_) {
729         ScrollOption(0.0);
730         return;
731     }
732     DatePickerScrollDirection dir =
733         scrollDelta_ > 0.0 ? DatePickerScrollDirection::DOWN : DatePickerScrollDirection::UP;
734     int32_t middleIndex = GetShowCount() / 2;
735     auto shiftDistance = (dir == DatePickerScrollDirection::UP) ? optionProperties_[middleIndex].prevDistance
736                                                                 : optionProperties_[middleIndex].nextDistance;
737     auto shiftThreshold = shiftDistance / 2;
738     if (std::abs(scrollDelta_) >= std::abs(shiftThreshold)) {
739         InnerHandleScroll(LessNotEqual(scrollDelta_, 0.0), true, false);
740         scrollDelta_ = scrollDelta_ - std::abs(shiftDistance) * (dir == DatePickerScrollDirection::UP ? -1 : 1);
741     }
742     auto curve = CreateAnimation(scrollDelta_, 0.0);
743     fromController_->ClearInterpolators();
744     fromController_->AddInterpolator(curve);
745     fromController_->Play();
746     frameNode->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
747 }
748 
CreateAnimation()749 void DatePickerColumnPattern::CreateAnimation()
750 {
751     CHECK_NULL_VOID_NOLOG(!animationCreated_);
752     toController_ = CREATE_ANIMATOR(PipelineContext::GetCurrentContext());
753     toController_->SetDuration(ANIMATION_ZERO_TO_OUTER); // 200ms for animation that from zero to outer.
754     auto weak = AceType::WeakClaim(this);
755     toController_->AddStopListener([weak]() {
756         auto column = weak.Upgrade();
757         CHECK_NULL_VOID(column);
758         column->HandleCurveStopped();
759     });
760     fromBottomCurve_ = CreateAnimation(jumpInterval_, 0.0);
761     fromTopCurve_ = CreateAnimation(0.0 - jumpInterval_, 0.0);
762     fromController_ = CREATE_ANIMATOR(PipelineContext::GetCurrentContext());
763     fromController_->SetDuration(CLICK_ANIMATION_DURATION); // 300ms for animation that from outer to zero.
764     animationCreated_ = true;
765 }
766 
CreateAnimation(double from,double to)767 RefPtr<CurveAnimation<double>> DatePickerColumnPattern::CreateAnimation(double from, double to)
768 {
769     auto weak = AceType::WeakClaim(this);
770     auto curve = AceType::MakeRefPtr<CurveAnimation<double>>(from, to, Curves::FAST_OUT_SLOW_IN);
771     curve->AddListener(Animation<double>::ValueCallback([weak](double value) {
772         auto column = weak.Upgrade();
773         CHECK_NULL_VOID(column);
774         column->ScrollOption(value);
775     }));
776     return curve;
777 }
778 
HandleCurveStopped()779 void DatePickerColumnPattern::HandleCurveStopped()
780 {
781     CHECK_NULL_VOID_NOLOG(animationCreated_);
782     if (NearZero(scrollDelta_)) {
783         return;
784     }
785     ScrollOption(0.0 - scrollDelta_);
786     InnerHandleScroll(GreatNotEqual(scrollDelta_, 0.0));
787     fromController_->ClearInterpolators();
788     if (LessNotEqual(scrollDelta_, 0.0)) {
789         fromController_->AddInterpolator(fromTopCurve_);
790     } else {
791         fromController_->AddInterpolator(fromBottomCurve_);
792     }
793     fromController_->Play();
794 }
795 
ScrollOption(double delta,bool isJump)796 void DatePickerColumnPattern::ScrollOption(double delta, bool isJump)
797 {
798     scrollDelta_ = delta;
799     auto midIndex = GetShowCount() / 2;
800     DatePickerScrollDirection dir = delta > 0.0 ? DatePickerScrollDirection::DOWN : DatePickerScrollDirection::UP;
801     auto shiftDistance = (dir == DatePickerScrollDirection::UP) ? optionProperties_[midIndex].prevDistance
802                                                                 : optionProperties_[midIndex].nextDistance;
803     auto distancePercent = delta / shiftDistance;
804     auto textThresHold = optionProperties_[midIndex].height / 4; // ux required
805     auto textLinearPercent = 0.0;
806     if (std::abs(delta) > textThresHold) {
807         textLinearPercent = (std::abs(delta) - textThresHold) / (std::abs(shiftDistance) - textThresHold);
808     }
809     UpdateTextPropertiesLinear(LessNotEqual(delta, 0.0), textLinearPercent);
810     CalcAlgorithmOffset(dir, distancePercent);
811     auto host = GetHost();
812     CHECK_NULL_VOID(host);
813     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
814 }
815 
ResetAlgorithmOffset()816 void DatePickerColumnPattern::ResetAlgorithmOffset()
817 {
818     algorithmOffset_.clear();
819 
820     uint32_t counts = GetShowCount();
821     for (uint32_t i = 0; i < counts; i++) {
822         algorithmOffset_.emplace_back(0.0f);
823     }
824 }
825 
CalcAlgorithmOffset(DatePickerScrollDirection dir,double distancePercent)826 void DatePickerColumnPattern::CalcAlgorithmOffset(DatePickerScrollDirection dir, double distancePercent)
827 {
828     algorithmOffset_.clear();
829     uint32_t counts = GetShowCount();
830 
831     for (uint32_t i = 0; i < counts; i++) {
832         auto distance = (dir == DatePickerScrollDirection::UP) ? optionProperties_[i].prevDistance
833                                                                : optionProperties_[i].nextDistance;
834         algorithmOffset_.emplace_back(distance * distancePercent);
835     }
836 }
837 
UpdateToss(double offsetY)838 void DatePickerColumnPattern::UpdateToss(double offsetY)
839 {
840     UpdateColumnChildPosition(offsetY);
841 }
842 
TossStoped()843 void DatePickerColumnPattern::TossStoped()
844 {
845     yOffset_ = 0.0;
846     yLast_ = 0.0;
847     ScrollOption(0.0);
848 }
849 
CalcScrollIndex(int32_t totalOptionCount,int32_t currentIndex,bool canLoop,int32_t step)850 int32_t DatePickerColumnPattern::CalcScrollIndex(
851     int32_t totalOptionCount, int32_t currentIndex, bool canLoop, int32_t step)
852 {
853     int32_t nextIndex = currentIndex;
854     if (!canLoop) {
855         // scroll down
856         if (step > 0) {
857             nextIndex = (currentIndex + step) > (totalOptionCount - 1) ? totalOptionCount - 1 : currentIndex + step;
858             // scroll up
859         } else if (step < 0) {
860             nextIndex = currentIndex + step < 0 ? 0 : currentIndex + step;
861         }
862     } else {
863         if (totalOptionCount != 0) {
864             nextIndex = (totalOptionCount + currentIndex + step) % totalOptionCount;
865         }
866     }
867     return nextIndex;
868 }
869 
GetShiftDistance(uint32_t index,DatePickerScrollDirection dir)870 float DatePickerColumnPattern::GetShiftDistance(uint32_t index, DatePickerScrollDirection dir)
871 {
872     auto pipeline = PipelineBase::GetCurrentContext();
873     CHECK_NULL_RETURN(pipeline, 0.0f);
874     auto theme = pipeline->GetTheme<PickerTheme>();
875     CHECK_NULL_RETURN(theme, 0.0f);
876     uint32_t optionCounts = theme->GetShowOptionCount();
877     LOGD("DatePickerColumnPattern::GetShiftDistance start showCount %{public}d", optionCounts);
878     uint32_t nextIndex = 0;
879     float distance = 0.0f;
880     float val = 0.0f;
881     auto isDown = dir == DatePickerScrollDirection::DOWN;
882     if (optionCounts == 0) {
883         return distance;
884     }
885     if (isDown) {
886         nextIndex = (optionCounts + index + 1) % optionCounts; // index add one
887     } else {
888         nextIndex = (optionCounts + index - 1) % optionCounts; // index reduce one
889     }
890     switch (static_cast<DatePickerOptionIndex>(index)) {
891         case DatePickerOptionIndex::COLUMN_INDEX_0: // first
892             distance = (dir == DatePickerScrollDirection::DOWN) ? optionProperties_[index].height
893                                                                 : (0.0f - optionProperties_[index].height);
894             break;
895         case DatePickerOptionIndex::COLUMN_INDEX_1:
896             if (dir == DatePickerScrollDirection::UP) {
897                 distance = -optionProperties_[nextIndex].height;
898             } else {
899                 distance = optionProperties_[index].height +
900                            (optionProperties_[nextIndex].height - optionProperties_[nextIndex].fontheight) /
901                                MINDDLE_CHILD_INDEX;
902             }
903             break;
904         case DatePickerOptionIndex::COLUMN_INDEX_2:
905             val = optionProperties_[index].height / MINDDLE_CHILD_INDEX + optionProperties_[nextIndex].height -
906                   optionProperties_[nextIndex].fontheight / MINDDLE_CHILD_INDEX;
907             distance = (dir == DatePickerScrollDirection::DOWN) ? val : (0.0f - val);
908             break;
909         case DatePickerOptionIndex::COLUMN_INDEX_3:
910             if (dir == DatePickerScrollDirection::DOWN) {
911                 distance = optionProperties_[nextIndex].height;
912             } else {
913                 val = optionProperties_[index].height +
914                       (optionProperties_[nextIndex].height - optionProperties_[nextIndex].fontheight) /
915                           MINDDLE_CHILD_INDEX;
916                 distance = 0.0f - val;
917             }
918             break;
919         case DatePickerOptionIndex::COLUMN_INDEX_4: // last
920             distance = (dir == DatePickerScrollDirection::DOWN) ? optionProperties_[index].height
921                                                                 : (0.0f - optionProperties_[index].height);
922             break;
923         default:
924             break;
925     }
926     return distance;
927 }
928 
GetShiftDistanceForLandscape(uint32_t index,DatePickerScrollDirection dir)929 float DatePickerColumnPattern::GetShiftDistanceForLandscape(uint32_t index, DatePickerScrollDirection dir)
930 {
931     auto pipeline = PipelineBase::GetCurrentContext();
932     CHECK_NULL_RETURN(pipeline, 0.0f);
933     auto theme = pipeline->GetTheme<PickerTheme>();
934     CHECK_NULL_RETURN(theme, 0.0f);
935     uint32_t optionCounts = theme->GetShowOptionCount();
936     uint32_t nextIndex = 0;
937     float distance = 0.0f;
938     float val = 0.0f;
939     auto isDown = dir == DatePickerScrollDirection::DOWN;
940     if (optionCounts == 0) {
941         return distance;
942     }
943     if (isDown) {
944         nextIndex = (optionCounts + index + 1) % optionCounts; // index add one
945     } else {
946         nextIndex = (optionCounts + index - 1) % optionCounts; // index reduce one
947     }
948 
949     switch (static_cast<DatePickerOptionIndex>(index)) {
950         case DatePickerOptionIndex::COLUMN_INDEX_0: // first
951 
952             if (dir == DatePickerScrollDirection::UP) {
953                 distance = 0.0f - optionProperties_[index].height;
954             } else {
955                 distance = optionProperties_[index].height +
956                            (optionProperties_[nextIndex].height - optionProperties_[nextIndex].fontheight) /
957                                MINDDLE_CHILD_INDEX;
958             }
959             break;
960         case DatePickerOptionIndex::COLUMN_INDEX_1:
961             val = optionProperties_[index].height / MINDDLE_CHILD_INDEX + optionProperties_[nextIndex].height -
962                   optionProperties_[nextIndex].fontheight / MINDDLE_CHILD_INDEX;
963             distance = (dir == DatePickerScrollDirection::DOWN) ? val : (0.0f - val);
964             break;
965         case DatePickerOptionIndex::COLUMN_INDEX_2: // last
966             if (dir == DatePickerScrollDirection::DOWN) {
967                 distance = optionProperties_[index].height;
968             } else {
969                 val = optionProperties_[index].height +
970                       (optionProperties_[nextIndex].height - optionProperties_[nextIndex].fontheight) /
971                           MINDDLE_CHILD_INDEX;
972                 distance = 0.0f - val;
973             }
974             break;
975         default:
976             break;
977     }
978     return distance;
979 }
980 
SetOptionShiftDistance()981 void DatePickerColumnPattern::SetOptionShiftDistance()
982 {
983     auto pipeline = PipelineBase::GetCurrentContext();
984     CHECK_NULL_VOID(pipeline);
985     auto theme = pipeline->GetTheme<PickerTheme>();
986     CHECK_NULL_VOID(theme);
987     uint32_t itemCounts = theme->GetShowOptionCount();
988     bool isLanscape = itemCounts == OPTION_COUNT_PHONE_LANDSCAPE;
989     for (uint32_t i = 0; i < itemCounts; i++) {
990         DatePickerOptionProperty& prop = optionProperties_[i];
991         if (isLanscape) {
992             prop.prevDistance = GetShiftDistanceForLandscape(i, DatePickerScrollDirection::UP);
993             prop.nextDistance = GetShiftDistanceForLandscape(i, DatePickerScrollDirection::DOWN);
994         } else {
995             prop.prevDistance = GetShiftDistance(i, DatePickerScrollDirection::UP);
996             prop.nextDistance = GetShiftDistance(i, DatePickerScrollDirection::DOWN);
997         }
998     }
999 }
1000 
UpdateColumnChildPosition(double offsetY)1001 void DatePickerColumnPattern::UpdateColumnChildPosition(double offsetY)
1002 {
1003     yLast_ = offsetY;
1004     auto dragDelta = yLast_ - yOffset_;
1005     if (!CanMove(LessNotEqual(dragDelta, 0))) {
1006         return;
1007     }
1008     auto midIndex = GetShowCount() / 2;
1009     DatePickerScrollDirection dir = dragDelta > 0.0 ? DatePickerScrollDirection::DOWN : DatePickerScrollDirection::UP;
1010     auto shiftDistance = (dir == DatePickerScrollDirection::UP) ? optionProperties_[midIndex].prevDistance
1011                                                                 : optionProperties_[midIndex].nextDistance;
1012     // the abs of drag delta is less than jump interval.
1013     if (GreatOrEqual(std::abs(dragDelta), std::abs(shiftDistance))) {
1014         InnerHandleScroll(LessNotEqual(dragDelta, 0.0), true, false);
1015         dragDelta = dragDelta - (abs(shiftDistance) * (LessNotEqual(dragDelta, 0.0) ? -1 : 1));
1016         yOffset_ = offsetY;
1017     }
1018     // update selected option
1019     ScrollOption(dragDelta);
1020 }
1021 
ShiftOptionProp(RefPtr<FrameNode> curNode,RefPtr<FrameNode> shiftNode)1022 void DatePickerColumnPattern::ShiftOptionProp(RefPtr<FrameNode> curNode, RefPtr<FrameNode> shiftNode)
1023 {
1024     RefPtr<TextPattern> curPattern = curNode->GetPattern<TextPattern>();
1025     CHECK_NULL_VOID(curPattern);
1026     RefPtr<TextLayoutProperty> curLayoutProperty = curPattern->GetLayoutProperty<TextLayoutProperty>();
1027     CHECK_NULL_VOID(curLayoutProperty);
1028 
1029     RefPtr<TextPattern> shiftPattern = shiftNode->GetPattern<TextPattern>();
1030     CHECK_NULL_VOID(shiftPattern);
1031     RefPtr<TextLayoutProperty> shiftLayoutProperty = shiftPattern->GetLayoutProperty<TextLayoutProperty>();
1032     CHECK_NULL_VOID(shiftLayoutProperty);
1033     curLayoutProperty->UpdateFontWeight(shiftLayoutProperty->GetFontWeight().value_or(FontWeight::W100));
1034 }
1035 
CanMove(bool isDown) const1036 bool DatePickerColumnPattern::CanMove(bool isDown) const
1037 {
1038     CHECK_NULL_RETURN_NOLOG(NotLoopOptions(), true);
1039     auto host = GetHost();
1040     CHECK_NULL_RETURN(host, false);
1041     auto options = GetOptions();
1042     int totalOptionCount = static_cast<int>(options[host].size());
1043 
1044     auto datePickerColumnPattern = host->GetPattern<DatePickerColumnPattern>();
1045     CHECK_NULL_RETURN(datePickerColumnPattern, false);
1046     int currentIndex = static_cast<int>(datePickerColumnPattern->GetCurrentIndex());
1047     int nextVirtualIndex = isDown ? currentIndex + 1 : currentIndex - 1;
1048     return nextVirtualIndex >= 0 && nextVirtualIndex < totalOptionCount;
1049 }
1050 
SetAccessibilityAction()1051 void DatePickerColumnPattern::SetAccessibilityAction()
1052 {
1053     auto host = GetHost();
1054     CHECK_NULL_VOID(host);
1055     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1056     CHECK_NULL_VOID(accessibilityProperty);
1057     accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
1058         const auto& pattern = weakPtr.Upgrade();
1059         CHECK_NULL_VOID(pattern);
1060         if (!pattern->CanMove(true)) {
1061             return;
1062         }
1063         CHECK_NULL_VOID(pattern->animationCreated_);
1064         pattern->InnerHandleScroll(true);
1065         pattern->fromController_->ClearInterpolators();
1066         pattern->fromController_->AddInterpolator(pattern->fromTopCurve_);
1067         pattern->fromController_->Play();
1068         auto frameNode = pattern->GetHost();
1069         CHECK_NULL_VOID(frameNode);
1070         frameNode->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
1071     });
1072 
1073     accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)]() {
1074         const auto& pattern = weakPtr.Upgrade();
1075         CHECK_NULL_VOID(pattern);
1076         if (!pattern->CanMove(false)) {
1077             return;
1078         }
1079         CHECK_NULL_VOID(pattern->animationCreated_);
1080         pattern->InnerHandleScroll(false);
1081         pattern->fromController_->ClearInterpolators();
1082         pattern->fromController_->AddInterpolator(pattern->fromBottomCurve_);
1083         pattern->fromController_->Play();
1084         auto frameNode = pattern->GetHost();
1085         CHECK_NULL_VOID(frameNode);
1086         frameNode->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
1087     });
1088 }
1089 
CreateItemClickEventListener(RefPtr<DatePickerEventParam> param)1090 RefPtr<ClickEvent> DatePickerColumnPattern::CreateItemClickEventListener(RefPtr<DatePickerEventParam> param)
1091 {
1092     auto clickEventHandler = [param, weak = WeakClaim(this)](const GestureEvent& /* info */) {
1093         auto pattern = weak.Upgrade();
1094         pattern->OnAroundButtonClick(param);
1095     };
1096     auto listener = AceType::MakeRefPtr<NG::ClickEvent>(clickEventHandler);
1097     return listener;
1098 }
1099 
OnAroundButtonClick(RefPtr<DatePickerEventParam> param)1100 void DatePickerColumnPattern::OnAroundButtonClick(RefPtr<DatePickerEventParam> param)
1101 {
1102     int32_t middleIndex = GetShowCount() / 2;
1103     int32_t step = param->itemIndex_ - middleIndex;
1104     if (step != 0) {
1105         if (fromController_->IsRunning()) {
1106             fromController_->Finish();
1107         }
1108         for (int32_t i = 0; i < abs(step); i++) {
1109             InnerHandleScroll(step > 0 ? true : false);
1110         }
1111         auto distance =
1112             (step > 0 ? optionProperties_[middleIndex].prevDistance : optionProperties_[middleIndex].nextDistance) *
1113             std::abs(step);
1114         auto curveTop = CreateAnimation(abs(distance), 0.0);
1115         auto curveBottom = CreateAnimation(0 - abs(distance), 0.0);
1116         fromController_->ClearInterpolators();
1117 
1118         fromController_->AddInterpolator(step > 0 ? curveTop : curveBottom);
1119         fromController_->SetDuration(CLICK_ANIMATION_DURATION);
1120         fromController_->Play();
1121     }
1122 }
1123 } // namespace OHOS::Ace::NG
1124