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