namespace XFESystem.Desktop;
[Flags]
public enum DesktopChromeAnimationChanges { None = 0, Minimize = 1, Close = 2, TitleHint = 4, Resize = 8 }
/// <summary>Monotonic reversible window affordances. Stable states never request another frame.</summary>
public sealed class DesktopChromeAnimation
{
public const int DurationMilliseconds = 180;
private readonly Channel _minimize = new(), _close = new(), _title = new(), _resize = new();
public double MinimizeProgress => _minimize.Progress;
public double CloseProgress => _close.Progress;
public double TitleHintProgress => _title.Progress;
public double ResizeProgress => _resize.Progress;
public bool IsAnimating => _minimize.IsAnimating || _close.IsAnimating || _title.IsAnimating || _resize.IsAnimating;
public DesktopChromeAnimationChanges SetTargets(long monotonicMs, bool minimize, bool close, bool titleHint, bool resize)
{
var changed = Advance(monotonicMs);
_minimize.SetTarget(minimize); _close.SetTarget(close); _title.SetTarget(titleHint); _resize.SetTarget(resize);
return changed;
}
public DesktopChromeAnimationChanges Advance(long monotonicMs)
{
var changed = DesktopChromeAnimationChanges.None;
if (_minimize.Advance(monotonicMs)) changed |= DesktopChromeAnimationChanges.Minimize;
if (_close.Advance(monotonicMs)) changed |= DesktopChromeAnimationChanges.Close;
if (_title.Advance(monotonicMs)) changed |= DesktopChromeAnimationChanges.TitleHint;
if (_resize.Advance(monotonicMs)) changed |= DesktopChromeAnimationChanges.Resize;
return changed;
}
private sealed class Channel
{
private double _phase;
private bool _target, _initialized;
private long _lastMs;
public double Progress => _phase * _phase * (3 - 2 * _phase);
public bool IsAnimating => _target ? _phase < 1 : _phase > 0;
public void SetTarget(bool target) => _target = target;
public bool Advance(long nowMs)
{
if (!_initialized) { _initialized = true; _lastMs = nowMs; return false; }
if (nowMs <= _lastMs) return false;
long elapsed = unchecked(nowMs - _lastMs);
_lastMs = nowMs;
double step = (elapsed < 0 ? DurationMilliseconds : Math.Min(elapsed, DurationMilliseconds)) / (double)DurationMilliseconds;
double previous = _phase;
_phase = Math.Clamp(_phase + (_target ? step : -step), 0, 1);
return previous != _phase;
}
}
}
namespace XFESystem.Desktop;
[Flags]
public enum DesktopChromeAnimationChanges { None = 0, Minimize = 1, Close = 2, TitleHint = 4, Resize = 8 }
/// <summary>Monotonic reversible window affordances. Stable states never request another frame.</summary>
public sealed class DesktopChromeAnimation
{
public const int DurationMilliseconds = 180;
private readonly Channel _minimize = new(), _close = new(), _title = new(), _resize = new();
public double MinimizeProgress => _minimize.Progress;
public double CloseProgress => _close.Progress;
public double TitleHintProgress => _title.Progress;
public double ResizeProgress => _resize.Progress;
public bool IsAnimating => _minimize.IsAnimating || _close.IsAnimating || _title.IsAnimating || _resize.IsAnimating;
public DesktopChromeAnimationChanges SetTargets(long monotonicMs, bool minimize, bool close, bool titleHint, bool resize)
{
var changed = Advance(monotonicMs);
_minimize.SetTarget(minimize); _close.SetTarget(close); _title.SetTarget(titleHint); _resize.SetTarget(resize);
return changed;
}
public DesktopChromeAnimationChanges Advance(long monotonicMs)
{
var changed = DesktopChromeAnimationChanges.None;
if (_minimize.Advance(monotonicMs)) changed |= DesktopChromeAnimationChanges.Minimize;
if (_close.Advance(monotonicMs)) changed |= DesktopChromeAnimationChanges.Close;
if (_title.Advance(monotonicMs)) changed |= DesktopChromeAnimationChanges.TitleHint;
if (_resize.Advance(monotonicMs)) changed |= DesktopChromeAnimationChanges.Resize;
return changed;
}
private sealed class Channel
{
private double _phase;
private bool _target, _initialized;
private long _lastMs;
public double Progress => _phase * _phase * (3 - 2 * _phase);
public bool IsAnimating => _target ? _phase < 1 : _phase > 0;
public void SetTarget(bool target) => _target = target;
public bool Advance(long nowMs)
{
if (!_initialized) { _initialized = true; _lastMs = nowMs; return false; }
if (nowMs <= _lastMs) return false;
long elapsed = unchecked(nowMs - _lastMs);
_lastMs = nowMs;
double step = (elapsed < 0 ? DurationMilliseconds : Math.Min(elapsed, DurationMilliseconds)) / (double)DurationMilliseconds;
double previous = _phase;
_phase = Math.Clamp(_phase + (_target ? step : -step), 0, 1);
return previous != _phase;
}
}
}