[NUI] Fix View.DisConnectFromSignals() to return when the native object has no body
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / Common / BaseHandle.cs
1 /*
2  * Copyright(c) 2021 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.CompilerServices;
20 using Tizen.NUI.Binding;
21
22 namespace Tizen.NUI
23 {
24     /// <summary>
25     /// BaseHandle is a handle to an internal Dali resource.
26     /// </summary>
27     /// <since_tizen> 3 </since_tizen>
28     public class BaseHandle : Element, global::System.IDisposable
29     {
30         /// <summary>
31         /// swigCMemOwn
32         /// </summary>
33         /// <since_tizen> 3 </since_tizen>
34         [Obsolete("Deprecated in API9, Will be removed in API11, Please use SwigCMemOwn")]
35         [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "<Pending>")]
36         protected bool swigCMemOwn;
37
38         /// <summary>
39         /// A flag to check if it is already disposed.
40         /// </summary>
41         /// <since_tizen> 3 </since_tizen>
42         [Obsolete("Deprecated in API9, Will be removed in API11, Please use Disposed")]
43         [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "<Pending>")]
44         protected bool disposed = false;
45
46         private global::System.Runtime.InteropServices.HandleRef swigCPtr;
47         private global::System.Runtime.InteropServices.HandleRef swigCPtrCopy;
48         private bool registerMe;
49
50         //A Flag to check who called Dispose(). (By User or DisposeQueue)
51         private bool isDisposeQueued = false;
52
53 #if NUI_DEBUG_ON
54         private static int debuggingCount = 0;
55 #endif
56
57         /// <summary>
58         /// Create an instance of BaseHandle.
59         /// </summary>
60         /// <since_tizen> 3 </since_tizen>
61         public BaseHandle() : this(Interop.BaseHandle.NewBaseHandle())
62         {
63             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
64         }
65
66         /// <summary>
67         /// Create an instance of BaseHandle.
68         /// </summary>
69         /// <param name="handle">The BaseHandle instance.</param>
70         /// <since_tizen> 3 </since_tizen>
71         public BaseHandle(BaseHandle handle) : this(Interop.BaseHandle.NewBaseHandle(BaseHandle.getCPtr(handle)))
72         {
73             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
74         }
75
76         internal BaseHandle(global::System.IntPtr cPtr, bool cMemoryOwn)
77         {
78             //to catch derived classes dali native exceptions
79             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
80
81             NUILog.Debug($"[Dispose] BaseHandle.contructor with cMemeryOwn:{cMemoryOwn} START");
82
83             registerMe = swigCMemOwn = cMemoryOwn;
84             swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
85             // using copy constructor to create another native handle so Registry.Unregister works fine.
86             swigCPtrCopy = new global::System.Runtime.InteropServices.HandleRef(this, Interop.BaseHandle.NewBaseHandle(swigCPtr));
87             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
88
89             if (registerMe)
90             {
91                 // Register this instance of BaseHandle in the registry.
92                 Registry.Register(this);
93             }
94
95 #if NUI_DEBUG_ON
96             NUILog.Debug($"[Dispose] type:{GetType()} copyNativeHandle:{swigCPtrCopy.Handle.ToString("X8")}");
97
98             debuggingCount++;
99             if (this is BaseComponents.View view)
100             {
101                 NUILog.Debug($"[Dispose] ID:{view.ID} Name:{view.Name} debuggingCount:{debuggingCount}");
102             }
103             NUILog.Debug($"[Dispose] BaseHandle.contructor with cMemeryOwn END");
104             NUILog.Debug($"=============================");
105 #endif
106         }
107
108         internal BaseHandle(global::System.IntPtr cPtr)
109         {
110             NUILog.Debug($"[Dispose] BaseHandle.contructor START");
111
112             registerMe = swigCMemOwn = true;
113
114             swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
115
116             // using copy constructor to create another native handle so Registry.Unregister works fine.
117             swigCPtrCopy = new global::System.Runtime.InteropServices.HandleRef(this, Interop.BaseHandle.NewBaseHandle(SwigCPtr));
118             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
119
120             if (registerMe)
121             {
122                 // Register this instance of BaseHandle in the registry.
123                 Registry.Register(this);
124             }
125
126 #if NUI_DEBUG_ON
127             NUILog.Debug($"[Dispose] type:{GetType()} copyNativeHandle:{swigCPtrCopy.Handle.ToString("X8")}");
128
129             debuggingCount++;
130             if (this is BaseComponents.View view)
131             {
132                 NUILog.Debug($"[Dispose] ID:{view.ID} Name:{view.Name} debuggingCount:{debuggingCount}");
133             }
134             NUILog.Debug($"[Dispose] BaseHandle.contructor END");
135             NUILog.Debug($"=============================");
136 #endif
137
138         }
139
140         /// <summary>
141         /// Dispose.
142         /// </summary>
143         /// <since_tizen> 3 </since_tizen>
144         // following this guide: https://docs.microsoft.com/ko-kr/dotnet/fundamentals/code-analysis/quality-rules/ca1063?view=vs-2019 (CA1063)
145         ~BaseHandle() => Dispose(false);
146
147         /// <summary>
148         /// Event when a property is set.
149         /// </summary>
150         /// <since_tizen> 5 </since_tizen>
151         /// <seealso cref="BindableObject.PropertyChanged"/>
152         [Obsolete("Deprecated in API9, Will be removed in API11, Please use BindableObject.PropertyChanged instead!")]
153         public event PropertyChangedEventHandler PropertySet;
154
155         internal global::System.Runtime.InteropServices.HandleRef GetBaseHandleCPtrHandleRef
156         {
157             get
158             {
159                 return swigCPtrCopy;
160             }
161         }
162
163         /// <summary>
164         /// Returns the bool value true to indicate that an operand is true and returns false otherwise.
165         /// </summary>
166         /// <since_tizen> 3 </since_tizen>
167         public static bool operator true(BaseHandle handle)
168         {
169             // if the C# object is null, return false
170             if (BaseHandle.ReferenceEquals(handle, null))
171             {
172                 return false;
173             }
174             // returns true if the handle has a body, false otherwise
175             return handle.HasBody();
176         }
177
178         /// <summary>
179         /// Returns the bool false  to indicate that an operand is false and returns true otherwise.
180         /// </summary>
181         /// <since_tizen> 3 </since_tizen>
182         public static bool operator false(BaseHandle handle)
183         {
184             // if the C# object is null, return true
185             if (BaseHandle.ReferenceEquals(handle, null))
186             {
187                 return true;
188             }
189             return !handle.HasBody();
190         }
191
192         /// <summary>
193         /// Explicit conversion from Handle to bool.
194         /// </summary>
195         /// <since_tizen> 3 </since_tizen>
196         public static explicit operator bool(BaseHandle handle)
197         {
198             // if the C# object is null, return false
199             if (BaseHandle.ReferenceEquals(handle, null))
200             {
201                 return false;
202             }
203             // returns true if the handle has a body, false otherwise
204             return handle.HasBody();
205         }
206
207         /// <summary>
208         /// Equality operator
209         /// </summary>
210         /// <since_tizen> 3 </since_tizen>
211         public static bool operator ==(BaseHandle x, BaseHandle y)
212         {
213             // if the C# objects are the same return true
214             if (BaseHandle.ReferenceEquals(x, y))
215             {
216                 return true;
217             }
218             if (!BaseHandle.ReferenceEquals(x, null) && !BaseHandle.ReferenceEquals(y, null))
219             {
220                 // drop into native code to see if both handles point to the same body
221                 return x.IsEqual(y);
222             }
223
224             if (BaseHandle.ReferenceEquals(x, null) && !BaseHandle.ReferenceEquals(y, null))
225             {
226                 if (y.HasBody()) return false;
227                 else return true;
228             }
229             if (!BaseHandle.ReferenceEquals(x, null) && BaseHandle.ReferenceEquals(y, null))
230             {
231                 if (x.HasBody()) return false;
232                 else return true;
233             }
234
235             return false;
236         }
237
238         /// <summary>
239         /// Inequality operator. Returns Null if either operand is Null
240         /// </summary>
241         /// <since_tizen> 3 </since_tizen>
242         public static bool operator !=(BaseHandle x, BaseHandle y)
243         {
244             return !(x == y);
245         }
246
247         /// <summary>
248         /// Logical AND operator.<br />
249         /// It's possible when doing a  operator this function (opBitwiseAnd) is never called due to short circuiting.<br />
250         /// </summary>
251         /// <since_tizen> 3 </since_tizen>
252         public static BaseHandle operator &(BaseHandle x, BaseHandle y)
253         {
254             if (x == y)
255             {
256                 return x;
257             }
258             return null;
259         }
260
261         /// <summary>
262         /// Logical OR operator for ||.<br />
263         /// It's possible when doing a || this function (opBitwiseOr) is never called due to short circuiting.<br />
264         /// </summary>
265         /// <since_tizen> 3 </since_tizen>
266         public static BaseHandle operator |(BaseHandle x, BaseHandle y)
267         {
268             if (!BaseHandle.ReferenceEquals(x, null) || !BaseHandle.ReferenceEquals(y, null))
269             {
270                 if (x != null && x.HasBody())
271                 {
272                     return x;
273                 }
274                 if (y != null && y.HasBody())
275                 {
276                     return y;
277                 }
278                 return null;
279             }
280             return null;
281         }
282
283         /// <summary>
284         /// Logical ! operator
285         /// </summary>
286         /// <since_tizen> 3 </since_tizen>
287         public static bool operator !(BaseHandle x)
288         {
289             // if the C# object is null, return true
290             if (BaseHandle.ReferenceEquals(x, null))
291             {
292                 return true;
293             }
294             if (x.HasBody())
295             {
296                 return false;
297             }
298             return true;
299         }
300
301         /// <summary>
302         /// Equals
303         /// </summary>
304         /// <param name="o">The object should be compared.</param>
305         /// <returns>True if equal.</returns>
306         /// <since_tizen> 5 </since_tizen>
307         public override bool Equals(object o)
308         {
309             return base.Equals(o);
310         }
311
312         /// <summary>
313         /// Gets the hash code of this baseHandle.
314         /// </summary>
315         /// <returns>The hash code.</returns>
316         /// <since_tizen> 5 </since_tizen>
317         public override int GetHashCode()
318         {
319             return base.GetHashCode();
320         }
321
322         /// <summary>
323         /// Dispose.
324         /// </summary>
325         /// <since_tizen> 3 </since_tizen>
326         public void Dispose()
327         {
328             if (isDisposeQueued)
329             {
330                 Dispose(DisposeTypes.Implicit);
331             }
332             else
333             {
334                 Dispose(true);
335             }
336             GC.SuppressFinalize(this);
337         }
338
339         /// <summary>
340         /// Hidden API (Inhouse API).
341         /// Dispose. 
342         /// </summary>
343         /// <remarks>
344         /// Following the guide of https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose.
345         /// This will replace "protected virtual void Dispose(DisposeTypes type)" which is exactly same in functionality.
346         /// </remarks>
347         /// <param name="disposing">true in order to free managed objects</param>
348         // Protected implementation of Dispose pattern.
349         [EditorBrowsable(EditorBrowsableState.Never)]
350         protected virtual void Dispose(bool disposing)
351         {
352             if (disposed)
353             {
354                 return;
355             }
356
357             if (disposing)
358             {
359                 // TODO: dispose managed state (managed objects).
360                 // Explicit call. user calls Dispose()
361
362                 //Throw exception if Dispose() is called in separate thread.
363                 if (!Window.IsInstalled())
364                 {
365                     throw new System.InvalidOperationException("This API called from separate thread. This API must be called from MainThread.");
366                 }
367
368                 if (isDisposeQueued)
369                 {
370                     Dispose(DisposeTypes.Implicit);
371                 }
372                 else
373                 {
374                     Dispose(DisposeTypes.Explicit);
375                 }
376             }
377             else
378             {
379                 // Implicit call. user doesn't call Dispose(), so this object is added into DisposeQueue to be disposed automatically.
380                 if (!isDisposeQueued)
381                 {
382                     isDisposeQueued = true;
383                     DisposeQueue.Instance.Add(this);
384                 }
385             }
386
387             // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
388             // TODO: set large fields to null.
389         }
390
391
392         /// <summary>
393         /// Performs an action on this object with the given action name and attributes.
394         /// </summary>
395         /// <param name="actionName">The command for the action.</param>
396         /// <param name="attributes">The list of attributes for the action.</param>
397         /// <returns>The action is performed by the object or not.</returns>
398         /// <since_tizen> 3 </since_tizen>
399         public bool DoAction(string actionName, PropertyMap attributes)
400         {
401             bool ret = Interop.BaseHandle.DoAction(swigCPtrCopy, actionName, PropertyMap.getCPtr(attributes));
402             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
403             return ret;
404         }
405
406         /// <summary>
407         /// Returns the type name for the Handle.<br />
408         /// Will return an empty string if the typename does not exist. This will happen for types that
409         /// have not registered with type-registry.
410         /// </summary>
411         /// <returns>The type name. Empty string if the typename does not exist.</returns>
412         /// <since_tizen> 3 </since_tizen>
413         public string GetTypeName()
414         {
415             string ret = Interop.BaseHandle.GetTypeName(swigCPtrCopy);
416             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
417             return ret;
418         }
419
420         /// <summary>
421         /// Returns the type info for the Handle.<br />
422         /// </summary>
423         /// <param name="info">The type information.</param>
424         /// <returns>True If get the type info.</returns>
425         /// <since_tizen> 3 </since_tizen>
426         public bool GetTypeInfo(Tizen.NUI.TypeInfo info)
427         {
428             bool ret = Interop.BaseHandle.GetTypeInfo(swigCPtrCopy, Tizen.NUI.TypeInfo.getCPtr(info));
429             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
430             return ret;
431         }
432
433         /// <summary>
434         /// Resets the handle.
435         /// </summary>
436         /// <since_tizen> 3 </since_tizen>
437         public void Reset()
438         {
439             Interop.BaseHandle.Reset(swigCPtrCopy);
440             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
441         }
442
443         /// <summary>
444         /// To check the BaseHandle instance is equal or not.
445         /// </summary>
446         /// <param name="rhs">The baseHandle instance.</param>
447         /// <returns>True If equal.</returns>
448         /// <since_tizen> 3 </since_tizen>
449         public bool EqualTo(BaseHandle rhs)
450         {
451             bool ret = Interop.BaseHandle.EqualTo(swigCPtrCopy, BaseHandle.getCPtr(rhs));
452             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
453             return ret;
454         }
455
456         /// <summary>
457         /// To check the BaseHandle instance is equal or not.
458         /// </summary>
459         /// <param name="rhs">The baseHandle instance.</param>
460         /// <returns>True If not equal.</returns>
461         /// <since_tizen> 3 </since_tizen>
462         public bool NotEqualTo(BaseHandle rhs)
463         {
464             bool ret = Interop.BaseHandle.NotEqualTo(swigCPtrCopy, BaseHandle.getCPtr(rhs));
465             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
466             return ret;
467         }
468
469         /// <summary>
470         /// To check the BaseHandle instance has body or not.
471         /// </summary>
472         /// <returns>True If the baseHandle instance has body.</returns>
473         /// <since_tizen> 3 </since_tizen>
474         public bool HasBody()
475         {
476             if (swigCPtrCopy.Handle == IntPtr.Zero)
477             {
478                 return false;
479             }
480
481             if (disposed == true)
482             {
483                 return false;
484             }
485             bool ret = Interop.BaseHandle.HasBody(swigCPtrCopy);
486             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
487             return ret;
488         }
489
490         /// <summary>
491         /// To check the BaseHandle instance is equal or not.
492         /// </summary>
493         /// <param name="rhs">The baseHandle instance.</param>
494         /// <returns>True If equal.</returns>
495         /// <since_tizen> 3 </since_tizen>
496         public bool IsEqual(BaseHandle rhs)
497         {
498             if (disposed == true)
499             {
500                 return false;
501             }
502
503             bool ret = Interop.BaseHandle.IsEqual(swigCPtrCopy, BaseHandle.getCPtr(rhs));
504             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
505             return ret;
506         }
507
508         internal static global::System.Runtime.InteropServices.HandleRef getCPtr(BaseHandle obj)
509         {
510             return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtrCopy;
511         }
512
513         internal void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
514         {
515             PropertySet?.Invoke(this, new PropertyChangedEventArgs(propertyName));
516         }
517
518         /// <summary>
519         /// Dispose.
520         /// </summary>
521         /// <since_tizen> 3 </since_tizen>
522         protected virtual void Dispose(DisposeTypes type)
523         {
524             if (disposed)
525             {
526                 return;
527             }
528
529             NUILog.Debug($"[Dispose] BaseHandle.Dispose({type}) START");
530
531             if (type == DisposeTypes.Explicit)
532             {
533                 //Called by User
534                 //Release your own managed resources here.
535                 //You should release all of your own disposable objects here.
536
537             }
538
539             //Release your own unmanaged resources here.
540             //You should not access any managed member here except static instance.
541             //because the execution order of Finalizes is non-deterministic.
542
543             //Unreference this instance from Registry.
544             if (registerMe)
545             {
546                 Registry.Unregister(this);
547             }
548
549 #if NUI_DEBUG_ON
550             debuggingCount--;
551             NUILog.Debug($"[Dispose] swigCMemOwn:{swigCMemOwn} debuggingCount:{debuggingCount} type:{GetType()} copyNativeHandle:{swigCPtrCopy.Handle.ToString("X8")}");
552 #endif
553
554             if (SwigCPtr.Handle != IntPtr.Zero)
555             {
556                 if (swigCMemOwn)
557                 {
558                     swigCMemOwn = false;
559                     ReleaseSwigCPtr(SwigCPtr);
560                 }
561                 swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
562             }
563             if (swigCPtrCopy.Handle != global::System.IntPtr.Zero)
564             {
565                 swigCMemOwn = false;
566                 Interop.BaseHandle.DeleteBaseHandle(swigCPtrCopy);
567                 swigCPtrCopy = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
568             }
569
570             disposed = true;
571
572             if (null != Application.Current)
573             {
574                 Application.Current.XamlResourceChanged -= OnResourcesChanged;
575             }
576             
577             NUILog.Debug($"[Dispose] BaseHandle.Dispose({type}) END");
578             NUILog.Debug($"=============================");
579         }
580
581         /// <summary>
582         /// Release swigCPtr.
583         /// </summary>
584         /// <since_tizen> 6 </since_tizen>
585         /// This will not be public opened.
586         [EditorBrowsable(EditorBrowsableState.Never)]
587         protected virtual void ReleaseSwigCPtr(System.Runtime.InteropServices.HandleRef swigCPtr)
588         {
589         }
590
591         /// <summary>
592         /// Contains event arguments for the FocusChangeRequested event.
593         /// </summary>
594         [Obsolete("Deprecated in API9; Will be removed in API11.")]
595         public class FocusRequestArgs : EventArgs
596         {
597
598             /// <summary>
599             /// Gets or sets a value that indicates the starting focus state of the element for which a focus change is requested.
600             /// </summary>
601             public bool Focus { get; set; }
602
603             /// <summary>
604             /// Gets or sets a value that indicates the ending focus state of the element for which a focus change is requested.
605             /// </summary>
606             public bool Result { get; set; }
607         }
608
609         internal global::System.Runtime.InteropServices.HandleRef SwigCPtr
610         {
611             get
612             {
613                 if (swigCPtr.Handle == IntPtr.Zero)
614                 {
615                     throw new ObjectDisposedException(nameof(SwigCPtr), "Error! NUI's native dali object is already disposed. OR the native dali object handle of NUI becomes null!");
616                 }
617                 return swigCPtr;
618             }
619         }
620
621         internal bool IsNativeHandleInvalid()
622         {
623             return swigCPtr.Handle == IntPtr.Zero;
624         }
625
626         /// <summary>
627         /// swigCMemOwn
628         /// </summary>
629         [EditorBrowsable(EditorBrowsableState.Never)]
630         protected internal bool SwigCMemOwn => swigCMemOwn;
631
632         /// <summary>
633         /// A flag to check if it is already disposed.
634         /// </summary>
635         [EditorBrowsable(EditorBrowsableState.Never)]
636         protected internal bool Disposed => disposed;
637     }
638 }