1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "core/components/toggle/render_toggle.h"
17
18 #include "base/log/log.h"
19 #include "core/components/common/properties/alignment.h"
20 #include "core/event/ace_event_helper.h"
21
22 namespace OHOS::Ace {
23 namespace {
24
25 constexpr int32_t HOVER_ANIMATION_DURATION = 250;
26
27 } // namespace
28
RenderToggle()29 RenderToggle::RenderToggle()
30 {
31 clickRecognizer_ = AceType::MakeRefPtr<ClickRecognizer>();
32 clickRecognizer_->SetOnClick([wp = AceType::WeakClaim(this)](const ClickInfo&) {
33 auto toggle = wp.Upgrade();
34 if (toggle) {
35 toggle->HandleClickEvent();
36 }
37 });
38 auto wp = AceType::WeakClaim(this);
39 touchRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
40 touchRecognizer_->SetOnTouchDown([wp](const TouchEventInfo&) {
41 auto toggle = wp.Upgrade();
42 if (toggle) {
43 toggle->HandleTouchEvent(true);
44 }
45 });
46 touchRecognizer_->SetOnTouchUp([wp](const TouchEventInfo&) {
47 auto toggle = wp.Upgrade();
48 if (toggle) {
49 toggle->HandleTouchEvent(false);
50 }
51 });
52 touchRecognizer_->SetOnTouchMove([wp](const TouchEventInfo& info) {
53 auto toggle = wp.Upgrade();
54 if (toggle) {
55 toggle->HandleMoveEvent(info);
56 }
57 });
58 }
59
HandleClickEvent()60 void RenderToggle::HandleClickEvent()
61 {
62 if (disabled_) {
63 return;
64 }
65 if (onClick_) {
66 onClick_();
67 }
68 auto checkValue = toggleComponent_->GetCheckedState();
69 toggleComponent_->SetCheckedState(!checkValue);
70 MarkNeedRender();
71 std::string checked = (!checkValue) ? "true" : "false";
72 std::string result = std::string(R"("change",{"checked":)").append(checked.append("},null"));
73 if (onChange_) {
74 onChange_(result);
75 }
76 if (onChangeToggle_) {
77 onChangeToggle_(!checkValue);
78 }
79 auto accessibilityNode = accessibilityNode_.Upgrade();
80 if (accessibilityNode) {
81 accessibilityNode->SetCheckedState(!checkValue);
82 }
83 }
84
HandleTouchEvent(bool touched)85 void RenderToggle::HandleTouchEvent(bool touched)
86 {
87 if (disabled_) {
88 return;
89 }
90 isPressed_ = touched;
91 if (isPressed_) {
92 isMoveEventValid_ = true;
93 }
94 if (isMoveEventValid_) {
95 MarkNeedRender();
96 }
97 }
98
HandleMoveEvent(const TouchEventInfo & info)99 void RenderToggle::HandleMoveEvent(const TouchEventInfo& info)
100 {
101 if (disabled_) {
102 return;
103 }
104 if (!isMoveEventValid_ || info.GetTouches().empty()) {
105 return;
106 }
107 const auto& locationInfo = info.GetTouches().front();
108 double moveX = locationInfo.GetLocalLocation().GetX();
109 double moveY = locationInfo.GetLocalLocation().GetY();
110 if ((LessNotEqual(moveX, 0.0) || GreatNotEqual(moveX, toggleSize_.Width()))
111 || (LessNotEqual(moveY, 0.0) || GreatNotEqual(moveY, toggleSize_.Height()))) {
112 isPressed_ = false;
113 isMoveEventValid_ = false;
114 MarkNeedRender();
115 }
116 }
117
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)118 void RenderToggle::OnTouchTestHit(
119 const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
120 {
121 if ((!touchRecognizer_) || (!clickRecognizer_)) {
122 return;
123 }
124 touchRecognizer_->SetCoordinateOffset(coordinateOffset);
125 clickRecognizer_->SetCoordinateOffset(coordinateOffset);
126 result.emplace_back(touchRecognizer_);
127 result.emplace_back(clickRecognizer_);
128 }
129
UpdateFocusAnimation()130 void RenderToggle::UpdateFocusAnimation()
131 {
132 auto context = context_.Upgrade();
133 if (!context) {
134 return;
135 }
136 double radius = toggleSize_.Height() / 2.0;
137 context->ShowFocusAnimation(RRect::MakeRRect(Rect(Offset(0, 0), toggleSize_), Radius(radius)),
138 Color(), GetGlobalOffset());
139 context->ShowShadow(RRect::MakeRRect(Rect(Offset(0, 0), toggleSize_), Radius(radius)), GetGlobalOffset());
140 }
141
OnMouseHoverEnterTest()142 void RenderToggle::OnMouseHoverEnterTest()
143 {
144 ResetController(hoverControllerExit_);
145 if (!hoverControllerEnter_) {
146 hoverControllerEnter_ = CREATE_ANIMATOR(context_);
147 }
148 scaleAnimationEnter_ = AceType::MakeRefPtr<KeyframeAnimation<float>>();
149 CreateFloatAnimation(scaleAnimationEnter_, 1.0, 1.05);
150 hoverControllerEnter_->AddInterpolator(scaleAnimationEnter_);
151 hoverControllerEnter_->SetDuration(HOVER_ANIMATION_DURATION);
152 hoverControllerEnter_->Play();
153 hoverControllerEnter_->SetFillMode(FillMode::FORWARDS);
154 }
155
OnMouseHoverExitTest()156 void RenderToggle::OnMouseHoverExitTest()
157 {
158 ResetController(hoverControllerEnter_);
159 if (!hoverControllerExit_) {
160 hoverControllerExit_ = CREATE_ANIMATOR(context_);
161 }
162 scaleAnimationExit_ = AceType::MakeRefPtr<KeyframeAnimation<float>>();
163 auto begin = scale_;
164 CreateFloatAnimation(scaleAnimationExit_, begin, 1.0);
165 hoverControllerExit_->AddInterpolator(scaleAnimationExit_);
166 hoverControllerExit_->SetDuration(HOVER_ANIMATION_DURATION);
167 hoverControllerExit_->Play();
168 hoverControllerExit_->SetFillMode(FillMode::FORWARDS);
169 }
170
OnMouseClickDownAnimation()171 void RenderToggle::OnMouseClickDownAnimation()
172 {
173 ResetController(clickControllerUp_);
174 if (!clickControllerDown_) {
175 clickControllerDown_ = CREATE_ANIMATOR(context_);
176 }
177 scaleAnimationDown_ = AceType::MakeRefPtr<KeyframeAnimation<float>>();
178 auto begin = scale_;
179 CreateFloatAnimation(scaleAnimationDown_, begin, 1.0);
180 clickControllerDown_->AddInterpolator(scaleAnimationDown_);
181 clickControllerDown_->SetDuration(HOVER_ANIMATION_DURATION);
182 clickControllerDown_->Play();
183 clickControllerDown_->SetFillMode(FillMode::FORWARDS);
184 }
185
OnMouseClickUpAnimation()186 void RenderToggle::OnMouseClickUpAnimation()
187 {
188 ResetController(clickControllerDown_);
189 if (!clickControllerUp_) {
190 clickControllerUp_ = CREATE_ANIMATOR(context_);
191 }
192 scaleAnimationUp_ = AceType::MakeRefPtr<KeyframeAnimation<float>>();
193 auto begin = scale_;
194 CreateFloatAnimation(scaleAnimationUp_, begin, 1.05);
195 clickControllerUp_->AddInterpolator(scaleAnimationUp_);
196 clickControllerUp_->SetDuration(HOVER_ANIMATION_DURATION);
197 clickControllerUp_->Play();
198 clickControllerUp_->SetFillMode(FillMode::FORWARDS);
199 }
200
CreateFloatAnimation(RefPtr<KeyframeAnimation<float>> & floatAnimation,float beginValue,float endValue)201 void RenderToggle::CreateFloatAnimation(RefPtr<KeyframeAnimation<float>>& floatAnimation, float beginValue,
202 float endValue)
203 {
204 if (!floatAnimation) {
205 return;
206 }
207 auto keyframeBegin = AceType::MakeRefPtr<Keyframe<float>>(0.0, beginValue);
208 auto keyframeEnd = AceType::MakeRefPtr<Keyframe<float>>(1.0, endValue);
209 floatAnimation->AddKeyframe(keyframeBegin);
210 floatAnimation->AddKeyframe(keyframeEnd);
211 floatAnimation->AddListener([weakToggle = AceType::WeakClaim(this)](float value) {
212 auto toggle = weakToggle.Upgrade();
213 if (toggle) {
214 toggle->scale_ = value;
215 toggle->MarkNeedRender();
216 }
217 });
218 }
219
ResetController(RefPtr<Animator> & controller)220 void RenderToggle::ResetController(RefPtr<Animator>& controller)
221 {
222 if (controller) {
223 if (!controller->IsStopped()) {
224 controller->Stop();
225 }
226 controller->ClearInterpolators();
227 }
228 }
229
Update(const RefPtr<Component> & component)230 void RenderToggle::Update(const RefPtr<Component>& component)
231 {
232 toggleComponent_ = AceType::DynamicCast<ToggleComponent>(component);
233 widthDefined_ = !NearZero(toggleComponent_->GetWidth().Value());
234 heightDefined_ = !NearZero(toggleComponent_->GetHeight().Value());
235 onClick_ = AceAsyncEvent<void()>::Create(toggleComponent_->GetClickEvent(), context_);
236 auto catchMode = toggleComponent_->GetClickEvent().IsEmpty() || toggleComponent_->GetClickEvent().GetCatchMode();
237 static const int32_t bubbleModeVersion = 6;
238 auto pipeline = context_.Upgrade();
239 if (!catchMode) {
240 if (pipeline && pipeline->GetMinPlatformVersion() >= bubbleModeVersion) {
241 catchMode = false;
242 } else {
243 catchMode = true;
244 }
245 }
246 clickRecognizer_->SetUseCatchMode(catchMode);
247 onChange_ = AceAsyncEvent<void(const std::string)>::Create(toggleComponent_->GetChangeEvent(), context_);
248 if (toggleComponent_->GetOnChange()) {
249 onChangeToggle_ = *toggleComponent_->GetOnChange();
250 }
251 disabled_ = toggleComponent_->IsDisabled();
252 ApplyRestoreInfo();
253 SetAccessibilityClickImpl();
254 MarkNeedLayout();
255 }
256
PerformLayout()257 void RenderToggle::PerformLayout()
258 {
259 toggleSize_ = Size(NormalizeToPx(toggleComponent_->GetWidth()), NormalizeToPx(toggleComponent_->GetHeight()));
260 Measure();
261 LayoutParam innerLayoutParam;
262 double maxWidth = widthDefined_ ? toggleSize_.Width() : GetLayoutParam().GetMaxSize().Width();
263 innerLayoutParam.SetMaxSize(Size(maxWidth, toggleSize_.Height()));
264 RefPtr<RenderNode> child;
265 Size childrenSize;
266 if (!GetChildren().empty()) {
267 child = GetChildren().front();
268 if (child) {
269 child->Layout(innerLayoutParam);
270 childrenSize.SetWidth(child->GetLayoutSize().Width());
271 childrenSize.SetHeight(child->GetLayoutSize().Height());
272 }
273 }
274 double width = widthDefined_ ? toggleSize_.Width() : childrenSize.Width();
275 double height = toggleSize_.Height();
276 if (!heightDefined_ && toggleComponent_->GetFontDefinedState()) {
277 height = childrenSize.Height();
278 }
279 Size layoutSize = Size(width, height);
280 layoutSize = GetLayoutParam().Constrain(layoutSize);
281 SetLayoutSize(layoutSize);
282 toggleSize_ = GetLayoutSize();
283 if (child) {
284 child->SetPosition(Alignment::GetAlignPosition(GetLayoutSize(), child->GetLayoutSize(), Alignment::CENTER));
285 }
286 }
287
SetAccessibilityClickImpl()288 void RenderToggle::SetAccessibilityClickImpl()
289 {
290 auto accessibilityNode = accessibilityNode_.Upgrade();
291 if (accessibilityNode) {
292 auto weakPtr = AceType::WeakClaim(AceType::RawPtr(clickRecognizer_));
293 accessibilityNode->AddSupportAction(AceAction::ACTION_CLICK);
294 accessibilityNode->SetClickableState(true);
295 accessibilityNode->SetCheckableState(true);
296 accessibilityNode->SetActionClickImpl([weakPtr]() {
297 auto click = weakPtr.Upgrade();
298 if (click) {
299 click->OnAccepted();
300 }
301 });
302 }
303 }
304
ProvideRestoreInfo()305 std::string RenderToggle::ProvideRestoreInfo()
306 {
307 auto jsonObj = JsonUtil::Create(true);
308 jsonObj->Put("checked", toggleComponent_->GetCheckedState());
309 jsonObj->Put("isPressed", isPressed_);
310
311 return jsonObj->ToString();
312 }
313
ApplyRestoreInfo()314 void RenderToggle::ApplyRestoreInfo()
315 {
316 if (GetRestoreInfo().empty()) {
317 return;
318 }
319 auto info = JsonUtil::ParseJsonString(GetRestoreInfo());
320 if (!info->IsValid() || !info->IsObject()) {
321 LOGW("RenderRadio:: restore info is invalid");
322 return;
323 }
324
325 auto jsonChecked = info->GetValue("checked");
326 auto jsonIsPressed = info->GetValue("isPressed");
327
328 toggleComponent_->SetCheckedState(jsonChecked->GetBool());
329 isPressed_ = jsonIsPressed->GetBool();
330 SetRestoreInfo("");
331 }
332
333 } // namespace OHOS::Ace
334