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