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