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/checkable/render_checkable.h"
17 
18 #include "base/log/event_report.h"
19 
20 namespace OHOS::Ace {
21 
Update(const RefPtr<Component> & component)22 void RenderCheckable::Update(const RefPtr<Component>& component)
23 {
24     auto checkable = AceType::DynamicCast<CheckableComponent>(component);
25     if (!checkable) {
26         LOGW("component is null");
27         return;
28     }
29     auto context = context_.Upgrade();
30     if (!context) {
31         return;
32     }
33     isDeclarative_ = context->GetIsDeclarative();
34     pointColor_ = checkable->GetPointColor().GetValue();
35     activeColor_ = checkable->GetActiveColor().GetValue();
36     pointColorInspector_ = checkable->GetPointColor();
37     activeColorInspector_ = checkable->GetActiveColor();
38     inactiveColor_ = checkable->GetInactiveColor().GetValue();
39     inactivePointColor_ = checkable->GetInactivePointColor().GetValue();
40     focusColor_ = checkable->GetFocusColor().GetValue();
41     shadowColor_ = checkable->GetShadowColor().GetValue();
42     shadowWidth_ = checkable->GetShadowWidth();
43     defaultWidth_ = checkable->GetDefaultWidth();
44     defaultHeight_ = checkable->GetDefaultHeight();
45     aspectRatio_ = checkable->GetAspectRatio();
46     backgroundSolid_ = checkable->IsBackgroundSolid();
47     hotZoneHorizontalPadding_ = checkable->GetHotZoneHorizontalPadding();
48     hotZoneVerticalPadding_ = checkable->GetHotZoneVerticalPadding();
49     if (isDeclarative_) {
50         disabled_ = checkable->IsDisabledStatus();
51     } else {
52         disabled_ = checkable->IsDisabled();
53     }
54     hoverAnimationType_ = checkable->GetMouseAnimationType();
55     auto clickId = checkable->GetClickEvent();
56     auto catchMode = true;
57     if (!clickId.IsEmpty()) {
58         catchMode = clickId.GetCatchMode();
59     }
60     clickEvent_ = AceAsyncEvent<void()>::Create(clickId, context_);
61     changeEvent_ = AceAsyncEvent<void(const std::string)>::Create(checkable->GetChangeEvent(), context_);
62     valueChangeEvent_ = checkable->GetChangeEvent().GetUiStrFunction();
63     domChangeEvent_ = AceAsyncEvent<void(const std::string&)>::Create(checkable->GetDomChangeEvent(), context_);
64     needFocus_ = checkable->GetNeedFocus();
65     if (checkable->GetOnChange()) {
66         onChange_ = *checkable->GetOnChange();
67     } else {
68         onChange_ = nullptr;
69     }
70     if (checkable->GetOnClick()) {
71         onClick_ = *checkable->GetOnClick();
72     }
73     InitTouchRecognizer();
74     InitClickRecognizer(catchMode);
75     AddAccessibilityAction();
76     MarkNeedLayout();
77 }
78 
AddAccessibilityAction()79 void RenderCheckable::AddAccessibilityAction()
80 {
81     auto accessibilityNode = GetAccessibilityNode().Upgrade();
82     if (!accessibilityNode) {
83         return;
84     }
85     accessibilityNode->AddSupportAction(AceAction::ACTION_CLICK);
86     accessibilityNode->SetActionClickImpl([weakPtr = AceType::WeakClaim(this)]() {
87         auto renderCheckable = weakPtr.Upgrade();
88         if (renderCheckable) {
89             renderCheckable->HandleClick();
90         }
91     });
92 }
93 
InitSize()94 void RenderCheckable::InitSize()
95 {
96     // Get parent(box)'s layoutParam.GetMaxSize as self size.
97     width_ = GetLayoutParam().GetMaxSize().Width();
98     if (NearEqual(Size::INFINITE_SIZE, width_)) {
99         width_ = NormalizeToPx(defaultWidth_);
100     }
101     height_ = GetLayoutParam().GetMaxSize().Height();
102     if (NearEqual(Size::INFINITE_SIZE, height_)) {
103         height_ = NormalizeToPx(defaultHeight_);
104     }
105 }
106 
CalculateSize()107 void RenderCheckable::CalculateSize()
108 {
109     // Fit hot zone to real size.
110     double hotZoneHorizontalPadding = NormalizePercentToPx(hotZoneHorizontalPadding_, false);
111     double hotZoneVerticalPadding = NormalizePercentToPx(hotZoneVerticalPadding_, true);
112     auto defaultWidth = NormalizeToPx(defaultWidth_);
113     auto defaultHeight = NormalizeToPx(defaultHeight_);
114     if ((width_ < defaultWidth) && (!NearZero(defaultWidth))) {
115         hotZoneHorizontalPadding *= width_ / defaultWidth;
116     }
117     if ((height_ < defaultHeight) && (!NearZero(defaultHeight))) {
118         hotZoneVerticalPadding *= height_ / defaultHeight;
119     }
120 
121     // Calculate draw size with hot zone and ratio of (width / height).
122     double width = width_ - 2 * hotZoneHorizontalPadding;
123     width = width > 0 ? width : 0;
124     double height = height_ - 2 * hotZoneVerticalPadding;
125     height = height > 0 ? height : 0;
126     drawSize_ = Size(width, height);
127     ApplyAspectRatio(drawSize_);
128     paintPosition_ = Alignment::GetAlignPosition(Size(width_, height_), drawSize_, Alignment::CENTER);
129 }
130 
InitTouchRecognizer()131 void RenderCheckable::InitTouchRecognizer()
132 {
133     auto wp = AceType::WeakClaim(this);
134     touchRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
135     touchRecognizer_->SetOnTouchDown([wp](const TouchEventInfo&) {
136         auto renderCheckable = wp.Upgrade();
137         if (renderCheckable) {
138             renderCheckable->isTouch_ = true;
139             renderCheckable->MarkNeedLayout();
140         }
141     });
142     touchRecognizer_->SetOnTouchUp([wp](const TouchEventInfo&) {
143         auto renderCheckable = wp.Upgrade();
144         if (renderCheckable) {
145             renderCheckable->isTouch_ = false;
146             renderCheckable->MarkNeedLayout();
147         }
148     });
149     touchRecognizer_->SetOnTouchCancel([wp](const TouchEventInfo&) {
150         auto renderCheckable = wp.Upgrade();
151         if (renderCheckable) {
152             renderCheckable->isTouch_ = false;
153             renderCheckable->MarkNeedLayout();
154         }
155     });
156     touchRecognizer_->SetOnTouchMove([wp](const TouchEventInfo& info) {
157         auto renderCheckable = wp.Upgrade();
158         if (renderCheckable) {
159             if (info.GetTouches().empty()) {
160                 return;
161             }
162             const auto& locationInfo = info.GetTouches().front();
163             double moveDeltaX = locationInfo.GetLocalLocation().GetX();
164             double moveDeltaY = locationInfo.GetLocalLocation().GetY();
165             if ((moveDeltaX < 0 || moveDeltaX > renderCheckable->width_)
166                 || (moveDeltaY < 0 || moveDeltaY > renderCheckable->height_)) {
167                 renderCheckable->isTouch_ = false;
168                 renderCheckable->MarkNeedLayout();
169             }
170         }
171     });
172 }
173 
InitClickRecognizer(bool catchMode)174 void RenderCheckable::InitClickRecognizer(bool catchMode)
175 {
176     if (!disabled_ && !clickRecognizer_) {
177         clickRecognizer_ = AceType::MakeRefPtr<ClickRecognizer>();
178         clickRecognizer_->SetOnClick([weak = AceType::WeakClaim(this)](const ClickInfo& info) {
179             auto renderCheckable = weak.Upgrade();
180             if (renderCheckable) {
181                 renderCheckable->HandleClick();
182             }
183         });
184         static const int32_t bubbleModeVersion = 6;
185         auto pipeline = context_.Upgrade();
186         if (!catchMode && pipeline && pipeline->GetMinPlatformVersion() >= bubbleModeVersion) {
187             clickRecognizer_->SetUseCatchMode(false);
188         } else {
189             clickRecognizer_->SetUseCatchMode(true);
190         }
191     } else if (disabled_ && clickRecognizer_) {
192         clickRecognizer_ = nullptr;
193     }
194 }
195 
ApplyAspectRatio(Size & drawSize) const196 void RenderCheckable::ApplyAspectRatio(Size& drawSize) const
197 {
198     // Protect from drawSize.Height() being zero.
199     double drawAreaAspectRatio = Size::INFINITE_SIZE;
200     if (!drawSize.IsValid()) {
201         return;
202     }
203     drawAreaAspectRatio = drawSize.Width() / drawSize.Height();
204     if (drawAreaAspectRatio > aspectRatio_) {
205         drawSize.SetWidth(drawSize.Height() * aspectRatio_);
206     } else if (!NearZero(aspectRatio_)) {
207         drawSize.SetHeight(drawSize.Width() / aspectRatio_);
208     }
209 }
210 
PerformLayout()211 void RenderCheckable::PerformLayout()
212 {
213     InitSize();
214     CalculateSize();
215     Size constrainSize = GetLayoutParam().Constrain(Size(width_, height_));
216     SetLayoutSize(constrainSize);
217 }
218 
OnStatusChanged(RenderStatus renderStatus)219 void RenderCheckable::OnStatusChanged(RenderStatus renderStatus)
220 {
221     onFocus_ = renderStatus == RenderStatus::FOCUS;
222     auto context = context_.Upgrade();
223     if (context && context->GetRenderFocusAnimation() && (renderStatus == RenderStatus::BLUR)) {
224         context->GetRenderFocusAnimation()->CancelFocusAnimation();
225     }
226     UpdateUIStatus();
227     MarkNeedRender();
228 }
229 
HandleClick()230 void RenderCheckable::HandleClick()
231 {
232     auto result = UpdateChangedResult();
233     if (!result.empty()) {
234         MarkNeedRender();
235         auto resultForChangeEvent = std::string(R"("change",{"checked":)").append(result.append("},null"));
236         OnHandleChangedResult(resultForChangeEvent);
237         if (changeEvent_) {
238             changeEvent_(resultForChangeEvent);
239         }
240         if (valueChangeEvent_) {
241             valueChangeEvent_(result);
242         }
243     }
244     if (onChange_) {
245         onChange_(checked_);
246     }
247 
248     if (clickEvent_) {
249         clickEvent_();
250     }
251 }
252 
OnHandleChangedResult(const std::string & result)253 void RenderCheckable::OnHandleChangedResult(const std::string& result)
254 {
255     if (domChangeEvent_) {
256         domChangeEvent_(result);
257     }
258 }
259 
UpdateChangedResult()260 std::string RenderCheckable::UpdateChangedResult()
261 {
262     checked_ = !checked_;
263     UpdateUIStatus();
264 
265     return checked_ ? "true" : "false";
266 }
267 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)268 void RenderCheckable::OnTouchTestHit(
269     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
270 {
271     if (dragRecognizer_) {
272         dragRecognizer_->SetCoordinateOffset(coordinateOffset);
273         result.emplace_back(dragRecognizer_);
274     }
275     if (clickRecognizer_) {
276         clickRecognizer_->SetCoordinateOffset(coordinateOffset);
277         result.emplace_back(clickRecognizer_);
278     }
279     if (touchRecognizer_) {
280         touchRecognizer_->SetCoordinateOffset(coordinateOffset);
281         result.emplace_back(touchRecognizer_);
282     }
283 }
284 
AnimateMouseHoverEnter()285 void RenderCheckable::AnimateMouseHoverEnter()
286 {
287     isHover_ = true;
288     MarkNeedLayout();
289 }
290 
AnimateMouseHoverExit()291 void RenderCheckable::AnimateMouseHoverExit()
292 {
293     isHover_ = false;
294     MarkNeedLayout();
295 }
296 
OnMouseHoverEnterTest()297 void RenderCheckable::OnMouseHoverEnterTest()
298 {
299     MarkNeedRender();
300 }
301 
OnMouseHoverExitTest()302 void RenderCheckable::OnMouseHoverExitTest()
303 {
304     MarkNeedRender();
305 }
306 
RequestFocusBorder(const Offset & focusOffset,const Size & focusSize,double borderRadius)307 void RenderCheckable::RequestFocusBorder(const Offset& focusOffset, const Size& focusSize, double borderRadius)
308 {
309     auto context = context_.Upgrade();
310     if (!context) {
311         LOGE("Pipeline context upgrade fail!");
312         return;
313     }
314     if (!context->GetRenderFocusAnimation()) {
315         LOGE("focusAnimation is null!");
316         EventReport::SendRenderException(RenderExcepType::RENDER_ANIMATION_ERR);
317         return;
318     }
319     context->ShowFocusAnimation(RRect::MakeRRect(Rect(Offset(), focusSize), borderRadius, borderRadius), Color::BLUE,
320         focusOffset + GetGlobalOffset());
321 }
322 
323 } // namespace OHOS::Ace
324