91 lines
3.6 KiB
C#
91 lines
3.6 KiB
C#
namespace Lumi.Companion.Overlay;
|
|
|
|
public enum OverlayItemState { Queued, Entering, Visible, Exiting, Removed }
|
|
|
|
public sealed class OverlayQueueItem(string id, string kind, object payload, DateTimeOffset queuedAt)
|
|
{
|
|
public string Id { get; } = id;
|
|
public string Kind { get; } = kind;
|
|
public object Payload { get; } = payload;
|
|
public DateTimeOffset QueuedAt { get; } = queuedAt;
|
|
public DateTimeOffset StateAt { get; internal set; } = queuedAt;
|
|
public OverlayItemState State { get; internal set; } = OverlayItemState.Queued;
|
|
}
|
|
|
|
public sealed class OverlayLifecycleQueue(Func<OverlayContainerSettings> settings, Func<DateTimeOffset>? now = null)
|
|
{
|
|
private readonly List<OverlayQueueItem> _items = [];
|
|
private readonly Func<DateTimeOffset> _now = now ?? (() => DateTimeOffset.UtcNow);
|
|
public IReadOnlyList<OverlayQueueItem> Items => _items;
|
|
public event Action? Changed;
|
|
|
|
public bool Add(string id, string kind, object payload)
|
|
{
|
|
if (_items.Any(item => item.Id == id && item.State != OverlayItemState.Removed)) return false;
|
|
var value = settings();
|
|
if (_items.Count(item => item.State != OverlayItemState.Removed) >= Math.Max(value.QueueLimit + 1, value.QueueLimit * 2))
|
|
return false;
|
|
var active = _items.Where(item => item.State is not OverlayItemState.Exiting and not OverlayItemState.Removed).ToList();
|
|
while (active.Count >= value.QueueLimit)
|
|
{
|
|
var oldest = active[0];
|
|
oldest.State = OverlayItemState.Exiting;
|
|
oldest.StateAt = _now();
|
|
active.RemoveAt(0);
|
|
}
|
|
var created = new OverlayQueueItem(id, kind, payload, _now()) { State = OverlayItemState.Entering, StateAt = _now() };
|
|
_items.Add(created);
|
|
Changed?.Invoke();
|
|
return true;
|
|
}
|
|
|
|
public void Advance()
|
|
{
|
|
var current = _now();
|
|
var value = settings();
|
|
var changed = false;
|
|
foreach (var item in _items)
|
|
{
|
|
var elapsed = current - item.StateAt;
|
|
if (item.State == OverlayItemState.Entering && elapsed.TotalMilliseconds >= value.EntryDurationMs)
|
|
{
|
|
item.State = OverlayItemState.Visible; item.StateAt = current; changed = true;
|
|
}
|
|
else if (item.State == OverlayItemState.Visible && elapsed.TotalMilliseconds >= value.LifetimeMs)
|
|
{
|
|
item.State = OverlayItemState.Exiting; item.StateAt = current; changed = true;
|
|
}
|
|
else if (item.State == OverlayItemState.Exiting && elapsed.TotalMilliseconds >= value.ExitDurationMs)
|
|
{
|
|
item.State = OverlayItemState.Removed; changed = true;
|
|
}
|
|
}
|
|
if (_items.RemoveAll(item => item.State == OverlayItemState.Removed) > 0) changed = true;
|
|
if (changed) Changed?.Invoke();
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
var current = _now();
|
|
foreach (var item in _items.Where(item => item.State is not OverlayItemState.Exiting and not OverlayItemState.Removed))
|
|
{
|
|
item.State = OverlayItemState.Exiting;
|
|
item.StateAt = current;
|
|
}
|
|
Changed?.Invoke();
|
|
}
|
|
|
|
public void ExitWhere(Func<OverlayQueueItem, bool> predicate)
|
|
{
|
|
var current = _now();
|
|
var changed = false;
|
|
foreach (var item in _items.Where(item => item.State is not OverlayItemState.Exiting and not OverlayItemState.Removed && predicate(item)))
|
|
{
|
|
item.State = OverlayItemState.Exiting;
|
|
item.StateAt = current;
|
|
changed = true;
|
|
}
|
|
if (changed) Changed?.Invoke();
|
|
}
|
|
}
|