fixes #13
This commit is contained in:
parent
4456431f25
commit
1eb1fc220e
8 changed files with 104 additions and 83 deletions
|
@ -1230,7 +1230,7 @@ MonoBehaviour:
|
|||
root: {fileID: 1281046468}
|
||||
panelSettings: {fileID: 11400000, guid: 2bc58aab5867867e5b0feeae2df42fd0, type: 2}
|
||||
despawnTimeout:
|
||||
duration: 2
|
||||
duration: 60
|
||||
unit: 2
|
||||
prefab: {fileID: 0}
|
||||
--- !u!1 &1363717994
|
||||
|
|
|
@ -3,7 +3,7 @@ using UnityEditor;
|
|||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
[CustomPropertyDrawer(typeof(SerializableDuration))]
|
||||
[CustomPropertyDrawer(typeof(Duration))]
|
||||
public class DurationPropertyDrawer : PropertyDrawer
|
||||
{
|
||||
public override VisualElement CreatePropertyGUI(SerializedProperty property)
|
||||
|
@ -12,7 +12,7 @@ public class DurationPropertyDrawer : PropertyDrawer
|
|||
container.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
var duration = new PropertyField(
|
||||
property.FindPropertyRelative("duration"),
|
||||
property.FindPropertyRelative("displayValue"),
|
||||
ObjectNames.NicifyVariableName(property.name)
|
||||
);
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using NUnit.Framework.Constraints;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace KitsuneCafe.System
|
||||
|
@ -13,24 +15,34 @@ namespace KitsuneCafe.System
|
|||
Days
|
||||
}
|
||||
|
||||
public readonly struct Duration : IComparable, IComparable<Duration>, IComparable<TimeSpan>, IEquatable<Duration>, IEquatable<TimeSpan>
|
||||
[Serializable]
|
||||
public struct Duration : IComparable, IComparable<Duration>, IComparable<TimeSpan>, IEquatable<Duration>, IEquatable<TimeSpan>, ISerializationCallbackReceiver
|
||||
{
|
||||
public readonly long Value;
|
||||
private readonly TimeUnit unit;
|
||||
[SerializeField]
|
||||
private double displayValue;
|
||||
|
||||
[SerializeField, HideInInspector]
|
||||
private long value;
|
||||
|
||||
[SerializeField]
|
||||
private TimeUnit unit;
|
||||
|
||||
public Duration(long ticks, TimeUnit unit)
|
||||
{
|
||||
Value = ticks;
|
||||
displayValue = ToDisplayValue(ticks, unit);
|
||||
value = ticks;
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
public Duration(long ticks) : this(ticks, TimeUnit.Ticks) { }
|
||||
|
||||
public static implicit operator Duration(TimeSpan ts) => new(ts.Ticks, TimeUnit.Ticks);
|
||||
public static implicit operator TimeSpan(Duration duration) => duration.AsTimeSpan();
|
||||
public static implicit operator TimeValue(Duration duration) => duration.AsTimeValue();
|
||||
public static Duration operator +(Duration lhs, Duration rhs) => lhs.AsTimeSpan() + rhs.AsTimeSpan();
|
||||
|
||||
public readonly TimeSpan AsTimeSpan() => new(Value);
|
||||
public readonly TimeValue AsTimeValue() => new(Into(TimeUnit.Seconds));
|
||||
public readonly TimeSpan AsTimeSpan() => new(value);
|
||||
public readonly TimeValue AsTimeValue() => new((float)Into(TimeUnit.Seconds));
|
||||
|
||||
public static Duration From(long value, TimeUnit unit)
|
||||
{
|
||||
|
@ -64,74 +76,107 @@ namespace KitsuneCafe.System
|
|||
return new Duration(Convert.ToInt64(ticks), unit);
|
||||
}
|
||||
|
||||
public long Into(TimeUnit unit)
|
||||
public static double ToDisplayValue(long ticks, TimeUnit unit)
|
||||
{
|
||||
return unit switch
|
||||
{
|
||||
TimeUnit.Ticks => Value,
|
||||
TimeUnit.Milliseconds => Value / TimeSpan.TicksPerMillisecond,
|
||||
TimeUnit.Seconds => Value / TimeSpan.TicksPerSecond,
|
||||
TimeUnit.Minutes => Value / TimeSpan.TicksPerMinute,
|
||||
TimeUnit.Hours => Value / TimeSpan.TicksPerHour,
|
||||
TimeUnit.Days => Value / TimeSpan.TicksPerHour,
|
||||
TimeUnit.Ticks => ticks,
|
||||
TimeUnit.Milliseconds => ticks / TimeSpan.TicksPerMillisecond,
|
||||
TimeUnit.Seconds => ticks / TimeSpan.TicksPerSecond,
|
||||
TimeUnit.Minutes => ticks / TimeSpan.TicksPerMinute,
|
||||
TimeUnit.Hours => ticks / TimeSpan.TicksPerHour,
|
||||
TimeUnit.Days => ticks / TimeSpan.TicksPerHour,
|
||||
var x => throw new ArgumentException($"{x} is not a valid TimeUnit.")
|
||||
};
|
||||
}
|
||||
|
||||
public int CompareTo(object obj)
|
||||
public static long FromDisplayValue(double value, TimeUnit unit)
|
||||
{
|
||||
var ticks = unit switch
|
||||
{
|
||||
TimeUnit.Ticks => value,
|
||||
TimeUnit.Milliseconds => TimeSpan.TicksPerMillisecond * value,
|
||||
TimeUnit.Seconds => TimeSpan.TicksPerSecond * value,
|
||||
TimeUnit.Minutes => TimeSpan.TicksPerMinute * value,
|
||||
TimeUnit.Hours => TimeSpan.TicksPerHour * value,
|
||||
TimeUnit.Days => TimeSpan.TicksPerHour * value,
|
||||
var x => throw new ArgumentException($"{x} is not a valid TimeUnit.")
|
||||
};
|
||||
|
||||
return Convert.ToInt64(ticks);
|
||||
}
|
||||
|
||||
public readonly double Into() => Into(unit);
|
||||
public readonly double Into(TimeUnit unit)
|
||||
{
|
||||
return ToDisplayValue(value, unit);
|
||||
}
|
||||
|
||||
public readonly int CompareTo(object obj)
|
||||
{
|
||||
return obj switch
|
||||
{
|
||||
Duration d => CompareTo(d),
|
||||
TimeSpan ts => CompareTo(ts),
|
||||
var value => throw new ArgumentException($"{value} is not a Duration or TimeSpan")
|
||||
TimeValue tv => CompareTo(tv),
|
||||
var value => throw new ArgumentException($"{value} is not comparable to Duration")
|
||||
};
|
||||
}
|
||||
|
||||
public static bool Equals(Duration a, Duration b)
|
||||
{
|
||||
return a.Value == b.Value;
|
||||
return a.value == b.value;
|
||||
}
|
||||
|
||||
public static int Compare(Duration a, Duration b)
|
||||
{
|
||||
return a.Value.CompareTo(b.Value);
|
||||
return a.value.CompareTo(b.value);
|
||||
}
|
||||
|
||||
public int CompareTo(Duration other)
|
||||
public readonly int CompareTo(Duration other)
|
||||
{
|
||||
return Compare(this, other);
|
||||
}
|
||||
|
||||
public int CompareTo(TimeSpan other)
|
||||
public readonly int CompareTo(TimeSpan other)
|
||||
{
|
||||
return TimeSpan.Compare(this, other);
|
||||
}
|
||||
|
||||
public bool Equals(Duration other)
|
||||
public readonly bool Equals(Duration other)
|
||||
{
|
||||
return Equals(this, other);
|
||||
}
|
||||
|
||||
public bool Equals(TimeSpan other)
|
||||
public readonly bool Equals(TimeSpan other)
|
||||
{
|
||||
return other.Equals(this);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
public override readonly bool Equals(object obj)
|
||||
{
|
||||
return (obj is Duration d && Equals(this, d))
|
||||
|| (obj is TimeSpan ts && Equals(this, ts));
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Value);
|
||||
return HashCode.Combine(value);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
public override readonly string ToString()
|
||||
{
|
||||
return $"{Value} {nameof(unit)}";
|
||||
return $"{value} {nameof(unit)}";
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
displayValue = Into();
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
value = FromDisplayValue(displayValue, unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace KitsuneCafe.System
|
||||
{
|
||||
[Serializable]
|
||||
public class SerializableDuration
|
||||
{
|
||||
[SerializeField]
|
||||
private float duration;
|
||||
|
||||
[SerializeField]
|
||||
private TimeUnit unit = TimeUnit.Seconds;
|
||||
|
||||
|
||||
public static implicit operator Duration(SerializableDuration d) => Duration.From(d.duration, d.unit);
|
||||
public static implicit operator TimeSpan(SerializableDuration d) => Duration.From(d.duration, d.unit);
|
||||
public static implicit operator TimeValue(SerializableDuration d) => Duration.From(d.duration, d.unit);
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 490bd35fe5e91e4489990185d89762ec
|
|
@ -6,6 +6,7 @@ using UnityEngine;
|
|||
using UnityEngine.UIElements;
|
||||
using KitsuneCafe.Extension;
|
||||
using Unit = R3.Unit;
|
||||
using System;
|
||||
|
||||
namespace KitsuneCafe.UI
|
||||
{
|
||||
|
@ -19,13 +20,13 @@ namespace KitsuneCafe.UI
|
|||
private string propertyName = "opacity";
|
||||
|
||||
[SerializeField, ShowIf("addTransition")]
|
||||
private SerializableDuration duration;
|
||||
private Duration duration;
|
||||
|
||||
[SerializeField, ShowIf("addTransition")]
|
||||
private EasingMode easing;
|
||||
|
||||
[SerializeField, ShowIf("addTransition")]
|
||||
private SerializableDuration delay;
|
||||
private Duration delay;
|
||||
|
||||
[SerializeField, Range(0f, 1f)]
|
||||
private float from;
|
||||
|
@ -35,7 +36,7 @@ namespace KitsuneCafe.UI
|
|||
|
||||
public override IUiEffect Instantiate()
|
||||
{
|
||||
var fade = new FadeEffectInstance(from, to);
|
||||
var fade = new FadeEffectInstance(from, to, duration + delay);
|
||||
|
||||
if (addTransition)
|
||||
{
|
||||
|
@ -51,10 +52,13 @@ namespace KitsuneCafe.UI
|
|||
public readonly float From;
|
||||
public readonly float To;
|
||||
|
||||
public FadeEffectInstance(float from, float to)
|
||||
public readonly TimeSpan Timeout;
|
||||
|
||||
public FadeEffectInstance(float from, float to, TimeSpan timeout)
|
||||
{
|
||||
From = from;
|
||||
To = to;
|
||||
Timeout = timeout;
|
||||
}
|
||||
|
||||
private Observable<Unit> ObserveGeometryChange(VisualElement target, CancellationToken token)
|
||||
|
@ -74,18 +78,25 @@ namespace KitsuneCafe.UI
|
|||
.Take(1);
|
||||
}
|
||||
|
||||
private bool IsOpacityEvent(TransitionEndEvent evt)
|
||||
{
|
||||
return evt.stylePropertyNames.Contains("opacity");
|
||||
}
|
||||
|
||||
public Observable<Unit> Execute(VisualElement target, CancellationToken token)
|
||||
{
|
||||
target.style.opacity = From;
|
||||
|
||||
var to = To;
|
||||
return Defer(target, token)
|
||||
.Do(_ => target.style.opacity = to)
|
||||
.Select(_ => target.ObserveEvent<TransitionEndEvent>())
|
||||
.Select(_ => target.ObserveEvent<TransitionEndEvent>(token))
|
||||
.Switch()
|
||||
.Where(evt => evt.stylePropertyNames.Contains("opacity"))
|
||||
.Where(IsOpacityEvent)
|
||||
.Take(1)
|
||||
.TakeUntil(token)
|
||||
.AsUnitObservable();
|
||||
.AsUnitObservable()
|
||||
.Race(Observable.Timer(Timeout));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ namespace KitsuneCafe.UI
|
|||
private IUiElement uiElement;
|
||||
private VisualElement visualElement;
|
||||
|
||||
private DisposableBag activeEffects;
|
||||
private IDisposable instance;
|
||||
|
||||
private CancellationTokenSource cts;
|
||||
|
@ -41,21 +40,20 @@ namespace KitsuneCafe.UI
|
|||
Document.panelSettings = settings;
|
||||
|
||||
instance = ConfigureWhenReady(element)
|
||||
.TakeUntil(cts.Token)
|
||||
.Subscribe();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal configuration of the VisualElement. Called when rootVisualElement is ready.
|
||||
/// </summary>
|
||||
private void Configure(IUiElement uiElement, VisualElement visualElement)
|
||||
private Observable<Unit> Configure(IUiElement uiElement, VisualElement visualElement)
|
||||
{
|
||||
this.visualElement = visualElement;
|
||||
|
||||
uiElement.Configure(visualElement);
|
||||
|
||||
Raise(UiEvent.OnCreate, cts.Token)
|
||||
.Subscribe()
|
||||
.AddTo(ref activeEffects);
|
||||
return Raise(UiEvent.OnCreate, cts.Token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -66,9 +64,7 @@ namespace KitsuneCafe.UI
|
|||
return Observable.EveryValueChanged(Document, d => d.rootVisualElement)
|
||||
.WhereNotNull()
|
||||
.Take(1)
|
||||
.TakeUntil(cts.Token)
|
||||
.Do(root => Configure(element, root))
|
||||
.AsUnitObservable();
|
||||
.SelectMany(root => Configure(element, root));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -102,9 +98,6 @@ namespace KitsuneCafe.UI
|
|||
cts?.Dispose();
|
||||
cts = null;
|
||||
|
||||
// activeEffects.Dispose();
|
||||
activeEffects.Clear();
|
||||
|
||||
instance?.Dispose();
|
||||
|
||||
uiElement = default;
|
||||
|
@ -115,18 +108,13 @@ namespace KitsuneCafe.UI
|
|||
/// Initiates the despawn process: runs OnDestroy effects, waits for them to finish,
|
||||
/// then disposes everything and releases the object back to the pool.
|
||||
/// </summary>
|
||||
public Observable<Unit> Despawn(CancellationTokenSource cts = default)
|
||||
public Observable<Unit> Despawn(CancellationToken token = default)
|
||||
{
|
||||
cts ??= new CancellationTokenSource();
|
||||
|
||||
return Raise(UiEvent.OnDestroy, cts.Token)
|
||||
.Do(onCompleted: _ =>
|
||||
{
|
||||
Dispose();
|
||||
PooledObject.Release();
|
||||
},
|
||||
onErrorResume: _ => cts.Cancel())
|
||||
.TakeUntil(cts.Token)
|
||||
cts.Cancel();
|
||||
return Raise(UiEvent.OnDestroy, token)
|
||||
.Do(onDispose: () => Debug.Log("Disposing OnDestroy"))
|
||||
.Do(onCompleted: _ => DespawnNow(),
|
||||
onErrorResume: _ => DespawnNow())
|
||||
.AsUnitObservable();
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace KitsuneCafe.UI
|
|||
|
||||
[SerializeField] private Transform root;
|
||||
[SerializeField] private PanelSettings panelSettings;
|
||||
[SerializeField] private SerializableDuration despawnTimeout;
|
||||
[SerializeField] private Duration globalTimeout;
|
||||
[SerializeField] private GameObject prefab;
|
||||
|
||||
private readonly Dictionary<ElementId, GameObject> activeElements = new();
|
||||
|
@ -141,7 +141,7 @@ namespace KitsuneCafe.UI
|
|||
if (obj.TryGetComponent<UiElementInstance>(out var instance))
|
||||
{
|
||||
instance.Despawn()
|
||||
.Race(Observable.Timer(despawnTimeout).Do(_ => instance.DespawnNow()))
|
||||
.Race(Observable.Timer(globalTimeout).Do(_ => instance.DespawnNow()))
|
||||
.Do(onCompleted: _ => activeElements.Remove(id))
|
||||
.Subscribe()
|
||||
.AddTo(obj);
|
||||
|
|
Loading…
Add table
Reference in a new issue