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