--- /dev/null
+/*
+ * Copyright(c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Concurrent;
+using System.ComponentModel;
+
+namespace Tizen.NUI
+{
+ /// <summary>
+ /// This is used to store a object reference until render thread rendered at least 1 times.
+ /// Note that Register itself should be called at main thread!
+ /// </summary>
+ internal sealed class RenderThreadObjectHolder : global::System.IDisposable
+ {
+ private static readonly RenderThreadObjectHolder renderThreadObjectHolder = new RenderThreadObjectHolder();
+
+ /// <summary>
+ /// static initialization singleton
+ /// </summary>
+ internal static RenderThreadObjectHolder Instance
+ {
+ get { return renderThreadObjectHolder; }
+ }
+
+ /// <summary>
+ /// Strong holder for the System.Delegate.
+ /// </summary>
+ private HashSet<System.Delegate> delegateSet;
+
+ private Animation threadCheckingAnimation;
+
+ private bool animationTriggered;
+
+ private RenderThreadObjectHolder()
+ {
+ delegateSet = new HashSet<System.Delegate>();
+ }
+
+ ~RenderThreadObjectHolder() => Dispose(false);
+
+ internal static void RegisterDelegates(List<System.Delegate> disposedDelgates)
+ {
+ if (disposedDelgates == null)
+ {
+ return;
+ }
+
+ foreach (var disposedDelgate in disposedDelgates)
+ {
+ RegisterDelegate(disposedDelgate);
+ }
+ }
+
+ internal static void RegisterDelegate(System.Delegate disposedDelgate)
+ {
+ var holder = Instance;
+
+ if (holder == null)
+ {
+ // Maybe applicatoin terminated case. We should ignore it.
+ return;
+ }
+
+ if (disposedDelgate == null)
+ {
+ return;
+ }
+
+ holder.delegateSet.Add(disposedDelgate);
+
+ if (!holder.animationTriggered)
+ {
+ if (holder.threadCheckingAnimation == null)
+ {
+ // Make 0ms animation, and Play + Stop.
+ holder.threadCheckingAnimation = new Animation(0);
+ holder.threadCheckingAnimation.Finished += OnFinished;
+ }
+ holder.animationTriggered = true;
+ holder.threadCheckingAnimation.Play();
+ holder.threadCheckingAnimation.Stop();
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ threadCheckingAnimation?.Dispose();
+ NUILog.ErrorBacktrace("We should not manually dispose for singleton class!");
+ }
+ }
+
+ private static void OnFinished(object o, EventArgs e)
+ {
+ var holder = Instance;
+
+ if (holder == null)
+ {
+ // Maybe applicatoin terminated case. We should ignore it.
+ return;
+ }
+
+ // Clear objects now.
+ holder.delegateSet.Clear();
+
+ holder.animationTriggered = false;
+ }
+ }
+}
+++ /dev/null
-//------------------------------------------------------------------------------
-// <auto-generated />
-//
-// This file was automatically generated by SWIG (http://www.swig.org).
-// Version 3.0.9
-//
-// Do not make changes to this file unless you know what you are doing--modify
-// the SWIG interface file instead.
-//------------------------------------------------------------------------------
-
-namespace Tizen.NUI
-{
- internal class SWIGTYPE_p_f_float__float
- {
- private global::System.Runtime.InteropServices.HandleRef swigCPtr;
-
- internal SWIGTYPE_p_f_float__float(global::System.IntPtr cPtr)
- {
- swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
- }
-
- protected SWIGTYPE_p_f_float__float()
- {
- swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
- }
-
- internal static global::System.Runtime.InteropServices.HandleRef getCPtr(SWIGTYPE_p_f_float__float obj)
- {
- return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr;
- }
- }
-}
/// <since_tizen> 3 </since_tizen>
public class AlphaFunction : Disposable
{
+ private static readonly object dummyObject = new object();
/// <summary>
/// The constructor.<br />
/// Creates an alpha function object with the user-defined alpha function.<br />
/// </summary>
/// <param name="func">User defined function. It must be a method formatted as float alphafunction(float progress)</param>
+ /// <remarks>
+ /// Alpha function called at seperated thread with main thread.
+ /// Due to the disposed infomation is not send to seperated thread,
+ /// Given function might be invoked even if animation class or alpha functoin itself disposed.
+ /// </remarks>
/// <since_tizen> 3 </since_tizen>
- public AlphaFunction(global::System.Delegate func) : this(Interop.AlphaFunction.NewAlphaFunction(SWIGTYPE_p_f_float__float.getCPtr(new SWIGTYPE_p_f_float__float(System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate<System.Delegate>(func)))), true)
+ public AlphaFunction(global::System.Delegate func) : this(Interop.AlphaFunction.NewAlphaFunction(new global::System.Runtime.InteropServices.HandleRef(dummyObject, System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate<System.Delegate>(func))), true)
{
+ CustomAlphaFunctionDelegate = func;
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
{
}
- // Not used : This will be remained by 2021-05-30 to check side effect. After 2021-05-30 this will be removed cleanly
- // internal AlphaFunction(SWIGTYPE_p_f_float__float function) : this(Interop.AlphaFunction.NewAlphaFunction(SWIGTYPE_p_f_float__float.getCPtr(function)), true)
- // {
- // if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
- // }
-
/// <summary>
/// This specifies the various types of built-in alpha functions available for animations.
/// </summary>
Bezier
}
+ internal global::System.Delegate CustomAlphaFunctionDelegate { get; private set; }
+
/// <summary>
/// Retrieves the control points of the alpha function.<br />
/// </summary>
return propertyKey;
}
- // Not used : This will be remained by 2021-05-30 to check side effect. After 2021-05-30 this will be removed cleanly
- // internal SWIGTYPE_p_f_float__float GetCustomFunction()
- // {
- // global::System.IntPtr cPtr = Interop.AlphaFunction.GetCustomFunction(SwigCPtr);
- // SWIGTYPE_p_f_float__float ret = (cPtr == global::System.IntPtr.Zero) ? null : new SWIGTYPE_p_f_float__float(cPtr);
- // if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
- // return ret;
- // }
-
/// This will not be public opened.
[EditorBrowsable(EditorBrowsableState.Never)]
protected override void ReleaseSwigCPtr(System.Runtime.InteropServices.HandleRef swigCPtr)
private List<int> startTimeList = null;
private List<int> endTimeList = null;
+ private List<System.Delegate> customAlphaFunctionDelegates = null;
+
/// <summary>
/// Creates an initialized animation.<br />
/// The animation will not loop.<br />
/// <since_tizen> 3 </since_tizen>
public void Clear()
{
+ ClearCustomAlphaFunctionDelegate();
+
Interop.Animation.Clear(SwigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
}
else
{
+ AppendCustomAlphaFunctionDelegate(alpha.CustomAlphaFunctionDelegate);
Interop.Animation.AnimateByAlphaFunction(SwigCPtr, Property.getCPtr(target), PropertyValue.getCPtr(relativeValue), AlphaFunction.getCPtr(alpha));
}
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
else
{
+ AppendCustomAlphaFunctionDelegate(alpha.CustomAlphaFunctionDelegate);
Interop.Animation.AnimateBy(SwigCPtr, Property.getCPtr(target), PropertyValue.getCPtr(relativeValue), AlphaFunction.getCPtr(alpha), TimePeriod.getCPtr(period));
}
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
else
{
+ AppendCustomAlphaFunctionDelegate(alpha.CustomAlphaFunctionDelegate);
Interop.Animation.AnimateToAlphaFunction(SwigCPtr, Property.getCPtr(target), PropertyValue.getCPtr(destinationValue), AlphaFunction.getCPtr(alpha));
}
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
else
{
+ AppendCustomAlphaFunctionDelegate(alpha.CustomAlphaFunctionDelegate);
Interop.Animation.AnimateTo(SwigCPtr, Property.getCPtr(target), PropertyValue.getCPtr(destinationValue), AlphaFunction.getCPtr(alpha), TimePeriod.getCPtr(period));
}
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
else
{
+ AppendCustomAlphaFunctionDelegate(alpha.CustomAlphaFunctionDelegate);
Interop.Animation.AnimateByAlphaFunction(SwigCPtr, Property.getCPtr(target), relativeValueIntPtr, AlphaFunction.getCPtr(alpha));
}
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
else
{
+ AppendCustomAlphaFunctionDelegate(alpha.CustomAlphaFunctionDelegate);
Interop.Animation.AnimateBy(SwigCPtr, Property.getCPtr(target), relativeValueIntPtr, AlphaFunction.getCPtr(alpha), TimePeriod.getCPtr(period));
}
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
else
{
+ AppendCustomAlphaFunctionDelegate(alpha.CustomAlphaFunctionDelegate);
Interop.Animation.AnimateToAlphaFunction(SwigCPtr, Property.getCPtr(target), destinationValueIntPtr, AlphaFunction.getCPtr(alpha));
}
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
else
{
+ AppendCustomAlphaFunctionDelegate(alpha.CustomAlphaFunctionDelegate);
Interop.Animation.AnimateTo(SwigCPtr, Property.getCPtr(target), destinationValueIntPtr, AlphaFunction.getCPtr(alpha), TimePeriod.getCPtr(period));
}
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
internal void AnimateBetween(Property target, KeyFrames keyFrames, AlphaFunction alpha)
{
+ AppendCustomAlphaFunctionDelegate(alpha?.CustomAlphaFunctionDelegate);
Interop.Animation.AnimateBetweenAlphaFunction(SwigCPtr, Property.getCPtr(target), KeyFrames.getCPtr(keyFrames), AlphaFunction.getCPtr(alpha));
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
}
else
{
+ AppendCustomAlphaFunctionDelegate(alpha.CustomAlphaFunctionDelegate);
Interop.Animation.AnimateBetweenAlphaFunctionInterpolation(SwigCPtr, Property.getCPtr(target), KeyFrames.getCPtr(keyFrames), AlphaFunction.getCPtr(alpha), (int)interpolation);
}
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
internal void AnimateBetween(Property target, KeyFrames keyFrames, AlphaFunction alpha, TimePeriod period)
{
+ AppendCustomAlphaFunctionDelegate(alpha?.CustomAlphaFunctionDelegate);
Interop.Animation.AnimateBetween(SwigCPtr, Property.getCPtr(target), KeyFrames.getCPtr(keyFrames), AlphaFunction.getCPtr(alpha), TimePeriod.getCPtr(period));
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
}
else
{
+ AppendCustomAlphaFunctionDelegate(alpha.CustomAlphaFunctionDelegate);
Interop.Animation.AnimateBetween(SwigCPtr, Property.getCPtr(target), KeyFrames.getCPtr(keyFrames), AlphaFunction.getCPtr(alpha), TimePeriod.getCPtr(period), (int)interpolation);
}
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
internal void Animate(View view, Path path, Vector3 forward, AlphaFunction alpha)
{
+ AppendCustomAlphaFunctionDelegate(alpha?.CustomAlphaFunctionDelegate);
Interop.Animation.AnimateAlphaFunction(SwigCPtr, View.getCPtr(view), Path.getCPtr(path), Vector3.getCPtr(forward), AlphaFunction.getCPtr(alpha));
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
internal void Animate(View view, Path path, Vector3 forward, AlphaFunction alpha, TimePeriod period)
{
+ AppendCustomAlphaFunctionDelegate(alpha?.CustomAlphaFunctionDelegate);
Interop.Animation.Animate(SwigCPtr, View.getCPtr(view), Path.getCPtr(path), Vector3.getCPtr(forward), AlphaFunction.getCPtr(alpha), TimePeriod.getCPtr(period));
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
animationProgressReachedEventCallback = null;
}
+ ClearCustomAlphaFunctionDelegate();
+
base.Dispose(type);
}
Interop.Animation.DeleteAnimation(swigCPtr);
}
+ private void AppendCustomAlphaFunctionDelegate(global::System.Delegate customFunction)
+ {
+ if (customFunction == null)
+ {
+ return;
+ }
+
+ if (customAlphaFunctionDelegates == null)
+ {
+ customAlphaFunctionDelegates = new List<System.Delegate>();
+ }
+ customAlphaFunctionDelegates.Add(customFunction);
+ }
+
+ private void ClearCustomAlphaFunctionDelegate()
+ {
+ if (customAlphaFunctionDelegates != null)
+ {
+ // Delete function delegates after 1 frame rendered.
+ RenderThreadObjectHolder.RegisterDelegates(customAlphaFunctionDelegates);
+ customAlphaFunctionDelegates = null;
+ }
+ }
+
private void OnFinished(IntPtr data)
{
if (animationFinishedEventHandler != null)
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
+ /// <summary>
+ /// you can override it to clean-up your own resources.
+ /// </summary>
+ /// <param name="type">DisposeTypes</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected override void Dispose(DisposeTypes type)
+ {
+ if (Disposed)
+ {
+ return;
+ }
+
+ if (type == DisposeTypes.Explicit)
+ {
+ //Called by User
+ //Release your own managed resources here.
+ //You should release all of your own disposable objects here.
+
+ // Keep delegate life ownership until next frame rendered.
+ RenderThreadObjectHolder.RegisterDelegate(glInitializeCallback);
+ RenderThreadObjectHolder.RegisterDelegate(glRenderFrameCallback);
+ RenderThreadObjectHolder.RegisterDelegate(glTerminateCallback);
+ RenderThreadObjectHolder.RegisterDelegate(internalRenderFrameCallback);
+
+ // DevNote : Do not make glRenderFrameCallback as null here, to avoid race condition or lock overhead.
+ }
+
+ base.Dispose(type);
+ }
+
private int OnRenderFrame(global::System.IntPtr cPtr)
{
if (glRenderFrameCallback != null)
finishAnimation.Clear();
customScrollAlphaFunction = new UserAlphaFunctionDelegate(CustomScrollAlphaFunction);
finishAnimation.DefaultAlphaFunction = new AlphaFunction(customScrollAlphaFunction);
- GC.KeepAlive(customScrollAlphaFunction);
finishAnimation.Duration = (int)finishAnimationDuration;
- finishAnimation.AnimateTo(controlView, "PositionX", destination);
+ finishAnimation.AnimateTo(controlView, "PositionX", destination, finishAnimation.DefaultAlphaFunction);
finishAnimation.Play();
+
+ // Remove alpha function now, for test UserAlphaFunctionDelegate alived.
+ customScrollAlphaFunction = null;
+ finishAnimation.DefaultAlphaFunction?.Dispose();
}
private float CustomScrollAlphaFunction(float progress)
}
};
+ // Remove alpha function now, for test UserAlphaFunctionDelegate alived.
+ customSpringAlphaFunction = null;
+ customAlphaFunction?.Dispose();
+
window.Add(root);
}