• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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