• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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