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 <cmath>
19 #include <cstdint>
20 
21 #include "base/memory/referenced.h"
22 #include "base/utils/utils.h"
23 #include "core/animation/curve.h"
24 #include "core/animation/curves.h"
25 #include "core/common/container.h"
26 #include "core/common/recorder/node_data_cache.h"
27 #include "core/components/checkable/checkable_theme.h"
28 #include "core/components_ng/pattern/toggle/switch_layout_algorithm.h"
29 #include "core/components_ng/pattern/toggle/switch_paint_property.h"
30 #include "core/components_ng/property/property.h"
31 #include "core/pipeline_ng/pipeline_context.h"
32 
33 namespace OHOS::Ace::NG {
34 namespace {
35 constexpr int32_t DEFAULT_DURATION = 200;
36 const Color ITEM_FILL_COLOR = Color::TRANSPARENT;
37 constexpr double NUMBER_TWO = 2.0;
38 } // namespace
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->GetEventHub<EventHub>();
77     CHECK_NULL_VOID(hub);
78     auto enabled = hub->IsEnabled();
79     if (enabled_ != enabled) {
80         enabled_ = enabled;
81         auto paintProperty = GetPaintProperty<SwitchPaintProperty>();
82         CHECK_NULL_VOID(paintProperty);
83         paintProperty->UpdatePropertyChangeFlag(PROPERTY_UPDATE_RENDER);
84     }
85     auto gestureHub = hub->GetOrCreateGestureEventHub();
86     CHECK_NULL_VOID(gestureHub);
87     InitPanEvent(gestureHub);
88     InitTouchEvent();
89     InitMouseEvent();
90     auto focusHub = host->GetFocusHub();
91     CHECK_NULL_VOID(focusHub);
92     InitOnKeyEvent(focusHub);
93     SetAccessibilityAction();
94     FireBuilder();
95 }
96 
UpdateSwitchPaintProperty()97 void SwitchPattern::UpdateSwitchPaintProperty()
98 {
99     auto host = GetHost();
100     CHECK_NULL_VOID(host);
101     auto switchPaintProperty = host->GetPaintProperty<SwitchPaintProperty>();
102     CHECK_NULL_VOID(switchPaintProperty);
103     auto geometryNode = host->GetGeometryNode();
104     CHECK_NULL_VOID(geometryNode);
105     if (!isOn_.has_value()) {
106         isOn_ = switchPaintProperty->GetIsOnValue(false);
107     }
108     auto isOn = switchPaintProperty->GetIsOnValue(false);
109     if (isOn != isOn_.value_or(false)) {
110         isOn_ = isOn;
111         OnChange();
112     }
113 }
114 
UpdateSwitchLayoutProperty()115 void SwitchPattern::UpdateSwitchLayoutProperty()
116 {
117     auto pipeline = GetContext();
118     CHECK_NULL_VOID(pipeline);
119     auto switchTheme = pipeline->GetTheme<SwitchTheme>();
120     CHECK_NULL_VOID(switchTheme);
121     MarginProperty margin;
122     margin.left = CalcLength(switchTheme->GetHotZoneHorizontalPadding().Value());
123     margin.right = CalcLength(switchTheme->GetHotZoneHorizontalPadding().Value());
124     margin.top = CalcLength(switchTheme->GetHotZoneVerticalPadding().Value());
125     margin.bottom = CalcLength(switchTheme->GetHotZoneVerticalPadding().Value());
126     auto host = GetHost();
127     CHECK_NULL_VOID(host);
128     auto layoutProperty = host->GetLayoutProperty();
129     CHECK_NULL_VOID(layoutProperty);
130     direction_ = layoutProperty->GetNonAutoLayoutDirection();
131     auto& setMargin = layoutProperty->GetMarginProperty();
132     if (setMargin) {
133         if (setMargin->left.has_value()) {
134             margin.left = setMargin->left;
135         }
136         if (setMargin->right.has_value()) {
137             margin.right = setMargin->right;
138         }
139         if (setMargin->top.has_value()) {
140             margin.top = setMargin->top;
141         }
142         if (setMargin->bottom.has_value()) {
143             margin.bottom = setMargin->bottom;
144         }
145     }
146     layoutProperty->UpdateMargin(margin);
147     hotZoneHorizontalPadding_ = switchTheme->GetHotZoneHorizontalPadding();
148     hotZoneVerticalPadding_ = switchTheme->GetHotZoneVerticalPadding();
149     if (layoutProperty->GetPositionProperty()) {
150         layoutProperty->UpdateAlignment(
151             layoutProperty->GetPositionProperty()->GetAlignment().value_or(Alignment::CENTER));
152     } else {
153         layoutProperty->UpdateAlignment(Alignment::CENTER);
154     }
155 }
156 
SetAccessibilityAction()157 void SwitchPattern::SetAccessibilityAction()
158 {
159     auto host = GetHost();
160     CHECK_NULL_VOID(host);
161     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
162     CHECK_NULL_VOID(accessibilityProperty);
163     accessibilityProperty->SetActionSelect([weakPtr = WeakClaim(this)]() {
164         const auto& pattern = weakPtr.Upgrade();
165         CHECK_NULL_VOID(pattern);
166         pattern->UpdateSelectStatus(true);
167     });
168 
169     accessibilityProperty->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
170         const auto& pattern = weakPtr.Upgrade();
171         CHECK_NULL_VOID(pattern);
172         pattern->UpdateSelectStatus(false);
173     });
174     FireBuilder();
175 }
176 
UpdateSelectStatus(bool isSelected)177 void SwitchPattern::UpdateSelectStatus(bool isSelected)
178 {
179     auto host = GetHost();
180     CHECK_NULL_VOID(host);
181     auto context = host->GetRenderContext();
182     CHECK_NULL_VOID(context);
183     MarkIsSelected(isSelected);
184     context->OnMouseSelectUpdate(isSelected, ITEM_FILL_COLOR, ITEM_FILL_COLOR);
185 }
186 
MarkIsSelected(bool isSelected)187 void SwitchPattern::MarkIsSelected(bool isSelected)
188 {
189     if (isOn_ == isSelected) {
190         return;
191     }
192     isOn_ = isSelected;
193     auto eventHub = GetEventHub<SwitchEventHub>();
194     CHECK_NULL_VOID(eventHub);
195     eventHub->UpdateChangeEvent(isSelected);
196     auto host = GetHost();
197     CHECK_NULL_VOID(host);
198     eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
199     host->OnAccessibilityEvent(AccessibilityEventType::COMPONENT_CHANGE);
200 }
201 
OnAfterModifyDone()202 void SwitchPattern::OnAfterModifyDone()
203 {
204     auto host = GetHost();
205     CHECK_NULL_VOID(host);
206     auto inspectorId = host->GetInspectorId().value_or("");
207     if (!inspectorId.empty()) {
208         Recorder::NodeDataCache::Get().PutBool(host, inspectorId, isOn_.value_or(false));
209     }
210 }
211 
GetCurve() const212 RefPtr<Curve> SwitchPattern::GetCurve() const
213 {
214     auto switchPaintProperty = GetPaintProperty<SwitchPaintProperty>();
215     CHECK_NULL_RETURN(switchPaintProperty, nullptr);
216     return switchPaintProperty->GetCurve().value_or(nullptr);
217 }
218 
GetDuration() const219 int32_t SwitchPattern::GetDuration() const
220 {
221     auto switchPaintProperty = GetPaintProperty<SwitchPaintProperty>();
222     CHECK_NULL_RETURN(switchPaintProperty, DEFAULT_DURATION);
223     return switchPaintProperty->GetDuration().value_or(DEFAULT_DURATION);
224 }
225 
OnChange()226 void SwitchPattern::OnChange()
227 {
228     auto host = GetHost();
229     CHECK_NULL_VOID(host);
230     auto switchPaintProperty = host->GetPaintProperty<SwitchPaintProperty>();
231     CHECK_NULL_VOID(switchPaintProperty);
232     switchPaintProperty->UpdateIsOn(isOn_.value_or(false));
233     UpdateChangeEvent();
234     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
235 }
236 
GetSwitchWidth() const237 float SwitchPattern::GetSwitchWidth() const
238 {
239     auto host = GetHost();
240     CHECK_NULL_RETURN(host, false);
241     auto geometryNode = host->GetGeometryNode();
242     CHECK_NULL_RETURN(geometryNode, false);
243     auto switchWidth = geometryNode->GetContentSize().Width() - geometryNode->GetContentSize().Height();
244     return switchWidth;
245 }
246 
GetSwitchContentOffsetX() const247 float SwitchPattern::GetSwitchContentOffsetX() const
248 {
249     auto host = GetHost();
250     CHECK_NULL_RETURN(host, 0.0f);
251     auto geometryNode = host->GetGeometryNode();
252     CHECK_NULL_RETURN(geometryNode, 0.0f);
253     return geometryNode->GetContentOffset().GetX();
254 }
255 
UpdateChangeEvent() const256 void SwitchPattern::UpdateChangeEvent() const
257 {
258     auto switchEventHub = GetEventHub<SwitchEventHub>();
259     CHECK_NULL_VOID(switchEventHub);
260     switchEventHub->UpdateChangeEvent(isOn_.value());
261 }
262 
OnClick()263 void SwitchPattern::OnClick()
264 {
265     if (UseContentModifier()) {
266         return;
267     }
268     isOn_ = !isOn_.value_or(false);
269     TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "switch click result %{public}d", isOn_.value_or(false));
270     OnChange();
271     auto host = GetHost();
272     CHECK_NULL_VOID(host);
273     host->OnAccessibilityEvent(AccessibilityEventType::COMPONENT_CHANGE);
274 }
275 
OnTouchDown()276 void SwitchPattern::OnTouchDown()
277 {
278     if (UseContentModifier()) {
279         return;
280     }
281     TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "switch touch down hover status %{public}d", isHover_);
282     if (isHover_) {
283         touchHoverType_ = TouchHoverAnimationType::HOVER_TO_PRESS;
284     } else {
285         touchHoverType_ = TouchHoverAnimationType::PRESS;
286     }
287     auto host = GetHost();
288     CHECK_NULL_VOID(host);
289     isTouch_ = true;
290     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
291 }
292 
OnTouchUp()293 void SwitchPattern::OnTouchUp()
294 {
295     if (UseContentModifier()) {
296         return;
297     }
298     TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "switch touch up hover status %{public}d", isHover_);
299     if (isHover_) {
300         touchHoverType_ = TouchHoverAnimationType::PRESS_TO_HOVER;
301     } else {
302         touchHoverType_ = TouchHoverAnimationType::NONE;
303     }
304     auto host = GetHost();
305     CHECK_NULL_VOID(host);
306     isTouch_ = false;
307     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
308     FireBuilder();
309 }
310 
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)311 void SwitchPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
312 {
313     if (panEvent_) {
314         return;
315     }
316 
317     auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& info) {
318         TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "switch drag start");
319         auto pattern = weak.Upgrade();
320         CHECK_NULL_VOID(pattern);
321         if (info.GetInputEventType() == InputEventType::AXIS) {
322             return;
323         }
324         pattern->HandleDragStart();
325     };
326 
327     auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
328         auto pattern = weak.Upgrade();
329         CHECK_NULL_VOID(pattern);
330         pattern->HandleDragUpdate(info);
331     };
332 
333     auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
334         TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "switch drag end");
335         auto pattern = weak.Upgrade();
336         CHECK_NULL_VOID(pattern);
337         if (info.GetInputEventType() == InputEventType::AXIS) {
338             return;
339         }
340         pattern->HandleDragEnd();
341     };
342 
343     auto actionCancelTask = [weak = WeakClaim(this)]() {
344         TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "switch drag cancel");
345         auto pattern = weak.Upgrade();
346         CHECK_NULL_VOID(pattern);
347         pattern->HandleDragEnd();
348     };
349 
350     PanDirection panDirection;
351     panDirection.type = PanDirection::HORIZONTAL;
352 
353     panEvent_ = MakeRefPtr<PanEvent>(
354         std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
355     gestureHub->AddPanEvent(panEvent_, panDirection, 1, DEFAULT_PAN_DISTANCE);
356 }
357 
InitClickEvent()358 void SwitchPattern::InitClickEvent()
359 {
360     if (clickListener_) {
361         return;
362     }
363     auto host = GetHost();
364     CHECK_NULL_VOID(host);
365     auto gesture = host->GetOrCreateGestureEventHub();
366     CHECK_NULL_VOID(gesture);
367     auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
368         auto switchPattern = weak.Upgrade();
369         CHECK_NULL_VOID(switchPattern);
370         switchPattern->OnClick();
371     };
372 
373     clickListener_ = MakeRefPtr<ClickEvent>(std::move(clickCallback));
374     gesture->AddClickEvent(clickListener_);
375 }
376 
InitTouchEvent()377 void SwitchPattern::InitTouchEvent()
378 {
379     if (touchListener_) {
380         return;
381     }
382     auto host = GetHost();
383     CHECK_NULL_VOID(host);
384     auto gesture = host->GetOrCreateGestureEventHub();
385     CHECK_NULL_VOID(gesture);
386     auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
387         auto switchPattern = weak.Upgrade();
388         CHECK_NULL_VOID(switchPattern);
389         if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
390             switchPattern->OnTouchDown();
391         }
392         if (info.GetTouches().front().GetTouchType() == TouchType::UP ||
393             info.GetTouches().front().GetTouchType() == TouchType::CANCEL) {
394             switchPattern->OnTouchUp();
395         }
396     };
397     touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
398     gesture->AddTouchEvent(touchListener_);
399 }
400 
InitMouseEvent()401 void SwitchPattern::InitMouseEvent()
402 {
403     if (mouseEvent_) {
404         return;
405     }
406     auto host = GetHost();
407     CHECK_NULL_VOID(host);
408     auto gesture = host->GetOrCreateGestureEventHub();
409     CHECK_NULL_VOID(gesture);
410     auto eventHub = GetHost()->GetEventHub<SwitchEventHub>();
411     auto inputHub = eventHub->GetOrCreateInputEventHub();
412 
413     auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
414         auto pattern = weak.Upgrade();
415         CHECK_NULL_VOID(pattern);
416         pattern->HandleMouseEvent(isHover);
417     };
418     mouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
419     inputHub->AddOnHoverEvent(mouseEvent_);
420 }
421 
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)422 void SwitchPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
423 {
424     auto getInnerPaintRectCallback = [wp = WeakClaim(this)](RoundRect& paintRect) {
425         auto pattern = wp.Upgrade();
426         if (pattern) {
427             pattern->GetInnerFocusPaintRect(paintRect);
428         }
429     };
430     focusHub->SetInnerFocusPaintRectCallback(getInnerPaintRectCallback);
431 }
432 
GetInnerFocusPaintRect(RoundRect & paintRect)433 void SwitchPattern::GetInnerFocusPaintRect(RoundRect& paintRect)
434 {
435     auto pipelineContext = PipelineBase::GetCurrentContext();
436     CHECK_NULL_VOID(pipelineContext);
437     auto switchTheme = pipelineContext->GetTheme<SwitchTheme>();
438     CHECK_NULL_VOID(switchTheme);
439     auto focusPaintPadding = switchTheme->GetFocusPaintPadding().ConvertToPx();
440 
441     auto height = height_ + focusPaintPadding * 2;
442     auto width = width_ + focusPaintPadding * 2;
443     auto radio = height / 2.0;
444     auto offsetX = offset_.GetX() - focusPaintPadding;
445     auto offsetY = offset_.GetY() - focusPaintPadding;
446     CHECK_NULL_VOID(paintMethod_);
447     auto switchModifier = paintMethod_->GetSwitchModifier();
448     CHECK_NULL_VOID(switchModifier);
449     auto trackRadius = switchModifier->GetTrackRadius();
450     auto pointRadius = switchModifier->GetPointRadius();
451     if (pointRadius * NUMBER_TWO > height_) {
452         width = width_ - height_ + pointRadius * NUMBER_TWO + focusPaintPadding * NUMBER_TWO;
453         height = pointRadius * NUMBER_TWO + focusPaintPadding * NUMBER_TWO;
454         radio = pointRadius + focusPaintPadding;
455         offsetX = offset_.GetX() - focusPaintPadding - (pointRadius - height_ / NUMBER_TWO);
456         offsetY = offset_.GetY() - focusPaintPadding - (pointRadius - height_ / NUMBER_TWO);
457         if (width_ < height_) {
458             width = width_ + (pointRadius - trackRadius + focusPaintPadding) * NUMBER_TWO;
459             offsetX = offset_.GetX() - (pointRadius - trackRadius + focusPaintPadding);
460         }
461     } else {
462         if (SWITCH_ERROR_RADIUS != trackRadius) {
463             radio = trackRadius + focusPaintPadding;
464         }
465         if (width_ < height_ && pointRadius > trackRadius) {
466             width = width_ + (pointRadius - trackRadius + focusPaintPadding) * NUMBER_TWO;
467             offsetX = offset_.GetX() - (pointRadius - trackRadius + focusPaintPadding);
468         }
469     }
470     auto Rect = RectF(offsetX, offsetY, width, height);
471 
472     paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_LEFT_POS, radio, radio);
473     paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_RIGHT_POS, radio, radio);
474     paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_LEFT_POS, radio, radio);
475     paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_RIGHT_POS, radio, radio);
476     paintRect.SetRect(Rect);
477 }
478 
HandleMouseEvent(bool isHover)479 void SwitchPattern::HandleMouseEvent(bool isHover)
480 {
481     TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "switch mouse event %{public}d", isHover);
482     isHover_ = isHover;
483     if (isHover) {
484         touchHoverType_ = TouchHoverAnimationType::HOVER;
485     } else {
486         touchHoverType_ = TouchHoverAnimationType::NONE;
487     }
488     auto host = GetHost();
489     CHECK_NULL_VOID(host);
490     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
491 }
492 
HandleDragStart()493 void SwitchPattern::HandleDragStart()
494 {
495     isDragEvent_ = true;
496 }
497 
HandleDragUpdate(const GestureEvent & info)498 void SwitchPattern::HandleDragUpdate(const GestureEvent& info)
499 {
500     dragOffsetX_ = static_cast<float>(info.GetLocalLocation().GetX());
501     TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "switch drag update %{public}f", dragOffsetX_);
502     auto host = GetHost();
503     CHECK_NULL_VOID(host);
504     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
505 }
506 
HandleDragEnd()507 void SwitchPattern::HandleDragEnd()
508 {
509     auto mainSize = GetSwitchWidth();
510     auto contentOffset = GetSwitchContentOffsetX();
511     if ((direction_ == TextDirection::RTL &&
512         ((isOn_.value() && dragOffsetX_ - contentOffset > mainSize / 2) ||
513         (!isOn_.value() && dragOffsetX_ - contentOffset <= mainSize / 2))) ||
514         (direction_ != TextDirection::RTL &&
515         ((isOn_.value() && dragOffsetX_ - contentOffset < mainSize / 2) ||
516         (!isOn_.value() && dragOffsetX_ - contentOffset >= mainSize / 2)))) {
517         OnClick();
518     }
519     isDragEvent_ = false;
520     dragOffsetX_ = 0.0f;
521     auto host = GetHost();
522     CHECK_NULL_VOID(host);
523     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
524 }
525 
IsOutOfBoundary(double mainOffset) const526 bool SwitchPattern::IsOutOfBoundary(double mainOffset) const
527 {
528     return mainOffset < 0 || mainOffset > GetSwitchWidth();
529 }
530 
531 // Set the default hot zone for the component.
AddHotZoneRect()532 void SwitchPattern::AddHotZoneRect()
533 {
534     hotZoneOffset_.SetX(offset_.GetX() - hotZoneHorizontalPadding_.ConvertToPx());
535     hotZoneOffset_.SetY(offset_.GetY() - hotZoneVerticalPadding_.ConvertToPx());
536     hotZoneSize_.SetWidth(size_.Width() + 2 * hotZoneHorizontalPadding_.ConvertToPx());
537     hotZoneSize_.SetHeight(size_.Height() + 2 * hotZoneVerticalPadding_.ConvertToPx());
538     DimensionRect hotZoneRegion;
539     hotZoneRegion.SetSize(DimensionSize(Dimension(hotZoneSize_.Width()), Dimension(hotZoneSize_.Height())));
540     hotZoneRegion.SetOffset(DimensionOffset(Dimension(hotZoneOffset_.GetX()), Dimension(hotZoneOffset_.GetY())));
541     auto host = GetHost();
542     CHECK_NULL_VOID(host);
543     auto gestureHub = host->GetOrCreateGestureEventHub();
544     CHECK_NULL_VOID(gestureHub);
545     std::vector<DimensionRect> hotZoneRegions;
546     hotZoneRegions.emplace_back(hotZoneRegion);
547     gestureHub->SetResponseRegion(hotZoneRegions);
548 }
549 
RemoveLastHotZoneRect() const550 void SwitchPattern::RemoveLastHotZoneRect() const
551 {
552     auto host = GetHost();
553     CHECK_NULL_VOID(host);
554     host->RemoveLastHotZoneRect();
555 }
556 
ProvideRestoreInfo()557 std::string SwitchPattern::ProvideRestoreInfo()
558 {
559     auto jsonObj = JsonUtil::Create(true);
560     jsonObj->Put("IsOn", isOn_.value_or(false));
561     return jsonObj->ToString();
562 }
563 
OnRestoreInfo(const std::string & restoreInfo)564 void SwitchPattern::OnRestoreInfo(const std::string& restoreInfo)
565 {
566     auto switchPaintProperty = GetPaintProperty<SwitchPaintProperty>();
567     CHECK_NULL_VOID(switchPaintProperty);
568     auto info = JsonUtil::ParseJsonString(restoreInfo);
569     if (!info->IsValid() || !info->IsObject()) {
570         return;
571     }
572     auto jsonIsOn = info->GetValue("IsOn");
573     switchPaintProperty->UpdateIsOn(jsonIsOn->GetBool());
574     OnModifyDone();
575 }
576 
OnColorConfigurationUpdate()577 void SwitchPattern::OnColorConfigurationUpdate()
578 {
579     auto host = GetHost();
580     CHECK_NULL_VOID(host);
581     auto pipeline = PipelineBase::GetCurrentContext();
582     CHECK_NULL_VOID(pipeline);
583     auto switchTheme = pipeline->GetTheme<SwitchTheme>();
584     CHECK_NULL_VOID(switchTheme);
585     auto switchPaintProperty = host->GetPaintProperty<SwitchPaintProperty>();
586     CHECK_NULL_VOID(switchPaintProperty);
587     switchPaintProperty->UpdateSwitchPointColor(switchTheme->GetPointColor());
588     CHECK_NULL_VOID(paintMethod_);
589     auto switchModifier = paintMethod_->GetSwitchModifier();
590     CHECK_NULL_VOID(switchModifier);
591     switchModifier->InitializeParam();
592     host->MarkDirtyNode();
593 }
594 
SetSwitchIsOn(bool ison)595 void SwitchPattern::SetSwitchIsOn(bool ison)
596 {
597     auto host = GetHost();
598     CHECK_NULL_VOID(host);
599     auto eventHub = host->GetEventHub<EventHub>();
600     CHECK_NULL_VOID(eventHub);
601     auto enabled = eventHub->IsEnabled();
602     if (!enabled) {
603         return;
604     }
605     auto paintProperty = host->GetPaintProperty<SwitchPaintProperty>();
606     CHECK_NULL_VOID(paintProperty);
607     paintProperty->UpdateIsOn(ison);
608     OnModifyDone();
609 }
610 
FireBuilder()611 void SwitchPattern::FireBuilder()
612 {
613     auto host = GetHost();
614     CHECK_NULL_VOID(host);
615     if (!makeFunc_.has_value()) {
616         auto children = host->GetChildren();
617         for (const auto& child : children) {
618             if (child->GetId() == nodeId_) {
619                 host->RemoveChildAndReturnIndex(child);
620                 host->MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE);
621                 break;
622             }
623         }
624         return;
625     }
626     auto node = BuildContentModifierNode();
627     if (contentModifierNode_ == node) {
628         return;
629     }
630     auto renderContext = host->GetRenderContext();
631     CHECK_NULL_VOID(renderContext);
632     renderContext->UpdateBackgroundColor(Color::TRANSPARENT);
633     host->RemoveChildAndReturnIndex(contentModifierNode_);
634     contentModifierNode_ = node;
635     CHECK_NULL_VOID(contentModifierNode_);
636     nodeId_ = contentModifierNode_->GetId();
637     host->AddChild(contentModifierNode_, 0);
638     host->MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE);
639 }
640 
BuildContentModifierNode()641 RefPtr<FrameNode> SwitchPattern::BuildContentModifierNode()
642 {
643     if (!makeFunc_.has_value()) {
644         return nullptr;
645     }
646     auto host = GetHost();
647     CHECK_NULL_RETURN(host, nullptr);
648     auto paintProperty = host->GetPaintProperty<SwitchPaintProperty>();
649     CHECK_NULL_RETURN(paintProperty, nullptr);
650     auto eventHub = host->GetEventHub<SwitchEventHub>();
651     CHECK_NULL_RETURN(eventHub, nullptr);
652     auto enabled = eventHub->IsEnabled();
653     bool isOn = false;
654     if (paintProperty->HasIsOn()) {
655         isOn = paintProperty->GetIsOnValue();
656     }
657     ToggleConfiguration toggleConfiguration(enabled, isOn);
658     return (makeFunc_.value())(toggleConfiguration);
659 }
660 } // namespace OHOS::Ace::NG
661