1 /*
2 * Copyright (c) 2022-2023 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_ng/pattern/slider/slider_pattern.h"
17
18 #include <valarray>
19
20 #include "base/geometry/offset.h"
21 #include "base/i18n/localization.h"
22 #include "base/utils/utils.h"
23 #include "core/components/theme/app_theme.h"
24 #include "core/components_ng/pattern/slider/slider_accessibility_property.h"
25 #include "core/components_ng/pattern/slider/slider_layout_property.h"
26 #include "core/components_ng/pattern/slider/slider_paint_property.h"
27 #include "core/components_ng/pattern/text/text_styles.h"
28 #include "core/components_ng/property/property.h"
29 #include "core/components_v2/inspector/inspector_constants.h"
30 #include "core/pipeline/pipeline_base.h"
31 #include "core/pipeline_ng/pipeline_context.h"
32
33 namespace OHOS::Ace::NG {
34 namespace {
35 constexpr float HALF = 0.5;
36 constexpr Dimension ARROW_WIDTH = 32.0_vp;
37 constexpr Dimension ARROW_HEIGHT = 8.0_vp;
38 constexpr float SLIDER_MIN = .0f;
39 constexpr float SLIDER_MAX = 100.0f;
40 } // namespace
41
OnModifyDone()42 void SliderPattern::OnModifyDone()
43 {
44 auto host = GetHost();
45 CHECK_NULL_VOID(host);
46 auto hub = host->GetEventHub<EventHub>();
47 CHECK_NULL_VOID(hub);
48 auto gestureHub = hub->GetOrCreateGestureEventHub();
49 CHECK_NULL_VOID(gestureHub);
50 auto inputEventHub = hub->GetOrCreateInputEventHub();
51 CHECK_NULL_VOID(inputEventHub);
52 auto layoutProperty = host->GetLayoutProperty<SliderLayoutProperty>();
53 CHECK_NULL_VOID(layoutProperty);
54 layoutProperty->UpdateAlignment(Alignment::CENTER);
55 auto sliderPaintProperty = host->GetPaintProperty<SliderPaintProperty>();
56 CHECK_NULL_VOID(sliderPaintProperty);
57 showTips_ = sliderPaintProperty->GetShowTips().value_or(false);
58 value_ = sliderPaintProperty->GetValue().value_or(0.0f);
59 float min = sliderPaintProperty->GetMin().value_or(0.0f);
60 float max = sliderPaintProperty->GetMax().value_or(100.0f);
61 float step = sliderPaintProperty->GetStep().value_or(1.0f);
62 CancelExceptionValue(min, max, step);
63 valueRatio_ = (value_ - min) / (max - min);
64 stepRatio_ = step / (max - min);
65 InitTouchEvent(gestureHub);
66 InitClickEvent(gestureHub);
67 InitPanEvent(gestureHub);
68 InitMouseEvent(inputEventHub);
69 auto focusHub = hub->GetFocusHub();
70 CHECK_NULL_VOID_NOLOG(focusHub);
71 InitOnKeyEvent(focusHub);
72 }
73
CancelExceptionValue(float & min,float & max,float & step)74 void SliderPattern::CancelExceptionValue(float& min, float& max, float& step)
75 {
76 auto sliderPaintProperty = GetPaintProperty<SliderPaintProperty>();
77 CHECK_NULL_VOID(sliderPaintProperty);
78 if (GreatOrEqual(min, max)) {
79 min = SLIDER_MIN;
80 max = SLIDER_MAX;
81 sliderPaintProperty->UpdateMin(min);
82 sliderPaintProperty->UpdateMax(max);
83 }
84 if (LessOrEqual(step, 0.0) || step > max - min) {
85 step = 1;
86 sliderPaintProperty->UpdateStep(step);
87 }
88 if (value_ < min || value_ > max) {
89 value_ = std::clamp(value_, min, max);
90 sliderPaintProperty->UpdateValue(value_);
91 FireChangeEvent(SliderChangeMode::End);
92 }
93 }
94
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,bool skipMeasure,bool)95 bool SliderPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, bool skipMeasure, bool /*skipLayout*/)
96 {
97 if (skipMeasure || dirty->SkipMeasureContent()) {
98 return false;
99 }
100
101 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
102 CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
103 auto sliderLayoutAlgorithm = DynamicCast<SliderLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
104 CHECK_NULL_RETURN(sliderLayoutAlgorithm, false);
105 trackThickness_ = sliderLayoutAlgorithm->GetTrackThickness();
106 blockDiameter_ = sliderLayoutAlgorithm->GetBlockDiameter();
107 blockHotSize_ = sliderLayoutAlgorithm->GetBlockHotSize();
108
109 auto host = GetHost();
110 CHECK_NULL_RETURN(host, false);
111 auto sliderLayoutProperty = host->GetLayoutProperty<SliderLayoutProperty>();
112 CHECK_NULL_RETURN(sliderLayoutProperty, false);
113 std::optional<SizeF> contentSize = GetHostContentSize();
114 CHECK_NULL_RETURN(contentSize.has_value(), false);
115 float length = sliderLayoutProperty->GetDirection().value_or(Axis::HORIZONTAL) == Axis::HORIZONTAL
116 ? contentSize.value().Width()
117 : contentSize.value().Height();
118
119 auto pipeline = PipelineContext::GetCurrentContext();
120 CHECK_NULL_RETURN(pipeline, false);
121 auto theme = pipeline->GetTheme<SliderTheme>();
122 CHECK_NULL_RETURN(theme, false);
123 Dimension hotBlockShadowWidth = sliderLayoutProperty->GetSliderMode().value_or(SliderModel::SliderMode::OUTSET) ==
124 SliderModel::SliderMode::OUTSET
125 ? theme->GetOutsetHotBlockShadowWidth()
126 : theme->GetInsetHotBlockShadowWidth();
127
128 hotBlockShadowWidth_ = static_cast<float>(hotBlockShadowWidth.ConvertToPx());
129 borderBlank_ = std::max(trackThickness_, blockDiameter_ + hotBlockShadowWidth_ / HALF);
130 // slider track length
131 sliderLength_ = length >= borderBlank_ ? length - borderBlank_ : 1;
132 borderBlank_ = (length - sliderLength_) * HALF;
133
134 return true;
135 }
136
InitTouchEvent(const RefPtr<GestureEventHub> & gestureHub)137 void SliderPattern::InitTouchEvent(const RefPtr<GestureEventHub>& gestureHub)
138 {
139 if (touchEvent_) {
140 return;
141 }
142 auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
143 auto pattern = weak.Upgrade();
144 CHECK_NULL_VOID_NOLOG(pattern);
145 pattern->HandleTouchEvent(info);
146 };
147 gestureHub->RemoveTouchEvent(touchEvent_);
148 touchEvent_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
149 gestureHub->AddTouchEvent(touchEvent_);
150 }
151
HandleTouchEvent(const TouchEventInfo & info)152 void SliderPattern::HandleTouchEvent(const TouchEventInfo& info)
153 {
154 auto touchType = info.GetTouches().front().GetTouchType();
155 if (touchType == TouchType::DOWN) {
156 hotFlag_ = true;
157 UpdateValueByLocalLocation(info.GetChangedTouches().front().GetLocalLocation());
158 if (showTips_) {
159 bubbleFlag_ = true;
160 InitializeBubble();
161 }
162 mousePressedFlag_ = true;
163 FireChangeEvent(SliderChangeMode::Begin);
164 OpenTranslateAnimation();
165 } else if (touchType == TouchType::UP) {
166 hotFlag_ = false;
167 if (bubbleFlag_) {
168 bubbleFlag_ = false;
169 }
170 mousePressedFlag_ = false;
171 CloseTranslateAnimation();
172 }
173 UpdateMarkDirtyNode(PROPERTY_UPDATE_RENDER);
174 }
175
InitClickEvent(const RefPtr<GestureEventHub> & gestureHub)176 void SliderPattern::InitClickEvent(const RefPtr<GestureEventHub>& gestureHub)
177 {
178 if (clickListener_) {
179 return;
180 }
181 auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
182 auto pattern = weak.Upgrade();
183 CHECK_NULL_VOID(pattern);
184 pattern->HandleClickEvent();
185 };
186 clickListener_ = MakeRefPtr<ClickEvent>(std::move(clickCallback));
187 gestureHub->AddClickEvent(clickListener_);
188 }
189
HandleClickEvent()190 void SliderPattern::HandleClickEvent()
191 {
192 FireChangeEvent(SliderChangeMode::Click);
193 FireChangeEvent(SliderChangeMode::End);
194 }
195
InitializeBubble()196 void SliderPattern::InitializeBubble()
197 {
198 CHECK_NULL_VOID(bubbleFlag_);
199 auto frameNode = GetHost();
200 CHECK_NULL_VOID(frameNode);
201 auto pipeline = PipelineBase::GetCurrentContext();
202 CHECK_NULL_VOID(pipeline);
203 auto sliderTheme = pipeline->GetTheme<SliderTheme>();
204 CHECK_NULL_VOID(sliderTheme);
205 std::string content = std::to_string(static_cast<int>(std::round(valueRatio_ * 100.0f))) + '%';
206 auto sliderPaintProperty = GetPaintProperty<SliderPaintProperty>();
207 sliderPaintProperty->UpdatePadding(sliderTheme->GetTipTextPadding());
208 sliderPaintProperty->UpdateTipColor(sliderTheme->GetTipColor());
209 sliderPaintProperty->UpdateTextColor(sliderTheme->GetTipTextColor());
210 sliderPaintProperty->UpdateFontSize(sliderTheme->GetTipFontSize());
211 sliderPaintProperty->UpdateContent(content);
212 UpdateBubble();
213 }
214
HandlingGestureEvent(const GestureEvent & info)215 void SliderPattern::HandlingGestureEvent(const GestureEvent& info)
216 {
217 if (info.GetInputEventType() == InputEventType::AXIS) {
218 info.GetMainDelta() > 0.0 ? MoveStep(-1) : MoveStep(1);
219 } else {
220 UpdateValueByLocalLocation(info.GetLocalLocation());
221 UpdateBubble();
222 }
223 UpdateMarkDirtyNode(PROPERTY_UPDATE_RENDER);
224 }
225
HandledGestureEvent()226 void SliderPattern::HandledGestureEvent()
227 {
228 hotFlag_ = false;
229 UpdateMarkDirtyNode(PROPERTY_UPDATE_RENDER);
230 }
231
UpdateValueByLocalLocation(const std::optional<Offset> & localLocation)232 void SliderPattern::UpdateValueByLocalLocation(const std::optional<Offset>& localLocation)
233 {
234 CHECK_NULL_VOID(localLocation.has_value());
235 auto host = GetHost();
236 CHECK_NULL_VOID(host);
237 auto sliderLayoutProperty = host->GetLayoutProperty<SliderLayoutProperty>();
238 CHECK_NULL_VOID(sliderLayoutProperty);
239 auto sliderPaintProperty = host->GetPaintProperty<SliderPaintProperty>();
240 float length = sliderLayoutProperty->GetDirection().value_or(Axis::HORIZONTAL) == Axis::HORIZONTAL
241 ? static_cast<float>(localLocation->GetX())
242 : static_cast<float>(localLocation->GetY());
243 float touchLength = sliderPaintProperty->GetReverse().value_or(false) ? borderBlank_ + sliderLength_ - length
244 : length - borderBlank_;
245 float min = sliderPaintProperty->GetMin().value_or(0.0f);
246 float max = sliderPaintProperty->GetMax().value_or(100.0f);
247 touchLength = std::clamp(touchLength, 0.0f, sliderLength_);
248 CHECK_NULL_VOID(sliderLength_ != 0);
249 valueRatio_ = touchLength / sliderLength_;
250 CHECK_NULL_VOID(stepRatio_ != 0);
251 valueRatio_ = NearEqual(valueRatio_, 1) ? 1 : std::round(valueRatio_ / stepRatio_) * stepRatio_;
252 float oldValue = value_;
253 value_ = valueRatio_ * (max - min) + min;
254 valueChangeFlag_ = !NearEqual(oldValue, value_);
255 }
256
UpdateTipsValue()257 void SliderPattern::UpdateTipsValue()
258 {
259 CHECK_NULL_VOID_NOLOG(valueChangeFlag_);
260 CHECK_NULL_VOID_NOLOG(showTips_);
261 CHECK_NULL_VOID(bubbleFlag_);
262 auto frameNode = GetHost();
263 CHECK_NULL_VOID(frameNode);
264 std::string content = std::to_string(static_cast<int>(std::round(valueRatio_ * 100.0f))) + '%';
265 frameNode->GetPaintProperty<SliderPaintProperty>()->UpdateContent(content);
266 }
267
UpdateCircleCenterOffset()268 void SliderPattern::UpdateCircleCenterOffset()
269 {
270 auto host = GetHost();
271 CHECK_NULL_VOID(host);
272 auto contentSize = GetHostContentSize();
273 CHECK_NULL_VOID(contentSize.has_value());
274 auto sliderPaintProperty = host->GetPaintProperty<SliderPaintProperty>();
275 CHECK_NULL_VOID(sliderPaintProperty);
276 auto touchLength = valueRatio_ * sliderLength_;
277 auto touchOffset = sliderPaintProperty->GetReverse().value_or(false) ? sliderLength_ - touchLength + borderBlank_
278 : touchLength + borderBlank_;
279 if (sliderPaintProperty->GetDirection().value_or(Axis::HORIZONTAL) == Axis::HORIZONTAL) {
280 circleCenter_.SetX(touchOffset);
281 circleCenter_.SetY(contentSize->Height() * HALF);
282 } else {
283 circleCenter_.SetX(contentSize->Width() * HALF);
284 circleCenter_.SetY(touchOffset);
285 }
286 }
287
UpdateBubbleSizeAndLayout()288 void SliderPattern::UpdateBubbleSizeAndLayout()
289 {
290 auto paintProperty = GetPaintProperty<SliderPaintProperty>();
291 CHECK_NULL_VOID(paintProperty);
292 auto pipeline = PipelineBase::GetCurrentContext();
293 CHECK_NULL_VOID(pipeline);
294 auto theme = pipeline->GetTheme<SliderTheme>();
295 CHECK_NULL_VOID(theme);
296 SizeF textSize = { 0, 0 };
297 if (paragraph_) {
298 textSize = SizeF(paragraph_->GetMaxIntrinsicWidth(), paragraph_->GetHeight());
299 }
300 OffsetF textOffsetInBubble = { 0, 0 };
301 auto padding = static_cast<float>(paintProperty->GetPadding().value_or(0.0_vp).ConvertToPx());
302 float bubbleSizeHeight = textSize.Height() + padding + padding;
303 float bubbleSizeWidth = textSize.Width();
304 if (paintProperty->GetDirection().value_or(Axis::HORIZONTAL) == Axis::HORIZONTAL) {
305 bubbleSizeWidth = std::max(static_cast<float>(ARROW_WIDTH.ConvertToPx()), bubbleSizeWidth);
306 bubbleSize_ = SizeF(
307 bubbleSizeWidth + bubbleSizeHeight, bubbleSizeHeight + static_cast<float>(ARROW_HEIGHT.ConvertToPx()));
308 textOffsetInBubble.SetX((bubbleSize_.Width() - textSize.Width()) * HALF);
309 textOffsetInBubble.SetY(padding);
310
311 bubbleOffset_.SetX(circleCenter_.GetX() - bubbleSize_.Width() * HALF);
312 bubbleOffset_.SetY(circleCenter_.GetY() -
313 static_cast<float>(theme->GetBubbleToCircleCenterDistance().ConvertToPx()) -
314 bubbleSize_.Height());
315 } else {
316 bubbleSizeHeight = std::max(static_cast<float>(ARROW_WIDTH.ConvertToPx()), bubbleSizeHeight);
317 bubbleSize_ =
318 SizeF(bubbleSizeWidth + static_cast<float>(ARROW_HEIGHT.ConvertToPx()), bubbleSizeHeight + bubbleSizeWidth);
319 textOffsetInBubble.SetY((bubbleSize_.Height() - textSize.Height()) * HALF);
320
321 bubbleOffset_.SetY(circleCenter_.GetY() - bubbleSize_.Height() * HALF);
322 bubbleOffset_.SetX(circleCenter_.GetX() -
323 static_cast<float>(theme->GetBubbleToCircleCenterDistance().ConvertToPx()) -
324 bubbleSize_.Width());
325 }
326 textOffset_ = bubbleOffset_ + textOffsetInBubble;
327 }
328
UpdateBubble()329 void SliderPattern::UpdateBubble()
330 {
331 CHECK_NULL_VOID_NOLOG(showTips_);
332 UpdateTipsValue();
333 CreateParagraphFunc();
334 UpdateCircleCenterOffset();
335 UpdateBubbleSizeAndLayout();
336 UpdateMarkDirtyNode(PROPERTY_UPDATE_RENDER);
337 }
338
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)339 void SliderPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
340 {
341 if (direction_ == GetDirection() && panEvent_) {
342 return;
343 }
344 direction_ = GetDirection();
345 auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& info) {
346 auto pattern = weak.Upgrade();
347 CHECK_NULL_VOID_NOLOG(pattern);
348 pattern->HandlingGestureEvent(info);
349 };
350 auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
351 auto pattern = weak.Upgrade();
352 CHECK_NULL_VOID_NOLOG(pattern);
353 pattern->HandlingGestureEvent(info);
354 pattern->FireChangeEvent(SliderChangeMode::Moving);
355 pattern->CloseTranslateAnimation();
356 };
357 auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& /*info*/) {
358 auto pattern = weak.Upgrade();
359 CHECK_NULL_VOID_NOLOG(pattern);
360 pattern->HandledGestureEvent();
361 pattern->FireChangeEvent(SliderChangeMode::End);
362 };
363 auto actionCancelTask = [weak = WeakClaim(this)]() {
364 auto pattern = weak.Upgrade();
365 CHECK_NULL_VOID_NOLOG(pattern);
366 pattern->HandledGestureEvent();
367 pattern->FireChangeEvent(SliderChangeMode::End);
368 };
369 if (panEvent_) {
370 gestureHub->RemovePanEvent(panEvent_);
371 }
372 panEvent_ = MakeRefPtr<PanEvent>(
373 std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
374
375 PanDirection panDirection;
376 panDirection.type = PanDirection::ALL;
377 float distance = static_cast<float>(Dimension(DEFAULT_PAN_DISTANCE, DimensionUnit::VP).ConvertToPx());
378 gestureHub->AddPanEvent(panEvent_, panDirection, 1, distance);
379 }
380
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)381 void SliderPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
382 {
383 auto getInnerPaintRectCallback = [wp = WeakClaim(this)](RoundRect& paintRect) {
384 auto pattern = wp.Upgrade();
385 CHECK_NULL_VOID_NOLOG(pattern);
386 pattern->GetInnerFocusPaintRect(paintRect);
387 };
388 focusHub->SetInnerFocusPaintRectCallback(getInnerPaintRectCallback);
389
390 auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
391 auto pattern = wp.Upgrade();
392 CHECK_NULL_RETURN_NOLOG(pattern, false);
393 return pattern->OnKeyEvent(event);
394 };
395 focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
396 }
397
GetInnerFocusPaintRect(RoundRect & paintRect)398 void SliderPattern::GetInnerFocusPaintRect(RoundRect& paintRect)
399 {
400 auto host = GetHost();
401 CHECK_NULL_VOID(host);
402 auto sliderLayoutProperty = host->GetLayoutProperty<SliderLayoutProperty>();
403 auto sliderMode = sliderLayoutProperty->GetSliderMode().value_or(SliderModel::SliderMode::OUTSET);
404 if (sliderMode == SliderModel::SliderMode::OUTSET) {
405 GetOutsetInnerFocusPaintRect(paintRect);
406 } else {
407 GetInsetInnerFocusPaintRect(paintRect);
408 }
409 }
410
GetOutsetInnerFocusPaintRect(RoundRect & paintRect)411 void SliderPattern::GetOutsetInnerFocusPaintRect(RoundRect& paintRect)
412 {
413 UpdateCircleCenterOffset();
414 const auto& content = GetHost()->GetGeometryNode()->GetContent();
415 CHECK_NULL_VOID(content);
416 auto contentOffset = content->GetRect().GetOffset();
417 auto theme = PipelineBase::GetCurrentContext()->GetTheme<SliderTheme>();
418 CHECK_NULL_VOID(theme);
419 auto appTheme = PipelineBase::GetCurrentContext()->GetTheme<AppTheme>();
420 CHECK_NULL_VOID(appTheme);
421 auto paintWidth = appTheme->GetFocusWidthVp();
422 auto focusSideDistance = theme->GetFocusSideDistance();
423 auto focusDistance = paintWidth * HALF + focusSideDistance;
424 auto focusRadius = blockDiameter_ * HALF + static_cast<float>(focusDistance.ConvertToPx());
425 paintRect.SetRect(RectF(circleCenter_.GetX() - focusRadius + contentOffset.GetX(),
426 circleCenter_.GetY() - focusRadius + contentOffset.GetY(), focusRadius / HALF, focusRadius / HALF));
427 paintRect.SetCornerRadius(focusRadius);
428 }
429
GetInsetInnerFocusPaintRect(RoundRect & paintRect)430 void SliderPattern::GetInsetInnerFocusPaintRect(RoundRect& paintRect)
431 {
432 auto frameSize = GetHostFrameSize();
433 CHECK_NULL_VOID(frameSize);
434 auto theme = PipelineBase::GetCurrentContext()->GetTheme<SliderTheme>();
435 CHECK_NULL_VOID(theme);
436 auto focusSideDistance = theme->GetFocusSideDistance();
437 auto appTheme = PipelineBase::GetCurrentContext()->GetTheme<AppTheme>();
438 CHECK_NULL_VOID(appTheme);
439 auto paintWidth = appTheme->GetFocusWidthVp();
440 auto focusDistance = paintWidth * HALF + focusSideDistance;
441 float offsetX = 0;
442 float offsetY = 0;
443 float width = frameSize->Width();
444 float height = frameSize->Height();
445 float focusRadius = trackThickness_ + static_cast<float>(focusDistance.ConvertToPx()) / HALF;
446 if (direction_ == Axis::HORIZONTAL) {
447 offsetX = borderBlank_ - trackThickness_ * HALF - static_cast<float>(focusDistance.ConvertToPx());
448 offsetY = (frameSize->Height() - trackThickness_) * HALF - static_cast<float>(focusDistance.ConvertToPx());
449 width = sliderLength_ + trackThickness_ + static_cast<float>(focusDistance.ConvertToPx()) / HALF;
450 height = trackThickness_ + static_cast<float>(focusDistance.ConvertToPx()) / HALF;
451 } else {
452 offsetX = (frameSize->Width() - trackThickness_) * HALF - static_cast<float>(focusDistance.ConvertToPx());
453 offsetY = borderBlank_ - trackThickness_ * HALF - static_cast<float>(focusDistance.ConvertToPx());
454 width = trackThickness_ + static_cast<float>(focusDistance.ConvertToPx()) / HALF;
455 height = sliderLength_ + trackThickness_ + static_cast<float>(focusDistance.ConvertToPx()) / HALF;
456 }
457 paintRect.SetRect(RectF(offsetX, offsetY, width, height));
458 paintRect.SetCornerRadius(focusRadius);
459 }
460
PaintFocusState()461 void SliderPattern::PaintFocusState()
462 {
463 auto host = GetHost();
464 CHECK_NULL_VOID(host);
465 RoundRect focusRect;
466 GetInnerFocusPaintRect(focusRect);
467
468 auto focusHub = host->GetFocusHub();
469 CHECK_NULL_VOID(focusHub);
470 focusHub->PaintInnerFocusState(focusRect);
471
472 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
473 }
474
OnKeyEvent(const KeyEvent & event)475 bool SliderPattern::OnKeyEvent(const KeyEvent& event)
476 {
477 if (event.action != KeyAction::DOWN) {
478 return false;
479 }
480 if ((direction_ == Axis::HORIZONTAL && event.code == KeyCode::KEY_DPAD_LEFT) ||
481 (direction_ == Axis::VERTICAL && event.code == KeyCode::KEY_DPAD_UP)) {
482 MoveStep(-1);
483 PaintFocusState();
484 }
485 if ((direction_ == Axis::HORIZONTAL && event.code == KeyCode::KEY_DPAD_RIGHT) ||
486 (direction_ == Axis::VERTICAL && event.code == KeyCode::KEY_DPAD_DOWN)) {
487 MoveStep(1);
488 PaintFocusState();
489 }
490 return false;
491 }
492
MoveStep(int32_t stepCount)493 bool SliderPattern::MoveStep(int32_t stepCount)
494 {
495 auto host = GetHost();
496 CHECK_NULL_RETURN(host, false);
497 auto sliderPaintProperty = host->GetPaintProperty<SliderPaintProperty>();
498 CHECK_NULL_RETURN(sliderPaintProperty, false);
499 float step = sliderPaintProperty->GetStep().value_or(1.0f);
500 float min = sliderPaintProperty->GetMin().value_or(0.0f);
501 float max = sliderPaintProperty->GetMax().value_or(100.0f);
502 if (NearZero(step)) {
503 return false;
504 }
505 float nextValue = -1.0;
506 nextValue = value_ + static_cast<float>(stepCount) * step;
507 if (NearEqual(nextValue, -1.0)) {
508 return false;
509 }
510 nextValue = std::clamp(nextValue, min, max);
511 nextValue = std::round(nextValue / step) * step;
512 if (NearEqual(nextValue, value_)) {
513 return false;
514 }
515 value_ = nextValue;
516 valueRatio_ = (value_ - min) / (max - min);
517 LOGD("Move %{public}d steps, Value change to %{public}f", stepCount, value_);
518 UpdateMarkDirtyNode(PROPERTY_UPDATE_RENDER);
519 return true;
520 }
521
InitMouseEvent(const RefPtr<InputEventHub> & inputEventHub)522 void SliderPattern::InitMouseEvent(const RefPtr<InputEventHub>& inputEventHub)
523 {
524 auto hoverEvent = [weak = WeakClaim(this)](bool isHover) {
525 auto pattern = weak.Upgrade();
526 CHECK_NULL_VOID_NOLOG(pattern);
527 pattern->HandleHoverEvent(isHover);
528 };
529 if (hoverEvent_) {
530 inputEventHub->RemoveOnHoverEvent(hoverEvent_);
531 }
532 hoverEvent_ = MakeRefPtr<InputEvent>(std::move(hoverEvent));
533 inputEventHub->AddOnHoverEvent(hoverEvent_);
534
535 auto mouseEvent = [weak = WeakClaim(this)](MouseInfo& info) {
536 auto pattern = weak.Upgrade();
537 CHECK_NULL_VOID_NOLOG(pattern);
538 pattern->HandleMouseEvent(info);
539 };
540 if (mouseEvent_) {
541 inputEventHub->RemoveOnMouseEvent(mouseEvent_);
542 }
543 mouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseEvent));
544 inputEventHub->AddOnMouseEvent(mouseEvent_);
545 }
546
HandleHoverEvent(bool isHover)547 void SliderPattern::HandleHoverEvent(bool isHover)
548 {
549 mouseHoverFlag_ = mouseHoverFlag_ && isHover;
550 UpdateMarkDirtyNode(PROPERTY_UPDATE_RENDER);
551 }
552
HandleMouseEvent(const MouseInfo & info)553 void SliderPattern::HandleMouseEvent(const MouseInfo& info)
554 {
555 UpdateCircleCenterOffset();
556 auto mouseToCenterDistanceX = static_cast<float>(std::abs(info.GetLocalLocation().GetX() - circleCenter_.GetX()));
557 auto mouseToCenterDistanceY = static_cast<float>(std::abs(info.GetLocalLocation().GetY() - circleCenter_.GetY()));
558 float mouseToCenterDistance = std::max(mouseToCenterDistanceX, mouseToCenterDistanceY);
559 mouseHoverFlag_ = LessOrEqual(mouseToCenterDistance, blockHotSize_ * HALF);
560 UpdateMarkDirtyNode(PROPERTY_UPDATE_RENDER);
561 }
562
FireChangeEvent(int32_t mode)563 void SliderPattern::FireChangeEvent(int32_t mode)
564 {
565 auto sliderEventHub = GetEventHub<SliderEventHub>();
566 CHECK_NULL_VOID(sliderEventHub);
567 if ((mode == SliderChangeMode::Click || mode == SliderChangeMode::Moving) &&
568 NearEqual(value_, sliderEventHub->GetValue())) {
569 return;
570 }
571 sliderEventHub->FireChangeEvent(static_cast<float>(value_), mode);
572 valueChangeFlag_ = false;
573 }
574
UpdateMarkDirtyNode(const PropertyChangeFlag & Flag)575 void SliderPattern::UpdateMarkDirtyNode(const PropertyChangeFlag& Flag)
576 {
577 auto host = GetHost();
578 CHECK_NULL_VOID(host);
579 host->MarkDirtyNode(Flag);
580 }
581
GetDirection() const582 Axis SliderPattern::GetDirection() const
583 {
584 auto sliderLayoutProperty = GetLayoutProperty<SliderLayoutProperty>();
585 CHECK_NULL_RETURN(sliderLayoutProperty, Axis::HORIZONTAL);
586 return sliderLayoutProperty->GetDirection().value_or(Axis::HORIZONTAL);
587 }
588
CreateAccessibilityProperty()589 RefPtr<AccessibilityProperty> SliderPattern::CreateAccessibilityProperty()
590 {
591 return MakeRefPtr<SliderAccessibilityProperty>();
592 }
593
CreateParagraphFunc()594 void SliderPattern::CreateParagraphFunc()
595 {
596 auto paintProperty = GetPaintProperty<SliderPaintProperty>();
597 CHECK_NULL_VOID(paintProperty);
598 auto pipeline = PipelineBase::GetCurrentContext();
599 CHECK_NULL_VOID(pipeline);
600 auto sliderTheme = pipeline->GetTheme<SliderTheme>();
601 CHECK_NULL_VOID(sliderTheme);
602 auto fontStyle = std::make_unique<NG::FontStyle>();
603 fontStyle->UpdateTextColor(paintProperty->GetTextColor().value_or(sliderTheme->GetTipTextColor()));
604 TextStyle textStyle = CreateTextStyleUsingTheme(fontStyle, nullptr, pipeline->GetTheme<TextTheme>());
605 auto layoutProperty = GetLayoutProperty<SliderLayoutProperty>();
606 auto contentSize = layoutProperty->CreateContentConstraint();
607 CreateParagraphAndLayout(textStyle, paintProperty->GetContent().value_or(""), contentSize);
608 }
609
CreateParagraphAndLayout(const TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint)610 void SliderPattern::CreateParagraphAndLayout(
611 const TextStyle& textStyle, const std::string& content, const LayoutConstraintF& contentConstraint)
612 {
613 if (!CreateParagraph(textStyle, content)) {
614 return;
615 }
616 CHECK_NULL_VOID(paragraph_);
617 auto size = contentConstraint.selfIdealSize;
618 size.UpdateIllegalSizeWithCheck(contentConstraint.maxSize);
619 auto maxSize = size.ConvertToSizeT();
620 paragraph_->Layout(maxSize.Width());
621 }
622
CreateParagraph(const TextStyle & textStyle,std::string content)623 bool SliderPattern::CreateParagraph(const TextStyle& textStyle, std::string content)
624 {
625 ParagraphStyle paraStyle = { .direction = TextDirection::LTR,
626 .align = textStyle.GetTextAlign(),
627 .maxLines = textStyle.GetMaxLines(),
628 .fontLocale = Localization::GetInstance()->GetFontLocale(),
629 .wordBreak = textStyle.GetWordBreak(),
630 .textOverflow = textStyle.GetTextOverflow() };
631 paragraph_ = Paragraph::Create(paraStyle, FontCollection::Current());
632 CHECK_NULL_RETURN(paragraph_, false);
633 paragraph_->PushStyle(textStyle);
634 StringUtils::TransformStrCase(content, static_cast<int32_t>(textStyle.GetTextCase()));
635 paragraph_->AddText(StringUtils::Str8ToStr16(content));
636 paragraph_->Build();
637 return true;
638 }
639
UpdateContentParameters()640 SliderContentModifier::Parameters SliderPattern::UpdateContentParameters()
641 {
642 auto paintProperty = GetPaintProperty<SliderPaintProperty>();
643 CHECK_NULL_RETURN(paintProperty, SliderContentModifier::Parameters());
644 auto pipeline = PipelineBase::GetCurrentContext();
645 CHECK_NULL_RETURN(pipeline, SliderContentModifier::Parameters());
646 auto theme = pipeline->GetTheme<SliderTheme>();
647 CHECK_NULL_RETURN(theme, SliderContentModifier::Parameters());
648 SliderContentModifier::Parameters parameters { trackThickness_, blockDiameter_, stepRatio_, hotBlockShadowWidth_,
649 mouseHoverFlag_, mousePressedFlag_ };
650 auto contentSize = GetHostContentSize();
651 auto contentOffset = GetHost()->GetGeometryNode()->GetContent()->GetRect().GetOffset();
652 // Distance between slide track and Content boundary
653 auto centerWidth = direction_ == Axis::HORIZONTAL ? contentSize->Height() : contentSize->Width();
654 centerWidth *= HALF;
655 parameters.selectColor = paintProperty->GetSelectColor().value_or(theme->GetTrackSelectedColor());
656 parameters.trackBackgroundColor = paintProperty->GetTrackBackgroundColor().value_or(theme->GetTrackBgColor());
657 parameters.blockColor = paintProperty->GetBlockColor().value_or(theme->GetBlockColor());
658
659 GetSelectPosition(parameters, centerWidth, contentOffset);
660 GetBackgroundPosition(parameters, centerWidth, contentOffset);
661 GetCirclePosition(parameters, centerWidth, contentOffset);
662 return parameters;
663 }
664
GetSelectPosition(SliderContentModifier::Parameters & parameters,float centerWidth,const OffsetF & offset)665 void SliderPattern::GetSelectPosition(
666 SliderContentModifier::Parameters& parameters, float centerWidth, const OffsetF& offset)
667 {
668 auto paintProperty = GetPaintProperty<SliderPaintProperty>();
669 CHECK_NULL_VOID(paintProperty);
670 float sliderSelectLength = std::clamp(sliderLength_ * valueRatio_, 0.0f, sliderLength_);
671 PointF start;
672 PointF end;
673 if (!paintProperty->GetReverseValue(false)) {
674 start = direction_ == Axis::HORIZONTAL ? PointF(offset.GetX() + borderBlank_, offset.GetY() + centerWidth)
675 : PointF(offset.GetX() + centerWidth, offset.GetY() + borderBlank_);
676 end = direction_ == Axis::HORIZONTAL
677 ? PointF(offset.GetX() + borderBlank_ + sliderSelectLength, offset.GetY() + centerWidth)
678 : PointF(offset.GetX() + centerWidth, offset.GetY() + borderBlank_ + sliderSelectLength);
679 } else {
680 start = direction_ == Axis::HORIZONTAL
681 ? PointF(offset.GetX() + borderBlank_ + sliderLength_, offset.GetY() + centerWidth)
682 : PointF(offset.GetX() + centerWidth, offset.GetY() + borderBlank_ + sliderLength_);
683 end =
684 direction_ == Axis::HORIZONTAL ?
685 PointF(offset.GetX() + borderBlank_ + sliderLength_ - sliderSelectLength, offset.GetY() + centerWidth) :
686 PointF(offset.GetX() + centerWidth, offset.GetY() + borderBlank_ + sliderLength_ - sliderSelectLength);
687 }
688 parameters.selectStart = start;
689 parameters.selectEnd = end;
690 }
691
GetBackgroundPosition(SliderContentModifier::Parameters & parameters,float centerWidth,const OffsetF & offset)692 void SliderPattern::GetBackgroundPosition(
693 SliderContentModifier::Parameters& parameters, float centerWidth, const OffsetF& offset)
694 {
695 auto startPointX = offset.GetX();
696 auto startPointY = offset.GetY();
697 auto start = direction_ == Axis::HORIZONTAL ? PointF(startPointX + borderBlank_, startPointY + centerWidth)
698 : PointF(startPointX + centerWidth, startPointY + borderBlank_);
699 auto end = direction_ == Axis::HORIZONTAL
700 ? PointF(startPointX + borderBlank_ + sliderLength_, startPointY + centerWidth)
701 : PointF(startPointX + centerWidth, startPointY + borderBlank_ + sliderLength_);
702 parameters.backStart = start;
703 parameters.backEnd = end;
704 }
705
GetCirclePosition(SliderContentModifier::Parameters & parameters,float centerWidth,const OffsetF & offset)706 void SliderPattern::GetCirclePosition(
707 SliderContentModifier::Parameters& parameters, float centerWidth, const OffsetF& offset)
708 {
709 float sliderSelectLength = std::clamp(sliderLength_ * valueRatio_, 0.0f, sliderLength_);
710 auto paintProperty = GetPaintProperty<SliderPaintProperty>();
711 CHECK_NULL_VOID(paintProperty);
712 PointF center;
713 if (!paintProperty->GetReverseValue(false)) {
714 center = direction_ == Axis::HORIZONTAL
715 ? PointF(offset.GetX() + borderBlank_ + sliderSelectLength, offset.GetY() + centerWidth)
716 : PointF(offset.GetX() + centerWidth, offset.GetY() + borderBlank_ + sliderSelectLength);
717 } else {
718 center =
719 direction_ == Axis::HORIZONTAL ?
720 PointF(offset.GetX() + borderBlank_ + sliderLength_ - sliderSelectLength, offset.GetY() + centerWidth) :
721 PointF(offset.GetX() + centerWidth, offset.GetY() + borderBlank_ + sliderLength_ - sliderSelectLength);
722 }
723 parameters.circleCenter = center;
724 }
725
OpenTranslateAnimation()726 void SliderPattern::OpenTranslateAnimation()
727 {
728 CHECK_NULL_VOID(sliderContentModifier_);
729 sliderContentModifier_->SetAnimated();
730 }
731
CloseTranslateAnimation()732 void SliderPattern::CloseTranslateAnimation()
733 {
734 CHECK_NULL_VOID(sliderContentModifier_);
735 sliderContentModifier_->SetNotAnimated();
736 }
737 } // namespace OHOS::Ace::NG
738