1 /*
2 * Copyright (c) 2021 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/slider/render_slider.h"
17
18 #include "base/json/json_util.h"
19 #include "base/log/event_report.h"
20 #include "core/components/box/render_box.h"
21 #include "core/components/bubble/render_bubble.h"
22 #include "core/components/tip/render_tip.h"
23 #include "core/event/ace_event_helper.h"
24
25 namespace OHOS::Ace {
26 namespace {
27
28 constexpr double DOUBLE_TO_PERCENT = 100.0;
29 constexpr double CHANGE_RATIO = 0.2;
30 constexpr double DEFAULT_NORMAL_RADIUS_SCALE = 1.0;
31 constexpr double DEFAULT_LARGE_RADIUS_SCALE = 1.1;
32 constexpr double DEFAULT_OUTSET_TRACK_THICKNESS = 4.0;
33 constexpr double DEFAULT_INSET_TRACK_THICKNESS = 20.0;
34 constexpr int32_t DEFAULT_SLIDER_ANIMATION_DURATION = 150;
35 constexpr int32_t SLIDER_MOVE_DURATION = 100;
36 constexpr Dimension DEFAULT_SLIDER_WIDTH_DP = 260.0_vp;
37 constexpr Dimension DEFAULT_SLIDER_HEIGHT_DP = 40.0_vp;
38
39 } // namespace
40
RenderSlider()41 RenderSlider::RenderSlider() : RenderNode(true) {}
42
Update(const RefPtr<Component> & component)43 void RenderSlider::Update(const RefPtr<Component>& component)
44 {
45 auto slider = AceType::DynamicCast<SliderComponent>(component);
46 if (!slider) {
47 LOGE("Update error, slider component is null");
48 return;
49 }
50 if (slider->GetOnChange()) {
51 onChange_ = *slider->GetOnChange();
52 }
53 sliderComponent_ = slider;
54 if (!blockActive_) {
55 Initialize();
56 if (!slider) {
57 LOGE("RenderSlider update with nullptr");
58 EventReport::SendRenderException(RenderExcepType::RENDER_COMPONENT_ERR);
59 return;
60 }
61 showSteps_ = slider->NeedShowSteps();
62 showTips_ = slider->NeedShowTips();
63 mode_ = slider->GetSliderMode();
64 min_ = slider->GetMinValue();
65 max_ = slider->GetMaxValue();
66 step_ = slider->GetStep();
67 disable_ = slider->GetDisable();
68 SetTextDirection(slider->GetTextDirection());
69 direction_ = slider->GetDirection();
70 isReverse_ = slider->IsReverse();
71 isError_ = false;
72 isValueError_ = false;
73 scaleValue_ = mode_ == SliderMode::INSET ? slider->GetThickness().Value() / DEFAULT_INSET_TRACK_THICKNESS :
74 slider->GetThickness().Value() / DEFAULT_OUTSET_TRACK_THICKNESS;
75 SyncValueToComponent(std::clamp(slider->GetValue(), min_, max_));
76 if (min_ >= max_ || step_ > (max_ - min_) || step_ <= 0.0) {
77 isValueError_ = true;
78 LOGE("RenderSlider update min, max, value, step error");
79 MarkNeedLayout();
80 return;
81 }
82 LOGD("Slider::RenderSlider::Update Min: %{public}lf max: %{public}lf step: %{public}lf value: %{public}lf",
83 min_, max_, step_, value_);
84 totalRatio_ = (value_ - min_) / (max_ - min_);
85
86 // Event update
87 if (!slider->GetOnMoveEndEventId().IsEmpty()) {
88 onMoveEnd_ = AceAsyncEvent<void(const std::string&)>::Create(slider->GetOnMoveEndEventId(), context_);
89 }
90 if (!slider->GetOnMovingEventId().IsEmpty()) {
91 onMoving_ = AceAsyncEvent<void(const std::string&)>::Create(slider->GetOnMovingEventId(), context_);
92 }
93 InitAccessibilityEventListener();
94
95 // animation control
96 if (!controller_) {
97 controller_ = AceType::MakeRefPtr<Animator>(GetContext());
98 }
99
100 const auto& rotationController = slider->GetRotationController();
101 if (rotationController) {
102 auto weak = AceType::WeakClaim(this);
103 rotationController->SetRequestRotationImpl(weak, context_);
104 }
105
106 MarkNeedLayout();
107 }
108 }
109
PerformLayout()110 void RenderSlider::PerformLayout()
111 {
112 Size size = Measure();
113
114 // Update layout size.
115 SetLayoutSize(size);
116
117 // The size of child will be set in flutter slider
118 UpdateTouchRegion();
119 }
120
OnPaintFinish()121 void RenderSlider::OnPaintFinish()
122 {
123 UpdateAccessibilityAttr();
124 }
125
UpdateAccessibilityAttr()126 void RenderSlider::UpdateAccessibilityAttr()
127 {
128 // Update text with slider value
129 auto accessibilityNode = GetAccessibilityNode().Upgrade();
130 if (!accessibilityNode) {
131 return;
132 }
133 accessibilityNode->SetText(std::to_string(value_));
134 accessibilityNode->SetAccessibilityValue(value_, min_, max_);
135 auto context = context_.Upgrade();
136 if (context) {
137 AccessibilityEvent sliderEvent;
138 sliderEvent.nodeId = accessibilityNode->GetNodeId();
139 sliderEvent.eventType = "selected";
140 sliderEvent.componentType = "slider";
141 sliderEvent.currentItemIndex = value_;
142 sliderEvent.itemCount = max_ - min_;
143 context->SendEventToAccessibility(sliderEvent);
144 }
145 }
146
InitAccessibilityEventListener()147 void RenderSlider::InitAccessibilityEventListener()
148 {
149 const auto& accessibilityNode = GetAccessibilityNode().Upgrade();
150 if (!accessibilityNode) {
151 return;
152 }
153 accessibilityNode->AddSupportAction(AceAction::ACTION_SCROLL_BACKWARD);
154 accessibilityNode->AddSupportAction(AceAction::ACTION_SCROLL_FORWARD);
155
156 accessibilityNode->SetActionScrollBackward([weakPtr = WeakClaim(this)]() {
157 const auto& slider = weakPtr.Upgrade();
158 if (slider) {
159 slider->HandleScrollUpdate(-1);
160 return true;
161 }
162 return false;
163 });
164
165 accessibilityNode->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
166 const auto& slider = weakPtr.Upgrade();
167 if (slider) {
168 slider->HandleScrollUpdate(1);
169 return true;
170 }
171 return false;
172 });
173 }
174
HandleScrollUpdate(double delta)175 void RenderSlider::HandleScrollUpdate(double delta)
176 {
177 value_ = value_ + (max_ - min_) * CHANGE_RATIO * delta;
178 if (value_ > max_) {
179 value_ = max_;
180 }
181 if (value_ < min_) {
182 value_ = min_;
183 }
184 SyncValueToComponent(value_);
185 if (min_ >= max_) {
186 return;
187 }
188 totalRatio_ = (value_ - min_) / (max_ - min_);
189 UpdateTouchRegion();
190 MarkNeedLayout();
191 FireMovingEvent(SliderEvent::ACCESSIBILITY);
192 }
193
Measure()194 Size RenderSlider::Measure()
195 {
196 if (direction_ == Axis::VERTICAL) {
197 Size layoutConstrainMax = GetLayoutParam().GetMaxSize();
198 LayoutParam childrenLayoutConstrain;
199 if (layoutConstrainMax.Height() == Size::INFINITE_SIZE) {
200 trackLength_ = NormalizeToPx(DEFAULT_SLIDER_WIDTH_DP) - 2 * NormalizeToPx(SLIDER_PADDING_DP);
201 childrenLayoutConstrain.SetMaxSize(
202 Size(NormalizeToPx(DEFAULT_SLIDER_HEIGHT_DP), NormalizeToPx(DEFAULT_SLIDER_WIDTH_DP)));
203 } else {
204 trackLength_ = layoutConstrainMax.Height() - 2 * NormalizeToPx(SLIDER_PADDING_DP);
205 childrenLayoutConstrain.SetMaxSize(Size(NormalizeToPx(DEFAULT_SLIDER_HEIGHT_DP), trackLength_));
206 }
207 for (const auto& item : GetChildren()) {
208 item->Layout(childrenLayoutConstrain);
209 }
210 if (trackLength_ < 0.0) {
211 trackLength_ = 0.0;
212 }
213 return Size(NormalizeToPx(DEFAULT_SLIDER_HEIGHT_DP), layoutConstrainMax.Height());
214 } else {
215 Size layoutConstrainMax = GetLayoutParam().GetMaxSize();
216 LayoutParam childrenLayoutConstrain;
217 if (layoutConstrainMax.Width() == Size::INFINITE_SIZE) {
218 // set the default size to (260dp, 40dp) and length to 160dp
219 trackLength_ = NormalizeToPx(DEFAULT_SLIDER_WIDTH_DP) - 2 * NormalizeToPx(SLIDER_PADDING_DP);
220 childrenLayoutConstrain.SetMaxSize(
221 Size(NormalizeToPx(DEFAULT_SLIDER_WIDTH_DP), NormalizeToPx(DEFAULT_SLIDER_HEIGHT_DP)));
222 } else {
223 trackLength_ = layoutConstrainMax.Width() - 2 * NormalizeToPx(SLIDER_PADDING_DP);
224 childrenLayoutConstrain.SetMaxSize(Size(trackLength_, NormalizeToPx(DEFAULT_SLIDER_HEIGHT_DP)));
225 }
226 for (const auto& item : GetChildren()) {
227 item->Layout(childrenLayoutConstrain);
228 }
229 if (trackLength_ < 0.0) {
230 trackLength_ = 0.0;
231 }
232 return Size(layoutConstrainMax.Width(), NormalizeToPx(DEFAULT_SLIDER_HEIGHT_DP));
233 }
234 }
235
Initialize()236 void RenderSlider::Initialize()
237 {
238 if (!dragDetector_) {
239 dragDetector_ = AceType::MakeRefPtr<HorizontalDragRecognizer>();
240 dragDetector_->SetOnDragStart([weakSlider = AceType::WeakClaim(this)](const DragStartInfo& info) {
241 auto slider = weakSlider.Upgrade();
242 if (slider) {
243 slider->HandleDragStart(info.GetLocalLocation());
244 }
245 });
246 dragDetector_->SetOnDragUpdate([weakSlider = AceType::WeakClaim(this)](const DragUpdateInfo& info) {
247 auto slider = weakSlider.Upgrade();
248 if (slider) {
249 slider->HandleDragUpdate(info.GetLocalLocation());
250 }
251 });
252 dragDetector_->SetOnDragEnd([weakSlider = AceType::WeakClaim(this)](const DragEndInfo& info) {
253 auto slider = weakSlider.Upgrade();
254 if (slider) {
255 slider->HandleDragEnd();
256 }
257 });
258 }
259 if (!clickDetector_) {
260 clickDetector_ = AceType::MakeRefPtr<ClickRecognizer>();
261 clickDetector_->SetOnClick([weakSlider = AceType::WeakClaim(this)](const ClickInfo& info) {
262 auto slider = weakSlider.Upgrade();
263 if (slider) {
264 slider->HandleClick(info.GetLocalLocation());
265 }
266 });
267 }
268 }
269
MouseHoverTest(const Point & parentLocalPoint)270 bool RenderSlider::MouseHoverTest(const Point& parentLocalPoint)
271 {
272 auto context = context_.Upgrade();
273 if (!context) {
274 return false;
275 }
276 if (blockTouchRegion_.ContainsInRegion(
277 parentLocalPoint.GetX() - GetPosition().GetX(), parentLocalPoint.GetY() - GetPosition().GetY())) {
278 if (mouseState_ == MouseState::NONE) {
279 OnMouseHoverEnterTest();
280 mouseState_ = MouseState::HOVER;
281 }
282 context->AddToHoverList(AceType::WeakClaim(this).Upgrade());
283 return true;
284 } else {
285 if (mouseState_ == MouseState::HOVER) {
286 OnMouseHoverExitTest();
287 mouseState_ = MouseState::NONE;
288 }
289 return false;
290 }
291 }
292
FireMoveEndEvent()293 void RenderSlider::FireMoveEndEvent()
294 {
295 if (onMoveEnd_) {
296 std::string param = std::string(R"("changed",{"progress":)")
297 .append(std::to_string(value_))
298 .append(R"(},{"value":)")
299 .append(std::to_string(value_))
300 .append("}");
301 onMoveEnd_(param);
302 }
303 }
304
FireMovingEvent(SliderEvent mode)305 void RenderSlider::FireMovingEvent(SliderEvent mode)
306 {
307 if (onMoving_ || onChange_) {
308 auto jsonResult = JsonUtil::Create(true);
309 jsonResult->Put("progress", std::to_string(value_).c_str());
310 switch (mode) {
311 case SliderEvent::MOVE_START:
312 jsonResult->Put("isEnd", "false");
313 jsonResult->Put("mode", "start");
314 if (onChange_) {
315 onChange_(value_, static_cast<int>(SliderEvent::MOVE_START));
316 }
317 break;
318 case SliderEvent::MOVE_MOVING:
319 jsonResult->Put("isEnd", "false");
320 jsonResult->Put("mode", "move");
321 if (onChange_) {
322 onChange_(value_, static_cast<int>(SliderEvent::MOVE_MOVING));
323 }
324 break;
325 case SliderEvent::MOVE_END:
326 jsonResult->Put("isEnd", "true");
327 jsonResult->Put("mode", "end");
328 if (onChange_) {
329 onChange_(value_, static_cast<int>(SliderEvent::MOVE_END));
330 }
331 break;
332 case SliderEvent::CLICK:
333 jsonResult->Put("isEnd", "true");
334 jsonResult->Put("mode", "click");
335 if (onChange_) {
336 onChange_(value_, static_cast<int>(SliderEvent::CLICK));
337 }
338 break;
339 case SliderEvent::ACCESSIBILITY:
340 jsonResult->Put("isEnd", "false");
341 jsonResult->Put("mode", "accessibility");
342 if (onChange_) {
343 onChange_(value_, static_cast<int>(SliderEvent::ACCESSIBILITY));
344 }
345 break;
346 case SliderEvent::FOCUS:
347 jsonResult->Put("isEnd", "true");
348 jsonResult->Put("mode", "keyevent");
349 if (onChange_) {
350 onChange_(value_, static_cast<int>(SliderEvent::FOCUS));
351 }
352 break;
353 }
354 jsonResult->Put("value", value_);
355 if (onMoving_) {
356 onMoving_(std::string(R"("change",)").append(jsonResult->ToString()));
357 }
358 }
359 }
360
HandleClick(const Offset & clickPosition)361 void RenderSlider::HandleClick(const Offset& clickPosition)
362 {
363 LOGD("Slider::Handle click position x:%{public}f y:%{public}f", clickPosition.GetX(), clickPosition.GetY());
364 if (NearZero(trackLength_)) {
365 totalRatio_ = 0.0;
366 return;
367 }
368 if (NeedSmoothMoving()) {
369 UpdateBlockPosition(clickPosition, true);
370 insideBlockRegion_ = false;
371 FireMovingEvent(SliderEvent::CLICK);
372 FireMoveEndEvent();
373 return;
374 }
375 RenderBlockPosition(clickPosition);
376 UpdateTouchRegion();
377 insideBlockRegion_ = false;
378 FireMovingEvent(SliderEvent::CLICK);
379 FireMoveEndEvent();
380 }
381
HandleDragStart(const Offset & startPoint)382 void RenderSlider::HandleDragStart(const Offset& startPoint)
383 {
384 if (showTips_ && tip_) {
385 tip_->SetVisible(true);
386 }
387 if (NearZero(trackLength_)) {
388 totalRatio_ = 0.0;
389 return;
390 }
391 if (blockTouchRegion_.ContainsInRegion(startPoint.GetX(), startPoint.GetY())) {
392 insideBlockRegion_ = true;
393 blockActive_ = true;
394 UpdateTouchRegion();
395 if (!controller_->IsStopped()) {
396 controller_->Stop();
397 }
398 UpdateAnimation();
399 controller_->Play();
400 isDraging_ = true;
401 FireMovingEvent(SliderEvent::MOVE_START);
402 }
403 }
404
HandleDragUpdate(const Offset & updatePoint)405 void RenderSlider::HandleDragUpdate(const Offset& updatePoint)
406 {
407 if (NearZero(trackLength_)) {
408 totalRatio_ = 0.0;
409 return;
410 }
411 if (insideBlockRegion_) {
412 RenderBlockPosition(updatePoint);
413 FireMovingEvent(SliderEvent::MOVE_MOVING);
414 }
415 }
416
HandleDragEnd()417 void RenderSlider::HandleDragEnd()
418 {
419 if (isDraging_) {
420 isDraging_ = false;
421 }
422 if (tip_) {
423 tip_->SetVisible(false);
424 }
425 if (NearZero(trackLength_)) {
426 totalRatio_ = 0.0;
427 return;
428 }
429 if (insideBlockRegion_) {
430 MarkNeedLayout();
431 UpdateTouchRegion();
432 }
433 FireMovingEvent(SliderEvent::MOVE_END);
434 FireMoveEndEvent();
435
436 insideBlockRegion_ = false;
437 blockActive_ = false;
438
439 if (!controller_->IsStopped()) {
440 controller_->Stop();
441 }
442 UpdateAnimation();
443 controller_->Play();
444 }
445
446 // Render the block position after clicking or dragging
RenderBlockPosition(const Offset & touchPosition)447 void RenderSlider::RenderBlockPosition(const Offset& touchPosition)
448 {
449 double diff = 0.0;
450 if (direction_ == Axis::VERTICAL) {
451 diff = isReverse_ ? GetLayoutSize().Height() - touchPosition.GetY() - NormalizeToPx(SLIDER_PADDING_DP) :
452 touchPosition.GetY() - NormalizeToPx(SLIDER_PADDING_DP);
453 } else {
454 if ((GetTextDirection() == TextDirection::LTR &&
455 !isReverse_) || (GetTextDirection() == TextDirection::RTL && isReverse_)) {
456 diff = touchPosition.GetX() - NormalizeToPx(SLIDER_PADDING_DP);
457 } else if ((GetTextDirection() == TextDirection::RTL &&
458 !isReverse_) || (GetTextDirection() == TextDirection::LTR && isReverse_)) {
459 diff = GetLayoutSize().Width() - touchPosition.GetX() - NormalizeToPx(SLIDER_PADDING_DP);
460 }
461 }
462 if (diff < 0.0) {
463 SyncValueToComponent(min_);
464 SetTotalRatio(0.0);
465 LOGD("Slider::RenderSlider RenderBlockPosition value: %{public}lf", value_);
466 MarkNeedLayout();
467 return;
468 }
469 totalRatio_ = diff / trackLength_;
470 LOGD("Slider::RenderSlider RenderBlockPosition totalRatio: %{public}lf", totalRatio_);
471 if (totalRatio_ > 1.0) {
472 value_ = max_;
473 SetTotalRatio(1.0);
474 } else {
475 if (NearEqual(step_, 0.0)) {
476 // continuous slider
477 value_ = (max_ - min_) * totalRatio_ + min_;
478 } else {
479 // The following line is used to find value which is the multiple of step.
480 // The example shows below
481 // "value < x < value + 0.5 * step --> x = value"
482 // "value + 0.5 * step < x < value + step --> x = value + step"
483 double stepRatio = step_ / (max_ - min_);
484 SetTotalRatio(stepRatio * std::floor((totalRatio_ + HALF * stepRatio) / stepRatio));
485 value_ = (max_ - min_) * totalRatio_ + min_;
486 }
487 }
488 SyncValueToComponent(value_);
489 LOGD("Slider::RenderSlider RenderBlockPosition value: %{public}lf", value_);
490 MarkNeedLayout();
491 }
492
UpdateBlockPosition(const Offset & touchPosition,bool isClick)493 void RenderSlider::UpdateBlockPosition(const Offset& touchPosition, bool isClick)
494 {
495 if (LessOrEqual(trackLength_, 0.0)) {
496 LOGE("slider parameter trackLength_ invalid");
497 return;
498 }
499 double diff = 0.0;
500 if (direction_ == Axis::VERTICAL) {
501 diff = isReverse_ ? GetLayoutSize().Height() - touchPosition.GetY() - NormalizeToPx(SLIDER_PADDING_DP) :
502 touchPosition.GetY() - NormalizeToPx(SLIDER_PADDING_DP);
503 } else {
504 if ((GetTextDirection() == TextDirection::LTR &&
505 !isReverse_) || (GetTextDirection() == TextDirection::RTL && isReverse_)) {
506 diff = touchPosition.GetX() - NormalizeToPx(SLIDER_PADDING_DP);
507 } else if ((GetTextDirection() == TextDirection::RTL &&
508 !isReverse_) || (GetTextDirection() == TextDirection::LTR && isReverse_)) {
509 diff = GetLayoutSize().Width() - touchPosition.GetX() - NormalizeToPx(SLIDER_PADDING_DP);
510 }
511 }
512 double totalRatio = diff / trackLength_;
513 if (LessOrEqual(diff, 0.0)) {
514 value_ = min_;
515 } else if (GreatOrEqual(totalRatio, 1.0)) {
516 value_ = max_;
517 } else {
518 double stepRatio = step_ / (max_ - min_);
519 double endRatio = stepRatio * std::floor((totalRatio + HALF * stepRatio) / stepRatio);
520 value_ = (max_ - min_) * endRatio + min_;
521 if (GreatOrEqual(value_, max_)) {
522 value_ = max_;
523 }
524 }
525 RestartMoveAnimation(value_, isClick);
526 }
527
UpdateTipText(double value)528 void RenderSlider::UpdateTipText(double value)
529 {
530 int32_t percent = std::round(value * DOUBLE_TO_PERCENT);
531 std::string valueText = std::to_string(percent).append("%");
532 if (tipText_ && renderText_) {
533 tipText_->SetData(valueText);
534 renderText_->Update(tipText_);
535 renderText_->PerformLayout();
536 }
537 }
538
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)539 void RenderSlider::OnTouchTestHit(
540 const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
541 {
542 if (!isValueError_ && !disable_) {
543 dragDetector_->SetCoordinateOffset(coordinateOffset);
544 clickDetector_->SetCoordinateOffset(coordinateOffset);
545 result.emplace_back(dragDetector_);
546 result.emplace_back(clickDetector_);
547 }
548 }
549
FindCenterVertex(double x,double y,double objectWidth,double objectHeight)550 Vertex RenderSlider::FindCenterVertex(double x, double y, double objectWidth, double objectHeight)
551 {
552 // 0.5 is used to find the center position.
553 return Vertex(x + objectWidth * HALF, y + objectHeight * HALF);
554 }
555
GetTopTouchRegion(const Vertex & center,double width,double height)556 TouchRegionPoint RenderSlider::GetTopTouchRegion(const Vertex& center, double width, double height)
557 {
558 // 0.5 is used to find the top left point of the touch region
559 return TouchRegionPoint(center.GetX() - width * HALF, center.GetY() - height * HALF);
560 }
561
GetBotTouchRegion(const Vertex & center,double width,double height)562 TouchRegionPoint RenderSlider::GetBotTouchRegion(const Vertex& center, double width, double height)
563 {
564 // 0.5 is used to find the bot right point of the touch region
565 return TouchRegionPoint(center.GetX() + width * HALF, center.GetY() + height * HALF);
566 }
567
UpdateTouchRegion()568 void RenderSlider::UpdateTouchRegion()
569 {
570 if (direction_ == Axis::VERTICAL) {
571 double dxOffset = GetLayoutSize().Width() * HALF;
572 double dyOffset = trackLength_ * totalRatio_ + NormalizeToPx(SLIDER_PADDING_DP);
573 Vertex blockCenter = isReverse_ ?
574 TouchRegionPoint(dxOffset, GetLayoutSize().Height() - dyOffset) : TouchRegionPoint(dxOffset, dyOffset);
575 TouchRegionPoint blockTopPoint =
576 GetTopTouchRegion(blockCenter, NormalizeToPx(blockHotWidth_), NormalizeToPx(blockHotHeight_));
577 TouchRegionPoint blockBottomPoint =
578 GetBotTouchRegion(blockCenter, NormalizeToPx(blockHotWidth_), NormalizeToPx(blockHotHeight_));
579 blockTouchRegion_ = TouchRegion(blockTopPoint, blockBottomPoint);
580 } else {
581 double dxOffset = trackLength_ * totalRatio_ + NormalizeToPx(SLIDER_PADDING_DP);
582 double dyOffset = GetLayoutSize().Height() * HALF;
583 Vertex blockCenter = TouchRegionPoint();
584 if ((GetTextDirection() == TextDirection::LTR &&
585 !isReverse_) || (GetTextDirection() == TextDirection::RTL && isReverse_)) {
586 blockCenter = TouchRegionPoint(dxOffset, dyOffset);
587 } else if ((GetTextDirection() == TextDirection::RTL &&
588 !isReverse_) || (GetTextDirection() == TextDirection::LTR && isReverse_)) {
589 blockCenter = TouchRegionPoint(GetLayoutSize().Width() - dxOffset, dyOffset);
590 }
591 TouchRegionPoint blockTopPoint =
592 GetTopTouchRegion(blockCenter, NormalizeToPx(blockHotWidth_), NormalizeToPx(blockHotHeight_));
593 TouchRegionPoint blockBottomPoint =
594 GetBotTouchRegion(blockCenter, NormalizeToPx(blockHotWidth_), NormalizeToPx(blockHotHeight_));
595 blockTouchRegion_ = TouchRegion(blockTopPoint, blockBottomPoint);
596 }
597 }
598
HandleFocusEvent(const KeyEvent & keyEvent)599 bool RenderSlider::HandleFocusEvent(const KeyEvent& keyEvent)
600 {
601 bool updateEvent = false;
602 if (NearZero(trackLength_)) {
603 totalRatio_ = 0.0;
604 return updateEvent;
605 }
606 switch (keyEvent.code) {
607 case KeyCode::TV_CONTROL_LEFT:
608 totalRatio_ -= step_ / (max_ - min_);
609 if (totalRatio_ < 0.0) {
610 totalRatio_ = 0.0;
611 }
612 SyncValueToComponent((max_ - min_) * totalRatio_ + min_);
613 MarkNeedLayout();
614 updateEvent = true;
615 break;
616 case KeyCode::TV_CONTROL_RIGHT:
617 totalRatio_ += step_ / (max_ - min_);
618 if (totalRatio_ > 1.0) {
619 totalRatio_ = 1.0;
620 }
621 SyncValueToComponent((max_ - min_) * totalRatio_ + min_);
622 MarkNeedLayout();
623 updateEvent = true;
624 break;
625 default:
626 updateEvent = false;
627 break;
628 }
629 if (updateEvent) {
630 FireMoveEndEvent();
631 FireMovingEvent(SliderEvent::FOCUS);
632 }
633 return updateEvent;
634 }
635
StartMoveAnimation(double from,double to,bool isClick)636 void RenderSlider::StartMoveAnimation(double from, double to, bool isClick)
637 {
638 if (NearEqual(from, to)) {
639 return;
640 }
641 if (!moveController_) {
642 moveController_ = AceType::MakeRefPtr<Animator>(GetContext());
643 } else if (moveController_->IsRunning()) {
644 moveController_->Finish();
645 }
646 moveController_->ClearInterpolators();
647 moveController_->ClearAllListeners();
648
649 moveController_->AddStartListener([weak = AceType::WeakClaim(this), to]() {
650 auto slider = weak.Upgrade();
651 if (slider) {
652 slider->animationEnd_ = to;
653 }
654 });
655
656 moveController_->AddStopListener([weak = AceType::WeakClaim(this), isClick]() {
657 auto slider = weak.Upgrade();
658 if (slider) {
659 slider->SyncValueToComponent(slider->totalRatio_ * (slider->max_ - slider->min_) + slider->min_);
660 slider->SetTotalRatio(slider->totalRatio_);
661 slider->UpdateTouchRegion();
662 }
663 });
664
665 ResetMoveAnimation(from, to);
666 moveController_->SetDuration(SLIDER_MOVE_DURATION);
667 moveController_->AddInterpolator(moveAnimation_);
668 moveController_->Play();
669 }
670
CalculateTotalRadio()671 void RenderSlider::CalculateTotalRadio()
672 {
673 auto ratio = (value_ - min_) / (max_ - min_);
674 totalRatio_ = std::clamp(ratio, 0.0, 1.0);
675 }
676
ResetMoveAnimation(double from,double to)677 void RenderSlider::ResetMoveAnimation(double from, double to)
678 {
679 moveAnimation_ = AceType::MakeRefPtr<CurveAnimation<double>>(from, to, Curves::MAGNETIC);
680 auto weak = AceType::WeakClaim(this);
681 moveAnimation_->AddListener(Animation<double>::ValueCallback([weak](double value) {
682 auto slider = weak.Upgrade();
683 if (slider) {
684 slider->value_ = value;
685 slider->CalculateTotalRadio();
686 slider->MarkNeedLayout();
687 }
688 }));
689 }
690
RestartMoveAnimation(double value,bool isClick)691 void RenderSlider::RestartMoveAnimation(double value, bool isClick)
692 {
693 if (moveController_ && moveController_->IsRunning()) {
694 if (!NearEqual(value, animationEnd_)) {
695 moveController_->Stop();
696 StartMoveAnimation(value_, value, isClick);
697 }
698 } else {
699 StartMoveAnimation(value_, value, isClick);
700 }
701 }
702
UpdateAnimation()703 void RenderSlider::UpdateAnimation()
704 {
705 double from = DEFAULT_NORMAL_RADIUS_SCALE;
706 double to = DEFAULT_LARGE_RADIUS_SCALE;
707 if (!blockActive_) {
708 from = DEFAULT_LARGE_RADIUS_SCALE;
709 to = DEFAULT_NORMAL_RADIUS_SCALE;
710 }
711
712 if (translate_) {
713 controller_->RemoveInterpolator(translate_);
714 }
715 translate_ = AceType::MakeRefPtr<CurveAnimation<double>>(from, to, Curves::FRICTION);
716 auto weak = AceType::WeakClaim(this);
717 translate_->AddListener(Animation<double>::ValueCallback([weak](double value) {
718 auto sliderComp = weak.Upgrade();
719 if (sliderComp) {
720 sliderComp->radiusScale_ = value;
721 sliderComp->MarkNeedLayout();
722 }
723 }));
724 controller_->SetDuration(DEFAULT_SLIDER_ANIMATION_DURATION);
725 controller_->AddInterpolator(translate_);
726 }
727
728 } // namespace OHOS::Ace
729