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