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