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