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