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