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
51 auto lastPoint = changeTouches.back();
52 const auto& type = lastPoint.GetTouchType();
53 if (type == TouchType::DOWN) {
54 stateStyleMgr->HandleTouchDown();
55 stateStyleMgr->pointerId_.insert(lastPoint.GetFingerId());
56 }
57 if ((type == TouchType::UP) || (type == TouchType::CANCEL)) {
58 stateStyleMgr->pointerId_.erase(lastPoint.GetFingerId());
59 if (stateStyleMgr->pointerId_.size() == 0) {
60 stateStyleMgr->HandleTouchUp();
61 }
62 }
63 if ((type == TouchType::MOVE) &&
64 (stateStyleMgr->IsCurrentStateOn(UI_STATE_PRESSED) || stateStyleMgr->IsPressedStatePending())) {
65 int32_t sourceType = static_cast<int32_t>(touches.front().GetSourceDevice());
66 if (stateStyleMgr->IsOutOfPressedRegion(sourceType, lastPoint.GetGlobalLocation())) {
67 auto frameNode = stateStyleMgr->GetFrameNode();
68 CHECK_NULL_VOID(frameNode);
69 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Move out of node pressed region: %{public}s",
70 frameNode->GetTag().c_str());
71 stateStyleMgr->pointerId_.erase(lastPoint.GetFingerId());
72 if (stateStyleMgr->pointerId_.size() == 0) {
73 stateStyleMgr->ResetPressedState();
74 stateStyleMgr->PostListItemPressStyleTask(stateStyleMgr->currentState_);
75 }
76 }
77 }
78 };
79 pressedFunc_ = MakeRefPtr<TouchEventImpl>(std::move(pressedCallback));
80 return pressedFunc_;
81 }
82
HandleTouchDown()83 void StateStyleManager::HandleTouchDown()
84 {
85 auto node = GetFrameNode();
86 CHECK_NULL_VOID(node);
87 #ifdef IS_RELEASE_VERSION
88 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Handle TouchDown event node: %{public}s", node->GetTag().c_str());
89 #else
90 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Handle TouchDown event node: %{public}s/%{public}d",
91 node->GetTag().c_str(), node->GetId());
92 #endif
93 HandleScrollingParent();
94 if (!hasScrollingParent_ || scrollingFeatureForbidden_) {
95 UpdateCurrentUIState(UI_STATE_PRESSED);
96 PostListItemPressStyleTask(currentState_);
97 } else if (!isFastScrolling_){
98 if (IsPressedCancelStatePending()) {
99 ResetPressedCancelState();
100 }
101 PostPressStyleTask(PRESS_STYLE_DELAY);
102 PendingPressedState();
103 }
104 }
105
HandleTouchUp()106 void StateStyleManager::HandleTouchUp()
107 {
108 auto node = GetFrameNode();
109 CHECK_NULL_VOID(node);
110 #ifdef IS_RELEASE_VERSION
111 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Handle TouchUp or Cancel event node: %{public}s",
112 node->GetTag().c_str());
113 #else
114 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Handle TouchUp or Cancel event node: %{public}s/%{public}d",
115 node->GetTag().c_str(), node->GetId());
116 #endif
117 if (IsPressedStatePending()) {
118 DeletePressStyleTask();
119 ResetPressedPendingState();
120 UpdateCurrentUIState(UI_STATE_PRESSED);
121 PostPressCancelStyleTask(PRESS_CANCEL_STYLE_DELAY);
122 PendingCancelPressedState();
123 } else if (!IsPressedCancelStatePending()) {
124 ResetPressedState();
125 PostListItemPressStyleTask(currentState_);
126 }
127 if (hasScrollingParent_) {
128 CleanScrollingParentListener();
129 }
130 }
131
SetCurrentUIState(UIState state,bool flag)132 void StateStyleManager::SetCurrentUIState(UIState state, bool flag)
133 {
134 if (flag) {
135 currentState_ |= state;
136 } else {
137 currentState_ &= ~state;
138 }
139
140 if (!HasStateStyle(state)) {
141 return;
142 }
143
144 // When the UIState changes, trigger the user subscription callback registered by FrameNode and CAPI.
145 // If the frontend has already added supported UIState, forcibly skip frontend subscriber handling.
146 bool skipFrontendForcibly = frontendSubscribers_ != UI_STATE_UNKNOWN ? true : false;
147 FireStateFunc(state, currentState_, !flag, skipFrontendForcibly);
148 }
149
IsCanUpdate(UIState subscribers,UIState handlingState,UIState currentState)150 static bool IsCanUpdate(UIState subscribers, UIState handlingState, UIState currentState)
151 {
152 if (subscribers == UI_STATE_UNKNOWN) {
153 return false;
154 }
155 return ((subscribers & handlingState) == handlingState || currentState == UI_STATE_NORMAL);
156 }
157
IsExcludeInner(UIState handlingState)158 bool StateStyleManager::IsExcludeInner(UIState handlingState)
159 {
160 return (userSubscribersExcludeConfigs_ & handlingState) == handlingState;
161 }
162
AddSupportedUIStateWithCallback(UIState state,std::function<void (uint64_t)> & callback,bool isInner,bool excludeInner)163 void StateStyleManager::AddSupportedUIStateWithCallback(
164 UIState state, std::function<void(uint64_t)>& callback, bool isInner, bool excludeInner)
165 {
166 if (state == UI_STATE_NORMAL) {
167 return;
168 }
169 if (!HasStateStyle(state)) {
170 supportedStates_ = supportedStates_ | state;
171 }
172 if (isInner) {
173 innerStateStyleSubscribers_.first |= state;
174 innerStateStyleSubscribers_.second = callback;
175 return;
176 }
177 userStateStyleSubscribers_.first |= state;
178 userStateStyleSubscribers_.second = callback;
179 if (excludeInner) {
180 userSubscribersExcludeConfigs_ |= state;
181 } else {
182 userSubscribersExcludeConfigs_ &= ~state;
183 }
184 }
185
RemoveSupportedUIState(UIState state,bool isInner)186 void StateStyleManager::RemoveSupportedUIState(UIState state, bool isInner)
187 {
188 if (state == UI_STATE_NORMAL) {
189 return;
190 }
191 if (isInner) {
192 innerStateStyleSubscribers_.first &= ~state;
193 } else {
194 userStateStyleSubscribers_.first &= ~state;
195 userSubscribersExcludeConfigs_ &= ~state;
196 }
197 UIState temp = frontendSubscribers_ | innerStateStyleSubscribers_.first | userStateStyleSubscribers_.first;
198 if ((temp & state) != state) {
199 supportedStates_ = supportedStates_ & ~state;
200 }
201 }
202
HandleStateChangeInternal(UIState handlingState,UIState currentState,bool isReset,bool skipFrontendForcibly)203 void StateStyleManager::HandleStateChangeInternal(
204 UIState handlingState, UIState currentState, bool isReset, bool skipFrontendForcibly)
205 {
206 std::function<void(UIState)> onStateStyleChange;
207 if (IsCanUpdate(innerStateStyleSubscribers_.first, handlingState, currentState) &&
208 !IsExcludeInner(handlingState)) {
209 onStateStyleChange = innerStateStyleSubscribers_.second;
210 if (onStateStyleChange) {
211 ScopedViewStackProcessor processor;
212 onStateStyleChange(currentState);
213 TAG_LOGD(AceLogTag::ACE_STATE_STYLE,
214 "Internal state style subscriber callbacks, currentState=%{public}" PRIu64 "", currentState);
215 }
216 }
217 if (IsCanUpdate(frontendSubscribers_, handlingState, currentState) && !skipFrontendForcibly) {
218 auto node = GetFrameNode();
219 CHECK_NULL_VOID(node);
220 auto nodeId = node->GetId();
221 #ifdef IS_RELEASE_VERSION
222 TAG_LOGI(AceLogTag::ACE_STATE_STYLE,"Start execution, node is %{public}s, "
223 "reset is %{public}d", node->GetTag().c_str(), isReset);
224 #else
225 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Start execution, node is %{public}s/%{public}d, "
226 "reset is %{public}d", node->GetTag().c_str(), nodeId, isReset);
227 #endif
228 auto uiNode = DynamicCast<UINode>(node);
229 CHECK_NULL_VOID(uiNode);
230 RefPtr<CustomNodeBase> customNode;
231 GetCustomNode(customNode, uiNode);
232 if (!customNode || (!customNode->FireHasNodeUpdateFunc(nodeId))) {
233 TAG_LOGW(AceLogTag::ACE_STATE_STYLE, "Can not find customNode!");
234 return;
235 }
236 ScopedViewStackProcessor processor;
237 customNode->FireNodeUpdateFunc(nodeId);
238 return;
239 }
240 if (IsCanUpdate(userStateStyleSubscribers_.first, handlingState, currentState)) {
241 onStateStyleChange = userStateStyleSubscribers_.second;
242 if (onStateStyleChange) {
243 ScopedViewStackProcessor processor;
244 onStateStyleChange(currentState);
245 }
246 }
247 }
248
FireStateFunc(UIState handlingState,UIState currentState,bool isReset,bool skipFrontendForcibly)249 void StateStyleManager::FireStateFunc(
250 UIState handlingState, UIState currentState, bool isReset, bool skipFrontendForcibly)
251 {
252 HandleStateChangeInternal(handlingState, currentState, isReset, skipFrontendForcibly);
253 }
254
GetCustomNode(RefPtr<CustomNodeBase> & customNode,RefPtr<UINode> node)255 void StateStyleManager::GetCustomNode(RefPtr<CustomNodeBase>& customNode, RefPtr<UINode> node)
256 {
257 auto nodeId = node->GetId();
258 while (node) {
259 if (GetCustomNodeFromNavgation(node, customNode, nodeId)) {
260 return;
261 }
262
263 auto parentFrameNode = DynamicCast<FrameNode>(node);
264 auto parentPattern = parentFrameNode ? parentFrameNode->GetPattern<PopupBasePattern>() : nullptr;
265 if (parentFrameNode && InstanceOf<PopupBasePattern>(parentPattern)) {
266 auto elementRegister = ElementRegister::GetInstance();
267 CHECK_NULL_VOID(elementRegister);
268 node = elementRegister->GetUINodeById(parentPattern->GetTargetId());
269 continue;
270 }
271
272 if (GetCustomNodeFromSelf(node, customNode, nodeId)) {
273 return;
274 }
275 CHECK_NULL_VOID(node);
276 node = node->GetParent();
277 }
278 }
279
GetCustomNodeFromSelf(RefPtr<UINode> & node,RefPtr<CustomNodeBase> & customNode,int32_t nodeId)280 bool StateStyleManager::GetCustomNodeFromSelf(RefPtr<UINode>& node, RefPtr<CustomNodeBase>& customNode, int32_t nodeId)
281 {
282 if (node && AceType::InstanceOf<CustomNodeBase>(node)) {
283 auto customNodeBase = DynamicCast<CustomNodeBase>(node);
284 if (customNodeBase && customNodeBase->FireHasNodeUpdateFunc(nodeId)) {
285 customNode = customNodeBase;
286 TAG_LOGI(
287 AceLogTag::ACE_STATE_STYLE, "Find customNode by self: %{public}s", customNode->GetJSViewName().c_str());
288 return true;
289 }
290 }
291 return false;
292 }
293
GetCustomNodeFromNavgation(RefPtr<UINode> & node,RefPtr<CustomNodeBase> & customNode,int32_t nodeId)294 bool StateStyleManager::GetCustomNodeFromNavgation(
295 RefPtr<UINode>& node, RefPtr<CustomNodeBase>& customNode, int32_t nodeId)
296 {
297 while (node && AceType::InstanceOf<NavDestinationGroupNode>(node)) {
298 auto navDestinationGroupNode = DynamicCast<NavDestinationGroupNode>(node);
299 CHECK_NULL_RETURN(navDestinationGroupNode, false);
300 auto navDestinationCustomNode = navDestinationGroupNode->GetNavDestinationCustomNode();
301 CHECK_NULL_RETURN(navDestinationCustomNode, false);
302 if (navDestinationCustomNode->FireHasNodeUpdateFunc(nodeId)) {
303 customNode = navDestinationCustomNode;
304 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Find customNode from Navgation: %{public}s",
305 customNode->GetJSViewName().c_str());
306 return true;
307 }
308 auto customParent = DynamicCast<CustomNode>(navDestinationCustomNode);
309 CHECK_NULL_RETURN(customParent, false);
310 node = customParent->GetParent();
311 }
312 return false;
313 }
314
PostPressStyleTask(uint32_t delayTime)315 void StateStyleManager::PostPressStyleTask(uint32_t delayTime)
316 {
317 auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
318 CHECK_NULL_VOID(pipeline);
319 auto taskExecutor = pipeline->GetTaskExecutor();
320 CHECK_NULL_VOID(taskExecutor);
321
322 if (IsPressedStatePending()) {
323 return;
324 }
325
326 auto weak = AceType::WeakClaim(this);
327 pressStyleTask_.Reset([weak = WeakClaim(this)] {
328 auto stateStyleMgr = weak.Upgrade();
329 CHECK_NULL_VOID(stateStyleMgr);
330 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Execute press task");
331 stateStyleMgr->ResetPressedPendingState();
332 stateStyleMgr->UpdateCurrentUIState(UI_STATE_PRESSED);
333 stateStyleMgr->PostListItemPressStyleTask(stateStyleMgr->currentState_);
334 });
335
336 taskExecutor->PostDelayedTask(pressStyleTask_, TaskExecutor::TaskType::UI, delayTime, "ArkUIPressStateStyle");
337 }
338
PostPressCancelStyleTask(uint32_t delayTime)339 void StateStyleManager::PostPressCancelStyleTask(uint32_t delayTime)
340 {
341 auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
342 CHECK_NULL_VOID(pipeline);
343 auto taskExecutor = pipeline->GetTaskExecutor();
344 CHECK_NULL_VOID(taskExecutor);
345
346 if (IsPressedStatePending() || IsPressedCancelStatePending()) {
347 return;
348 }
349
350 auto weak = AceType::WeakClaim(this);
351 pressCancelStyleTask_.Reset([weak = WeakClaim(this)] {
352 auto stateStyleMgr = weak.Upgrade();
353 CHECK_NULL_VOID(stateStyleMgr);
354 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Execute press clear task");
355 stateStyleMgr->ResetPressedCancelPendingState();
356 stateStyleMgr->ResetCurrentUIState(UI_STATE_PRESSED);
357 stateStyleMgr->PostListItemPressStyleTask(stateStyleMgr->currentState_);
358 });
359
360 taskExecutor->PostDelayedTask(
361 pressCancelStyleTask_, TaskExecutor::TaskType::UI, delayTime, "ArkUIPressCancelStateStyle");
362 }
363
PostListItemPressStyleTask(UIState state)364 void StateStyleManager::PostListItemPressStyleTask(UIState state)
365 {
366 bool isPressed = (state & UI_STATE_PRESSED) > 0;
367 auto node = GetFrameNode();
368 CHECK_NULL_VOID(node);
369 auto nodeId = node->GetId();
370 if (node->GetTag() == V2::LIST_ITEM_ETS_TAG) {
371 auto frameNode = node->GetAncestorNodeOfFrame(false);
372 CHECK_NULL_VOID(frameNode);
373 if (frameNode->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
374 auto listGroupPattern = DynamicCast<ListItemGroupPattern>(frameNode->GetPattern());
375 CHECK_NULL_VOID(listGroupPattern);
376 listGroupPattern->SetItemPressed(isPressed, nodeId);
377 frameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
378 }
379 if (frameNode->GetTag() == V2::LIST_ETS_TAG) {
380 auto listPattern = DynamicCast<ListPattern>(frameNode->GetPattern());
381 CHECK_NULL_VOID(listPattern);
382 listPattern->SetItemPressed(isPressed, nodeId);
383 frameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
384 }
385 }
386 }
387
HandleScrollingParent()388 void StateStyleManager::HandleScrollingParent()
389 {
390 auto node = GetFrameNode();
391 CHECK_NULL_VOID(node);
392
393 auto scrollingEventCallback = [weak = WeakClaim(this)]() {
394 auto stateStyleMgr = weak.Upgrade();
395 CHECK_NULL_VOID(stateStyleMgr);
396 stateStyleMgr->ResetCurrentUIState(UI_STATE_PRESSED);
397 stateStyleMgr->PostListItemPressStyleTask(stateStyleMgr->currentState_);
398 stateStyleMgr->pointerId_.clear();
399 stateStyleMgr->ResetPressedPendingState();
400 if (stateStyleMgr->pressStyleTask_) {
401 stateStyleMgr->DeletePressStyleTask();
402 }
403 };
404
405 auto scrollingListener = MakeRefPtr<ScrollingListener>(std::move(scrollingEventCallback));
406 isFastScrolling_ = false;
407 auto parent = node->GetAncestorNodeOfFrame(false);
408 while (parent) {
409 auto pattern = parent->GetPattern();
410 CHECK_NULL_VOID(pattern);
411 if (pattern->ShouldDelayChildPressedState()) {
412 hasScrollingParent_ = true;
413 pattern->RegisterScrollingListener(scrollingListener);
414 isFastScrolling_ = isFastScrolling_ || pattern->ShouldPreventChildPressedState();
415 }
416 parent = parent->GetAncestorNodeOfFrame(false);
417 }
418 }
419
CleanScrollingParentListener()420 void StateStyleManager::CleanScrollingParentListener()
421 {
422 auto node = GetFrameNode();
423 CHECK_NULL_VOID(node);
424
425 auto parent = node->GetAncestorNodeOfFrame(false);
426 while (parent) {
427 auto pattern = parent->GetPattern();
428 CHECK_NULL_VOID(pattern);
429 if (pattern->ShouldDelayChildPressedState()) {
430 pattern->CleanScrollingListener();
431 }
432 parent = parent->GetAncestorNodeOfFrame(false);
433 }
434 }
435
Transform(PointF & localPointF,const WeakPtr<FrameNode> & node) const436 void StateStyleManager::Transform(PointF& localPointF, const WeakPtr<FrameNode>& node) const
437 {
438 if (node.Invalid()) {
439 return;
440 }
441
442 std::vector<Matrix4> vTrans {};
443 auto host = node.Upgrade();
444 while (host) {
445 auto context = host->GetRenderContext();
446 CHECK_NULL_VOID(context);
447 auto localMat = context->GetLocalTransformMatrix();
448 vTrans.emplace_back(localMat);
449 host = host->GetAncestorNodeOfFrame(true);
450 }
451
452 Point temp(localPointF.GetX(), localPointF.GetY());
453 for (auto iter = vTrans.rbegin(); iter != vTrans.rend(); iter++) {
454 temp = *iter * temp;
455 }
456 localPointF.SetX(temp.GetX());
457 localPointF.SetY(temp.GetY());
458 }
459
IsOutOfPressedRegion(int32_t sourceType,const Offset & location) const460 bool StateStyleManager::IsOutOfPressedRegion(int32_t sourceType, const Offset& location) const
461 {
462 auto node = GetFrameNode();
463 CHECK_NULL_RETURN(node, false);
464 if (IsOutOfPressedRegionWithoutClip(node, sourceType, location)) {
465 return true;
466 }
467 auto parent = node->GetAncestorNodeOfFrame(true);
468 while (parent) {
469 auto renderContext = parent->GetRenderContext();
470 if (!renderContext) {
471 parent = parent->GetAncestorNodeOfFrame(true);
472 continue;
473 }
474 // If the parent node has a "clip" attribute, the press region should be re-evaluated.
475 auto clip = renderContext->GetClipEdge().value_or(false);
476 if (clip && IsOutOfPressedRegionWithoutClip(parent, sourceType, location)) {
477 return true;
478 }
479 parent = parent->GetAncestorNodeOfFrame(true);
480 }
481 return false;
482 }
483
IsOutOfPressedRegionWithoutClip(RefPtr<FrameNode> node,int32_t sourceType,const Offset & location) const484 bool StateStyleManager::IsOutOfPressedRegionWithoutClip(RefPtr<FrameNode> node, int32_t sourceType,
485 const Offset& location) const
486 {
487 CHECK_NULL_RETURN(node, false);
488 auto renderContext = node->GetRenderContext();
489 CHECK_NULL_RETURN(renderContext, false);
490
491 auto paintRect = renderContext->GetPaintRectWithoutTransform();
492 auto responseRegionList = node->GetResponseRegionList(paintRect, sourceType);
493 Offset offset = { paintRect.GetOffset().GetX(), paintRect.GetOffset().GetY() };
494 PointF current = { location.GetX(), location.GetY() };
495 NGGestureRecognizer::Transform(current, node);
496 PointF parentPoint = { current.GetX() + offset.GetX(), current.GetY() + offset.GetY() };
497 if (!node->InResponseRegionList(parentPoint, responseRegionList)) {
498 return true;
499 }
500 return false;
501 }
502
GetFrameNode() const503 RefPtr<FrameNode> StateStyleManager::GetFrameNode() const
504 {
505 return host_.Upgrade();
506 }
507 } // namespace OHOS::Ace::NG
508