[NUI][TCSACR-487] DragAndDrop: Add drag and drop APIs
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / DragAndDrop / DragAndDrop.cs
1 /*
2  * Copyright(c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 using System;
18 using System.ComponentModel;
19 using System.Runtime.InteropServices;
20 using System.Collections.Generic;
21 using System.Diagnostics.CodeAnalysis;
22 using Tizen.NUI.BaseComponents;
23
24 namespace Tizen.NUI
25 {
26     /// <summary>
27     /// DragAndDrop controls the drag object and data.
28     /// </summary>
29     /// <since_tizen> 10 </since_tizen>
30     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000: Dispose objects before losing scope", Justification = "It does not have ownership.")]
31     public class DragAndDrop : BaseHandle
32     {
33         public delegate void SourceEventHandler(DragSourceEventType sourceEventType);
34         private delegate void InternalSourceEventHandler(int sourceEventType);
35         public delegate void DragAndDropEventHandler(View targetView, DragEvent dragEvent);
36         private delegate void InternalDragAndDropEventHandler(global::System.IntPtr dragEvent);
37         private InternalSourceEventHandler sourceEventCb;
38         private Dictionary<View, InternalDragAndDropEventHandler> targetEventDictionary = new Dictionary<View, InternalDragAndDropEventHandler>();
39         private View mShadowView;
40         private Window mDragWindow;
41         private int shadowWidth = 100;
42         private int shadowHeight = 100;
43
44         private DragAndDrop() : this(Interop.DragAndDrop.New(), true)
45         {
46             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
47         }
48
49         private DragAndDrop(global::System.IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
50         {
51
52         }
53
54         /// <summary>
55         /// Gets the singleton instance of DragAndDrop.
56         /// </summary>
57         /// <since_tizen> 10 </since_tizen>
58         public static DragAndDrop Instance { get; } = new DragAndDrop();
59
60         /// <summary>
61         /// Starts drag and drop.
62         /// </summary>
63         /// <param name="sourceView">The soruce view</param>
64         /// <param name="shadowView">The shadow view for drag object</param>
65         /// <param name="dragData">The data to send</param>
66         /// <param name="callback">The source event callback</param>
67         /// <since_tizen> 10 </since_tizen>
68         public void StartDragAndDrop(View sourceView, View shadowView, DragData dragData, SourceEventHandler callback)
69         {
70             if (Window.IsSupportedMultiWindow() == false)
71             {
72                 throw new NotSupportedException("This device does not support surfaceless_context. So Window cannot be created.");
73             }
74
75             if (null == shadowView)
76             {
77                 throw new ArgumentNullException(nameof(shadowView));
78             }
79
80             shadowWidth = (int)shadowView.Size.Width;
81             shadowHeight = (int)shadowView.Size.Height;
82
83             // Prevents shadowView size from being smaller than 100 pixel
84             if (shadowView.Size.Width < 100)
85             {
86                 shadowWidth = 100;
87             }
88
89             if (shadowView.Size.Height < 100)
90             {
91                 shadowHeight = 100;
92             }
93
94             if (null == mDragWindow)
95             {
96                 mDragWindow = new Window("DragWindow", new Rectangle(-shadowWidth, -shadowHeight, shadowWidth, shadowHeight), true)
97                 {
98                     BackgroundColor = Color.Transparent,
99                 };
100             }
101
102             //Initialize Drag Window Position and Size based on Shadow View Position and Size
103             mDragWindow.SetPosition(new Position2D((int)shadowView.Position.X, (int)shadowView.Position.Y));
104             mDragWindow.SetWindowSize(new Size(shadowWidth, shadowHeight));
105
106             //Make Shadow View Transparent
107             shadowView.SetOpacity(0.9f);
108
109             //Make Position 0, 0 for Moving into Drag Window
110             shadowView.Position = new Position(0, 0);
111
112             if (mShadowView)
113             {
114                 mShadowView.Hide();
115                 mDragWindow.Remove(mShadowView);
116                 mShadowView.Dispose();
117             }
118
119             mShadowView = shadowView;
120             mDragWindow.Add(mShadowView);
121
122             //Update Window Directly
123             mDragWindow.VisibiltyChangedSignalEmit(true);
124             mDragWindow.RenderOnce();
125
126             sourceEventCb = (sourceEventType) =>
127             {
128                 if ((DragSourceEventType)sourceEventType == DragSourceEventType.Finish)
129                 {
130                     if (mShadowView)
131                     {
132                         mShadowView.Hide();
133                         mDragWindow.Remove(mShadowView);
134                         mShadowView.Dispose();
135                     }
136
137                     //Update Window Directly
138                     mDragWindow.VisibiltyChangedSignalEmit(true);
139                     mDragWindow.RenderOnce();
140                 }
141
142                 callback((DragSourceEventType)sourceEventType);
143             };
144
145             if (!Interop.DragAndDrop.StartDragAndDrop(SwigCPtr, View.getCPtr(sourceView), Window.getCPtr(mDragWindow), dragData.MimeType, dragData.Data,
146                                                       new global::System.Runtime.InteropServices.HandleRef(this, Marshal.GetFunctionPointerForDelegate<Delegate>(sourceEventCb))))
147             {
148                 throw new InvalidOperationException("Fail to StartDragAndDrop");
149             }
150
151             mDragWindow.Show();
152         }
153
154         /// <summary>
155         /// Adds listener for drop targets
156         /// </summary>
157         /// <param name="targetView">The target view</param>
158         /// <param name="callback">The callback function to get drag event when the drag source enters, moves, leaves and drops on the drop target</param>
159         /// <since_tizen> 10 </since_tizen>
160         public void AddListener(View targetView, DragAndDropEventHandler callback)
161         {
162             InternalDragAndDropEventHandler cb = (dragEvent) =>
163             {
164                 DragType type = (DragType)Interop.DragAndDrop.GetAction(dragEvent);
165                 DragEvent ev = new DragEvent();
166                 global::System.IntPtr cPtr = Interop.DragAndDrop.GetPosition(dragEvent);
167                 ev.Position = (cPtr == global::System.IntPtr.Zero) ? null : new Position(cPtr, false);
168
169                 if (type == DragType.Enter)
170                 {
171                     ev.DragType = type;
172                     callback(targetView, ev);
173                 }
174                 else if (type == DragType.Leave)
175                 {
176                     ev.DragType = type;
177                     callback(targetView, ev);
178                 }
179                 else if (type == DragType.Move)
180                 {
181                     ev.DragType = type;
182                     callback(targetView, ev);
183                 }
184                 else if (type == DragType.Drop)
185                 {
186                     ev.DragType = type;
187                     ev.MimeType = Interop.DragAndDrop.GetMimeType(dragEvent);
188                     ev.Data = Interop.DragAndDrop.GetData(dragEvent);
189                     callback(targetView, ev);
190                 }
191             };
192
193             targetEventDictionary.Add(targetView, cb);
194
195             if (!Interop.DragAndDrop.AddListener(SwigCPtr, View.getCPtr(targetView),
196                                                  new global::System.Runtime.InteropServices.HandleRef(this, Marshal.GetFunctionPointerForDelegate<Delegate>(cb))))
197             {
198                  throw new InvalidOperationException("Fail to AddListener");
199             }
200         }
201
202         /// <summary>
203         /// Removes listener for drop targets
204         /// </summary>
205         /// <param name="targetView">The target view</param>
206         /// <param name="callback">The callback function to remove</param>
207         /// <since_tizen> 10 </since_tizen>
208         public void RemoveListener(View targetView, DragAndDropEventHandler callback)
209         {
210             if (!targetEventDictionary.ContainsKey(targetView))
211             {
212                  throw new InvalidOperationException("Fail to RemoveListener");
213             }
214
215             InternalDragAndDropEventHandler cb = targetEventDictionary[targetView];
216             targetEventDictionary.Remove(targetView);
217             if (!Interop.DragAndDrop.RemoveListener(SwigCPtr, View.getCPtr(targetView),
218                                                     new global::System.Runtime.InteropServices.HandleRef(this, Marshal.GetFunctionPointerForDelegate<Delegate>(cb))))
219             {
220                  throw new InvalidOperationException("Fail to RemoveListener");
221             }
222         }
223     }
224 }