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