1 /*
2 * Copyright (c) 2024 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/pattern/list/list_item_drag_manager.h"
17
18 #include "core/pipeline_ng/pipeline_context.h"
19 #include "core/components/common/properties/shadow_config.h"
20 #include "core/components_ng/pattern/list/list_pattern.h"
21 #include "core/components_ng/syntax/lazy_for_each_node.h"
22
23 namespace OHOS::Ace::NG {
24 namespace {
25 static constexpr Dimension HOT_ZONE_HEIGHT_VP_DIM = 59.0_vp;
26 static constexpr Dimension HOT_ZONE_WIDTH_VP_DIM = 26.0_vp;
27 static constexpr int32_t DEFAULT_Z_INDEX = 100;
28 static constexpr float DEFAULT_SCALE = 1.05f;
29 }
30
GetListFrameNode() const31 RefPtr<FrameNode> ListItemDragManager::GetListFrameNode() const
32 {
33 auto host = GetHost();
34 CHECK_NULL_RETURN(host, nullptr);
35 auto parent = host->GetParentFrameNode();
36 CHECK_NULL_RETURN(parent, nullptr);
37 if (parent->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
38 parent = parent->GetParentFrameNode();
39 CHECK_NULL_RETURN(parent, nullptr);
40 }
41 if (parent->GetTag() == V2::LIST_ETS_TAG) {
42 return parent;
43 }
44 return nullptr;
45 }
46
InitDragDropEvent()47 void ListItemDragManager::InitDragDropEvent()
48 {
49 auto host = GetHost();
50 CHECK_NULL_VOID(host);
51 auto listItemEventHub = host->GetEventHub<ListItemEventHub>();
52 CHECK_NULL_VOID(listItemEventHub);
53 auto gestureHub = listItemEventHub->GetOrCreateGestureEventHub();
54 CHECK_NULL_VOID(gestureHub);
55 if (gestureHub->HasDragEvent()) {
56 return;
57 }
58 auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& info) {
59 auto manager = weak.Upgrade();
60 CHECK_NULL_VOID(manager);
61 manager->HandleOnItemDragStart(info);
62 };
63
64 auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
65 auto manager = weak.Upgrade();
66 CHECK_NULL_VOID(manager);
67 manager->HandleOnItemDragUpdate(info);
68 };
69
70 auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
71 auto manager = weak.Upgrade();
72 CHECK_NULL_VOID(manager);
73 manager->HandleOnItemDragEnd(info);
74 };
75
76 auto actionCancelTask = [weak = WeakClaim(this)]() {
77 auto manager = weak.Upgrade();
78 CHECK_NULL_VOID(manager);
79 manager->HandleOnItemDragCancel();
80 };
81
82 auto actionLongPress = [weak = WeakClaim(this)](const GestureEvent& info) {
83 auto manager = weak.Upgrade();
84 CHECK_NULL_VOID(manager);
85 manager->HandleOnItemLongPress(info);
86 };
87
88 auto dragEvent = MakeRefPtr<DragEvent>(
89 std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
90 dragEvent->SetLongPressEventFunc(std::move(actionLongPress));
91 gestureHub->SetDragEvent(dragEvent, { PanDirection::ALL }, DEFAULT_PAN_FINGER, DEFAULT_PAN_DISTANCE);
92 }
93
DeInitDragDropEvent()94 void ListItemDragManager::DeInitDragDropEvent()
95 {
96 auto host = GetHost();
97 CHECK_NULL_VOID(host);
98 auto listItemEventHub = host->GetEventHub<ListItemEventHub>();
99 CHECK_NULL_VOID(listItemEventHub);
100 auto gestureHub = listItemEventHub->GetOrCreateGestureEventHub();
101 CHECK_NULL_VOID(gestureHub);
102 gestureHub->RemoveDragEvent();
103 }
104
HandleOnItemDragStart(const GestureEvent & info)105 void ListItemDragManager::HandleOnItemDragStart(const GestureEvent& info)
106 {
107 auto host = GetHost();
108 CHECK_NULL_VOID(host);
109 auto geometry = host->GetGeometryNode();
110 CHECK_NULL_VOID(geometry);
111 dragOffset_ = geometry->GetMarginFrameOffset();
112
113 auto parent = listNode_.Upgrade();
114 CHECK_NULL_VOID(parent);
115 auto pattern = parent->GetPattern<ListPattern>();
116 CHECK_NULL_VOID(pattern);
117 axis_ = pattern->GetAxis();
118 lanes_ = pattern->GetLanes();
119
120 auto forEach = forEachNode_.Upgrade();
121 CHECK_NULL_VOID(forEach);
122 totalCount_ = forEach->FrameCount();
123 fromIndex_ = GetIndex();
124 }
125
HandleOnItemLongPress(const GestureEvent & info)126 void ListItemDragManager::HandleOnItemLongPress(const GestureEvent& info)
127 {
128 auto host = GetHost();
129 CHECK_NULL_VOID(host);
130 auto renderContext = host->GetRenderContext();
131 CHECK_NULL_VOID(renderContext);
132 if (renderContext->HasTransformScale()) {
133 prevScale_ = renderContext->GetTransformScaleValue({ 1.0f, 1.0f });
134 } else {
135 renderContext->UpdateTransformScale({ 1.0f, 1.0f });
136 }
137 if (renderContext->HasBackShadow()) {
138 prevShadow_ = renderContext->GetBackShadowValue(ShadowConfig::NoneShadow);
139 } else {
140 renderContext->UpdateBackShadow(ShadowConfig::NoneShadow);
141 }
142 prevZIndex_ = renderContext->GetZIndexValue(0);
143
144 AnimationOption option;
145 option.SetCurve(Curves::FRICTION);
146 option.SetDuration(300); /* 300:animate duration */
147 AnimationUtils::Animate(option, [weak = WeakClaim(this)]() {
148 auto manager = weak.Upgrade();
149 CHECK_NULL_VOID(manager);
150 auto host = manager->GetHost();
151 CHECK_NULL_VOID(host);
152 auto renderContext = host->GetRenderContext();
153 CHECK_NULL_VOID(renderContext);
154 auto newScale = manager->prevScale_ * DEFAULT_SCALE;
155 renderContext->UpdateTransformScale(newScale);
156 renderContext->UpdateZIndex(DEFAULT_Z_INDEX);
157 renderContext->UpdateBackShadow(ShadowConfig::DefaultShadowS);
158 },
159 option.GetOnFinishEvent()
160 );
161 }
162
SetNearbyNodeScale(RefPtr<FrameNode> node,float scale)163 void ListItemDragManager::SetNearbyNodeScale(RefPtr<FrameNode> node, float scale)
164 {
165 auto renderContext = node->GetRenderContext();
166 CHECK_NULL_VOID(renderContext);
167 auto it = prevScaleNode_.find(renderContext);
168 VectorF prevScale = it != prevScaleNode_.end() ? it->second :
169 renderContext->GetTransformScaleValue({ 1.0f, 1.0f });
170 renderContext->UpdateTransformScale(prevScale * scale);
171 scaleNode_.emplace(renderContext, prevScale);
172 }
173
ResetPrevScaleNode()174 void ListItemDragManager::ResetPrevScaleNode()
175 {
176 for (auto& [weakNode, scale] : prevScaleNode_) {
177 if (scaleNode_.find(weakNode) == scaleNode_.end()) {
178 auto node = weakNode.Upgrade();
179 if (node) {
180 node->UpdateTransformScale(scale);
181 }
182 }
183 }
184 prevScaleNode_.swap(scaleNode_);
185 scaleNode_.clear();
186 }
187
ScaleAxisNearItem(int32_t index,const RectF & rect,const OffsetF & delta,Axis axis)188 ListItemDragManager::ScaleResult ListItemDragManager::ScaleAxisNearItem(
189 int32_t index, const RectF& rect, const OffsetF& delta, Axis axis)
190 {
191 ScaleResult res = { false, 1.0f };
192 auto forEach = forEachNode_.Upgrade();
193 CHECK_NULL_RETURN(forEach, res);
194
195 auto node = forEach->GetFrameNode(index);
196 CHECK_NULL_RETURN(node, res);
197 auto geometry = node->GetGeometryNode();
198 CHECK_NULL_RETURN(geometry, res);
199 auto nearRect = geometry->GetMarginFrameRect();
200 if (axis != axis_) {
201 float offset1 = nearRect.GetOffset().GetMainOffset(axis_);
202 if (!NearEqual(offset1, rect.GetOffset().GetMainOffset(axis_))) {
203 return res;
204 }
205 }
206 float mainDelta = delta.GetMainOffset(axis);
207 float c0 = rect.GetOffset().GetMainOffset(axis) + rect.GetSize().MainSize(axis) / 2;
208 float c1 = nearRect.GetOffset().GetMainOffset(axis) + nearRect.GetSize().MainSize(axis) / 2;
209 if (NearEqual(c0, c1)) {
210 return res;
211 }
212 float sharped = Curves::SHARP->MoveInternal(std::abs(mainDelta / (c1 - c0)));
213 float scale = 1 - sharped * 0.05f;
214 SetNearbyNodeScale(node, scale);
215 res.scale = scale;
216
217 if (Positive(mainDelta)) {
218 float th = (nearRect.GetOffset().GetMainOffset(axis) + nearRect.GetSize().MainSize(axis) -
219 rect.GetOffset().GetMainOffset(axis) - rect.GetSize().MainSize(axis)) / 2;
220 if (GreatNotEqual(mainDelta, th)) {
221 res.needMove = true;
222 return res;
223 }
224 }
225 if (Negative(mainDelta)) {
226 float th = (nearRect.GetOffset().GetMainOffset(axis) - rect.GetOffset().GetMainOffset(axis)) / 2;
227 if (LessNotEqual(mainDelta, th)) {
228 res.needMove = true;
229 return res;
230 }
231 }
232 return res;
233 }
234
ScaleDiagonalItem(int32_t index,const RectF & rect,const OffsetF & delta)235 void ListItemDragManager::ScaleDiagonalItem(int32_t index, const RectF& rect, const OffsetF& delta)
236 {
237 auto forEach = forEachNode_.Upgrade();
238 CHECK_NULL_VOID(forEach);
239
240 auto node = forEach->GetFrameNode(index);
241 CHECK_NULL_VOID(node);
242 auto geometry = node->GetGeometryNode();
243 CHECK_NULL_VOID(geometry);
244 auto diagonalRect = geometry->GetMarginFrameRect();
245
246 OffsetF c0 = rect.GetOffset() + OffsetF(rect.Width() / 2, rect.Height() / 2);
247 OffsetF c1 = diagonalRect.GetOffset() + OffsetF(diagonalRect.Width() / 2, diagonalRect.Height() / 2);
248 OffsetF c2 = c0 + delta;
249
250 float d0 = c0.GetDistance(c1);
251 if (NearZero(d0)) {
252 return;
253 }
254 float d1 = c2.GetDistance(c1);
255
256 float sharped = Curves::SHARP->MoveInternal(std::abs(1 - d1 / d0));
257 float scale = 1 - sharped * 0.05f;
258 SetNearbyNodeScale(node, scale);
259 }
260
ScaleNearItem(int32_t index,const RectF & rect,const OffsetF & delta)261 int32_t ListItemDragManager::ScaleNearItem(int32_t index, const RectF& rect, const OffsetF& delta)
262 {
263 int32_t nearIndex = index;
264 float mainDelta = delta.GetMainOffset(axis_);
265 if (Positive(mainDelta)) {
266 nearIndex = index + lanes_;
267 } else if (Negative(mainDelta)) {
268 nearIndex = index - lanes_;
269 }
270 ScaleResult mainRes = { false, 1.0f };
271 if (nearIndex != index) {
272 mainRes = ScaleAxisNearItem(nearIndex, rect, delta, axis_);
273 }
274
275 int32_t crossNearIndex = index;
276 float crossDelta = delta.GetCrossOffset(axis_);
277 if (Positive(crossDelta)) {
278 crossNearIndex = index + 1;
279 } else if (Negative(crossDelta)) {
280 crossNearIndex = index - 1;
281 }
282 ScaleResult crossRes = { false, 1.0f };
283 if (crossNearIndex != index) {
284 Axis crossAxis = axis_ == Axis::VERTICAL ? Axis::HORIZONTAL : Axis::VERTICAL;
285 crossRes = ScaleAxisNearItem(crossNearIndex, rect, delta, crossAxis);
286 }
287
288 int32_t diagonalIndex = index;
289 if (!NearEqual(mainRes.scale, 1.0f) && !NearEqual(crossRes.scale, 1.0f)) {
290 diagonalIndex = Positive(crossDelta) ? nearIndex + 1 : nearIndex - 1;
291 ScaleDiagonalItem(diagonalIndex, rect, delta);
292 }
293
294 ResetPrevScaleNode();
295 if (mainRes.needMove && crossRes.needMove) {
296 return diagonalIndex;
297 } else if (mainRes.needMove) {
298 return nearIndex;
299 } else if (crossRes.needMove) {
300 return crossNearIndex;
301 }
302 return index;
303 }
304
IsInHotZone(int32_t index,const RectF & frameRect) const305 bool ListItemDragManager::IsInHotZone(int32_t index, const RectF& frameRect) const
306 {
307 auto parent = listNode_.Upgrade();
308 CHECK_NULL_RETURN(parent, false);
309 auto listGeometry = parent->GetGeometryNode();
310 CHECK_NULL_RETURN(listGeometry, false);
311 auto listSize = listGeometry->GetFrameSize();
312 float hotZone = axis_ == Axis::VERTICAL ?
313 HOT_ZONE_HEIGHT_VP_DIM.ConvertToPx() : HOT_ZONE_WIDTH_VP_DIM.ConvertToPx();
314 float startOffset = frameRect.GetOffset().GetMainOffset(axis_);
315 float endOffset = startOffset + frameRect.GetSize().MainSize(axis_);
316 bool reachStart = (index == 0 && startOffset > hotZone);
317 bool reachEnd = (index == totalCount_ - 1) && endOffset < (listSize.MainSize(axis_) - hotZone);
318 return (!reachStart && !reachEnd);
319 }
320
HandleAutoScroll(int32_t index,const PointF & point,const RectF & frameRect)321 void ListItemDragManager::HandleAutoScroll(int32_t index, const PointF& point, const RectF& frameRect)
322 {
323 auto parent = listNode_.Upgrade();
324 CHECK_NULL_VOID(parent);
325 auto pattern = parent->GetPattern<ListPattern>();
326 CHECK_NULL_VOID(pattern);
327 if (IsInHotZone(index, frameRect)) {
328 pattern->HandleMoveEventInComp(point);
329 if (!scrolling_) {
330 pattern->SetHotZoneScrollCallback([weak = WeakClaim(this)]() {
331 auto manager = weak.Upgrade();
332 CHECK_NULL_VOID(manager);
333 manager->HandleScrollCallback();
334 });
335 scrolling_ = true;
336 }
337 } else if (scrolling_) {
338 pattern->HandleLeaveHotzoneEvent();
339 pattern->SetHotZoneScrollCallback(nullptr);
340 scrolling_ = false;
341 }
342 }
343
HandleScrollCallback()344 void ListItemDragManager::HandleScrollCallback()
345 {
346 auto host = GetHost();
347 CHECK_NULL_VOID(host);
348 auto geometry = host->GetGeometryNode();
349 CHECK_NULL_VOID(geometry);
350 auto frameRect = geometry->GetMarginFrameRect();
351 int32_t from = GetIndex();
352 if (scrolling_ && !IsInHotZone(from, frameRect)) {
353 auto parent = listNode_.Upgrade();
354 CHECK_NULL_VOID(parent);
355 auto pattern = parent->GetPattern<ListPattern>();
356 CHECK_NULL_VOID(pattern);
357 pattern->HandleLeaveHotzoneEvent();
358 pattern->SetHotZoneScrollCallback(nullptr);
359 scrolling_ = false;
360 }
361 int32_t to = ScaleNearItem(from, frameRect, realOffset_ - frameRect.GetOffset());
362 if (to == from) {
363 return;
364 }
365 HandleSwapAnimation(from, to);
366 }
367
SetPosition(const OffsetF & offset)368 void ListItemDragManager::SetPosition(const OffsetF& offset)
369 {
370 auto host = GetHost();
371 CHECK_NULL_VOID(host);
372 auto renderContext = host->GetRenderContext();
373 CHECK_NULL_VOID(renderContext);
374 renderContext->UpdatePosition({ Dimension(offset.GetX(), DimensionUnit::PX),
375 Dimension(offset.GetY(), DimensionUnit::PX) });
376 }
377
HandleOnItemDragUpdate(const GestureEvent & info)378 void ListItemDragManager::HandleOnItemDragUpdate(const GestureEvent& info)
379 {
380 auto host = GetHost();
381 CHECK_NULL_VOID(host);
382 auto geometry = host->GetGeometryNode();
383 CHECK_NULL_VOID(geometry);
384 auto frameRect = geometry->GetMarginFrameRect();
385 OffsetF gestureOffset(info.GetOffsetX(), info.GetOffsetY());
386 realOffset_ = gestureOffset + dragOffset_;
387 lanes_ = GetLanes();
388 if (lanes_ == 1) {
389 if (axis_ == Axis::VERTICAL) {
390 realOffset_.SetX(dragOffset_.GetX());
391 } else {
392 realOffset_.SetY(dragOffset_.GetY());
393 }
394 }
395 SetPosition(realOffset_);
396
397 int32_t from = GetIndex();
398 PointF point(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
399 HandleAutoScroll(from, point, frameRect);
400
401 int32_t to = ScaleNearItem(from, frameRect, realOffset_ - frameRect.GetOffset());
402 if (to == from) {
403 return;
404 }
405 HandleSwapAnimation(from, to);
406 }
407
HandleSwapAnimation(int32_t from,int32_t to)408 void ListItemDragManager::HandleSwapAnimation(int32_t from, int32_t to)
409 {
410 auto forEach = forEachNode_.Upgrade();
411 CHECK_NULL_VOID(forEach);
412 CHECK_NULL_VOID(forEach->GetFrameNode(to));
413 auto list = listNode_.Upgrade();
414 CHECK_NULL_VOID(list);
415 if (list->CheckNeedForceMeasureAndLayout()) {
416 auto pipeline = PipelineContext::GetCurrentContext();
417 if (pipeline) {
418 pipeline->FlushUITasks();
419 }
420 }
421 AnimationOption option;
422 auto curve = AceType::MakeRefPtr<InterpolatingSpring>(0, 1, 400, 38); /* 400:stiffness, 38:damping */
423 option.SetCurve(curve);
424 option.SetDuration(30); /* 30:duration */
425 AnimationUtils::Animate(option, [weak = forEachNode_, from, to]() {
426 auto forEach = weak.Upgrade();
427 CHECK_NULL_VOID(forEach);
428 forEach->MoveData(from, to);
429 auto pipeline = PipelineContext::GetCurrentContext();
430 if (pipeline) {
431 pipeline->FlushUITasks();
432 }
433 },
434 option.GetOnFinishEvent()
435 );
436 }
437
HandleDragEndAnimation()438 void ListItemDragManager::HandleDragEndAnimation()
439 {
440 AnimationOption option;
441 auto curve = AceType::MakeRefPtr<InterpolatingSpring>(0, 1, 400, 38); /* 400:stiffness, 38:damping */
442 option.SetCurve(curve);
443 option.SetDuration(30); /* 30:duration */
444 AnimationUtils::Animate(option, [weak = WeakClaim(this)]() {
445 auto manager = weak.Upgrade();
446 CHECK_NULL_VOID(manager);
447 manager->ResetPrevScaleNode();
448 auto host = manager->GetHost();
449 CHECK_NULL_VOID(host);
450 auto renderContext = host->GetRenderContext();
451 CHECK_NULL_VOID(renderContext);
452 renderContext->UpdateZIndex(manager->prevZIndex_);
453 renderContext->ResetPosition();
454 renderContext->OnPositionUpdate(OffsetT<Dimension>());
455 },
456 option.GetOnFinishEvent()
457 );
458
459 option.SetCurve(Curves::FRICTION);
460 option.SetDuration(300); /* animate duration:300ms */
461 AnimationUtils::Animate(option, [weak = WeakClaim(this)]() {
462 auto manager = weak.Upgrade();
463 CHECK_NULL_VOID(manager);
464 auto host = manager->GetHost();
465 CHECK_NULL_VOID(host);
466 auto renderContext = host->GetRenderContext();
467 CHECK_NULL_VOID(renderContext);
468 renderContext->UpdateBackShadow(manager->prevShadow_);
469 },
470 option.GetOnFinishEvent()
471 );
472
473 /* 14:init velocity, 170:stiffness, 17:damping */
474 option.SetCurve(AceType::MakeRefPtr<InterpolatingSpring>(14, 1, 170, 17));
475 option.SetDuration(30); /* 30:duration */
476 option.SetDelay(150); /* 150:animate delay */
477 AnimationUtils::Animate(option, [weak = WeakClaim(this)]() {
478 auto manager = weak.Upgrade();
479 CHECK_NULL_VOID(manager);
480 auto host = manager->GetHost();
481 CHECK_NULL_VOID(host);
482 auto renderContext = host->GetRenderContext();
483 CHECK_NULL_VOID(renderContext);
484 renderContext->UpdateTransformScale(manager->prevScale_);
485 },
486 option.GetOnFinishEvent()
487 );
488 }
489
HandleOnItemDragEnd(const GestureEvent & info)490 void ListItemDragManager::HandleOnItemDragEnd(const GestureEvent& info)
491 {
492 if (scrolling_) {
493 auto parent = listNode_.Upgrade();
494 CHECK_NULL_VOID(parent);
495 auto pattern = parent->GetPattern<ListPattern>();
496 pattern->HandleLeaveHotzoneEvent();
497 scrolling_ = false;
498 }
499 HandleDragEndAnimation();
500 int32_t to = GetIndex();
501 if (fromIndex_ != to) {
502 auto forEach = forEachNode_.Upgrade();
503 CHECK_NULL_VOID(forEach);
504 forEach->FireOnMove(fromIndex_, to);
505 }
506 }
507
HandleOnItemDragCancel()508 void ListItemDragManager::HandleOnItemDragCancel()
509 {
510 HandleDragEndAnimation();
511 }
512
GetIndex() const513 int32_t ListItemDragManager::GetIndex() const
514 {
515 auto forEach = forEachNode_.Upgrade();
516 CHECK_NULL_RETURN(forEach, -1);
517 return forEach->GetFrameNodeIndex(GetHost());
518 }
519
GetLanes() const520 int32_t ListItemDragManager::GetLanes() const
521 {
522 auto parent = listNode_.Upgrade();
523 CHECK_NULL_RETURN(parent, 1);
524 auto pattern = parent->GetPattern<ListPattern>();
525 CHECK_NULL_RETURN(pattern, 1);
526 return pattern->GetLanes();
527 }
528 } // namespace OHOS::Ace::NG
529