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