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