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 textLayoutProperty->UpdateAdaptMaxFontSize(Dimension());
746 textLayoutProperty->UpdateAdaptMinFontSize(Dimension());
747 } else {
748 textLayoutProperty->UpdateAdaptMaxFontSize(normalOptionSize);
749 textLayoutProperty->UpdateAdaptMinFontSize(pickerTheme->GetOptionStyle(false, false).GetAdaptMinFontSize());
750 }
751 textLayoutProperty->UpdateFontWeight(textPickerLayoutProperty->GetDisappearWeight().value_or(
752 pickerTheme->GetOptionStyle(false, false).GetFontWeight()));
753 auto fontFamilyVector = textPickerLayoutProperty->GetDisappearFontFamily().value_or(
754 pickerTheme->GetOptionStyle(false, false).GetFontFamilies());
755 textLayoutProperty->UpdateFontFamily(fontFamilyVector.empty() ? FONT_FAMILY_DEFAULT : fontFamilyVector);
756 textLayoutProperty->UpdateItalicFontStyle(textPickerLayoutProperty->GetDisappearFontStyle().value_or(
757 pickerTheme->GetOptionStyle(false, false).GetFontStyle()));
758 }
759
UpdateCandidateTextProperties(const RefPtr<PickerTheme> & pickerTheme,const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty)760 void TextPickerColumnPattern::UpdateCandidateTextProperties(const RefPtr<PickerTheme>& pickerTheme,
761 const RefPtr<TextLayoutProperty>& textLayoutProperty,
762 const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty)
763 {
764 auto focusOptionSize = pickerTheme->GetOptionStyle(false, false).GetFontSize() + FONT_SIZE;
765 textLayoutProperty->UpdateTextColor(
766 textPickerLayoutProperty->GetColor().value_or(pickerTheme->GetOptionStyle(false, false).GetTextColor()));
767 if (textPickerLayoutProperty->HasFontSize()) {
768 textLayoutProperty->UpdateFontSize(textPickerLayoutProperty->GetFontSize().value());
769 textLayoutProperty->UpdateAdaptMaxFontSize(Dimension());
770 textLayoutProperty->UpdateAdaptMinFontSize(Dimension());
771 } else {
772 textLayoutProperty->UpdateAdaptMaxFontSize(focusOptionSize);
773 textLayoutProperty->UpdateAdaptMinFontSize(
774 pickerTheme->GetOptionStyle(true, false).GetAdaptMinFontSize() - FOCUS_SIZE);
775 }
776 textLayoutProperty->UpdateFontWeight(
777 textPickerLayoutProperty->GetWeight().value_or(pickerTheme->GetOptionStyle(false, false).GetFontWeight()));
778 auto fontFamilyVector = textPickerLayoutProperty->GetFontFamily().value_or(
779 pickerTheme->GetOptionStyle(false, false).GetFontFamilies());
780 textLayoutProperty->UpdateFontFamily(fontFamilyVector.empty() ? FONT_FAMILY_DEFAULT : fontFamilyVector);
781 textLayoutProperty->UpdateItalicFontStyle(
782 textPickerLayoutProperty->GetFontStyle().value_or(pickerTheme->GetOptionStyle(false, false).GetFontStyle()));
783 }
784
UpdateSelectedTextProperties(const RefPtr<PickerTheme> & pickerTheme,const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty)785 void TextPickerColumnPattern::UpdateSelectedTextProperties(const RefPtr<PickerTheme>& pickerTheme,
786 const RefPtr<TextLayoutProperty>& textLayoutProperty,
787 const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty)
788 {
789 auto selectedOptionSize = pickerTheme->GetOptionStyle(true, false).GetFontSize();
790 textLayoutProperty->UpdateTextColor(
791 textPickerLayoutProperty->GetSelectedColor().value_or(pickerTheme->GetOptionStyle(true, false).GetTextColor()));
792 if (textPickerLayoutProperty->HasSelectedFontSize()) {
793 textLayoutProperty->UpdateFontSize(textPickerLayoutProperty->GetSelectedFontSize().value());
794 textLayoutProperty->UpdateAdaptMaxFontSize(Dimension());
795 textLayoutProperty->UpdateAdaptMinFontSize(Dimension());
796 } else {
797 textLayoutProperty->UpdateAdaptMaxFontSize(selectedOptionSize);
798 textLayoutProperty->UpdateAdaptMinFontSize(pickerTheme->GetOptionStyle(true, false).GetAdaptMinFontSize());
799 }
800 textLayoutProperty->UpdateFontWeight(textPickerLayoutProperty->GetSelectedWeight().value_or(
801 pickerTheme->GetOptionStyle(true, false).GetFontWeight()));
802 auto fontFamilyVector = textPickerLayoutProperty->GetSelectedFontFamily().value_or(
803 pickerTheme->GetOptionStyle(true, false).GetFontFamilies());
804 textLayoutProperty->UpdateFontFamily(fontFamilyVector.empty() ? FONT_FAMILY_DEFAULT : fontFamilyVector);
805 textLayoutProperty->UpdateItalicFontStyle(textPickerLayoutProperty->GetSelectedFontStyle().value_or(
806 pickerTheme->GetOptionStyle(true, false).GetFontStyle()));
807 }
808
UpdateDefaultTextProperties(const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty)809 void TextPickerColumnPattern::UpdateDefaultTextProperties(const RefPtr<TextLayoutProperty>& textLayoutProperty,
810 const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty)
811 {
812 CHECK_NULL_VOID(textLayoutProperty);
813 CHECK_NULL_VOID(textPickerLayoutProperty);
814 auto host = GetHost();
815 CHECK_NULL_VOID(host);
816 auto context = host->GetContext();
817 CHECK_NULL_VOID(context);
818 auto theme = context->GetTheme<TextTheme>();
819 CHECK_NULL_VOID(theme);
820 auto textStyle = theme->GetTextStyle();
821 textLayoutProperty->UpdateFontSize(
822 textPickerLayoutProperty->GetDefaultFontSize().value_or(textStyle.GetFontSize()));
823 textLayoutProperty->UpdateFontWeight(
824 textPickerLayoutProperty->GetDefaultWeight().value_or(textStyle.GetFontWeight()));
825 textLayoutProperty->UpdateTextColor(textPickerLayoutProperty->GetDefaultColor().value_or(textStyle.GetTextColor()));
826 textLayoutProperty->UpdateFontFamily(
827 textPickerLayoutProperty->GetDefaultFontFamily().value_or(textStyle.GetFontFamilies()));
828 textLayoutProperty->UpdateItalicFontStyle(
829 textPickerLayoutProperty->GetDefaultFontStyle().value_or(textStyle.GetFontStyle()));
830 textLayoutProperty->UpdateAdaptMinFontSize(textPickerLayoutProperty->GetDefaultMinFontSize().value_or(Dimension()));
831 textLayoutProperty->UpdateAdaptMaxFontSize(textPickerLayoutProperty->GetDefaultMaxFontSize().value_or(Dimension()));
832 if (textPickerLayoutProperty->GetDefaultTextOverflow().has_value() &&
833 textPickerLayoutProperty->GetDefaultTextOverflow().value() != TextOverflow::MARQUEE) {
834 textLayoutProperty->UpdateTextOverflow(textPickerLayoutProperty->GetDefaultTextOverflow().value());
835 } else {
836 textLayoutProperty->UpdateTextOverflow(textStyle.GetTextOverflow());
837 }
838 textLayoutProperty->UpdateHeightAdaptivePolicy(TextHeightAdaptivePolicy::MIN_FONT_SIZE_FIRST);
839 textLayoutProperty->UpdateMaxLines(1);
840 }
841
AddAnimationTextProperties(uint32_t currentIndex,const RefPtr<TextLayoutProperty> & textLayoutProperty)842 void TextPickerColumnPattern::AddAnimationTextProperties(
843 uint32_t currentIndex, const RefPtr<TextLayoutProperty>& textLayoutProperty)
844 {
845 TextProperties properties{};
846 if (textLayoutProperty->HasFontSize()) {
847 MeasureContext measureContext;
848 measureContext.textContent = MEASURE_STRING;
849 measureContext.fontSize = textLayoutProperty->GetFontSize().value();
850 if (textLayoutProperty->HasFontFamily()) {
851 auto fontFamilyVector = textLayoutProperty->GetFontFamily().value();
852 if (fontFamilyVector.empty()) {
853 measureContext.fontFamily = FONT_FAMILY_DEFAULT[0];
854 } else {
855 measureContext.fontFamily = fontFamilyVector[0];
856 }
857 } else {
858 measureContext.fontFamily = FONT_FAMILY_DEFAULT[0];
859 }
860 auto size = MeasureUtil::MeasureTextSize(measureContext);
861 if (!optionProperties_.empty()) {
862 optionProperties_[currentIndex].fontheight = size.Height();
863 if (optionProperties_[currentIndex].fontheight > optionProperties_[currentIndex].height) {
864 optionProperties_[currentIndex].fontheight = optionProperties_[currentIndex].height;
865 }
866 }
867 SetOptionShiftDistance();
868 properties.fontSize = Dimension(textLayoutProperty->GetFontSize().value().ConvertToPx());
869 }
870 if (textLayoutProperty->HasTextColor()) {
871 properties.currentColor = textLayoutProperty->GetTextColor().value();
872 }
873 if (textLayoutProperty->HasFontWeight()) {
874 properties.fontWeight = textLayoutProperty->GetFontWeight().value();
875 }
876 if (currentIndex > 0) {
877 properties.upFontSize = animationProperties_[currentIndex - 1].fontSize;
878 animationProperties_[currentIndex - 1].downFontSize = properties.fontSize;
879
880 properties.upColor = animationProperties_[currentIndex - 1].currentColor;
881 animationProperties_[currentIndex - 1].downColor = properties.currentColor;
882
883 properties.upFontWeight = animationProperties_[currentIndex - 1].fontWeight;
884 animationProperties_[currentIndex - 1].downFontWeight = properties.fontWeight;
885 }
886 animationProperties_.emplace_back(properties);
887 }
888
UpdatePickerTextProperties(const RefPtr<TextLayoutProperty> & textLayoutProperty,const RefPtr<TextPickerLayoutProperty> & textPickerLayoutProperty,uint32_t currentIndex,uint32_t middleIndex,uint32_t showCount)889 void TextPickerColumnPattern::UpdatePickerTextProperties(const RefPtr<TextLayoutProperty>& textLayoutProperty,
890 const RefPtr<TextPickerLayoutProperty>& textPickerLayoutProperty, uint32_t currentIndex, uint32_t middleIndex,
891 uint32_t showCount)
892 {
893 if (textPickerLayoutProperty && textPickerLayoutProperty->GetDisableTextStyleAnimation().value_or(false)) {
894 UpdateDefaultTextProperties(textLayoutProperty, textPickerLayoutProperty);
895 return;
896 }
897 auto host = GetHost();
898 CHECK_NULL_VOID(host);
899 auto context = host->GetContext();
900 CHECK_NULL_VOID(context);
901 auto pickerTheme = context->GetTheme<PickerTheme>();
902 CHECK_NULL_VOID(pickerTheme);
903 if (currentIndex == middleIndex) {
904 UpdateSelectedTextProperties(pickerTheme, textLayoutProperty, textPickerLayoutProperty);
905 textLayoutProperty->UpdateAlignment(Alignment::CENTER);
906 } else if ((currentIndex == middleIndex + 1) || (currentIndex == middleIndex - 1)) {
907 UpdateCandidateTextProperties(pickerTheme, textLayoutProperty, textPickerLayoutProperty);
908 } else {
909 UpdateDisappearTextProperties(pickerTheme, textLayoutProperty, textPickerLayoutProperty);
910 }
911 if (currentIndex < middleIndex) {
912 textLayoutProperty->UpdateAlignment(Alignment::TOP_CENTER);
913 } else if (currentIndex > middleIndex) {
914 textLayoutProperty->UpdateAlignment(Alignment::BOTTOM_CENTER);
915 }
916 textLayoutProperty->UpdateMaxLines(1);
917 textLayoutProperty->UpdateTextOverflow(TextOverflow::CLIP);
918 AddAnimationTextProperties(currentIndex, textLayoutProperty);
919 }
920
TextPropertiesLinearAnimation(const RefPtr<TextLayoutProperty> & textLayoutProperty,uint32_t idx,uint32_t showCount,bool isDown,double scaleSize)921 void TextPickerColumnPattern::TextPropertiesLinearAnimation(const RefPtr<TextLayoutProperty>& textLayoutProperty,
922 uint32_t idx, uint32_t showCount, bool isDown, double scaleSize)
923 {
924 uint32_t deltaIdx = static_cast<uint32_t>(GetOverScrollDeltaIndex());
925 auto index = idx;
926 if (GreatNotEqual(scrollDelta_, 0.0f)) {
927 index = index + deltaIdx;
928 } else {
929 if (index < deltaIdx) {
930 return;
931 }
932 index = index - deltaIdx;
933 }
934
935 auto percent = distancePercent_ - deltaIdx;
936 auto scale = scaleSize - deltaIdx;
937
938 if (index >= animationProperties_.size()) {
939 return;
940 }
941 Dimension startFontSize = animationProperties_[index].fontSize;
942 Color startColor = animationProperties_[index].currentColor;
943 if ((!index && isDown) || ((index == (showCount - 1)) && !isDown)) {
944 textLayoutProperty->UpdateFontSize(startFontSize);
945 textLayoutProperty->UpdateTextColor(startColor);
946 return;
947 }
948 Dimension endFontSize;
949 Color endColor;
950 if (GreatNotEqual(scrollDelta_, 0.0)) {
951 endFontSize = animationProperties_[index].downFontSize;
952 endColor = animationProperties_[index].downColor;
953 if (GreatOrEqual(scale, FONTWEIGHT)) {
954 textLayoutProperty->UpdateFontWeight(animationProperties_[index].downFontWeight);
955 }
956 } else {
957 endFontSize = animationProperties_[index].upFontSize;
958 endColor = animationProperties_[index].upColor;
959 if (GreatOrEqual(scale, FONTWEIGHT)) {
960 textLayoutProperty->UpdateFontWeight(animationProperties_[index].upFontWeight);
961 }
962 }
963 Dimension updateSize = LinearFontSize(startFontSize, endFontSize, percent);
964 textLayoutProperty->UpdateFontSize(updateSize);
965 auto colorEvaluator = AceType::MakeRefPtr<LinearEvaluator<Color>>();
966 Color updateColor = colorEvaluator->Evaluate(startColor, endColor, std::abs(percent));
967 textLayoutProperty->UpdateTextColor(updateColor);
968 if (scale < FONTWEIGHT) {
969 textLayoutProperty->UpdateFontWeight(animationProperties_[index].fontWeight);
970 }
971 }
972
GetOverScrollDeltaIndex() const973 int32_t TextPickerColumnPattern::GetOverScrollDeltaIndex() const
974 {
975 auto deltaIdx = 0;
976 if (NotLoopOptions() && overscroller_.IsOverScroll()) {
977 auto midIndex = GetShowOptionCount() / HALF_NUMBER;
978 auto shiftDistance = optionProperties_[midIndex].nextDistance;
979 for (auto idx = midIndex; idx < GetShowOptionCount(); idx++) {
980 if (shiftDistance > std::abs(scrollDelta_)) {
981 break;
982 }
983 shiftDistance += optionProperties_[idx].nextDistance;
984 deltaIdx++;
985 }
986 }
987 return deltaIdx;
988 }
989
UpdateTextPropertiesLinear(bool isDown,double scale)990 void TextPickerColumnPattern::UpdateTextPropertiesLinear(bool isDown, double scale)
991 {
992 if (columnkind_ == ICON) {
993 return;
994 }
995 auto host = GetHost();
996 CHECK_NULL_VOID(host);
997 uint32_t showCount = GetShowOptionCount();
998 auto child = host->GetChildren();
999 auto iter = child.begin();
1000 if (child.size() != showCount) {
1001 return;
1002 }
1003 for (uint32_t index = 0; index < showCount; index++) {
1004 auto rangeNode = DynamicCast<FrameNode>(*iter);
1005 CHECK_NULL_VOID(rangeNode);
1006 RefPtr<TextLayoutProperty> textLayoutProperty;
1007 if (columnkind_ == TEXT) {
1008 auto textPattern = rangeNode->GetPattern<TextPattern>();
1009 CHECK_NULL_VOID(textPattern);
1010 textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
1011 CHECK_NULL_VOID(textLayoutProperty);
1012 TextPropertiesLinearAnimation(textLayoutProperty, index, showCount, isDown, scale);
1013 } else if (columnkind_ == MIXTURE) {
1014 auto children = rangeNode->GetChildren();
1015 if (children.size() != MIXTURE_CHILD_COUNT) {
1016 continue;
1017 }
1018 auto textNode = DynamicCast<FrameNode>(rangeNode->GetLastChild());
1019 auto textPattern = textNode->GetPattern<TextPattern>();
1020 textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
1021 CHECK_NULL_VOID(textLayoutProperty);
1022 TextPropertiesLinearAnimation(textLayoutProperty, index, showCount, isDown, scale);
1023 textNode->MarkModifyDone();
1024 textNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1025 }
1026 rangeNode->MarkModifyDone();
1027 rangeNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1028 iter++;
1029 }
1030 }
1031
LinearFontSize(const Dimension & startFontSize,const Dimension & endFontSize,double percent)1032 Dimension TextPickerColumnPattern::LinearFontSize(
1033 const Dimension& startFontSize, const Dimension& endFontSize, double percent)
1034 {
1035 if (percent > FONT_SIZE_PERCENT) {
1036 return startFontSize + (endFontSize - startFontSize);
1037 } else {
1038 return startFontSize + (endFontSize - startFontSize) * std::abs(percent);
1039 }
1040 }
1041
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)1042 void TextPickerColumnPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
1043 {
1044 CHECK_NULL_VOID(!panEvent_);
1045 auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& event) {
1046 auto pattern = weak.Upgrade();
1047 CHECK_NULL_VOID(pattern);
1048 if (event.GetInputEventType() == InputEventType::AXIS && event.GetSourceTool() == SourceTool::MOUSE) {
1049 return;
1050 }
1051 pattern->HandleDragStart(event);
1052 };
1053 auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& event) {
1054 auto pattern = weak.Upgrade();
1055 CHECK_NULL_VOID(pattern);
1056 pattern->SetMainVelocity(event.GetMainVelocity());
1057 pattern->HandleDragMove(event);
1058 };
1059 auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1060 auto pattern = weak.Upgrade();
1061 CHECK_NULL_VOID(pattern);
1062 if (info.GetInputEventType() == InputEventType::AXIS && info.GetSourceTool() == SourceTool::MOUSE) {
1063 return;
1064 }
1065 pattern->SetMainVelocity(info.GetMainVelocity());
1066 pattern->HandleDragEnd();
1067 };
1068 auto actionCancelTask = [weak = WeakClaim(this)]() {
1069 auto pattern = weak.Upgrade();
1070 CHECK_NULL_VOID(pattern);
1071 pattern->HandleDragEnd();
1072 };
1073 PanDirection panDirection;
1074 panDirection.type = PanDirection::VERTICAL;
1075 panEvent_ = MakeRefPtr<PanEvent>(
1076 std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
1077 gestureHub->AddPanEvent(panEvent_, panDirection, DEFAULT_PAN_FINGER, DEFAULT_PAN_DISTANCE);
1078 }
1079
GetParentLayout() const1080 RefPtr<TextPickerLayoutProperty> TextPickerColumnPattern::GetParentLayout() const
1081 {
1082 auto host = GetHost();
1083 CHECK_NULL_RETURN(host, nullptr);
1084 auto blendNode = DynamicCast<FrameNode>(host->GetParent());
1085 CHECK_NULL_RETURN(blendNode, nullptr);
1086 auto stackNode = DynamicCast<FrameNode>(blendNode->GetParent());
1087 CHECK_NULL_RETURN(stackNode, nullptr);
1088 auto parentNode = DynamicCast<FrameNode>(stackNode->GetParent());
1089 CHECK_NULL_RETURN(parentNode, nullptr);
1090
1091 auto property = parentNode->GetLayoutProperty<TextPickerLayoutProperty>();
1092 return property;
1093 }
1094
HandleDragStart(const GestureEvent & event)1095 void TextPickerColumnPattern::HandleDragStart(const GestureEvent& event)
1096 {
1097 CHECK_NULL_VOID(GetToss());
1098 auto toss = GetToss();
1099 auto offsetY = event.GetGlobalPoint().GetY();
1100 toss->SetStart(offsetY);
1101 yLast_ = offsetY;
1102 overscroller_.SetStart(offsetY);
1103 pressed_ = true;
1104 auto frameNode = GetHost();
1105 CHECK_NULL_VOID(frameNode);
1106 frameNode->AddFRCSceneInfo(PICKER_DRAG_SCENE, event.GetMainVelocity(), SceneStatus::START);
1107 // AccessibilityEventType::SCROLL_START
1108
1109 if (animation_) {
1110 AnimationUtils::StopAnimation(animation_);
1111 }
1112
1113 if (NotLoopOptions() && reboundAnimation_) {
1114 AnimationUtils::StopAnimation(reboundAnimation_);
1115 isReboundInProgress_ = false;
1116 overscroller_.ResetVelocity();
1117 overscroller_.SetOverScroll(scrollDelta_);
1118 }
1119 }
1120
HandleDragMove(const GestureEvent & event)1121 void TextPickerColumnPattern::HandleDragMove(const GestureEvent& event)
1122 {
1123 if (event.GetFingerList().size() > 1) {
1124 return;
1125 }
1126 if (event.GetInputEventType() == InputEventType::AXIS && event.GetSourceTool() == SourceTool::MOUSE) {
1127 if (InnerHandleScroll(LessNotEqual(event.GetDelta().GetY(), 0.0), true)) {
1128 HandleScrollStopEventCallback(true);
1129 }
1130 return;
1131 }
1132 animationBreak_ = false;
1133 CHECK_NULL_VOID(pressed_);
1134 CHECK_NULL_VOID(GetHost());
1135 CHECK_NULL_VOID(GetToss());
1136 auto toss = GetToss();
1137 auto offsetY =
1138 event.GetGlobalPoint().GetY() + (event.GetInputEventType() == InputEventType::AXIS ? event.GetOffsetY() : 0.0);
1139 if (NearEqual(offsetY, yLast_, MOVE_THRESHOLD)) { // if changing less than MOVE_THRESHOLD, no need to handle
1140 return;
1141 }
1142 toss->SetEnd(offsetY);
1143 UpdateColumnChildPosition(offsetY);
1144 auto frameNode = GetHost();
1145 CHECK_NULL_VOID(frameNode);
1146 frameNode->AddFRCSceneInfo(PICKER_DRAG_SCENE, event.GetMainVelocity(), SceneStatus::RUNNING);
1147 }
1148
HandleDragEnd()1149 void TextPickerColumnPattern::HandleDragEnd()
1150 {
1151 pressed_ = false;
1152 CHECK_NULL_VOID(GetToss());
1153 auto toss = GetToss();
1154 auto frameNode = GetHost();
1155 CHECK_NULL_VOID(frameNode);
1156 if (NotLoopOptions()) {
1157 frameNode->AddFRCSceneInfo(PICKER_DRAG_SCENE, mainVelocity_, SceneStatus::END);
1158 if (overscroller_.IsOverScroll()) { // Start rebound animation. Higher priority than fling
1159 CreateReboundAnimation(overscroller_.GetOverScroll(), 0.0);
1160 HandleScrollStopEventCallback(true);
1161 return;
1162 }
1163 }
1164 if (toss->Play()) { // Start fling animation
1165 frameNode->AddFRCSceneInfo(PICKER_DRAG_SCENE, mainVelocity_, SceneStatus::END);
1166 // AccessibilityEventType::SCROLL_END
1167 return;
1168 }
1169 yOffset_ = 0.0;
1170 yLast_ = 0.0;
1171 if (!animationCreated_) {
1172 ScrollOption(0.0);
1173 return;
1174 }
1175 int32_t middleIndex = static_cast<int32_t>(GetShowOptionCount()) / HALF_NUMBER;
1176 ScrollDirection dir = GreatNotEqual(scrollDelta_, 0.0) ? ScrollDirection::DOWN : ScrollDirection::UP;
1177 auto shiftDistance = (dir == ScrollDirection::UP) ? optionProperties_[middleIndex].prevDistance
1178 : optionProperties_[middleIndex].nextDistance;
1179 auto shiftThreshold = shiftDistance / HALF_NUMBER;
1180 if (std::abs(scrollDelta_) >= std::abs(shiftThreshold)) {
1181 InnerHandleScroll(LessNotEqual(scrollDelta_, 0.0), true, false);
1182 scrollDelta_ = scrollDelta_ - std::abs(shiftDistance) * (dir == ScrollDirection::UP ? -1 : 1);
1183 if (NearZero(scrollDelta_)) {
1184 HandleScrollStopEventCallback(true);
1185 }
1186 }
1187 CreateAnimation(scrollDelta_, 0.0);
1188 frameNode->AddFRCSceneInfo(PICKER_DRAG_SCENE, mainVelocity_, SceneStatus::END);
1189 if (!NearZero(scrollDelta_)) {
1190 HandleScrollStopEventCallback(true);
1191 }
1192 // AccessibilityEventType::SCROLL_END
1193 }
1194
CreateAnimation()1195 void TextPickerColumnPattern::CreateAnimation()
1196 {
1197 CHECK_NULL_VOID(!animationCreated_);
1198 auto host = GetHost();
1199 CHECK_NULL_VOID(host);
1200 auto renderContext = host->GetRenderContext();
1201 CHECK_NULL_VOID(renderContext);
1202 auto propertyCallback = [weak = AceType::WeakClaim(this)](float value) {
1203 auto column = weak.Upgrade();
1204 CHECK_NULL_VOID(column);
1205 column->ScrollOption(value);
1206 };
1207 scrollProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1208 renderContext->AttachNodeAnimatableProperty(scrollProperty_);
1209
1210 auto aroundClickCallback = [weak = AceType::WeakClaim(this)](float value) {
1211 auto column = weak.Upgrade();
1212 CHECK_NULL_VOID(column);
1213 column->UpdateColumnChildPosition(value);
1214 };
1215 aroundClickProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(aroundClickCallback));
1216 renderContext->AttachNodeAnimatableProperty(aroundClickProperty_);
1217 animationCreated_ = true;
1218 }
1219
CreateAnimation(double from,double to)1220 void TextPickerColumnPattern::CreateAnimation(double from, double to)
1221 {
1222 AnimationOption option;
1223 option.SetCurve(Curves::FAST_OUT_SLOW_IN);
1224 option.SetDuration(CLICK_ANIMATION_DURATION);
1225 scrollProperty_->Set(from);
1226 AnimationUtils::Animate(option, [weak = AceType::WeakClaim(this), to]() {
1227 auto column = weak.Upgrade();
1228 CHECK_NULL_VOID(column);
1229 column->scrollProperty_->Set(to);
1230 });
1231 }
1232
CreateReboundAnimation(double from,double to)1233 void TextPickerColumnPattern::CreateReboundAnimation(double from, double to)
1234 {
1235 isReboundInProgress_ = true;
1236 AnimationOption option;
1237 option.SetCurve(overscroller_.GetReboundCurve());
1238 option.SetDuration(CLICK_ANIMATION_DURATION);
1239 scrollProperty_->Set(from);
1240 reboundAnimation_ = AnimationUtils::StartAnimation(option, [weak = AceType::WeakClaim(this), to]() {
1241 auto column = weak.Upgrade();
1242 CHECK_NULL_VOID(column);
1243 column->scrollProperty_->Set(to);
1244 }, [weak = AceType::WeakClaim(this)]() { // On Finish
1245 auto column = weak.Upgrade();
1246 CHECK_NULL_VOID(column);
1247 if (column->isReboundInProgress_) {
1248 column->isReboundInProgress_ = false;
1249 column->overscroller_.Reset();
1250 column->yLast_ = 0.0;
1251 column->yOffset_ = 0.0;
1252 }
1253 });
1254 }
1255
ScrollOption(double delta)1256 void TextPickerColumnPattern::ScrollOption(double delta)
1257 {
1258 scrollDelta_ = delta;
1259 auto midIndex = GetShowOptionCount() / HALF_NUMBER;
1260 ScrollDirection dir = GreatNotEqual(delta, 0.0) ? ScrollDirection::DOWN : ScrollDirection::UP;
1261 auto shiftDistance = (dir == ScrollDirection::UP) ? optionProperties_[midIndex].prevDistance
1262 : optionProperties_[midIndex].nextDistance;
1263 distancePercent_ = delta / shiftDistance;
1264 auto textLinearPercent = 0.0;
1265 textLinearPercent = (std::abs(delta)) / (optionProperties_[midIndex].height);
1266 UpdateTextPropertiesLinear(LessNotEqual(delta, 0.0), textLinearPercent);
1267 CalcAlgorithmOffset(dir, distancePercent_);
1268 auto host = GetHost();
1269 CHECK_NULL_VOID(host);
1270 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
1271 }
1272
ResetAlgorithmOffset()1273 void TextPickerColumnPattern::ResetAlgorithmOffset()
1274 {
1275 algorithmOffset_.clear();
1276
1277 uint32_t counts = GetShowOptionCount();
1278 for (uint32_t i = 0; i < counts; i++) {
1279 algorithmOffset_.emplace_back(0.0);
1280 }
1281 }
1282
UpdateScrollDelta(double delta)1283 void TextPickerColumnPattern::UpdateScrollDelta(double delta)
1284 {
1285 SetCurrentOffset(delta);
1286 auto host = GetHost();
1287 CHECK_NULL_VOID(host);
1288 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1289 }
1290
CalcAlgorithmOffset(ScrollDirection dir,double distancePercent)1291 void TextPickerColumnPattern::CalcAlgorithmOffset(ScrollDirection dir, double distancePercent)
1292 {
1293 algorithmOffset_.clear();
1294
1295 uint32_t counts = GetShowOptionCount();
1296
1297 for (uint32_t i = 0; i < counts; i++) {
1298 auto distance = (dir == ScrollDirection::UP) ? optionProperties_[i].prevDistance
1299 : optionProperties_[i].nextDistance;
1300 auto val = std::trunc(distance * distancePercent);
1301 algorithmOffset_.emplace_back(static_cast<int32_t>(val));
1302 }
1303 }
1304
GetShiftDistance(int32_t index,ScrollDirection dir)1305 double TextPickerColumnPattern::GetShiftDistance(int32_t index, ScrollDirection dir)
1306 {
1307 if (optionProperties_.empty()) {
1308 return 0.0;
1309 }
1310 int32_t optionCounts = static_cast<int32_t>(GetShowOptionCount());
1311 if (optionCounts == 0) {
1312 return 0.0;
1313 }
1314 int32_t nextIndex = 0;
1315 auto isDown = dir == ScrollDirection::DOWN;
1316 nextIndex = isDown ? (optionCounts + index + 1) % optionCounts : (optionCounts + index - 1) % optionCounts;
1317 double distance = 0.0;
1318 switch (static_cast<OptionIndex>(index)) {
1319 case OptionIndex::COLUMN_INDEX_0: // first
1320 distance = (dir == ScrollDirection::DOWN) ? optionProperties_[index].height
1321 : (0.0 - optionProperties_[index].height);
1322 break;
1323 case OptionIndex::COLUMN_INDEX_1:
1324 distance = (dir == ScrollDirection::DOWN) ? optionProperties_[index].height
1325 : (0.0 - optionProperties_[nextIndex].height);
1326 break;
1327 case OptionIndex::COLUMN_INDEX_2:
1328 distance = GetUpCandidateDistance(index, nextIndex, dir);
1329 break;
1330
1331 case OptionIndex::COLUMN_INDEX_3:
1332 distance = GetSelectedDistance(index, nextIndex, dir);
1333 break;
1334
1335 case OptionIndex::COLUMN_INDEX_4:
1336 distance = GetDownCandidateDistance(index, nextIndex, dir);
1337 break;
1338 case OptionIndex::COLUMN_INDEX_5:
1339 distance = (dir == ScrollDirection::DOWN) ? optionProperties_[index].height
1340 : (0.0 - optionProperties_[nextIndex].height);
1341 break;
1342 case OptionIndex::COLUMN_INDEX_6: // last
1343 distance = (dir == ScrollDirection::DOWN) ? optionProperties_[index].height
1344 : (0.0 - optionProperties_[nextIndex].height);
1345 break;
1346 default:
1347 break;
1348 }
1349
1350 return distance;
1351 }
1352
GetSelectedDistance(int32_t index,int32_t nextIndex,ScrollDirection dir)1353 double TextPickerColumnPattern::GetSelectedDistance(int32_t index, int32_t nextIndex, ScrollDirection dir)
1354 {
1355 double distance = 0.0;
1356 double val = 0.0;
1357 if (columnkind_ == TEXT) {
1358 if (GreatOrEqual(optionProperties_[nextIndex].fontheight, optionProperties_[nextIndex].height)) {
1359 distance = (dir == ScrollDirection::UP) ?
1360 - optionProperties_[nextIndex].height : optionProperties_[index].height;
1361 } else {
1362 val = optionProperties_[index].height / HALF_NUMBER + optionProperties_[nextIndex].height -
1363 optionProperties_[nextIndex].fontheight / HALF_NUMBER;
1364 val = std::round(val);
1365 distance = (dir == ScrollDirection::DOWN) ? val : (0.0 - val);
1366 }
1367 } else {
1368 val = std::round((optionProperties_[index].height + optionProperties_[nextIndex].height) / HALF_NUMBER);
1369 distance = (dir == ScrollDirection::DOWN) ? val : (0.0 - val);
1370 }
1371 return distance;
1372 }
1373
GetUpCandidateDistance(int32_t index,int32_t nextIndex,ScrollDirection dir)1374 double TextPickerColumnPattern::GetUpCandidateDistance(int32_t index, int32_t nextIndex, ScrollDirection dir)
1375 {
1376 double distance = 0.0;
1377 double val = 0.0;
1378 // the index of last element in optionProperties_. return -1 while the arraySize equals 0.
1379 auto maxIndex = static_cast<int32_t>(optionProperties_.size()) - 1;
1380 auto minIndex = 0;
1381 if (index > maxIndex || index < minIndex || nextIndex > maxIndex || nextIndex < minIndex) {
1382 return distance;
1383 }
1384 if (columnkind_ == TEXT) {
1385 if (dir == ScrollDirection::UP) {
1386 distance = -optionProperties_[nextIndex].height;
1387 } else {
1388 val = optionProperties_[index].height +
1389 (optionProperties_[nextIndex].height - optionProperties_[nextIndex].fontheight) / HALF_NUMBER;
1390 distance = std::round(val);
1391 }
1392 } else {
1393 val = std::round((optionProperties_[index].height + optionProperties_[nextIndex].height) / HALF_NUMBER);
1394 distance = (dir == ScrollDirection::DOWN) ? val : (0.0 - val);
1395 }
1396 return distance;
1397 }
1398
GetDownCandidateDistance(int32_t index,int32_t nextIndex,ScrollDirection dir)1399 double TextPickerColumnPattern::GetDownCandidateDistance(int32_t index, int32_t nextIndex, ScrollDirection dir)
1400 {
1401 double distance = 0.0;
1402 double val = 0.0;
1403 if (columnkind_ == TEXT) {
1404 if (dir == ScrollDirection::DOWN) {
1405 distance = optionProperties_[index].height;
1406 } else {
1407 val = optionProperties_[index].height +
1408 (optionProperties_[nextIndex].height - optionProperties_[nextIndex].fontheight) / HALF_NUMBER;
1409 if (GreatNotEqual(optionProperties_[nextIndex].fontheight, optionProperties_[index].height)) {
1410 val = val + (optionProperties_[nextIndex].fontheight - optionProperties_[index].height);
1411 }
1412 distance = - std::round(val);
1413 }
1414 } else {
1415 val = std::round((optionProperties_[index].height + optionProperties_[nextIndex].height) / HALF_NUMBER);
1416 distance = (dir == ScrollDirection::DOWN) ? val : (0.0 - val);
1417 }
1418 return distance;
1419 }
1420
GetShiftDistanceForLandscape(int32_t index,ScrollDirection dir)1421 double TextPickerColumnPattern::GetShiftDistanceForLandscape(int32_t index, ScrollDirection dir)
1422 {
1423 int32_t optionCounts = static_cast<int32_t>(GetShowOptionCount());
1424 if (optionCounts == 0) {
1425 return 0.0;
1426 }
1427 int32_t nextIndex = 0;
1428 auto isDown = dir == ScrollDirection::DOWN;
1429 nextIndex = isDown ? (optionCounts + index + 1) % optionCounts : (optionCounts + index - 1) % optionCounts;
1430 double distance = 0.0;
1431 switch (static_cast<OptionIndex>(index)) {
1432 case OptionIndex::COLUMN_INDEX_0:
1433 distance = GetUpCandidateDistance(index, nextIndex, dir);
1434 break;
1435
1436 case OptionIndex::COLUMN_INDEX_1:
1437 distance = GetSelectedDistance(index, nextIndex, dir);
1438 break;
1439
1440 case OptionIndex::COLUMN_INDEX_2:
1441 distance = GetDownCandidateDistance(index, nextIndex, dir);
1442 break;
1443 default:
1444 break;
1445 }
1446
1447 return distance;
1448 }
1449
SetOptionShiftDistance()1450 void TextPickerColumnPattern::SetOptionShiftDistance()
1451 {
1452 CHECK_EQUAL_VOID(optionProperties_.empty(), true);
1453 int32_t itemCounts = static_cast<int32_t>(GetShowOptionCount());
1454 bool isLanscape = (itemCounts == OPTION_COUNT_PHONE_LANDSCAPE + BUFFER_NODE_NUMBER);
1455 for (int32_t i = 0; i < static_cast<int32_t>(itemCounts); i++) {
1456 TextPickerOptionProperty& prop = optionProperties_[i];
1457 if (isLanscape) {
1458 prop.prevDistance = GetShiftDistanceForLandscape(i, ScrollDirection::UP);
1459 prop.nextDistance = GetShiftDistanceForLandscape(i, ScrollDirection::DOWN);
1460 } else {
1461 prop.prevDistance = GetShiftDistance(i, ScrollDirection::UP);
1462 prop.nextDistance = GetShiftDistance(i, ScrollDirection::DOWN);
1463 }
1464 }
1465 }
1466
UpdateToss(double offsetY)1467 void TextPickerColumnPattern::UpdateToss(double offsetY)
1468 {
1469 UpdateColumnChildPosition(offsetY);
1470 }
1471
TossStoped()1472 void TextPickerColumnPattern::TossStoped()
1473 {
1474 yOffset_ = 0.0;
1475 yLast_ = 0.0;
1476 ScrollOption(0.0);
1477 }
1478
TossAnimationStoped()1479 void TextPickerColumnPattern::TossAnimationStoped()
1480 {
1481 yLast_ = 0.0;
1482 }
1483
GetSelectedObject(bool isColumnChange,int32_t status) const1484 std::string TextPickerColumnPattern::GetSelectedObject(bool isColumnChange, int32_t status) const
1485 {
1486 auto host = GetHost();
1487 CHECK_NULL_RETURN(host, "");
1488 auto value = GetOption(GetSelected());
1489 auto index = GetSelected();
1490 if (isColumnChange) {
1491 value = GetCurrentText();
1492 index = GetCurrentIndex();
1493 }
1494
1495 auto context = host->GetContext();
1496 CHECK_NULL_RETURN(context, "");
1497
1498 if (context->GetIsDeclarative()) {
1499 return std::string("{\"value\":") + "\"" + value + "\"" + ",\"index\":" + std::to_string(index) +
1500 ",\"status\":" + std::to_string(status) + "}";
1501 } else {
1502 return std::string("{\"newValue\":") + "\"" + value + "\"" + ",\"newSelected\":" + std::to_string(index) +
1503 ",\"status\":" + std::to_string(status) + "}";
1504 }
1505 }
1506
ResetTotalDelta()1507 void TextPickerColumnPattern::ResetTotalDelta()
1508 {
1509 totalDragDelta_ = 0.0;
1510 }
1511
SpringCurveTailMoveProcess(bool useRebound,double & dragDelta)1512 bool TextPickerColumnPattern::SpringCurveTailMoveProcess(bool useRebound, double& dragDelta)
1513 {
1514 if (useRebound) {
1515 return false;
1516 }
1517 auto toss = GetToss();
1518 if (toss && toss->GetTossPlaying()) {
1519 if (std::abs(dragDelta) < CURVE_MOVE_THRESHOLD) {
1520 dragDelta = dragDelta > 0 ? CURVE_MOVE_THRESHOLD : -CURVE_MOVE_THRESHOLD;
1521 }
1522 totalDragDelta_ += dragDelta;
1523 if (std::abs(totalDragDelta_) >= std::abs(toss->GetTossEndPosition())) {
1524 dragDelta -= (totalDragDelta_ - toss->GetTossEndPosition());
1525 ResetTotalDelta();
1526 return true;
1527 }
1528 }
1529 return false;
1530 }
1531
SpringCurveTailEndProcess(bool useRebound,bool stopMove)1532 void TextPickerColumnPattern::SpringCurveTailEndProcess(bool useRebound, bool stopMove)
1533 {
1534 if (useRebound || !stopMove) {
1535 return;
1536 }
1537 auto toss = GetToss();
1538 if (toss) {
1539 toss->SetTossPlaying(false);
1540 toss->StopTossAnimation();
1541 }
1542 }
1543
UpdateColumnChildPosition(double offsetY)1544 void TextPickerColumnPattern::UpdateColumnChildPosition(double offsetY)
1545 {
1546 double dragDelta = offsetY - yLast_;
1547 auto midIndex = GetShowOptionCount() / HALF_NUMBER;
1548 ScrollDirection dir = GreatNotEqual(dragDelta, 0.0) ? ScrollDirection::DOWN : ScrollDirection::UP;
1549 auto shiftDistance = (dir == ScrollDirection::UP) ? optionProperties_[midIndex].prevDistance
1550 : optionProperties_[midIndex].nextDistance;
1551 auto useRebound = NotLoopOptions();
1552 auto stopMove = SpringCurveTailMoveProcess(useRebound, dragDelta);
1553 offsetCurSet_ = 0.0;
1554
1555 // the abs of drag delta is less than jump interval.
1556 dragDelta = dragDelta + yOffset_;
1557 auto isOverScroll = useRebound && overscroller_.IsOverScroll();
1558 if ((std::abs(dragDelta) >= std::abs(shiftDistance)) && !isOverScroll) {
1559 int32_t shiftDistanceCount = static_cast<int>(std::abs(dragDelta) / std::abs(shiftDistance));
1560 double additionalShift = dragDelta - shiftDistanceCount * shiftDistance;
1561 if (GreatNotEqual(std::abs(additionalShift), std::abs(dragDelta))) {
1562 additionalShift = dragDelta + shiftDistanceCount * shiftDistance;
1563 }
1564 for (int32_t i = 0; i < shiftDistanceCount; i++) {
1565 ScrollOption(shiftDistance);
1566 InnerHandleScroll(dragDelta < 0, true, false);
1567 }
1568 dragDelta = additionalShift;
1569 if (NearZero(dragDelta)) {
1570 HandleScrollStopEventCallback(true);
1571 }
1572 }
1573 if (useRebound && !isReboundInProgress_) {
1574 if (overscroller_.ApplyCurrentOffset(yLast_, offsetY, dragDelta)) {
1575 dragDelta =
1576 overscroller_.IsBackOverScroll() ? overscroller_.GetBackScroll() : overscroller_.GetOverScroll();
1577 }
1578 }
1579
1580 // Set options position
1581 ScrollOption(dragDelta);
1582 offsetCurSet_ = dragDelta;
1583 yOffset_ = dragDelta;
1584 yLast_ = offsetY;
1585
1586 if (useRebound && !pressed_ && isTossStatus_ && !isReboundInProgress_ && overscroller_.IsOverScroll()) {
1587 overscroller_.UpdateTossSpring(offsetY);
1588 if (overscroller_.ShouldStartRebound()) {
1589 auto toss = GetToss();
1590 CHECK_NULL_VOID(toss);
1591 toss->StopTossAnimation(); // Stop fling animation and start rebound animation implicitly
1592 }
1593 }
1594 SpringCurveTailEndProcess(useRebound, stopMove);
1595 }
1596
CanMove(bool isDown) const1597 bool TextPickerColumnPattern::CanMove(bool isDown) const
1598 {
1599 if (!NotLoopOptions()) {
1600 return true;
1601 }
1602 auto host = GetHost();
1603 CHECK_NULL_RETURN(host, false);
1604 int totalOptionCount = static_cast<int>(GetOptionCount());
1605 int currentIndex = static_cast<int>(GetCurrentIndex());
1606 int nextVirtualIndex = isDown ? currentIndex + 1 : currentIndex - 1;
1607 return nextVirtualIndex >= 0 && nextVirtualIndex < totalOptionCount;
1608 }
1609
NotLoopOptions() const1610 bool TextPickerColumnPattern::NotLoopOptions() const
1611 {
1612 RefPtr<TextPickerLayoutProperty> layout = GetParentLayout();
1613 CHECK_NULL_RETURN(layout, false);
1614 bool canLoop = layout->GetCanLoop().value_or(true);
1615 return !canLoop;
1616 }
1617
InnerHandleScroll(bool isDown,bool isUpatePropertiesOnly,bool isUpdateAnimationProperties)1618 bool TextPickerColumnPattern::InnerHandleScroll(
1619 bool isDown, bool isUpatePropertiesOnly, bool isUpdateAnimationProperties)
1620 {
1621 auto host = GetHost();
1622 CHECK_NULL_RETURN(host, false);
1623 auto totalOptionCount = GetOptionCount();
1624 if (totalOptionCount == 0) {
1625 return false;
1626 }
1627
1628 if (NotLoopOptions() && ((isDown && currentIndex_ == totalOptionCount - 1) || (!isDown && currentIndex_ == 0))) {
1629 return false;
1630 }
1631
1632 uint32_t currentIndex = GetCurrentIndex();
1633 if (isDown) {
1634 currentIndex = (totalOptionCount + currentIndex + 1) % totalOptionCount; // index add one
1635 } else {
1636 auto totalCountAndIndex = totalOptionCount + currentIndex;
1637 currentIndex = (totalCountAndIndex ? totalCountAndIndex - 1 : 0) % totalOptionCount; // index reduce one
1638 }
1639 SetCurrentIndex(currentIndex);
1640 FlushCurrentOptions(isDown, isUpatePropertiesOnly, isUpdateAnimationProperties);
1641 HandleChangeCallback(isDown, true);
1642 HandleEventCallback(true);
1643
1644 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
1645 host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE);
1646 return true;
1647 }
1648
OnKeyEvent(const KeyEvent & event)1649 bool TextPickerColumnPattern::OnKeyEvent(const KeyEvent& event)
1650 {
1651 if (event.action != KeyAction::DOWN) {
1652 return false;
1653 }
1654 if (event.code == KeyCode::KEY_DPAD_UP || event.code == KeyCode::KEY_DPAD_DOWN ||
1655 event.code == KeyCode::KEY_DPAD_LEFT || event.code == KeyCode::KEY_DPAD_RIGHT) {
1656 HandleDirectionKey(event.code);
1657 return true;
1658 }
1659 return false;
1660 }
1661
HandleDirectionKey(KeyCode code)1662 bool TextPickerColumnPattern::HandleDirectionKey(KeyCode code)
1663 {
1664 auto host = GetHost();
1665 CHECK_NULL_RETURN(host, false);
1666 auto currentIndex = GetCurrentIndex();
1667 auto totalOptionCount = GetOptionCount();
1668 if (totalOptionCount == 0) {
1669 return false;
1670 }
1671 if (code == KeyCode::KEY_DPAD_UP) {
1672 auto totalCountAndIndex = totalOptionCount + currentIndex;
1673 SetCurrentIndex((totalCountAndIndex ? totalCountAndIndex - 1 : 0) % totalOptionCount);
1674 FlushCurrentOptions();
1675 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1676 return true;
1677 }
1678 if (code == KeyCode::KEY_DPAD_DOWN) {
1679 SetCurrentIndex((totalOptionCount + currentIndex + 1) % totalOptionCount);
1680 FlushCurrentOptions(true);
1681 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1682 return true;
1683 }
1684 return false;
1685 }
SetAccessibilityAction()1686 void TextPickerColumnPattern::SetAccessibilityAction()
1687 {
1688 auto host = GetHost();
1689 CHECK_NULL_VOID(host);
1690 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1691 CHECK_NULL_VOID(accessibilityProperty);
1692 accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
1693 const auto& pattern = weakPtr.Upgrade();
1694 CHECK_NULL_VOID(pattern);
1695 CHECK_NULL_VOID(pattern->animationCreated_);
1696 if (!pattern->CanMove(true)) {
1697 return;
1698 }
1699 pattern->InnerHandleScroll(true);
1700 pattern->CreateAnimation(0.0 - pattern->jumpInterval_, 0.0);
1701 pattern->HandleScrollStopEventCallback(true);
1702 // AccessibilityEventType::SCROLL_END
1703 });
1704
1705 accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)]() {
1706 const auto& pattern = weakPtr.Upgrade();
1707 CHECK_NULL_VOID(pattern);
1708 CHECK_NULL_VOID(pattern->animationCreated_);
1709 if (!pattern->CanMove(false)) {
1710 return;
1711 }
1712 pattern->InnerHandleScroll(false);
1713 pattern->CreateAnimation(pattern->jumpInterval_, 0.0);
1714 pattern->HandleScrollStopEventCallback(true);
1715 // AccessibilityEventType::SCROLL_END
1716 });
1717 }
1718
OnAroundButtonClick(RefPtr<EventParam> param)1719 void TextPickerColumnPattern::OnAroundButtonClick(RefPtr<EventParam> param)
1720 {
1721 if (clickBreak_) {
1722 return;
1723 }
1724 int32_t middleIndex = static_cast<int32_t>(GetShowOptionCount()) / HALF_NUMBER;
1725 int32_t step = param->itemIndex - middleIndex;
1726 auto overFirst = static_cast<int32_t>(currentIndex_) + step < 0 && step < 0;
1727 auto overLast =
1728 (static_cast<int32_t>(currentIndex_) + step > static_cast<int32_t>(GetOptionCount()) - 1) && step > 0;
1729 if (NotLoopOptions() && (overscroller_.IsOverScroll() || overFirst || overLast)) {
1730 return;
1731 }
1732 if (step != 0) {
1733 if (animation_) {
1734 AnimationUtils::StopAnimation(animation_);
1735 yOffset_ = 0.0;
1736 }
1737 double distance =
1738 (step > 0 ? optionProperties_[middleIndex].prevDistance : optionProperties_[middleIndex].nextDistance) *
1739 std::abs(step);
1740 AnimationOption option;
1741 option.SetCurve(Curves::FAST_OUT_SLOW_IN);
1742 option.SetDuration(CLICK_ANIMATION_DURATION);
1743 yLast_ = 0.0;
1744 aroundClickProperty_->Set(0.0);
1745 animation_ = AnimationUtils::StartAnimation(option, [weak = AceType::WeakClaim(this), step, distance]() {
1746 auto column = weak.Upgrade();
1747 CHECK_NULL_VOID(column);
1748 column->aroundClickProperty_->Set(step > 0 ? 0.0 - std::abs(distance) : std::abs(distance));
1749 });
1750 auto host = GetHost();
1751 CHECK_NULL_VOID(host);
1752 auto pipeline = host->GetContext();
1753 CHECK_NULL_VOID(pipeline);
1754 pipeline->RequestFrame();
1755 }
1756 }
1757
PlayResetAnimation()1758 void TextPickerColumnPattern::PlayResetAnimation()
1759 {
1760 int32_t middleIndex = static_cast<int32_t>(GetShowOptionCount()) / HALF_NUMBER;
1761 ScrollDirection dir = GreatNotEqual(scrollDelta_, 0.0) ? ScrollDirection::DOWN : ScrollDirection::UP;
1762 double shiftDistance = (dir == ScrollDirection::UP) ? optionProperties_[middleIndex].prevDistance
1763 : optionProperties_[middleIndex].nextDistance;
1764 double shiftThreshold = shiftDistance / HALF_NUMBER;
1765 if (std::abs(scrollDelta_) >= std::abs(shiftThreshold)) {
1766 InnerHandleScroll(LessNotEqual(scrollDelta_, 0.0), true, false);
1767 scrollDelta_ = scrollDelta_ - std::abs(shiftDistance) * (dir == ScrollDirection::UP ? -1 : 1);
1768 if (NearZero(scrollDelta_)) {
1769 HandleScrollStopEventCallback(true);
1770 }
1771 }
1772 CreateAnimation(scrollDelta_, 0.0);
1773 if (!NearZero(scrollDelta_)) {
1774 HandleScrollStopEventCallback(true);
1775 }
1776 }
1777
SetCanLoop(bool isLoop)1778 void TextPickerColumnPattern::SetCanLoop(bool isLoop)
1779 {
1780 if (isLoop_ == isLoop) {
1781 return;
1782 }
1783
1784 isLoop_ = isLoop;
1785 if (overscroller_.IsOverScroll()) {
1786 overscroller_.Reset();
1787 isReboundInProgress_ = false;
1788 yOffset_ = 0.0;
1789 ScrollOption(0.0);
1790 }
1791
1792 if (!isLoop && isTossStatus_) {
1793 auto toss = GetToss();
1794 CHECK_NULL_VOID(toss);
1795 overscroller_.SetLoopTossOffset(toss->GetTossOffset());
1796 }
1797 }
1798 } // namespace OHOS::Ace::NG
1799