Merge remote-tracking branch 'origin/master' into tizen
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / DragAndDrop / DragAndDrop.cs
1 /*
2  * Copyright(c) 2023 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         [EditorBrowsable(EditorBrowsableState.Never)]
37         public delegate void DragAndDropWindowEventHandler(Window targetWindow, DragEvent dragEvent);
38         private delegate void InternalDragAndDropEventHandler(global::System.IntPtr dragEvent);
39         private InternalSourceEventHandler sourceEventCb;
40         private Dictionary<View, InternalDragAndDropEventHandler> targetEventDictionary = new Dictionary<View, InternalDragAndDropEventHandler>();
41         private Dictionary<Window, InternalDragAndDropEventHandler> targetWindowEventDictionary = new Dictionary<Window, InternalDragAndDropEventHandler>();
42         private View mShadowView;
43         private Window mDragWindow;
44         private int shadowWidth;
45         private int shadowHeight;
46         private int dragWindowOffsetX = 0;
47         private int dragWindowOffsetY = 0;
48
49         private bool initDrag = false;
50
51         private const int MinDragWindowWidth = 100;
52         private const int MinDragWindowHeight = 100;
53
54         private DragAndDrop() : this(Interop.DragAndDrop.New(), true)
55         {
56             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
57         }
58
59         private DragAndDrop(global::System.IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
60         {
61
62         }
63
64         private void ReleaseDragWindow()
65         {
66             if (mDragWindow)
67             {                        
68                 if (mShadowView)
69                 {
70                     //Application has Shadow View ownership, so DnD doesn't dispose Shadow View
71                     mDragWindow.Remove(mShadowView);
72                     mShadowView = null;
73                 }
74             
75                 mDragWindow.Dispose();
76                 mDragWindow = null;                      
77             }         
78         }
79
80         /// <summary>
81         /// Gets the singleton instance of DragAndDrop.
82         /// </summary>
83         /// <since_tizen> 10 </since_tizen>
84         public static DragAndDrop Instance { get; } = new DragAndDrop();
85
86         /// <summary>
87         /// Starts drag and drop.
88         /// </summary>
89         /// <param name="sourceView">The soruce view</param>
90         /// <param name="shadowView">The shadow view for drag object</param>
91         /// <param name="dragData">The data to send</param>
92         /// <param name="callback">The source event callback</param>
93         /// <since_tizen> 10 </since_tizen>
94         public void StartDragAndDrop(View sourceView, View shadowView, DragData dragData, SourceEventHandler callback)
95         {            
96             if (initDrag)
97             {
98                  Tizen.Log.Fatal("NUI", "Start Drag And Drop Initializing...");
99                  return;
100             }
101             initDrag = true;
102
103             if (Window.IsSupportedMultiWindow() == false)
104             {
105                 throw new NotSupportedException("This device does not support surfaceless_context. So Window cannot be created.");
106             }
107
108             if (null == shadowView)
109             {
110                 throw new ArgumentNullException(nameof(shadowView));
111             }
112
113             ReleaseDragWindow();
114
115             shadowWidth = (int)shadowView.Size.Width;
116             shadowHeight = (int)shadowView.Size.Height;
117
118             if (shadowView.Size.Width < MinDragWindowWidth)
119             {
120                 shadowWidth = MinDragWindowWidth;
121             }
122
123             if (shadowView.Size.Height < MinDragWindowHeight)
124             {
125                 shadowHeight = MinDragWindowHeight;
126             }
127
128             mDragWindow = new Window("DragWindow", new Rectangle(dragWindowOffsetX, dragWindowOffsetY, shadowWidth, shadowHeight), true)
129             {
130                 BackgroundColor = Color.Transparent,
131             };
132
133             if (mDragWindow)
134             {
135                 //Set Window Orientation Available
136                 List<Window.WindowOrientation> list = new List<Window.WindowOrientation>();
137                 list.Add(Window.WindowOrientation.Landscape);
138                 list.Add(Window.WindowOrientation.LandscapeInverse);
139                 list.Add(Window.WindowOrientation.NoOrientationPreference);
140                 list.Add(Window.WindowOrientation.Portrait);
141                 list.Add(Window.WindowOrientation.PortraitInverse);
142                 mDragWindow.SetAvailableOrientations(list);
143
144                 //Initialize Drag Window Size based on Shadow View Size,
145                 //Don't set Drag Window Posiiton, Window Server sets Position Internally
146                 mDragWindow.SetWindowSize(new Size(shadowWidth, shadowHeight));
147
148                 //Make Position 0, 0 for Moving into Drag Window
149                 shadowView.Position = new Position(0, 0);
150             
151                 mShadowView = shadowView;
152                 mDragWindow.Add(mShadowView);
153            
154                 sourceEventCb = (sourceEventType) =>
155                 {   
156                     if ((DragSourceEventType)sourceEventType != DragSourceEventType.Start)
157                     {     
158                         Tizen.Log.Fatal("NUI", "DnD Source Event is Called");  
159                         ReleaseDragWindow();                
160                     }
161
162                     callback((DragSourceEventType)sourceEventType);
163                 };
164
165                 //Show Drag Window before StartDragAndDrop
166                 mDragWindow.Show();
167
168                 if (!Interop.DragAndDrop.StartDragAndDrop(SwigCPtr, View.getCPtr(sourceView), Window.getCPtr(mDragWindow), dragData.MimeType, dragData.Data,
169                                                         new global::System.Runtime.InteropServices.HandleRef(this, Marshal.GetFunctionPointerForDelegate<Delegate>(sourceEventCb))))
170                 {
171                     throw new InvalidOperationException("Fail to StartDragAndDrop");
172                 }
173
174             }         
175             
176             initDrag = false;
177         }
178
179         /// <summary>
180         /// Adds listener for drop targets
181         /// </summary>
182         /// <param name="targetView">The target view</param>
183         /// <param name="callback">The callback function to get drag event when the drag source enters, moves, leaves and drops on the drop target</param>
184         /// <since_tizen> 10 </since_tizen>
185         public void AddListener(View targetView, DragAndDropEventHandler callback)
186         {
187             InternalDragAndDropEventHandler cb = (dragEvent) =>
188             {
189                 DragType type = (DragType)Interop.DragAndDrop.GetAction(dragEvent);
190                 DragEvent ev = new DragEvent();
191                 global::System.IntPtr cPtr = Interop.DragAndDrop.GetPosition(dragEvent);
192                 ev.Position = (cPtr == global::System.IntPtr.Zero) ? null : new Position(cPtr, true);
193
194                 if (type == DragType.Enter)
195                 {
196                     ev.DragType = type;
197                     ev.MimeType = Interop.DragAndDrop.GetMimeType(dragEvent);
198                     callback(targetView, ev);
199                 }
200                 else if (type == DragType.Leave)
201                 {
202                     ev.DragType = type;
203                     callback(targetView, ev);
204                 }
205                 else if (type == DragType.Move)
206                 {
207                     ev.DragType = type;
208                     ev.MimeType = Interop.DragAndDrop.GetMimeType(dragEvent);
209                     callback(targetView, ev);
210                 }
211                 else if (type == DragType.Drop)
212                 {
213                     ev.DragType = type;
214                     ev.MimeType = Interop.DragAndDrop.GetMimeType(dragEvent);
215                     ev.Data = Interop.DragAndDrop.GetData(dragEvent);
216                     callback(targetView, ev);
217                 }
218             };
219
220             targetEventDictionary.Add(targetView, cb);
221
222             if (!Interop.DragAndDrop.AddListener(SwigCPtr, View.getCPtr(targetView),
223                                                  new global::System.Runtime.InteropServices.HandleRef(this, Marshal.GetFunctionPointerForDelegate<Delegate>(cb))))
224             {
225                  throw new InvalidOperationException("Fail to AddListener for View");
226             }
227         }
228
229         /// <summary>
230         /// Removes listener for drop targets
231         /// </summary>
232         /// <param name="targetView">The target view</param>
233         /// <param name="callback">The callback function to remove</param>
234         /// <since_tizen> 10 </since_tizen>
235         public void RemoveListener(View targetView, DragAndDropEventHandler callback)
236         {
237             if (!targetEventDictionary.ContainsKey(targetView))
238             {
239                  throw new InvalidOperationException("Fail to RemoveListener for View");
240             }
241
242             InternalDragAndDropEventHandler cb = targetEventDictionary[targetView];
243             targetEventDictionary.Remove(targetView);
244             if (!Interop.DragAndDrop.RemoveListener(SwigCPtr, View.getCPtr(targetView),
245                                                     new global::System.Runtime.InteropServices.HandleRef(this, Marshal.GetFunctionPointerForDelegate<Delegate>(cb))))
246             {
247                  throw new InvalidOperationException("Fail to RemoveListener for View");
248             }
249         }
250
251         /// <summary>
252         /// Adds listener for drop targets
253         /// </summary>
254         /// <param name="targetWindow">The target Window</param>
255         /// <param name="callback">The callback function to get drag event when the drag source enters, moves, leaves and drops on the drop target</param>
256         [EditorBrowsable(EditorBrowsableState.Never)]
257         public void AddListener(Window targetWindow, DragAndDropWindowEventHandler callback)
258         {
259             InternalDragAndDropEventHandler cb = (dragEvent) =>
260             {
261                 DragType type = (DragType)Interop.DragAndDrop.GetAction(dragEvent);
262                 DragEvent ev = new DragEvent();
263                 global::System.IntPtr cPtr = Interop.DragAndDrop.GetPosition(dragEvent);
264                 ev.Position = (cPtr == global::System.IntPtr.Zero) ? null : new Position(cPtr, false);
265
266                 if (type == DragType.Enter)
267                 {
268                     ev.DragType = type;
269                     callback(targetWindow, ev);
270                 }
271                 else if (type == DragType.Leave)
272                 {
273                     ev.DragType = type;
274                     callback(targetWindow, ev);
275                 }
276                 else if (type == DragType.Move)
277                 {
278                     ev.DragType = type;
279                     callback(targetWindow, ev);
280                 }
281                 else if (type == DragType.Drop)
282                 {
283                     ev.DragType = type;
284                     ev.MimeType = Interop.DragAndDrop.GetMimeType(dragEvent);
285                     ev.Data = Interop.DragAndDrop.GetData(dragEvent);
286                     callback(targetWindow, ev);
287                 }
288             };
289
290             targetWindowEventDictionary.Add(targetWindow, cb);
291
292             if (!Interop.DragAndDrop.WindowAddListener(SwigCPtr, Window.getCPtr(targetWindow),
293                                                        new global::System.Runtime.InteropServices.HandleRef(this, Marshal.GetFunctionPointerForDelegate<Delegate>(cb))))
294             {
295                  throw new InvalidOperationException("Fail to AddListener for Window");
296             }
297         }
298
299         /// <summary>
300         /// Removes listener for drop targets
301         /// </summary>
302         /// <param name="targetWindow">The target window</param>
303         /// <param name="callback">The callback function to remove</param>
304         [EditorBrowsable(EditorBrowsableState.Never)]
305         public void RemoveListener(Window targetWindow, DragAndDropWindowEventHandler callback)
306         {
307             if (!targetWindowEventDictionary.ContainsKey(targetWindow))
308             {
309                  throw new InvalidOperationException("Fail to RemoveListener for Window");
310             }
311
312             InternalDragAndDropEventHandler cb = targetWindowEventDictionary[targetWindow];
313             targetWindowEventDictionary.Remove(targetWindow);
314             if (!Interop.DragAndDrop.WindowRemoveListener(SwigCPtr, Window.getCPtr(targetWindow),
315                                                           new global::System.Runtime.InteropServices.HandleRef(this, Marshal.GetFunctionPointerForDelegate<Delegate>(cb))))
316             {
317                  throw new InvalidOperationException("Fail to RemoveListener for Window");
318             }
319         }
320
321         /// <summary>
322         /// Sets drag window offset
323         /// </summary>
324         /// <param name="x">The x direction offset</param>
325         /// <param name="y">The y direction offset</param>
326         [EditorBrowsable(EditorBrowsableState.Never)]
327         public void SetDragWindowOffset(int x, int y)
328         {
329             dragWindowOffsetX = x;
330             dragWindowOffsetY = y;
331         }
332     }
333 }