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