using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using Unity.Properties; using UnityEngine.UIElements; namespace Uigenx.Binding { /// /// Base class for hand written data sources. Derive from it, mark bindable members with /// [CreateProperty] and call from every setter. Unity's runtime /// binding reads the members directly; is what the studio bridge /// and the UI Creator binder use. /// public abstract class UgxDataSource : IUgxDataSource, INotifyBindablePropertyChanged, IDataSourceViewHashProvider { long _version; public event EventHandler propertyChanged; public long GetViewHashCode() => _version; /// Raises the change notification for the calling property and bumps the view hash. protected void Notify([CallerMemberName] string property = null) { if (string.IsNullOrEmpty(property)) return; _version++; // Bindings are foreign code from the source's point of view. try { propertyChanged?.Invoke(this, new BindablePropertyChangedEventArgs(property)); } catch (Exception e) { UnityEngine.Debug.LogError("[ugx] a property change listener threw: " + e); } } /// /// Announces that everything changed. Listeners read the empty path as "refresh all", so /// this cannot go through , which ignores an empty name. /// protected void NotifyAll() { _version++; try { propertyChanged?.Invoke(this, new BindablePropertyChangedEventArgs(string.Empty)); } catch (Exception e) { UnityEngine.Debug.LogError("[ugx] a property change listener threw: " + e); } } public virtual bool TryGet(string path, out object value) { var container = (object)this; return PropertyContainer.TryGetValue(ref container, UgxPropertyPaths.Get(path), out value); } public virtual bool TrySet(string path, object value) { var container = (object)this; if (!PropertyContainer.TrySetValue(ref container, UgxPropertyPaths.Get(path), value)) return false; _version++; Notify(path); return true; } public virtual IEnumerable Paths { get { foreach (var entry in UgxSchemaWalker.Collect(GetType())) yield return entry.Path; } } } }