1 /*
2 * Copyright (c) 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_ng/event/state_style_manager.h"
17
18 #include "core/components_ng/pattern/list/list_pattern.h"
19 #include "core/components_ng/pattern/navigation/navigation_group_node.h"
20 #include "core/components_ng/pattern/overlay/popup_base_pattern.h"
21 #include "core/event/touch_event.h"
22 #include "core/pipeline_ng/pipeline_context.h"
23 #include <cstdint>
24
25 namespace OHOS::Ace::NG {
26
27 namespace {
28 constexpr uint32_t PRESS_STYLE_DELAY = 300;
29 constexpr uint32_t PRESS_CANCEL_STYLE_DELAY = 64;
30 }
31
StateStyleManager(WeakPtr<FrameNode> frameNode)32 StateStyleManager::StateStyleManager(WeakPtr<FrameNode> frameNode) : host_(std::move(frameNode)) {}
33
34 StateStyleManager::~StateStyleManager() = default;
35
GetPressedListener()36 const RefPtr<TouchEventImpl>& StateStyleManager::GetPressedListener()
37 {
38 if (pressedFunc_) {
39 return pressedFunc_;
40 }
41 auto pressedCallback = [weak = WeakClaim(this)](TouchEventInfo& info) {
42 auto stateStyleMgr = weak.Upgrade();
43 CHECK_NULL_VOID(stateStyleMgr);
44 const auto& touches = info.GetTouches();
45 const auto& changeTouches = info.GetChangedTouches();
46 if (touches.empty() || changeTouches.empty()) {
47 TAG_LOGW(AceLogTag::ACE_STATE_STYLE, "the touch info is illegal");
48 return;
49 }
50 auto lastPoint = changeTouches.back();
51 const auto& type = lastPoint.GetTouchType();
52 if (type == TouchType::DOWN) {
53 stateStyleMgr->HandleTouchDown();
54 stateStyleMgr->pointerId_.insert(lastPoint.GetFingerId());
55 }
56 if ((type == TouchType::UP) || (type == TouchType::CANCEL)) {
57 stateStyleMgr->pointerId_.erase(lastPoint.GetFingerId());
58 if (stateStyleMgr->pointerId_.size() == 0) {
59 stateStyleMgr->HandleTouchUp();
60 }
61 }
62 if ((type == TouchType::MOVE) &&
63 (stateStyleMgr->IsCurrentStateOn(UI_STATE_PRESSED) || stateStyleMgr->IsPressedStatePending())) {
64 int32_t sourceType = static_cast<int32_t>(touches.front().GetSourceDevice());
65 if (stateStyleMgr->IsOutOfPressedRegion(sourceType, lastPoint.GetGlobalLocation())) {
66 auto frameNode = stateStyleMgr->GetFrameNode();
67 CHECK_NULL_VOID(frameNode);
68 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Move out of node pressed region: %{public}s",
69 frameNode->GetTag().c_str());
70 stateStyleMgr->pointerId_.erase(lastPoint.GetFingerId());
71 if (stateStyleMgr->pointerId_.size() == 0) {
72 stateStyleMgr->ResetPressedState();
73 stateStyleMgr->PostListItemPressStyleTask(stateStyleMgr->currentState_);
74 }
75 }
76 }
77 };
78 pressedFunc_ = MakeRefPtr<TouchEventImpl>(std::move(pressedCallback));
79 return pressedFunc_;
80 }
81
HandleTouchDown()82 void StateStyleManager::HandleTouchDown()
83 {
84 auto node = GetFrameNode();
85 CHECK_NULL_VOID(node);
86 #ifdef IS_RELEASE_VERSION
87 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Handle TouchDown event node: %{public}s", node->GetTag().c_str());
88 #else
89 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Handle TouchDown event node: %{public}s/%{public}d",
90 node->GetTag().c_str(), node->GetId());
91 #endif
92 HandleScrollingParent();
93 if (!hasScrollingParent_) {
94 UpdateCurrentUIState(UI_STATE_PRESSED);
95 PostListItemPressStyleTask(currentState_);
96 } else {
97 if (IsPressedCancelStatePending()) {
98 ResetPressedCancelState();
99 }
100 PostPressStyleTask(PRESS_STYLE_DELAY);
101 PendingPressedState();
102 }
103 }
104
HandleTouchUp()105 void StateStyleManager::HandleTouchUp()
106 {
107 auto node = GetFrameNode();
108 CHECK_NULL_VOID(node);
109 #ifdef IS_RELEASE_VERSION
110 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Handle TouchUp or Cancel event node: %{public}s",
111 node->GetTag().c_str());
112 #else
113 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Handle TouchUp or Cancel event node: %{public}s/%{public}d",
114 node->GetTag().c_str(), node->GetId());
115 #endif
116 if (IsPressedStatePending()) {
117 DeletePressStyleTask();
118 ResetPressedPendingState();
119 UpdateCurrentUIState(UI_STATE_PRESSED);
120 PostPressCancelStyleTask(PRESS_CANCEL_STYLE_DELAY);
121 PendingCancelPressedState();
122 } else if (!IsPressedCancelStatePending()) {
123 ResetPressedState();
124 PostListItemPressStyleTask(currentState_);
125 }
126 if (hasScrollingParent_) {
127 CleanScrollingParentListener();
128 }
129 }
130
FireStateFunc(bool isReset)131 void StateStyleManager::FireStateFunc(bool isReset)
132 {
133 auto node = GetFrameNode();
134 CHECK_NULL_VOID(node);
135 auto nodeId = node->GetId();
136 #ifdef IS_RELEASE_VERSION
137 TAG_LOGI(AceLogTag::ACE_STATE_STYLE,"Start execution, node is %{public}s, "
138 "reset is %{public}d", node->GetTag().c_str(), isReset);
139 #else
140 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Start execution, node is %{public}s/%{public}d, "
141 "reset is %{public}d", node->GetTag().c_str(), nodeId, isReset);
142 #endif
143 auto uiNode = DynamicCast<UINode>(node);
144 CHECK_NULL_VOID(uiNode);
145 RefPtr<CustomNodeBase> customNode;
146 GetCustomNode(customNode, uiNode);
147 if (!customNode || (!customNode->FireHasNodeUpdateFunc(nodeId))) {
148 TAG_LOGW(AceLogTag::ACE_STATE_STYLE, "Can not find customNode!");
149 return;
150 }
151 ScopedViewStackProcessor processor;
152 customNode->FireNodeUpdateFunc(nodeId);
153 }
154
GetCustomNode(RefPtr<CustomNodeBase> & customNode,RefPtr<UINode> node)155 void StateStyleManager::GetCustomNode(RefPtr<CustomNodeBase>& customNode, RefPtr<UINode> node)
156 {
157 auto nodeId = node->GetId();
158 while (node) {
159 if (GetCustomNodeFromNavgation(node, customNode, nodeId)) {
160 return;
161 }
162
163 auto parentFrameNode = DynamicCast<FrameNode>(node);
164 auto parentPattern = parentFrameNode ? parentFrameNode->GetPattern<PopupBasePattern>() : nullptr;
165 if (parentFrameNode && InstanceOf<PopupBasePattern>(parentPattern)) {
166 auto elementRegister = ElementRegister::GetInstance();
167 CHECK_NULL_VOID(elementRegister);
168 node = elementRegister->GetUINodeById(parentPattern->GetTargetId());
169 continue;
170 }
171
172 if (GetCustomNodeFromSelf(node, customNode, nodeId)) {
173 return;
174 }
175 node = node->GetParent();
176 }
177 }
178
GetCustomNodeFromSelf(RefPtr<UINode> & node,RefPtr<CustomNodeBase> & customNode,int32_t nodeId)179 bool StateStyleManager::GetCustomNodeFromSelf(RefPtr<UINode>& node, RefPtr<CustomNodeBase>& customNode, int32_t nodeId)
180 {
181 if (node && AceType::InstanceOf<CustomNodeBase>(node)) {
182 auto customNodeBase = DynamicCast<CustomNodeBase>(node);
183 if (customNodeBase && customNodeBase->FireHasNodeUpdateFunc(nodeId)) {
184 customNode = customNodeBase;
185 TAG_LOGI(
186 AceLogTag::ACE_STATE_STYLE, "Find customNode by self: %{public}s", customNode->GetJSViewName().c_str());
187 return true;
188 }
189 }
190 return false;
191 }
192
GetCustomNodeFromNavgation(RefPtr<UINode> & node,RefPtr<CustomNodeBase> & customNode,int32_t nodeId)193 bool StateStyleManager::GetCustomNodeFromNavgation(
194 RefPtr<UINode>& node, RefPtr<CustomNodeBase>& customNode, int32_t nodeId)
195 {
196 while (node && AceType::InstanceOf<NavDestinationGroupNode>(node)) {
197 auto navDestinationGroupNode = DynamicCast<NavDestinationGroupNode>(node);
198 CHECK_NULL_RETURN(navDestinationGroupNode, false);
199 auto navDestinationCustomNode = navDestinationGroupNode->GetNavDestinationCustomNode();
200 CHECK_NULL_RETURN(navDestinationCustomNode, false);
201 if (navDestinationCustomNode->FireHasNodeUpdateFunc(nodeId)) {
202 customNode = navDestinationCustomNode;
203 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Find customNode from Navgation: %{public}s",
204 customNode->GetJSViewName().c_str());
205 return true;
206 }
207 auto customParent = DynamicCast<CustomNode>(navDestinationCustomNode);
208 CHECK_NULL_RETURN(navDestinationCustomNode, false);
209 node = customParent->GetParent();
210 }
211 return false;
212 }
213
PostPressStyleTask(uint32_t delayTime)214 void StateStyleManager::PostPressStyleTask(uint32_t delayTime)
215 {
216 auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
217 CHECK_NULL_VOID(pipeline);
218 auto taskExecutor = pipeline->GetTaskExecutor();
219 CHECK_NULL_VOID(taskExecutor);
220
221 if (IsPressedStatePending()) {
222 return;
223 }
224
225 auto weak = AceType::WeakClaim(this);
226 pressStyleTask_.Reset([weak = WeakClaim(this)] {
227 auto stateStyleMgr = weak.Upgrade();
228 CHECK_NULL_VOID(stateStyleMgr);
229 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Execute press task");
230 stateStyleMgr->ResetPressedPendingState();
231 stateStyleMgr->UpdateCurrentUIState(UI_STATE_PRESSED);
232 stateStyleMgr->PostListItemPressStyleTask(stateStyleMgr->currentState_);
233 });
234
235 taskExecutor->PostDelayedTask(pressStyleTask_, TaskExecutor::TaskType::UI, delayTime, "ArkUIPressStateStyle");
236 }
237
PostPressCancelStyleTask(uint32_t delayTime)238 void StateStyleManager::PostPressCancelStyleTask(uint32_t delayTime)
239 {
240 auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
241 CHECK_NULL_VOID(pipeline);
242 auto taskExecutor = pipeline->GetTaskExecutor();
243 CHECK_NULL_VOID(taskExecutor);
244
245 if (IsPressedStatePending() || IsPressedCancelStatePending()) {
246 return;
247 }
248
249 auto weak = AceType::WeakClaim(this);
250 pressCancelStyleTask_.Reset([weak = WeakClaim(this)] {
251 auto stateStyleMgr = weak.Upgrade();
252 CHECK_NULL_VOID(stateStyleMgr);
253 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Execute press clear task");
254 stateStyleMgr->ResetPressedCancelPendingState();
255 stateStyleMgr->ResetCurrentUIState(UI_STATE_PRESSED);
256 stateStyleMgr->PostListItemPressStyleTask(stateStyleMgr->currentState_);
257 });
258
259 taskExecutor->PostDelayedTask(
260 pressCancelStyleTask_, TaskExecutor::TaskType::UI, delayTime, "ArkUIPressCancelStateStyle");
261 }
262
PostListItemPressStyleTask(UIState state)263 void StateStyleManager::PostListItemPressStyleTask(UIState state)
264 {
265 bool isPressed = state == UI_STATE_PRESSED;
266 auto node = GetFrameNode();
267 CHECK_NULL_VOID(node);
268 auto nodeId = node->GetId();
269 if (node->GetTag() == V2::LIST_ITEM_ETS_TAG) {
270 auto frameNode = node->GetAncestorNodeOfFrame(false);
271 CHECK_NULL_VOID(frameNode);
272 if (frameNode->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
273 auto listGroupPattern = DynamicCast<ListItemGroupPattern>(frameNode->GetPattern());
274 CHECK_NULL_VOID(listGroupPattern);
275 listGroupPattern->SetItemPressed(isPressed, nodeId);
276 frameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
277 }
278 if (frameNode->GetTag() == V2::LIST_ETS_TAG) {
279 auto listPattern = DynamicCast<ListPattern>(frameNode->GetPattern());
280 CHECK_NULL_VOID(listPattern);
281 listPattern->SetItemPressed(isPressed, nodeId);
282 frameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
283 }
284 }
285 }
286
HandleScrollingParent()287 void StateStyleManager::HandleScrollingParent()
288 {
289 auto node = GetFrameNode();
290 CHECK_NULL_VOID(node);
291
292 auto scrollingEventCallback = [weak = WeakClaim(this)]() {
293 auto stateStyleMgr = weak.Upgrade();
294 CHECK_NULL_VOID(stateStyleMgr);
295 stateStyleMgr->ResetCurrentUIState(UI_STATE_PRESSED);
296 stateStyleMgr->PostListItemPressStyleTask(stateStyleMgr->currentState_);
297 stateStyleMgr->pointerId_.clear();
298 stateStyleMgr->ResetPressedPendingState();
299 if (stateStyleMgr->pressStyleTask_) {
300 stateStyleMgr->DeletePressStyleTask();
301 }
302 };
303
304 auto scrollingListener = MakeRefPtr<ScrollingListener>(std::move(scrollingEventCallback));
305
306 auto parent = node->GetAncestorNodeOfFrame(false);
307 while (parent) {
308 auto pattern = parent->GetPattern();
309 CHECK_NULL_VOID(pattern);
310 if (pattern->ShouldDelayChildPressedState()) {
311 hasScrollingParent_ = true;
312 pattern->RegisterScrollingListener(scrollingListener);
313 }
314 parent = parent->GetAncestorNodeOfFrame(false);
315 }
316 }
317
CleanScrollingParentListener()318 void StateStyleManager::CleanScrollingParentListener()
319 {
320 auto node = GetFrameNode();
321 CHECK_NULL_VOID(node);
322 auto parent = node->GetAncestorNodeOfFrame(false);
323 while (parent) {
324 auto pattern = parent->GetPattern();
325 CHECK_NULL_VOID(pattern);
326 if (pattern->ShouldDelayChildPressedState()) {
327 pattern->CleanScrollingListener();
328 }
329 parent = parent->GetAncestorNodeOfFrame(false);
330 }
331 }
332
Transform(PointF & localPointF,const WeakPtr<FrameNode> & node) const333 void StateStyleManager::Transform(PointF& localPointF, const WeakPtr<FrameNode>& node) const
334 {
335 if (node.Invalid()) {
336 return;
337 }
338
339 std::vector<Matrix4> vTrans {};
340 auto host = node.Upgrade();
341 while (host) {
342 auto context = host->GetRenderContext();
343 CHECK_NULL_VOID(context);
344 auto localMat = context->GetLocalTransformMatrix();
345 vTrans.emplace_back(localMat);
346 host = host->GetAncestorNodeOfFrame(false);
347 }
348
349 Point temp(localPointF.GetX(), localPointF.GetY());
350 for (auto iter = vTrans.rbegin(); iter != vTrans.rend(); iter++) {
351 temp = *iter * temp;
352 }
353 localPointF.SetX(temp.GetX());
354 localPointF.SetY(temp.GetY());
355 }
356
IsOutOfPressedRegion(int32_t sourceType,const Offset & location) const357 bool StateStyleManager::IsOutOfPressedRegion(int32_t sourceType, const Offset& location) const
358 {
359 auto node = GetFrameNode();
360 CHECK_NULL_RETURN(node, false);
361 if (IsOutOfPressedRegionWithoutClip(node, sourceType, location)) {
362 return true;
363 }
364 auto parent = node->GetAncestorNodeOfFrame(false);
365 while (parent) {
366 auto renderContext = parent->GetRenderContext();
367 if (!renderContext) {
368 parent = parent->GetAncestorNodeOfFrame(false);
369 continue;
370 }
371 // If the parent node has a "clip" attribute, the press region should be re-evaluated.
372 auto clip = renderContext->GetClipEdge().value_or(false);
373 if (clip && IsOutOfPressedRegionWithoutClip(parent, sourceType, location)) {
374 return true;
375 }
376 parent = parent->GetAncestorNodeOfFrame(false);
377 }
378 return false;
379 }
380
IsOutOfPressedRegionWithoutClip(RefPtr<FrameNode> node,int32_t sourceType,const Offset & location) const381 bool StateStyleManager::IsOutOfPressedRegionWithoutClip(RefPtr<FrameNode> node, int32_t sourceType,
382 const Offset& location) const
383 {
384 CHECK_NULL_RETURN(node, false);
385 auto renderContext = node->GetRenderContext();
386 CHECK_NULL_RETURN(renderContext, false);
387
388 auto paintRect = renderContext->GetPaintRectWithoutTransform();
389 auto responseRegionList = node->GetResponseRegionList(paintRect, sourceType);
390 Offset offset = { paintRect.GetOffset().GetX(), paintRect.GetOffset().GetY() };
391 PointF current = { location.GetX(), location.GetY() };
392 NGGestureRecognizer::Transform(current, node);
393 PointF parentPoint = { current.GetX() + offset.GetX(), current.GetY() + offset.GetY() };
394 if (!node->InResponseRegionList(parentPoint, responseRegionList)) {
395 return true;
396 }
397 return false;
398 }
399
GetFrameNode() const400 RefPtr<FrameNode> StateStyleManager::GetFrameNode() const
401 {
402 return host_.Upgrade();
403 }
404 } // namespace OHOS::Ace::NG
405