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/toggle/switch_pattern.h"
17
18 #include "base/log/dump_log.h"
19 #include "core/common/recorder/node_data_cache.h"
20 #include "core/components/toggle/toggle_theme.h"
21 #include "core/pipeline_ng/pipeline_context.h"
22
23 namespace OHOS::Ace::NG {
24 namespace {
25 constexpr int32_t DEFAULT_DURATION = 200;
26 const Color ITEM_FILL_COLOR = Color::TRANSPARENT;
27 constexpr double NUMBER_TWO = 2.0;
28 constexpr int32_t HOTZONE_SPACE = 2;
29 } // namespace
30
OnAttachToFrameNode()31 void SwitchPattern::OnAttachToFrameNode()
32 {
33 auto host = GetHost();
34 CHECK_NULL_VOID(host);
35 auto renderContext = host->GetRenderContext();
36 CHECK_NULL_VOID(renderContext);
37 renderContext->SetAlphaOffscreen(true);
38 }
39
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,bool skipMeasure,bool skipLayout)40 bool SwitchPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, bool skipMeasure, bool skipLayout)
41 {
42 if (skipMeasure || dirty->SkipMeasureContent()) {
43 return false;
44 }
45 if (isOn_.value_or(false)) {
46 currentOffset_ = GetSwitchWidth();
47 }
48
49 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
50 CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
51 auto switchLayoutAlgorithm = DynamicCast<SwitchLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
52 CHECK_NULL_RETURN(switchLayoutAlgorithm, false);
53
54 auto height = switchLayoutAlgorithm->GetHeight();
55 auto width = switchLayoutAlgorithm->GetWidth();
56
57 width_ = width;
58 height_ = height;
59 auto geometryNode = dirty->GetGeometryNode();
60 offset_ = geometryNode->GetContentOffset();
61 size_ = geometryNode->GetContentSize();
62 if (!isUserSetResponseRegion_) {
63 AddHotZoneRect();
64 }
65 return true;
66 }
67
OnModifyDone()68 void SwitchPattern::OnModifyDone()
69 {
70 Pattern::OnModifyDone();
71 UpdateSwitchLayoutProperty();
72 UpdateSwitchPaintProperty();
73 InitClickEvent();
74 auto host = GetHost();
75 CHECK_NULL_VOID(host);
76 auto hub = host->GetOrCreateEventHub<EventHub>();
77 CHECK_NULL_VOID(hub);
78 auto gestureHub = hub->GetOrCreateGestureEventHub();
79 CHECK_NULL_VOID(gestureHub);
80 auto pipeline = host->GetContextRefPtr();
81 CHECK_NULL_VOID(pipeline);
82 switchTheme_ = pipeline->GetTheme<SwitchTheme>(host->GetThemeScopeId());
83 InitPanEvent(gestureHub);
84 InitTouchEvent();
85 InitMouseEvent();
86 InitFocusEvent();
87 auto focusHub = host->GetFocusHub();
88 CHECK_NULL_VOID(focusHub);
89 InitOnKeyEvent(focusHub);
90 SetAccessibilityAction();
91 FireBuilder();
92 HandleEnabled();
93 }
94
InitFocusEvent()95 void SwitchPattern::InitFocusEvent()
96 {
97 auto host = GetHost();
98 CHECK_NULL_VOID(host);
99 auto focusHub = host->GetOrCreateFocusHub();
100 CHECK_NULL_VOID(focusHub);
101 auto focusTask = [weak = WeakClaim(this)](FocusReason reason) {
102 TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "switch button handle focus event");
103 auto pattern = weak.Upgrade();
104 CHECK_NULL_VOID(pattern);
105 pattern->HandleFocusEvent();
106 };
107 focusHub->SetOnFocusInternal(focusTask);
108
109 auto blurTask = [weak = WeakClaim(this)]() {
110 TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "switch button handle blur event");
111 auto pattern = weak.Upgrade();
112 CHECK_NULL_VOID(pattern);
113 pattern->HandleBlurEvent();
114 };
115 focusHub->SetOnBlurInternal(blurTask);
116 }
117
HandleBlurEvent()118 void SwitchPattern::HandleBlurEvent()
119 {
120 RemoveIsFocusActiveUpdateEvent();
121 OnIsFocusActiveUpdate(false);
122 }
123
HandleFocusEvent()124 void SwitchPattern::HandleFocusEvent()
125 {
126 auto host = GetHost();
127 CHECK_NULL_VOID(host);
128 auto pipeline = host->GetContextRefPtr();
129 CHECK_NULL_VOID(pipeline);
130 AddIsFocusActiveUpdateEvent();
131 if (pipeline->GetIsFocusActive()) {
132 OnIsFocusActiveUpdate(true);
133 }
134 }
135
AddIsFocusActiveUpdateEvent()136 void SwitchPattern::AddIsFocusActiveUpdateEvent()
137 {
138 if (!isFocusActiveUpdateEvent_) {
139 isFocusActiveUpdateEvent_ = [weak = WeakClaim(this)](bool isFocusAcitve) {
140 auto pattern = weak.Upgrade();
141 CHECK_NULL_VOID(pattern);
142 pattern->OnIsFocusActiveUpdate(isFocusAcitve);
143 };
144 }
145 auto host = GetHost();
146 CHECK_NULL_VOID(host);
147 auto pipeline = host->GetContextRefPtr();
148 CHECK_NULL_VOID(pipeline);
149 pipeline->AddIsFocusActiveUpdateEvent(host, isFocusActiveUpdateEvent_);
150 }
151
RemoveIsFocusActiveUpdateEvent()152 void SwitchPattern::RemoveIsFocusActiveUpdateEvent()
153 {
154 auto host = GetHost();
155 CHECK_NULL_VOID(host);
156 auto pipeline = host->GetContextRefPtr();
157 CHECK_NULL_VOID(pipeline);
158 pipeline->RemoveIsFocusActiveUpdateEvent(host);
159 }
160
OnIsFocusActiveUpdate(bool isFocusAcitve)161 void SwitchPattern::OnIsFocusActiveUpdate(bool isFocusAcitve)
162 {
163 if (isFocusAcitve) {
164 touchHoverType_ = TouchHoverAnimationType::FOCUS;
165 } else {
166 touchHoverType_ = TouchHoverAnimationType::NONE;
167 }
168 auto host = GetHost();
169 CHECK_NULL_VOID(host);
170 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
171 }
172
HandleEnabled()173 void SwitchPattern::HandleEnabled()
174 {
175 if (UseContentModifier()) {
176 return;
177 }
178 auto host = GetHost();
179 CHECK_NULL_VOID(host);
180 auto eventHub = host->GetOrCreateEventHub<EventHub>();
181 CHECK_NULL_VOID(eventHub);
182 auto enabled = eventHub->IsEnabled();
183 auto renderContext = host->GetRenderContext();
184 CHECK_NULL_VOID(renderContext);
185 auto pipeline = host->GetContextWithCheck();
186 CHECK_NULL_VOID(pipeline);
187 auto theme = pipeline->GetTheme<ToggleTheme>();
188 CHECK_NULL_VOID(theme);
189 auto alpha = theme->GetDisabledAlpha();
190 auto originalOpacity = renderContext->GetOpacityValue(1.0);
191 renderContext->OnOpacityUpdate(enabled ? originalOpacity : alpha * originalOpacity);
192 }
193
UpdateSwitchPaintProperty()194 void SwitchPattern::UpdateSwitchPaintProperty()
195 {
196 auto host = GetHost();
197 CHECK_NULL_VOID(host);
198 auto switchPaintProperty = host->GetPaintProperty<SwitchPaintProperty>();
199 CHECK_NULL_VOID(switchPaintProperty);
200 auto geometryNode = host->GetGeometryNode();
201 CHECK_NULL_VOID(geometryNode);
202 if (!isOn_.has_value()) {
203 isOn_ = switchPaintProperty->GetIsOnValue(false);
204 }
205 auto isOn = switchPaintProperty->GetIsOnValue(false);
206 if (isOn != isOn_.value_or(false)) {
207 isOn_ = isOn;
208 OnChange();
209 }
210 }
211
UpdateSwitchLayoutProperty()212 void SwitchPattern::UpdateSwitchLayoutProperty()
213 {
214 auto pipeline = GetContext();
215 CHECK_NULL_VOID(pipeline);
216 auto switchTheme = pipeline->GetTheme<SwitchTheme>();
217 CHECK_NULL_VOID(switchTheme);
218 hotZoneHorizontalPadding_ = switchTheme->GetHotZoneHorizontalPadding();
219 hotZoneVerticalPadding_ = switchTheme->GetHotZoneVerticalPadding();
220 hotZoneHorizontalSize_ = switchTheme->GetHotZoneHorizontalSize();
221 hotZoneVerticalSize_ = switchTheme->GetHotZoneVerticalSize();
222 auto host = GetHost();
223 CHECK_NULL_VOID(host);
224 auto layoutProperty = host->GetLayoutProperty();
225 CHECK_NULL_VOID(layoutProperty);
226 direction_ = layoutProperty->GetNonAutoLayoutDirection();
227 InitDefaultMargin();
228 if (layoutProperty->GetPositionProperty()) {
229 layoutProperty->UpdateAlignment(
230 layoutProperty->GetPositionProperty()->GetAlignment().value_or(Alignment::CENTER));
231 } else {
232 layoutProperty->UpdateAlignment(Alignment::CENTER);
233 }
234 }
235
InitDefaultMargin()236 void SwitchPattern::InitDefaultMargin()
237 {
238 if (makeFunc_.has_value()) {
239 ResetDefaultMargin();
240 return;
241 }
242 auto host = GetHost();
243 CHECK_NULL_VOID(host);
244 auto layoutProperty = host->GetLayoutProperty();
245 CHECK_NULL_VOID(layoutProperty);
246 MarginProperty margin;
247 margin.left = CalcLength(hotZoneHorizontalPadding_.Value());
248 margin.right = CalcLength(hotZoneHorizontalPadding_.Value());
249 margin.top = CalcLength(hotZoneVerticalPadding_.Value());
250 margin.bottom = CalcLength(hotZoneVerticalPadding_.Value());
251 auto& setMargin = layoutProperty->GetMarginProperty();
252 if (setMargin) {
253 if (setMargin->left.has_value()) {
254 margin.left = setMargin->left;
255 }
256 if (setMargin->right.has_value()) {
257 margin.right = setMargin->right;
258 }
259 if (setMargin->top.has_value()) {
260 margin.top = setMargin->top;
261 }
262 if (setMargin->bottom.has_value()) {
263 margin.bottom = setMargin->bottom;
264 }
265 }
266 layoutProperty->UpdateMargin(margin);
267 }
268
ResetDefaultMargin()269 void SwitchPattern::ResetDefaultMargin()
270 {
271 if (isUserSetMargin_) {
272 return;
273 }
274 auto host = GetHost();
275 CHECK_NULL_VOID(host);
276 auto layoutProperty = host->GetLayoutProperty();
277 CHECK_NULL_VOID(layoutProperty);
278 MarginProperty margin;
279 layoutProperty->UpdateMargin(margin);
280 }
281
SetAccessibilityAction()282 void SwitchPattern::SetAccessibilityAction()
283 {
284 auto host = GetHost();
285 CHECK_NULL_VOID(host);
286 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
287 CHECK_NULL_VOID(accessibilityProperty);
288 accessibilityProperty->SetActionSelect([weakPtr = WeakClaim(this)]() {
289 const auto& pattern = weakPtr.Upgrade();
290 CHECK_NULL_VOID(pattern);
291 pattern->UpdateSelectStatus(true);
292 });
293
294 accessibilityProperty->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
295 const auto& pattern = weakPtr.Upgrade();
296 CHECK_NULL_VOID(pattern);
297 pattern->UpdateSelectStatus(false);
298 });
299 FireBuilder();
300 }
301
UpdateSelectStatus(bool isSelected)302 void SwitchPattern::UpdateSelectStatus(bool isSelected)
303 {
304 auto host = GetHost();
305 CHECK_NULL_VOID(host);
306 auto context = host->GetRenderContext();
307 CHECK_NULL_VOID(context);
308 MarkIsSelected(isSelected);
309 context->OnMouseSelectUpdate(isSelected, ITEM_FILL_COLOR, ITEM_FILL_COLOR);
310 }
311
MarkIsSelected(bool isSelected)312 void SwitchPattern::MarkIsSelected(bool isSelected)
313 {
314 if (isOn_ == isSelected) {
315 return;
316 }
317 isOn_ = isSelected;
318 auto eventHub = GetOrCreateEventHub<SwitchEventHub>();
319 CHECK_NULL_VOID(eventHub);
320 eventHub->UpdateChangeEvent(isSelected);
321 auto host = GetHost();
322 CHECK_NULL_VOID(host);
323 eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
324 host->OnAccessibilityEvent(AccessibilityEventType::COMPONENT_CHANGE);
325 }
326
OnAfterModifyDone()327 void SwitchPattern::OnAfterModifyDone()
328 {
329 auto host = GetHost();
330 CHECK_NULL_VOID(host);
331 auto inspectorId = host->GetInspectorId().value_or("");
332 if (!inspectorId.empty()) {
333 Recorder::NodeDataCache::Get().PutBool(host, inspectorId, isOn_.value_or(false));
334 }
335 }
336
GetCurve() const337 RefPtr<Curve> SwitchPattern::GetCurve() const
338 {
339 auto switchPaintProperty = GetPaintProperty<SwitchPaintProperty>();
340 CHECK_NULL_RETURN(switchPaintProperty, nullptr);
341 return switchPaintProperty->GetCurve().value_or(nullptr);
342 }
343
GetDuration() const344 int32_t SwitchPattern::GetDuration() const
345 {
346 auto switchPaintProperty = GetPaintProperty<SwitchPaintProperty>();
347 CHECK_NULL_RETURN(switchPaintProperty, DEFAULT_DURATION);
348 return switchPaintProperty->GetDuration().value_or(DEFAULT_DURATION);
349 }
350
OnChange()351 void SwitchPattern::OnChange()
352 {
353 auto host = GetHost();
354 CHECK_NULL_VOID(host);
355 auto switchPaintProperty = host->GetPaintProperty<SwitchPaintProperty>();
356 CHECK_NULL_VOID(switchPaintProperty);
357 CHECK_NULL_VOID(paintMethod_);
358 auto switchModifier = paintMethod_->GetSwitchModifier();
359 CHECK_NULL_VOID(switchModifier);
360 switchModifier->SetIsOn(isOn_.value_or(false));
361 switchPaintProperty->UpdateIsOn(isOn_.value_or(false));
362 UpdateChangeEvent();
363 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
364 }
365
GetSwitchWidth() const366 float SwitchPattern::GetSwitchWidth() const
367 {
368 auto host = GetHost();
369 CHECK_NULL_RETURN(host, false);
370 auto geometryNode = host->GetGeometryNode();
371 CHECK_NULL_RETURN(geometryNode, false);
372 auto switchWidth = geometryNode->GetContentSize().Width() - geometryNode->GetContentSize().Height();
373 return switchWidth;
374 }
375
GetSwitchContentOffsetX() const376 float SwitchPattern::GetSwitchContentOffsetX() const
377 {
378 auto host = GetHost();
379 CHECK_NULL_RETURN(host, 0.0f);
380 auto geometryNode = host->GetGeometryNode();
381 CHECK_NULL_RETURN(geometryNode, 0.0f);
382 return geometryNode->GetContentOffset().GetX();
383 }
384
UpdateChangeEvent() const385 void SwitchPattern::UpdateChangeEvent() const
386 {
387 auto switchEventHub = GetOrCreateEventHub<SwitchEventHub>();
388 CHECK_NULL_VOID(switchEventHub);
389 switchEventHub->UpdateChangeEvent(isOn_.value_or(false));
390 }
391
OnClick()392 void SwitchPattern::OnClick()
393 {
394 if (UseContentModifier()) {
395 return;
396 }
397 isOn_ = !isOn_.value_or(false);
398 TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "switch click result %{public}d", isOn_.value_or(false));
399 UpdateColorWhenIsOn(isOn_.value_or(false));
400 OnChange();
401 auto host = GetHost();
402 CHECK_NULL_VOID(host);
403 host->OnAccessibilityEvent(AccessibilityEventType::COMPONENT_CHANGE);
404 }
405
UpdateColorWhenIsOn(bool isOn)406 void SwitchPattern::UpdateColorWhenIsOn(bool isOn)
407 {
408 auto host = GetHost();
409 CHECK_NULL_VOID(host);
410 auto switchPaintProperty = host->GetPaintProperty<SwitchPaintProperty>();
411 CHECK_NULL_VOID(switchPaintProperty);
412 CHECK_NULL_VOID(switchTheme_);
413 CHECK_NULL_VOID(paintMethod_);
414 auto switchModifier = paintMethod_->GetSwitchModifier();
415 CHECK_NULL_VOID(switchModifier);
416
417 Color onBgColor = switchTheme_->GetActiveColor();
418 Color offBgColor = switchTheme_->GetInactiveColor();
419 if (isOn) {
420 if (switchPaintProperty->HasSelectedColor() && switchPaintProperty->GetSelectedColor() == onBgColor) {
421 switchPaintProperty->UpdateSelectedColor(onBgColor);
422 }
423 } else {
424 if (switchPaintProperty->HasUnselectedColor() && switchPaintProperty->GetUnselectedColor() == offBgColor) {
425 Color bgColor = isFocus_ ? switchTheme_->GetFocusedBGColorUnselected() : switchTheme_->GetInactiveColor();
426 switchPaintProperty->UpdateUnselectedColor(bgColor);
427 }
428 if (isFocus_) {
429 switchModifier->SetFocusPointColor(switchTheme_->GetPointColorUnselectedFocus());
430 }
431 }
432 }
433
OnTouchDown()434 void SwitchPattern::OnTouchDown()
435 {
436 if (UseContentModifier()) {
437 return;
438 }
439 TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "switch touch down hover status %{public}d", isHover_);
440 if (isHover_) {
441 touchHoverType_ = TouchHoverAnimationType::HOVER_TO_PRESS;
442 } else {
443 touchHoverType_ = TouchHoverAnimationType::PRESS;
444 }
445 auto host = GetHost();
446 CHECK_NULL_VOID(host);
447 isTouch_ = true;
448 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
449 }
450
OnTouchUp()451 void SwitchPattern::OnTouchUp()
452 {
453 if (UseContentModifier()) {
454 return;
455 }
456 TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "switch touch up hover status %{public}d", isHover_);
457 if (isHover_) {
458 touchHoverType_ = TouchHoverAnimationType::PRESS_TO_HOVER;
459 } else {
460 touchHoverType_ = TouchHoverAnimationType::NONE;
461 }
462 auto host = GetHost();
463 CHECK_NULL_VOID(host);
464 isTouch_ = false;
465 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
466 FireBuilder();
467 }
468
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)469 void SwitchPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
470 {
471 if (panEvent_) {
472 return;
473 }
474
475 auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& info) {
476 TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "switch drag start");
477 auto pattern = weak.Upgrade();
478 CHECK_NULL_VOID(pattern);
479 if (info.GetInputEventType() == InputEventType::AXIS) {
480 return;
481 }
482 pattern->HandleDragStart();
483 };
484
485 auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
486 auto pattern = weak.Upgrade();
487 CHECK_NULL_VOID(pattern);
488 pattern->HandleDragUpdate(info);
489 };
490
491 auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
492 TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "switch drag end");
493 auto pattern = weak.Upgrade();
494 CHECK_NULL_VOID(pattern);
495 if (info.GetInputEventType() == InputEventType::AXIS) {
496 return;
497 }
498 pattern->HandleDragEnd();
499 };
500
501 auto actionCancelTask = [weak = WeakClaim(this)]() {
502 TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "switch drag cancel");
503 auto pattern = weak.Upgrade();
504 CHECK_NULL_VOID(pattern);
505 pattern->HandleDragEnd();
506 };
507
508 PanDirection panDirection;
509 panDirection.type = PanDirection::HORIZONTAL;
510
511 panEvent_ = MakeRefPtr<PanEvent>(
512 std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
513 PanDistanceMap distanceMap = { { SourceTool::UNKNOWN, DEFAULT_PAN_DISTANCE.ConvertToPx() },
514 { SourceTool::PEN, DEFAULT_PEN_PAN_DISTANCE.ConvertToPx() } };
515 gestureHub->AddPanEvent(panEvent_, panDirection, 1, distanceMap);
516 }
517
InitClickEvent()518 void SwitchPattern::InitClickEvent()
519 {
520 if (clickListener_) {
521 return;
522 }
523 auto host = GetHost();
524 CHECK_NULL_VOID(host);
525 auto gesture = host->GetOrCreateGestureEventHub();
526 CHECK_NULL_VOID(gesture);
527 auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
528 auto switchPattern = weak.Upgrade();
529 CHECK_NULL_VOID(switchPattern);
530 switchPattern->OnClick();
531 };
532
533 clickListener_ = MakeRefPtr<ClickEvent>(std::move(clickCallback));
534 gesture->AddClickEvent(clickListener_);
535 }
536
InitTouchEvent()537 void SwitchPattern::InitTouchEvent()
538 {
539 if (touchListener_) {
540 return;
541 }
542 auto host = GetHost();
543 CHECK_NULL_VOID(host);
544 auto gesture = host->GetOrCreateGestureEventHub();
545 CHECK_NULL_VOID(gesture);
546 auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
547 auto switchPattern = weak.Upgrade();
548 CHECK_NULL_VOID(switchPattern);
549 if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
550 switchPattern->OnTouchDown();
551 }
552 if (info.GetTouches().front().GetTouchType() == TouchType::UP ||
553 info.GetTouches().front().GetTouchType() == TouchType::CANCEL) {
554 switchPattern->OnTouchUp();
555 }
556 };
557 touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
558 gesture->AddTouchEvent(touchListener_);
559 }
560
InitMouseEvent()561 void SwitchPattern::InitMouseEvent()
562 {
563 if (mouseEvent_) {
564 return;
565 }
566 auto host = GetHost();
567 CHECK_NULL_VOID(host);
568 auto gesture = host->GetOrCreateGestureEventHub();
569 CHECK_NULL_VOID(gesture);
570 auto eventHub = GetHost()->GetOrCreateEventHub<SwitchEventHub>();
571 auto inputHub = eventHub->GetOrCreateInputEventHub();
572
573 auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
574 auto pattern = weak.Upgrade();
575 CHECK_NULL_VOID(pattern);
576 pattern->HandleMouseEvent(isHover);
577 };
578 mouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
579 inputHub->AddOnHoverEvent(mouseEvent_);
580 }
581
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)582 void SwitchPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
583 {
584 auto getInnerPaintRectCallback = [wp = WeakClaim(this)](RoundRect& paintRect) {
585 auto pattern = wp.Upgrade();
586 if (pattern) {
587 pattern->GetInnerFocusPaintRect(paintRect);
588 }
589 };
590 focusHub->SetInnerFocusPaintRectCallback(getInnerPaintRectCallback);
591 auto onKeyCallbackFunc = [wp = WeakClaim(this)](const KeyEvent& keyEventInfo) -> bool {
592 auto pattern = wp.Upgrade();
593 if (pattern) {
594 return pattern->OnKeyEvent(keyEventInfo);
595 }
596 TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "InitOnKeyEvent return false");
597 return false;
598 };
599 focusHub->SetOnKeyEventInternal(std::move(onKeyCallbackFunc));
600 }
601
OnKeyEvent(const KeyEvent & keyEventInfo)602 bool SwitchPattern::OnKeyEvent(const KeyEvent& keyEventInfo)
603 {
604 if (keyEventInfo.action == KeyAction::DOWN && keyEventInfo.code == KeyCode::KEY_FUNCTION) {
605 this->OnClick();
606 return true;
607 }
608 return false;
609 }
610
GetInnerFocusPaintRect(RoundRect & paintRect)611 void SwitchPattern::GetInnerFocusPaintRect(RoundRect& paintRect)
612 {
613 auto pipelineContext = PipelineBase::GetCurrentContext();
614 CHECK_NULL_VOID(pipelineContext);
615 auto switchTheme = pipelineContext->GetTheme<SwitchTheme>();
616 CHECK_NULL_VOID(switchTheme);
617 auto focusPaintPadding = switchTheme->GetFocusPaintPadding().ConvertToPx();
618
619 auto height = height_ + focusPaintPadding * 2;
620 auto width = width_ + focusPaintPadding * 2;
621 auto radio = height / 2.0;
622 auto offsetX = offset_.GetX() - focusPaintPadding;
623 auto offsetY = offset_.GetY() - focusPaintPadding;
624 CHECK_NULL_VOID(paintMethod_);
625 auto switchModifier = paintMethod_->GetSwitchModifier();
626 CHECK_NULL_VOID(switchModifier);
627 auto trackRadius = switchModifier->GetTrackRadius();
628 auto pointRadius = switchModifier->GetPointRadius();
629 if (pointRadius * NUMBER_TWO > height_) {
630 width = width_ - height_ + pointRadius * NUMBER_TWO + focusPaintPadding * NUMBER_TWO;
631 height = pointRadius * NUMBER_TWO + focusPaintPadding * NUMBER_TWO;
632 radio = pointRadius + focusPaintPadding;
633 offsetX = offset_.GetX() - focusPaintPadding - (pointRadius - height_ / NUMBER_TWO);
634 offsetY = offset_.GetY() - focusPaintPadding - (pointRadius - height_ / NUMBER_TWO);
635 if (width_ < height_) {
636 width = width_ + (pointRadius - trackRadius + focusPaintPadding) * NUMBER_TWO;
637 offsetX = offset_.GetX() - (pointRadius - trackRadius + focusPaintPadding);
638 }
639 } else {
640 if (SWITCH_ERROR_RADIUS != trackRadius) {
641 radio = trackRadius + focusPaintPadding;
642 }
643 if (width_ < height_ && pointRadius > trackRadius) {
644 width = width_ + (pointRadius - trackRadius + focusPaintPadding) * NUMBER_TWO;
645 offsetX = offset_.GetX() - (pointRadius - trackRadius + focusPaintPadding);
646 }
647 }
648 auto Rect = RectF(offsetX, offsetY, width, height);
649
650 paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_LEFT_POS, radio, radio);
651 paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_RIGHT_POS, radio, radio);
652 paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_LEFT_POS, radio, radio);
653 paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_RIGHT_POS, radio, radio);
654 paintRect.SetRect(Rect);
655 }
656
HandleMouseEvent(bool isHover)657 void SwitchPattern::HandleMouseEvent(bool isHover)
658 {
659 TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "switch mouse event %{public}d", isHover);
660 isHover_ = isHover;
661 if (isHover) {
662 touchHoverType_ = TouchHoverAnimationType::HOVER;
663 } else {
664 touchHoverType_ = TouchHoverAnimationType::NONE;
665 }
666 auto host = GetHost();
667 CHECK_NULL_VOID(host);
668 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
669 }
670
HandleDragStart()671 void SwitchPattern::HandleDragStart()
672 {
673 isDragEvent_ = true;
674 }
675
HandleDragUpdate(const GestureEvent & info)676 void SwitchPattern::HandleDragUpdate(const GestureEvent& info)
677 {
678 dragOffsetX_ = static_cast<float>(info.GetLocalLocation().GetX());
679 TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "switch drag update %{public}f", dragOffsetX_);
680 auto host = GetHost();
681 CHECK_NULL_VOID(host);
682 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
683 }
684
HandleDragEnd()685 void SwitchPattern::HandleDragEnd()
686 {
687 auto mainSize = GetSwitchWidth();
688 auto contentOffset = GetSwitchContentOffsetX();
689 if ((direction_ == TextDirection::RTL &&
690 ((isOn_.value_or(false) && dragOffsetX_ - contentOffset > mainSize / 2) ||
691 (!isOn_.value_or(false) && dragOffsetX_ - contentOffset <= mainSize / 2))) ||
692 (direction_ != TextDirection::RTL &&
693 ((isOn_.value_or(false) && dragOffsetX_ - contentOffset < mainSize / 2) ||
694 (!isOn_.value_or(false) && dragOffsetX_ - contentOffset >= mainSize / 2)))) {
695 OnClick();
696 }
697 isDragEvent_ = false;
698 dragOffsetX_ = 0.0f;
699 auto host = GetHost();
700 CHECK_NULL_VOID(host);
701 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
702 }
703
IsOutOfBoundary(double mainOffset) const704 bool SwitchPattern::IsOutOfBoundary(double mainOffset) const
705 {
706 return mainOffset < 0 || mainOffset > GetSwitchWidth();
707 }
708
709 // Set the default hot zone for the component.
AddHotZoneRect()710 void SwitchPattern::AddHotZoneRect()
711 {
712 hotZoneOffset_.SetX(offset_.GetX() - hotZoneHorizontalSize_.ConvertToPx());
713 hotZoneOffset_.SetY(offset_.GetY() - hotZoneVerticalSize_.ConvertToPx());
714 hotZoneSize_.SetWidth(size_.Width() + HOTZONE_SPACE * hotZoneHorizontalSize_.ConvertToPx());
715 hotZoneSize_.SetHeight(size_.Height() + HOTZONE_SPACE * hotZoneVerticalSize_.ConvertToPx());
716 DimensionRect hotZoneRegion;
717 hotZoneRegion.SetSize(DimensionSize(Dimension(hotZoneSize_.Width()), Dimension(hotZoneSize_.Height())));
718 hotZoneRegion.SetOffset(DimensionOffset(Dimension(hotZoneOffset_.GetX()), Dimension(hotZoneOffset_.GetY())));
719 auto host = GetHost();
720 CHECK_NULL_VOID(host);
721 auto gestureHub = host->GetOrCreateGestureEventHub();
722 CHECK_NULL_VOID(gestureHub);
723 std::vector<DimensionRect> hotZoneRegions;
724 hotZoneRegions.emplace_back(hotZoneRegion);
725 gestureHub->SetResponseRegion(hotZoneRegions);
726 }
727
RemoveLastHotZoneRect() const728 void SwitchPattern::RemoveLastHotZoneRect() const
729 {
730 auto host = GetHost();
731 CHECK_NULL_VOID(host);
732 host->RemoveLastHotZoneRect();
733 }
734
ProvideRestoreInfo()735 std::string SwitchPattern::ProvideRestoreInfo()
736 {
737 auto jsonObj = JsonUtil::Create(true);
738 jsonObj->Put("IsOn", isOn_.value_or(false));
739 return jsonObj->ToString();
740 }
741
OnRestoreInfo(const std::string & restoreInfo)742 void SwitchPattern::OnRestoreInfo(const std::string& restoreInfo)
743 {
744 auto switchPaintProperty = GetPaintProperty<SwitchPaintProperty>();
745 CHECK_NULL_VOID(switchPaintProperty);
746 auto info = JsonUtil::ParseJsonString(restoreInfo);
747 if (!info->IsValid() || !info->IsObject()) {
748 return;
749 }
750 auto jsonIsOn = info->GetValue("IsOn");
751 switchPaintProperty->UpdateIsOn(jsonIsOn->GetBool());
752 OnModifyDone();
753 }
754
OnColorConfigurationUpdate()755 void SwitchPattern::OnColorConfigurationUpdate()
756 {
757 auto host = GetHost();
758 CHECK_NULL_VOID(host);
759 auto pipeline = PipelineBase::GetCurrentContext();
760 CHECK_NULL_VOID(pipeline);
761 auto switchTheme = pipeline->GetTheme<SwitchTheme>(host->GetThemeScopeId());
762 CHECK_NULL_VOID(switchTheme);
763 CHECK_NULL_VOID(paintMethod_);
764 auto switchModifier = paintMethod_->GetSwitchModifier();
765 CHECK_NULL_VOID(switchModifier);
766 switchModifier->InitializeParam(host->GetThemeScopeId());
767 if (SystemProperties::ConfigChangePerform()) {
768 auto pipeline = host->GetContext();
769 CHECK_NULL_VOID(pipeline);
770 auto theme = pipeline->GetTheme<SwitchTheme>();
771 CHECK_NULL_VOID(theme);
772 auto pops = host->GetPaintProperty<SwitchPaintProperty>();
773 CHECK_NULL_VOID(pops);
774 if (!pops->GetSelectedColorSetByUserValue(false)) {
775 Color color = theme->GetActiveColor();
776 pops->UpdateSelectedColor(color);
777 }
778 if (!pops->GetSwitchPointColorSetByUserValue(false)) {
779 Color color = theme->GetPointColor();
780 pops->UpdateSwitchPointColor(color);
781 }
782 if (!pops->GetUnselectedColorSetByUserValue(false)) {
783 Color color = theme->GetInactiveColor();
784 pops->UpdateUnselectedColor(color);
785 }
786 }
787 host->MarkDirtyNode();
788 host->SetNeedCallChildrenUpdate(false);
789 }
790
OnThemeScopeUpdate(int32_t themeScopeId)791 bool SwitchPattern::OnThemeScopeUpdate(int32_t themeScopeId)
792 {
793 bool result = false;
794 auto host = GetHost();
795 CHECK_NULL_RETURN(host, false);
796 auto paintProperty = host->GetPaintProperty<SwitchPaintProperty>();
797 CHECK_NULL_RETURN(paintProperty, false);
798
799 if (!paintProperty->HasSelectedColor() || !paintProperty->HasSwitchPointColor()) {
800 result = true;
801 }
802
803 return result;
804 }
805
DumpInfo()806 void SwitchPattern::DumpInfo()
807 {
808 auto paintProperty = GetPaintProperty<SwitchPaintProperty>();
809 CHECK_NULL_VOID(paintProperty);
810 if (paintProperty->HasIsOn()) {
811 DumpLog::GetInstance().AddDesc("IsOn: " + std::string(paintProperty->GetIsOn().value() ? "true" : "false"));
812 }
813 if (paintProperty->HasSelectedColor()) {
814 DumpLog::GetInstance().AddDesc("SelectedColor: " + paintProperty->GetSelectedColor().value().ToString());
815 }
816 if (paintProperty->HasUnselectedColor()) {
817 DumpLog::GetInstance().AddDesc("UnselectedColor: " + paintProperty->GetUnselectedColor().value().ToString());
818 }
819 if (paintProperty->HasSwitchPointColor()) {
820 DumpLog::GetInstance().AddDesc("SwitchPointColor: " + paintProperty->GetSwitchPointColor().value().ToString());
821 }
822 if (paintProperty->HasPointRadius()) {
823 DumpLog::GetInstance().AddDesc("PointRadius: " + paintProperty->GetPointRadius().value().ToString());
824 }
825 if (paintProperty->HasTrackBorderRadius()) {
826 DumpLog::GetInstance().AddDesc(
827 "TrackBorderRadius: " + paintProperty->GetTrackBorderRadius().value().ToString());
828 }
829 }
830
SetSwitchIsOn(bool ison)831 void SwitchPattern::SetSwitchIsOn(bool ison)
832 {
833 auto host = GetHost();
834 CHECK_NULL_VOID(host);
835 auto eventHub = host->GetOrCreateEventHub<EventHub>();
836 CHECK_NULL_VOID(eventHub);
837 auto enabled = eventHub->IsEnabled();
838 if (!enabled) {
839 return;
840 }
841 auto paintProperty = host->GetPaintProperty<SwitchPaintProperty>();
842 CHECK_NULL_VOID(paintProperty);
843 paintProperty->UpdateIsOn(ison);
844 OnModifyDone();
845 }
846
FireBuilder()847 void SwitchPattern::FireBuilder()
848 {
849 auto host = GetHost();
850 CHECK_NULL_VOID(host);
851 if (!makeFunc_.has_value()) {
852 auto children = host->GetChildren();
853 for (const auto& child : children) {
854 if (child->GetId() == nodeId_) {
855 host->RemoveChildAndReturnIndex(child);
856 host->MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE);
857 break;
858 }
859 }
860 return;
861 }
862 auto node = BuildContentModifierNode();
863 if (contentModifierNode_ == node) {
864 return;
865 }
866 auto renderContext = host->GetRenderContext();
867 CHECK_NULL_VOID(renderContext);
868 renderContext->UpdateBackgroundColor(Color::TRANSPARENT);
869 host->RemoveChildAndReturnIndex(contentModifierNode_);
870 contentModifierNode_ = node;
871 CHECK_NULL_VOID(contentModifierNode_);
872 nodeId_ = contentModifierNode_->GetId();
873 host->AddChild(contentModifierNode_, 0);
874 host->MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE);
875 }
876
BuildContentModifierNode()877 RefPtr<FrameNode> SwitchPattern::BuildContentModifierNode()
878 {
879 if (!makeFunc_.has_value()) {
880 return nullptr;
881 }
882 auto host = GetHost();
883 CHECK_NULL_RETURN(host, nullptr);
884 auto paintProperty = host->GetPaintProperty<SwitchPaintProperty>();
885 CHECK_NULL_RETURN(paintProperty, nullptr);
886 auto eventHub = host->GetOrCreateEventHub<SwitchEventHub>();
887 CHECK_NULL_RETURN(eventHub, nullptr);
888 auto enabled = eventHub->IsEnabled();
889 bool isOn = false;
890 if (paintProperty->HasIsOn()) {
891 isOn = paintProperty->GetIsOnValue();
892 }
893 ToggleConfiguration toggleConfiguration(enabled, isOn);
894 return (makeFunc_.value())(toggleConfiguration);
895 }
896 } // namespace OHOS::Ace::NG
897