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