1 /*
2  * Copyright (c) 2022-2023 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/grid/grid_event_hub.h"
17 
18 #include "core/animation/spring_curve.h"
19 #include "core/components_ng/base/frame_node.h"
20 #include "core/components_ng/pattern/grid/grid_item_layout_property.h"
21 #include "core/components_ng/pattern/grid/grid_item_pattern.h"
22 #include "core/components_ng/pattern/grid/grid_layout_property.h"
23 #include "core/components_ng/pattern/grid/grid_pattern.h"
24 #include "core/components_ng/render/adapter/component_snapshot.h"
25 #include "core/pipeline_ng/pipeline_context.h"
26 #include "core/pipeline_ng/ui_task_scheduler.h"
27 
28 namespace OHOS::Ace::NG {
29 #if defined(PIXEL_MAP_SUPPORTED)
30 constexpr int32_t CREATE_PIXELMAP_TIME = 80;
31 #endif
32 
InitItemDragEvent(const RefPtr<GestureEventHub> & gestureHub)33 void GridEventHub::InitItemDragEvent(const RefPtr<GestureEventHub>& gestureHub)
34 {
35     auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& info) {
36         auto eventHub = weak.Upgrade();
37         if (eventHub) {
38             eventHub->HandleOnItemDragStart(info);
39         }
40     };
41 
42     auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
43         auto eventHub = weak.Upgrade();
44         if (eventHub) {
45             eventHub->HandleOnItemDragUpdate(info);
46         }
47     };
48 
49     auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
50         auto eventHub = weak.Upgrade();
51         if (eventHub) {
52             eventHub->HandleOnItemDragEnd(info);
53         }
54     };
55 
56     auto actionCancelTask = [weak = WeakClaim(this)]() {
57         auto eventHub = weak.Upgrade();
58         if (eventHub) {
59             eventHub->HandleOnItemDragCancel();
60         }
61     };
62 
63     auto dragEvent = MakeRefPtr<DragEvent>(
64         std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
65     gestureHub->SetDragEvent(dragEvent, { PanDirection::ALL }, DEFAULT_PAN_FINGER, DEFAULT_PAN_DISTANCE);
66 }
67 
CheckPostionInGrid(float x,float y)68 bool GridEventHub::CheckPostionInGrid(float x, float y)
69 {
70     auto host = GetFrameNode();
71     CHECK_NULL_RETURN(host, false);
72     auto size = host->GetRenderContext()->GetPaintRectWithTransform();
73     size.SetOffset(host->GetTransformRelativeOffset());
74     return size.IsInRegion(PointF(x, y));
75 }
76 
GetInsertPosition(float x,float y)77 int32_t GridEventHub::GetInsertPosition(float x, float y)
78 {
79     if (!CheckPostionInGrid(x, y)) {
80         return -1;
81     }
82 
83     auto host = GetFrameNode();
84     CHECK_NULL_RETURN(host, -1);
85     auto pattern = AceType::DynamicCast<GridPattern>(host->GetPattern());
86     CHECK_NULL_RETURN(pattern, -1);
87     auto itemFrameNode = host->FindChildByPositionWithoutChildTransform(x, y);
88     if (itemFrameNode) {
89         RefPtr<GridItemLayoutProperty> itemLayoutProperty = itemFrameNode->GetLayoutProperty<GridItemLayoutProperty>();
90         CHECK_NULL_RETURN(itemLayoutProperty, 0);
91         auto mainIndex = itemLayoutProperty->GetMainIndex().value_or(-1);
92         auto crossIndex = itemLayoutProperty->GetCrossIndex().value_or(-1);
93         return mainIndex * pattern->GetCrossCount() + crossIndex;
94     }
95 
96     // on virtual grid item dragged in this grid
97     if (pattern->GetGridLayoutInfo().currentRect_.IsInRegion(PointF(x, y))) {
98         return pattern->GetOriginalIndex();
99     }
100 
101     // in grid, but not on any grid item
102     return pattern->GetChildrenCount();
103 }
104 
GetFrameNodeChildSize()105 int GridEventHub::GetFrameNodeChildSize()
106 {
107     auto host = GetFrameNode();
108     CHECK_NULL_RETURN(host, 0);
109     auto pattern = host->GetPattern<GridPattern>();
110     CHECK_NULL_RETURN(pattern, 0);
111     return pattern->GetChildrenCount();
112 }
113 
GetGridItemIndex(const RefPtr<FrameNode> & frameNode)114 int32_t GridEventHub::GetGridItemIndex(const RefPtr<FrameNode>& frameNode)
115 {
116     CHECK_NULL_RETURN(frameNode, 0);
117     auto gridFrameNode = GetFrameNode();
118     CHECK_NULL_RETURN(gridFrameNode, 0);
119     auto gridPattern = gridFrameNode->GetPattern<GridPattern>();
120     CHECK_NULL_RETURN(gridPattern, 0);
121     RefPtr<GridItemPattern> itemPattern = frameNode->GetPattern<GridItemPattern>();
122     CHECK_NULL_RETURN(itemPattern, 0);
123     auto itemProperty = frameNode->GetLayoutProperty<GridItemLayoutProperty>();
124     CHECK_NULL_RETURN(itemProperty, 0);
125 
126     auto gridLayoutInfo = gridPattern->GetGridLayoutInfo();
127     auto mainIndex = itemProperty->GetMainIndex().value_or(-1);
128     auto crossIndex = itemProperty->GetCrossIndex().value_or(-1);
129     auto crossIndexIterator = gridLayoutInfo.gridMatrix_.find(mainIndex);
130     if (crossIndexIterator != gridLayoutInfo.gridMatrix_.end()) {
131         auto crossIndexMap = crossIndexIterator->second;
132 
133         auto indexIterator = crossIndexMap.find(crossIndex);
134         if (indexIterator != crossIndexMap.end()) {
135             return indexIterator->second;
136         }
137     }
138 
139     return 0;
140 }
141 
GetEditable() const142 bool GridEventHub::GetEditable() const
143 {
144     auto host = GetFrameNode();
145     CHECK_NULL_RETURN(host, false);
146     auto layoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
147     CHECK_NULL_RETURN(layoutProperty, false);
148     return layoutProperty->GetEditable().value_or(false);
149 }
150 
HandleOnItemDragStart(const GestureEvent & info)151 void GridEventHub::HandleOnItemDragStart(const GestureEvent& info)
152 {
153     if (!GetEditable()) {
154         return;
155     }
156 
157     auto host = GetFrameNode();
158     CHECK_NULL_VOID(host);
159     auto pipeline = host->GetContext();
160     CHECK_NULL_VOID(pipeline);
161 
162     auto globalX = static_cast<float>(info.GetGlobalPoint().GetX());
163     auto globalY = static_cast<float>(info.GetGlobalPoint().GetY());
164 
165     auto gridItem = host->FindChildByPositionWithoutChildTransform(globalX, globalY);
166     CHECK_NULL_VOID(gridItem);
167     draggedIndex_ = GetGridItemIndex(gridItem);
168 
169     OHOS::Ace::ItemDragInfo itemDragInfo;
170     itemDragInfo.SetX(globalX);
171     itemDragInfo.SetY(globalY);
172     auto customNode = FireOnItemDragStart(itemDragInfo, draggedIndex_);
173     CHECK_NULL_VOID(customNode);
174     auto dragDropManager = pipeline->GetDragDropManager();
175     CHECK_NULL_VOID(dragDropManager);
176     dragDropManager->SetDraggingPointer(info.GetPointerId());
177     dragDropManager->SetDraggingPressedState(true);
178 #if defined(PIXEL_MAP_SUPPORTED)
179     auto callback = [id = Container::CurrentId(), pipeline, info, host, gridItem, weak = WeakClaim(this)](
180                         std::shared_ptr<Media::PixelMap> mediaPixelMap, int32_t /*arg*/,
181                         const std::function<void()>& /*unused*/) {
182         ContainerScope scope(id);
183         if (!mediaPixelMap) {
184             TAG_LOGE(AceLogTag::ACE_DRAG, "gridItem drag start failed, custom component screenshot is empty.");
185             return;
186         }
187         auto pixelMap = PixelMap::CreatePixelMap(reinterpret_cast<void*>(&mediaPixelMap));
188         CHECK_NULL_VOID(pixelMap);
189         auto taskScheduler = pipeline->GetTaskExecutor();
190         CHECK_NULL_VOID(taskScheduler);
191         taskScheduler->PostTask(
192             [weak, pipeline, info, pixelMap, host, gridItem]() {
193                 auto eventHub = weak.Upgrade();
194                 CHECK_NULL_VOID(eventHub);
195                 auto manager = pipeline->GetDragDropManager();
196                 CHECK_NULL_VOID(manager);
197                 eventHub->dragDropProxy_ = manager->CreateAndShowDragWindow(pixelMap, info);
198                 CHECK_NULL_VOID(eventHub->dragDropProxy_);
199                 eventHub->dragDropProxy_->OnItemDragStart(info, host);
200                 gridItem->GetLayoutProperty()->UpdateVisibility(VisibleType::INVISIBLE);
201                 eventHub->draggingItem_ = gridItem;
202                 if (!manager->IsDraggingPressed(info.GetPointerId())) {
203                     eventHub->HandleOnItemDragEnd(info);
204                 }
205             },
206             TaskExecutor::TaskType::UI, "ArkUIGridItemDragStart");
207     };
208     SnapshotParam param;
209     param.delay = CREATE_PIXELMAP_TIME;
210     NG::ComponentSnapshot::Create(customNode, std::move(callback), true, param);
211 #else
212     auto manager = pipeline->GetDragDropManager();
213     CHECK_NULL_VOID(manager);
214     dragDropProxy_ = manager->CreateAndShowDragWindow(customNode, info);
215     CHECK_NULL_VOID(dragDropProxy_);
216     dragDropProxy_->OnItemDragStart(info, host);
217     gridItem->GetLayoutProperty()->UpdateVisibility(VisibleType::INVISIBLE);
218     draggingItem_ = gridItem;
219     if (!manager->IsDraggingPressed(info.GetPointerId())) {
220         HandleOnItemDragEnd(info);
221     }
222 #endif
223 }
224 
HandleOnItemDragUpdate(const GestureEvent & info)225 void GridEventHub::HandleOnItemDragUpdate(const GestureEvent& info)
226 {
227     if (!GetEditable()) {
228         return;
229     }
230 
231     CHECK_NULL_VOID(dragDropProxy_);
232     dragDropProxy_->OnItemDragMove(info, draggedIndex_, DragType::GRID);
233 }
234 
HandleOnItemDragEnd(const GestureEvent & info)235 void GridEventHub::HandleOnItemDragEnd(const GestureEvent& info)
236 {
237     CHECK_NULL_VOID(dragDropProxy_);
238     if (GetEditable()) {
239         dragDropProxy_->OnItemDragEnd(info, draggedIndex_, DragType::GRID);
240     } else {
241         dragDropProxy_->onItemDragCancel();
242     }
243     dragDropProxy_->DestroyDragWindow();
244     dragDropProxy_ = nullptr;
245     draggedIndex_ = 0;
246     if (draggingItem_) {
247         draggingItem_->GetLayoutProperty()->UpdateVisibility(VisibleType::VISIBLE);
248         draggingItem_ = nullptr;
249     }
250 
251     auto host = GetFrameNode();
252     CHECK_NULL_VOID(host);
253     auto pattern = AceType::DynamicCast<GridPattern>(host->GetPattern());
254     CHECK_NULL_VOID(pattern);
255     pattern->ClearDragState();
256 }
257 
HandleOnItemDragCancel()258 void GridEventHub::HandleOnItemDragCancel()
259 {
260     CHECK_NULL_VOID(dragDropProxy_);
261     dragDropProxy_->onItemDragCancel();
262     dragDropProxy_->DestroyDragWindow();
263     dragDropProxy_ = nullptr;
264     draggedIndex_ = 0;
265     if (draggingItem_) {
266         draggingItem_->GetLayoutProperty()->UpdateVisibility(VisibleType::VISIBLE);
267         draggingItem_ = nullptr;
268     }
269 
270     auto host = GetFrameNode();
271     CHECK_NULL_VOID(host);
272     auto pattern = AceType::DynamicCast<GridPattern>(host->GetPattern());
273     CHECK_NULL_VOID(pattern);
274     pattern->ClearDragState();
275 }
276 
FireOnItemDragEnter(const ItemDragInfo & dragInfo)277 void GridEventHub::FireOnItemDragEnter(const ItemDragInfo& dragInfo)
278 {
279     if (onItemDragEnter_) {
280         onItemDragEnter_(dragInfo);
281     }
282 }
283 
FireOnItemDragLeave(const ItemDragInfo & dragInfo,int32_t itemIndex)284 void GridEventHub::FireOnItemDragLeave(const ItemDragInfo& dragInfo, int32_t itemIndex)
285 {
286     if (itemIndex == -1) {
287         auto host = GetFrameNode();
288         CHECK_NULL_VOID(host);
289         auto pattern = AceType::DynamicCast<GridPattern>(host->GetPattern());
290         CHECK_NULL_VOID(pattern);
291         auto insertIndex = pattern->GetChildrenCount();
292         MoveItems(itemIndex, insertIndex);
293     }
294 
295     if (onItemDragLeave_) {
296         onItemDragLeave_(dragInfo, itemIndex);
297     }
298 }
299 
FireOnItemDrop(const ItemDragInfo & dragInfo,int32_t itemIndex,int32_t insertIndex,bool isSuccess)300 bool GridEventHub::FireOnItemDrop(const ItemDragInfo& dragInfo, int32_t itemIndex, int32_t insertIndex, bool isSuccess)
301 {
302     TAG_LOGI(AceLogTag::ACE_GRID, "itemIndex:%{public}d, insertIndex:%{public}d", itemIndex, insertIndex);
303     auto host = GetFrameNode();
304     CHECK_NULL_RETURN(host, false);
305     auto pattern = AceType::DynamicCast<GridPattern>(host->GetPattern());
306     CHECK_NULL_RETURN(pattern, false);
307     if (pattern->SupportAnimation()) {
308         insertIndex = (itemIndex == -1 || insertIndex == -1) ? insertIndex : pattern->GetOriginalIndex();
309         pattern->ClearDragState();
310     }
311 
312     if (onItemDrop_) {
313         onItemDrop_(dragInfo, itemIndex, insertIndex, isSuccess);
314         host->ChildrenUpdatedFrom(0);
315         return true;
316     }
317     host->ChildrenUpdatedFrom(0);
318     return false;
319 }
320 
FireOnItemDragMove(const ItemDragInfo & dragInfo,int32_t itemIndex,int32_t insertIndex) const321 void GridEventHub::FireOnItemDragMove(const ItemDragInfo& dragInfo, int32_t itemIndex, int32_t insertIndex) const
322 {
323     MoveItems(itemIndex, insertIndex);
324 
325     if (onItemDragMove_) {
326         auto host = GetFrameNode();
327         CHECK_NULL_VOID(host);
328         auto pattern = AceType::DynamicCast<GridPattern>(host->GetPattern());
329         CHECK_NULL_VOID(pattern);
330         if (pattern->SupportAnimation()) {
331             insertIndex = (itemIndex == -1 || insertIndex == -1) ? insertIndex : pattern->GetOriginalIndex();
332         }
333         onItemDragMove_(dragInfo, itemIndex, insertIndex);
334     }
335 }
336 
MoveItems(int32_t itemIndex,int32_t insertIndex) const337 void GridEventHub::MoveItems(int32_t itemIndex, int32_t insertIndex) const
338 {
339     auto host = GetFrameNode();
340     CHECK_NULL_VOID(host);
341     auto pattern = AceType::DynamicCast<GridPattern>(host->GetPattern());
342     CHECK_NULL_VOID(pattern);
343     if (!pattern->SupportAnimation()) {
344         return;
345     }
346     constexpr float ANIMATION_CURVE_VELOCITY = 0.0f;    // The move animation spring curve velocity is 0.0
347     constexpr float ANIMATION_CURVE_MASS = 1.0f;        // The move animation spring curve mass is 1.0
348     constexpr float ANIMATION_CURVE_STIFFNESS = 400.0f; // The move animation spring curve stiffness is 110.0
349     constexpr float ANIMATION_CURVE_DAMPING = 38.0f;    // The move animation spring curve damping is 17.0
350     AnimationOption option;
351     constexpr int32_t duration = 400;
352     option.SetDuration(duration);
353     auto curve = MakeRefPtr<SpringCurve>(
354         ANIMATION_CURVE_VELOCITY, ANIMATION_CURVE_MASS, ANIMATION_CURVE_STIFFNESS, ANIMATION_CURVE_DAMPING);
355     option.SetCurve(curve);
356     AnimationUtils::Animate(
357         option, [pattern, itemIndex, insertIndex]() { pattern->MoveItems(itemIndex, insertIndex); }, nullptr);
358 }
359 } // namespace OHOS::Ace::NG