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