• 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/navigation/navigation_group_node.h"
26 #include "core/components_ng/pattern/overlay/popup_base_pattern.h"
27 #include "core/event/touch_event.h"
28 #include "core/pipeline_ng/pipeline_context.h"
29 
30 namespace OHOS::Ace::NG {
31 
32 namespace {
33 constexpr uint32_t PRESS_STYLE_DELAY = 300;
34 constexpr uint32_t PRESS_CANCEL_STYLE_DELAY = 64;
35 }
36 
StateStyleManager(WeakPtr<FrameNode> frameNode)37 StateStyleManager::StateStyleManager(WeakPtr<FrameNode> frameNode) : host_(std::move(frameNode)) {}
38 
39 StateStyleManager::~StateStyleManager() = default;
40 
GetPressedListener()41 const RefPtr<TouchEventImpl>& StateStyleManager::GetPressedListener()
42 {
43     if (pressedFunc_) {
44         return pressedFunc_;
45     }
46     auto pressedCallback = [weak = WeakClaim(this)](TouchEventInfo& info) {
47         auto stateStyleMgr = weak.Upgrade();
48         CHECK_NULL_VOID(stateStyleMgr);
49         const auto& touches = info.GetTouches();
50         const auto& changeTouches = info.GetChangedTouches();
51         if (touches.empty() || changeTouches.empty()) {
52             LOGW("the touch info is illegal");
53             return;
54         }
55 
56         auto lastPoint = changeTouches.back();
57         const auto& type = lastPoint.GetTouchType();
58         if (type == TouchType::DOWN) {
59             stateStyleMgr->HandleTouchDown();
60             stateStyleMgr->pointerId_.insert(lastPoint.GetFingerId());
61         }
62         if ((type == TouchType::UP) || (type == TouchType::CANCEL)) {
63             stateStyleMgr->pointerId_.erase(lastPoint.GetFingerId());
64             if (stateStyleMgr->pointerId_.size() == 0) {
65                 stateStyleMgr->HandleTouchUp();
66             }
67         }
68         if ((type == TouchType::MOVE) &&
69             (stateStyleMgr->IsCurrentStateOn(UI_STATE_PRESSED) || stateStyleMgr->IsPressedStatePending())) {
70             int32_t sourceType = static_cast<int32_t>(touches.front().GetSourceDevice());
71             if (stateStyleMgr->IsOutOfPressedRegion(sourceType, lastPoint.GetGlobalLocation())) {
72                 stateStyleMgr->pointerId_.erase(lastPoint.GetFingerId());
73                 if (stateStyleMgr->pointerId_.size() == 0) {
74                     stateStyleMgr->ResetPressedState();
75                 }
76             }
77         }
78     };
79     pressedFunc_ = MakeRefPtr<TouchEventImpl>(std::move(pressedCallback));
80     return pressedFunc_;
81 }
82 
HandleTouchDown()83 void StateStyleManager::HandleTouchDown()
84 {
85     HandleScrollingParent();
86     if (!hasScrollingParent_) {
87         UpdateCurrentUIState(UI_STATE_PRESSED);
88     } else {
89         if (IsPressedCancelStatePending()) {
90             ResetPressedCancelState();
91         }
92         PostPressStyleTask(PRESS_STYLE_DELAY);
93         PendingPressedState();
94     }
95 }
96 
HandleTouchUp()97 void StateStyleManager::HandleTouchUp()
98 {
99     if (IsPressedStatePending()) {
100         DeletePressStyleTask();
101         ResetPressedPendingState();
102         UpdateCurrentUIState(UI_STATE_PRESSED);
103         PostPressCancelStyleTask(PRESS_CANCEL_STYLE_DELAY);
104         PendingCancelPressedState();
105     } else if (!IsPressedCancelStatePending()) {
106         ResetPressedState();
107     }
108     if (hasScrollingParent_) {
109         CleanScrollingParentListener();
110     }
111 }
112 
FireStateFunc()113 void StateStyleManager::FireStateFunc()
114 {
115     auto node = host_.Upgrade();
116     CHECK_NULL_VOID(node);
117     auto nodeId = node->GetId();
118     RefPtr<CustomNodeBase> customNode;
119     if (AceType::InstanceOf<CustomNodeBase>(node)) {
120         customNode = DynamicCast<CustomNodeBase>(node);
121     }
122     if (!customNode) {
123         auto parent = node->GetParent();
124         while (parent) {
125             if (AceType::InstanceOf<NavDestinationGroupNode>(parent)) {
126                 auto navDestinationGroupNode = DynamicCast<NavDestinationGroupNode>(parent);
127                 CHECK_NULL_VOID(navDestinationGroupNode);
128                 customNode = navDestinationGroupNode->GetNavDestinationCustomNode();
129             }
130 
131             auto parentFrameNode = DynamicCast<FrameNode>(parent);
132             auto parentPattern = parentFrameNode ? parentFrameNode->GetPattern<PopupBasePattern>() : nullptr;
133             if (parentFrameNode && InstanceOf<PopupBasePattern>(parentPattern)) {
134                 parent = ElementRegister::GetInstance()->GetUINodeById(parentPattern->GetTargetId());
135                 continue;
136             }
137 
138             if (customNode) {
139                 break;
140             }
141 
142             if (AceType::InstanceOf<CustomNodeBase>(parent)) {
143                 customNode = DynamicCast<CustomNodeBase>(parent);
144                 break;
145             }
146             parent = parent->GetParent();
147         }
148     }
149     if (!customNode) {
150         return;
151     }
152     ScopedViewStackProcessor processor;
153     customNode->FireNodeUpdateFunc(nodeId);
154 }
155 
PostPressStyleTask(uint32_t delayTime)156 void StateStyleManager::PostPressStyleTask(uint32_t delayTime)
157 {
158     auto pipeline = PipelineContext::GetCurrentContext();
159     CHECK_NULL_VOID(pipeline);
160     auto taskExecutor = pipeline->GetTaskExecutor();
161     CHECK_NULL_VOID(taskExecutor);
162 
163     if (IsPressedStatePending()) {
164         return;
165     }
166 
167     auto weak = AceType::WeakClaim(this);
168     pressStyleTask_.Reset([weak = WeakClaim(this)] {
169         auto stateStyleMgr = weak.Upgrade();
170         CHECK_NULL_VOID(stateStyleMgr);
171         stateStyleMgr->ResetPressedPendingState();
172         stateStyleMgr->UpdateCurrentUIState(UI_STATE_PRESSED);
173     });
174 
175     taskExecutor->PostDelayedTask(pressStyleTask_, TaskExecutor::TaskType::UI, delayTime);
176 }
177 
PostPressCancelStyleTask(uint32_t delayTime)178 void StateStyleManager::PostPressCancelStyleTask(uint32_t delayTime)
179 {
180     auto pipeline = PipelineContext::GetCurrentContext();
181     CHECK_NULL_VOID(pipeline);
182     auto taskExecutor = pipeline->GetTaskExecutor();
183     CHECK_NULL_VOID(taskExecutor);
184 
185     if (IsPressedStatePending() || IsPressedCancelStatePending()) {
186         return;
187     }
188 
189     auto weak = AceType::WeakClaim(this);
190     pressCancelStyleTask_.Reset([weak = WeakClaim(this)] {
191         auto stateStyleMgr = weak.Upgrade();
192         CHECK_NULL_VOID(stateStyleMgr);
193         stateStyleMgr->ResetPressedCancelPendingState();
194         stateStyleMgr->ResetCurrentUIState(UI_STATE_PRESSED);
195     });
196 
197     taskExecutor->PostDelayedTask(pressCancelStyleTask_, TaskExecutor::TaskType::UI, delayTime);
198 }
199 
HandleScrollingParent()200 void StateStyleManager::HandleScrollingParent()
201 {
202     auto node = host_.Upgrade();
203     CHECK_NULL_VOID(node);
204 
205     auto scrollingEventCallback = [weak = WeakClaim(this)]() {
206         auto stateStyleMgr = weak.Upgrade();
207         CHECK_NULL_VOID(stateStyleMgr);
208         stateStyleMgr->ResetCurrentUIState(UI_STATE_PRESSED);
209         stateStyleMgr->pointerId_.clear();
210         stateStyleMgr->ResetPressedPendingState();
211         if (stateStyleMgr->pressStyleTask_) {
212             stateStyleMgr->DeletePressStyleTask();
213         }
214     };
215 
216     auto scrollingListener = MakeRefPtr<ScrollingListener>(std::move(scrollingEventCallback));
217 
218     auto parent = node->GetAncestorNodeOfFrame();
219     while (parent) {
220         auto pattern = parent->GetPattern();
221         CHECK_NULL_VOID(pattern);
222         if (pattern->ShouldDelayChildPressedState()) {
223             hasScrollingParent_ = true;
224             pattern->RegisterScrollingListener(scrollingListener);
225         }
226         parent = parent->GetAncestorNodeOfFrame();
227     }
228 }
229 
CleanScrollingParentListener()230 void StateStyleManager::CleanScrollingParentListener()
231 {
232     auto node = host_.Upgrade();
233     CHECK_NULL_VOID(node);
234 
235     auto parent = node->GetAncestorNodeOfFrame();
236     while (parent) {
237         auto pattern = parent->GetPattern();
238         CHECK_NULL_VOID(pattern);
239         if (pattern->ShouldDelayChildPressedState()) {
240             pattern->CleanScrollingListener();
241         }
242         parent = parent->GetAncestorNodeOfFrame();
243     }
244 }
245 
Transform(PointF & localPointF,const WeakPtr<FrameNode> & node) const246 void StateStyleManager::Transform(PointF& localPointF, const WeakPtr<FrameNode>& node) const
247 {
248     if (node.Invalid()) {
249         return;
250     }
251 
252     std::vector<Matrix4> vTrans {};
253     auto host = node.Upgrade();
254     while (host) {
255         auto context = host->GetRenderContext();
256         CHECK_NULL_VOID(context);
257         auto localMat = context->GetLocalTransformMatrix();
258         vTrans.emplace_back(localMat);
259         host = host->GetAncestorNodeOfFrame();
260     }
261 
262     Point temp(localPointF.GetX(), localPointF.GetY());
263     for (auto iter = vTrans.rbegin(); iter != vTrans.rend(); iter++) {
264         temp = *iter * temp;
265     }
266     localPointF.SetX(temp.GetX());
267     localPointF.SetY(temp.GetY());
268 }
269 
IsOutOfPressedRegion(int32_t sourceType,const Offset & location) const270 bool StateStyleManager::IsOutOfPressedRegion(int32_t sourceType, const Offset& location) const
271 {
272     auto node = host_.Upgrade();
273     CHECK_NULL_RETURN(node, false);
274     if (IsOutOfPressedRegionWithoutClip(node, sourceType, location)) {
275         return true;
276     }
277     auto parent = node->GetAncestorNodeOfFrame();
278     while (parent) {
279         auto renderContext = parent->GetRenderContext();
280         if (!renderContext) {
281             parent = parent->GetAncestorNodeOfFrame();
282             continue;
283         }
284         // If the parent node has a "clip" attribute, the press region should be re-evaluated.
285         auto clip = renderContext->GetClipEdge().value_or(false);
286         if (clip && IsOutOfPressedRegionWithoutClip(parent, sourceType, location)) {
287             return true;
288         }
289         parent = parent->GetAncestorNodeOfFrame();
290     }
291     return false;
292 }
293 
IsOutOfPressedRegionWithoutClip(RefPtr<FrameNode> node,int32_t sourceType,const Offset & location) const294 bool StateStyleManager::IsOutOfPressedRegionWithoutClip(RefPtr<FrameNode> node, int32_t sourceType,
295     const Offset& location) const
296 {
297     CHECK_NULL_RETURN(node, false);
298     auto renderContext = node->GetRenderContext();
299     CHECK_NULL_RETURN(renderContext, false);
300 
301     auto paintRect = renderContext->GetPaintRectWithoutTransform();
302     auto responseRegionList = node->GetResponseRegionList(paintRect, sourceType);
303     Offset offset = { paintRect.GetOffset().GetX(), paintRect.GetOffset().GetY() };
304     PointF current = { location.GetX(), location.GetY() };
305     Transform(current, node);
306     PointF parentPoint = { current.GetX() + offset.GetX(), current.GetY() + offset.GetY() };
307     if (!node->InResponseRegionList(parentPoint, responseRegionList)) {
308         return true;
309     }
310     return false;
311 }
312 } // namespace OHOS::Ace::NG