using System; using System.Collections.Generic; using System.Runtime.InteropServices; using Rive.Utils; namespace Rive { /// /// A view model instance property that holds a list of view model instances. /// public sealed class ViewModelInstanceListProperty : ViewModelInstancePrimitiveProperty { internal ViewModelInstanceListProperty(IntPtr instanceValuePtr, ViewModelInstance rootInstance) : base(instanceValuePtr, rootInstance) { } /// /// The number of items in the list. /// public int Count { get { ThrowIfOwnerDisposed(); if (InstancePropertyPtr == IntPtr.Zero) { DebugLogger.Instance.LogWarning("Trying to get length of a null list property."); return 0; } return (int)getViewModelInstanceListSize(InstancePropertyPtr); } } /// /// Gets the view model instance at the specified index. /// /// The index of the item to get. /// The view model instance at the specified index, or null if the index is out of bounds. public ViewModelInstance GetInstanceAt(int index) { ThrowIfOwnerDisposed(); if (InstancePropertyPtr == IntPtr.Zero) { DebugLogger.Instance.LogError("Trying to get item from a null list property."); return null; } if (index < 0 || index >= Count) { DebugLogger.Instance.LogError($"Index {index} is out of bounds for list of length {Count}."); return null; } IntPtr instancePtr = getViewModelInstanceListItemAt(InstancePropertyPtr, index); if (instancePtr == IntPtr.Zero) { return null; } ViewModelInstance vmi = GetOrCreateVMInstanceFromPtr(instancePtr); return vmi; } private ViewModelInstance GetOrCreateVMInstanceFromPtr(IntPtr instancePtr) { if (instancePtr == IntPtr.Zero) { return null; } return ViewModelInstance.GetOrCreateFromPointer(instancePtr, this.RootInstance?.RiveFile, this.RootInstance); } /// /// Adds a view model instance to the end of the list. /// /// The view model instance to add. public void Add(ViewModelInstance instance) { ThrowIfOwnerDisposed(); if (InstancePropertyPtr == IntPtr.Zero) { DebugLogger.Instance.LogError("Trying to add to a null list property."); return; } if (instance == null || instance.NativeSafeHandle.IsInvalid) { DebugLogger.Instance.LogError("Cannot add null or invalid view model instance to list."); return; } addViewModelInstanceToList(InstancePropertyPtr, instance.NativeSafeHandle.DangerousGetHandle()); instance.AddParent(this.RootInstance); } /// /// Inserts a view model instance at the specified index. /// /// The view model instance to insert. /// The index at which to insert the instance. public void Insert(ViewModelInstance instance, int index) { ThrowIfOwnerDisposed(); if (instance == null) { DebugLogger.Instance.LogError("Cannot insert null or invalid view model instance into list."); return; } if (InstancePropertyPtr == IntPtr.Zero) { DebugLogger.Instance.LogError("Trying to insert into a null list property."); return; } if (index < 0) { DebugLogger.Instance.LogError($"Index {index} is out of bounds for list of length {Count}."); return; } if (!addViewModelInstanceToListAt(InstancePropertyPtr, instance.NativeSafeHandle.DangerousGetHandle(), index)) { DebugLogger.Instance.LogError($"Failed to insert view model instance at index {index}."); return; } instance.AddParent(this.RootInstance); } /// /// Removes a view model instance from the list. /// /// The view model instance to remove. /// /// This method will remove every occurrence of the instance from the list. /// public void Remove(ViewModelInstance instance) { ThrowIfOwnerDisposed(); if (InstancePropertyPtr == IntPtr.Zero) { DebugLogger.Instance.LogError("Trying to remove from a null list property."); return; } if (instance == null || instance.NativeSafeHandle.IsInvalid) { DebugLogger.Instance.LogError("Cannot remove null or invalid view model instance from list."); return; } removeViewModelInstanceFromList(InstancePropertyPtr, instance.NativeSafeHandle.DangerousGetHandle()); instance.RemoveParent(this.RootInstance); } /// /// Removes the view model instance at the specified index. /// /// The index of the item to remove. public void RemoveAt(int index) { ThrowIfOwnerDisposed(); if (InstancePropertyPtr == IntPtr.Zero) { DebugLogger.Instance.LogError("Trying to remove from a null list property."); return; } if (index < 0 || index >= Count) { DebugLogger.Instance.LogError($"Index {index} is out of bounds for list of length {Count}."); return; } ViewModelInstance instance = GetInstanceAt(index); if (instance == null) { DebugLogger.Instance.LogError($"No instance found at index {index}."); return; } removeViewModelInstanceFromListAt(InstancePropertyPtr, index); } /// /// Swaps the view model instances at the specified indices. /// /// The index of the first item to swap. /// The index of the second item to swap. public void Swap(int indexA, int indexB) { ThrowIfOwnerDisposed(); if (InstancePropertyPtr == IntPtr.Zero) { DebugLogger.Instance.LogError("Trying to swap instances in a null list property."); return; } if (indexA == indexB) { DebugLogger.Instance.LogError("Cannot swap instances at the same index."); return; } if (indexA < 0 || indexA >= Count) { DebugLogger.Instance.LogError($"Index {indexA} is out of bounds for list of length {Count}."); return; } if (indexB < 0 || indexB >= Count) { DebugLogger.Instance.LogError($"Index {indexB} is out of bounds for list of length {Count}."); return; } swapViewModelInstancesInList(InstancePropertyPtr, indexA, indexB); } /// /// Called when the list property value changes. /// internal override void RaiseChangedEvent() { // List changes don't have a specific value, just notify that the list changed m_onTriggered?.Invoke(); } /// /// Event that is raised when the list changes. /// public event Action OnChanged { add => AddPropertyCallback(value, ref m_onTriggered); remove => RemovePropertyCallback(value, ref m_onTriggered); } private Action m_onTriggered; /// /// Clears all callbacks registered with this property. /// internal override void ClearAllCallbacks() { m_onTriggered = null; base.ClearAllCallbacks(); } internal override void ClearDelegatesOnly() { m_onTriggered = null; } [DllImport(NativeLibrary.name)] private static extern nuint getViewModelInstanceListSize(IntPtr listProperty); [DllImport(NativeLibrary.name)] private static extern IntPtr getViewModelInstanceListItemAt(IntPtr listProperty, int index); [DllImport(NativeLibrary.name)] private static extern void addViewModelInstanceToList(IntPtr listProperty, IntPtr instance); [DllImport(NativeLibrary.name)] private static extern bool addViewModelInstanceToListAt(IntPtr listProperty, IntPtr instance, int index); [DllImport(NativeLibrary.name)] private static extern void removeViewModelInstanceFromList(IntPtr listProperty, IntPtr instance); [DllImport(NativeLibrary.name)] private static extern void removeViewModelInstanceFromListAt(IntPtr listProperty, int index); [DllImport(NativeLibrary.name)] private static extern void swapViewModelInstancesInList(IntPtr listProperty, int indexA, int indexB); } }