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