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                 CHECK_NULL_VOID(frameNode);
77                 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Move out of node pressed region: %{public}s/%{public}d",
78                     frameNode->GetTag().c_str(), frameNode->GetId());
79                 stateStyleMgr->pointerId_.erase(lastPoint.GetFingerId());
80                 if (stateStyleMgr->pointerId_.size() == 0) {
81                     stateStyleMgr->ResetPressedState();
82                     stateStyleMgr->PostListItemPressStyleTask(stateStyleMgr->currentState_);
83                 }
84             }
85         }
86     };
87     pressedFunc_ = MakeRefPtr<TouchEventImpl>(std::move(pressedCallback));
88     return pressedFunc_;
89 }
90 
HandleTouchDown()91 void StateStyleManager::HandleTouchDown()
92 {
93     auto node = GetFrameNode();
94     CHECK_NULL_VOID(node);
95     TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Handle TouchDown event node: %{public}s/%{public}d",
96         node->GetTag().c_str(), node->GetId());
97     HandleScrollingParent();
98     if (!hasScrollingParent_) {
99         UpdateCurrentUIState(UI_STATE_PRESSED);
100         PostListItemPressStyleTask(currentState_);
101     } else {
102         if (IsPressedCancelStatePending()) {
103             ResetPressedCancelState();
104         }
105         PostPressStyleTask(PRESS_STYLE_DELAY);
106         PendingPressedState();
107     }
108 }
109 
HandleTouchUp()110 void StateStyleManager::HandleTouchUp()
111 {
112     auto node = GetFrameNode();
113     CHECK_NULL_VOID(node);
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     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     TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Start execution, node is %{public}s/%{public}d, "
137         "reset is %{public}d", node->GetTag().c_str(), nodeId, isReset);
138     RefPtr<CustomNodeBase> customNode;
139     GetCustomNode(customNode, node);
140     if (!customNode) {
141         TAG_LOGW(AceLogTag::ACE_STATE_STYLE, "Can not find customNode!");
142         return;
143     }
144     ScopedViewStackProcessor processor;
145     customNode->FireNodeUpdateFunc(nodeId);
146 }
147 
GetCustomNode(RefPtr<CustomNodeBase> & customNode,RefPtr<FrameNode> & node)148 void StateStyleManager::GetCustomNode(RefPtr<CustomNodeBase>& customNode,
149     RefPtr<FrameNode>& node)
150 {
151     auto nodeId = node->GetId();
152     if (AceType::InstanceOf<CustomNodeBase>(node)) {
153         customNode = DynamicCast<CustomNodeBase>(node);
154         if (customNode && customNode->FireHasNodeUpdateFunc(nodeId)) {
155             TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Find customNode by self: %{public}s",
156                 customNode->GetJSViewName().c_str());
157             return;
158         }
159     }
160     auto parent = node->GetParent();
161     while (parent) {
162         if (AceType::InstanceOf<NavDestinationGroupNode>(parent)) {
163             auto navDestinationGroupNode = DynamicCast<NavDestinationGroupNode>(parent);
164             CHECK_NULL_VOID(navDestinationGroupNode);
165             customNode = navDestinationGroupNode->GetNavDestinationCustomNode();
166             if (customNode && customNode->FireHasNodeUpdateFunc(nodeId)) {
167                 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Find customNode from Navgation: %{public}s",
168                     customNode->GetJSViewName().c_str());
169                 return;
170             }
171         }
172 
173         auto parentFrameNode = DynamicCast<FrameNode>(parent);
174         auto parentPattern = parentFrameNode ? parentFrameNode->GetPattern<PopupBasePattern>() : nullptr;
175         if (parentFrameNode && InstanceOf<PopupBasePattern>(parentPattern)) {
176             parent = ElementRegister::GetInstance()->GetUINodeById(parentPattern->GetTargetId());
177             continue;
178         }
179 
180         if (AceType::InstanceOf<CustomNodeBase>(parent)) {
181             customNode = DynamicCast<CustomNodeBase>(parent);
182             if (customNode && customNode->FireHasNodeUpdateFunc(nodeId)) {
183                 TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Find customNode from parent: %{public}s",
184                     customNode->GetJSViewName().c_str());
185                 return;
186             }
187         }
188         parent = parent->GetParent();
189     }
190 }
191 
PostPressStyleTask(uint32_t delayTime)192 void StateStyleManager::PostPressStyleTask(uint32_t delayTime)
193 {
194     auto pipeline = PipelineContext::GetCurrentContext();
195     CHECK_NULL_VOID(pipeline);
196     auto taskExecutor = pipeline->GetTaskExecutor();
197     CHECK_NULL_VOID(taskExecutor);
198 
199     if (IsPressedStatePending()) {
200         return;
201     }
202 
203     auto weak = AceType::WeakClaim(this);
204     pressStyleTask_.Reset([weak = WeakClaim(this)] {
205         auto stateStyleMgr = weak.Upgrade();
206         CHECK_NULL_VOID(stateStyleMgr);
207         TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Execute press task");
208         stateStyleMgr->ResetPressedPendingState();
209         stateStyleMgr->UpdateCurrentUIState(UI_STATE_PRESSED);
210         stateStyleMgr->PostListItemPressStyleTask(stateStyleMgr->currentState_);
211     });
212 
213     taskExecutor->PostDelayedTask(pressStyleTask_, TaskExecutor::TaskType::UI, delayTime, "ArkUIPressStateStyle");
214 }
215 
PostPressCancelStyleTask(uint32_t delayTime)216 void StateStyleManager::PostPressCancelStyleTask(uint32_t delayTime)
217 {
218     auto pipeline = PipelineContext::GetCurrentContext();
219     CHECK_NULL_VOID(pipeline);
220     auto taskExecutor = pipeline->GetTaskExecutor();
221     CHECK_NULL_VOID(taskExecutor);
222 
223     if (IsPressedStatePending() || IsPressedCancelStatePending()) {
224         return;
225     }
226 
227     auto weak = AceType::WeakClaim(this);
228     pressCancelStyleTask_.Reset([weak = WeakClaim(this)] {
229         auto stateStyleMgr = weak.Upgrade();
230         CHECK_NULL_VOID(stateStyleMgr);
231         TAG_LOGI(AceLogTag::ACE_STATE_STYLE, "Execute press clear task");
232         stateStyleMgr->ResetPressedCancelPendingState();
233         stateStyleMgr->ResetCurrentUIState(UI_STATE_PRESSED);
234         stateStyleMgr->PostListItemPressStyleTask(stateStyleMgr->currentState_);
235     });
236 
237     taskExecutor->PostDelayedTask(
238         pressCancelStyleTask_, TaskExecutor::TaskType::UI, delayTime, "ArkUIPressCancelStateStyle");
239 }
240 
PostListItemPressStyleTask(UIState state)241 void StateStyleManager::PostListItemPressStyleTask(UIState state)
242 {
243     bool isPressed = state == UI_STATE_PRESSED;
244     auto node = GetFrameNode();
245     CHECK_NULL_VOID(node);
246     auto nodeId = node->GetId();
247     if (node->GetTag() == V2::LIST_ITEM_ETS_TAG) {
248         auto frameNode = node->GetAncestorNodeOfFrame();
249         CHECK_NULL_VOID(frameNode);
250         if (frameNode->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
251             auto listGroupPattern = DynamicCast<ListItemGroupPattern>(frameNode->GetPattern());
252             CHECK_NULL_VOID(listGroupPattern);
253             listGroupPattern->SetItemPressed(isPressed, nodeId);
254             frameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
255         }
256         if (frameNode->GetTag() == V2::LIST_ETS_TAG) {
257             auto listPattern = DynamicCast<ListPattern>(frameNode->GetPattern());
258             CHECK_NULL_VOID(listPattern);
259             listPattern->SetItemPressed(isPressed, nodeId);
260             frameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
261         }
262     }
263 }
264 
HandleScrollingParent()265 void StateStyleManager::HandleScrollingParent()
266 {
267     auto node = GetFrameNode();
268     CHECK_NULL_VOID(node);
269 
270     auto scrollingEventCallback = [weak = WeakClaim(this)]() {
271         auto stateStyleMgr = weak.Upgrade();
272         CHECK_NULL_VOID(stateStyleMgr);
273         stateStyleMgr->ResetCurrentUIState(UI_STATE_PRESSED);
274         stateStyleMgr->PostListItemPressStyleTask(stateStyleMgr->currentState_);
275         stateStyleMgr->pointerId_.clear();
276         stateStyleMgr->ResetPressedPendingState();
277         if (stateStyleMgr->pressStyleTask_) {
278             stateStyleMgr->DeletePressStyleTask();
279         }
280     };
281 
282     auto scrollingListener = MakeRefPtr<ScrollingListener>(std::move(scrollingEventCallback));
283 
284     auto parent = node->GetAncestorNodeOfFrame();
285     while (parent) {
286         auto pattern = parent->GetPattern();
287         CHECK_NULL_VOID(pattern);
288         if (pattern->ShouldDelayChildPressedState()) {
289             hasScrollingParent_ = true;
290             pattern->RegisterScrollingListener(scrollingListener);
291         }
292         parent = parent->GetAncestorNodeOfFrame();
293     }
294 }
295 
CleanScrollingParentListener()296 void StateStyleManager::CleanScrollingParentListener()
297 {
298     auto node = GetFrameNode();
299     CHECK_NULL_VOID(node);
300 
301     auto parent = node->GetAncestorNodeOfFrame();
302     while (parent) {
303         auto pattern = parent->GetPattern();
304         CHECK_NULL_VOID(pattern);
305         if (pattern->ShouldDelayChildPressedState()) {
306             pattern->CleanScrollingListener();
307         }
308         parent = parent->GetAncestorNodeOfFrame();
309     }
310 }
311 
Transform(PointF & localPointF,const WeakPtr<FrameNode> & node) const312 void StateStyleManager::Transform(PointF& localPointF, const WeakPtr<FrameNode>& node) const
313 {
314     if (node.Invalid()) {
315         return;
316     }
317 
318     std::vector<Matrix4> vTrans {};
319     auto host = node.Upgrade();
320     while (host) {
321         auto context = host->GetRenderContext();
322         CHECK_NULL_VOID(context);
323         auto localMat = context->GetLocalTransformMatrix();
324         vTrans.emplace_back(localMat);
325         host = host->GetAncestorNodeOfFrame();
326     }
327 
328     Point temp(localPointF.GetX(), localPointF.GetY());
329     for (auto iter = vTrans.rbegin(); iter != vTrans.rend(); iter++) {
330         temp = *iter * temp;
331     }
332     localPointF.SetX(temp.GetX());
333     localPointF.SetY(temp.GetY());
334 }
335 
IsOutOfPressedRegion(int32_t sourceType,const Offset & location) const336 bool StateStyleManager::IsOutOfPressedRegion(int32_t sourceType, const Offset& location) const
337 {
338     auto node = GetFrameNode();
339     CHECK_NULL_RETURN(node, false);
340     if (IsOutOfPressedRegionWithoutClip(node, sourceType, location)) {
341         return true;
342     }
343     auto parent = node->GetAncestorNodeOfFrame();
344     while (parent) {
345         auto renderContext = parent->GetRenderContext();
346         if (!renderContext) {
347             parent = parent->GetAncestorNodeOfFrame();
348             continue;
349         }
350         // If the parent node has a "clip" attribute, the press region should be re-evaluated.
351         auto clip = renderContext->GetClipEdge().value_or(false);
352         if (clip && IsOutOfPressedRegionWithoutClip(parent, sourceType, location)) {
353             return true;
354         }
355         parent = parent->GetAncestorNodeOfFrame();
356     }
357     return false;
358 }
359 
IsOutOfPressedRegionWithoutClip(RefPtr<FrameNode> node,int32_t sourceType,const Offset & location) const360 bool StateStyleManager::IsOutOfPressedRegionWithoutClip(RefPtr<FrameNode> node, int32_t sourceType,
361     const Offset& location) const
362 {
363     CHECK_NULL_RETURN(node, false);
364     auto renderContext = node->GetRenderContext();
365     CHECK_NULL_RETURN(renderContext, false);
366 
367     auto paintRect = renderContext->GetPaintRectWithoutTransform();
368     auto responseRegionList = node->GetResponseRegionList(paintRect, sourceType);
369     Offset offset = { paintRect.GetOffset().GetX(), paintRect.GetOffset().GetY() };
370     PointF current = { location.GetX(), location.GetY() };
371     Transform(current, node);
372     PointF parentPoint = { current.GetX() + offset.GetX(), current.GetY() + offset.GetY() };
373     if (!node->InResponseRegionList(parentPoint, responseRegionList)) {
374         return true;
375     }
376     return false;
377 }
378 
GetFrameNode() const379 RefPtr<FrameNode> StateStyleManager::GetFrameNode() const
380 {
381     return host_.Upgrade();
382 }
383 } // namespace OHOS::Ace::NG