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