6 changed files with 196 additions and 3 deletions
@ -0,0 +1,55 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Perspex.Threading; |
||||
|
|
||||
|
namespace Perspex.Utilities |
||||
|
{ |
||||
|
public class WeakTimer |
||||
|
{ |
||||
|
public interface IWeakTimerSubscriber |
||||
|
{ |
||||
|
bool Tick(); |
||||
|
} |
||||
|
|
||||
|
private readonly WeakReference<IWeakTimerSubscriber> _subscriber; |
||||
|
private DispatcherTimer _timer; |
||||
|
|
||||
|
public WeakTimer(IWeakTimerSubscriber subscriber) |
||||
|
{ |
||||
|
_subscriber = new WeakReference<IWeakTimerSubscriber>(subscriber); |
||||
|
_timer = new DispatcherTimer(); |
||||
|
|
||||
|
_timer.Tick += delegate { OnTick(); }; |
||||
|
_timer.Start(); |
||||
|
} |
||||
|
|
||||
|
private void OnTick() |
||||
|
{ |
||||
|
IWeakTimerSubscriber subscriber; |
||||
|
if (!_subscriber.TryGetTarget(out subscriber) || !subscriber.Tick()) |
||||
|
Stop(); |
||||
|
} |
||||
|
|
||||
|
public TimeSpan Interval |
||||
|
{ |
||||
|
get { return _timer.Interval; } |
||||
|
set { _timer.Interval = value; } |
||||
|
} |
||||
|
|
||||
|
public void Start() => _timer.Start(); |
||||
|
|
||||
|
public void Stop() => _timer.Stop(); |
||||
|
|
||||
|
|
||||
|
public static WeakTimer StartWeakTimer(IWeakTimerSubscriber subscriber, TimeSpan interval) |
||||
|
{ |
||||
|
var timer = new WeakTimer(subscriber) {Interval = interval}; |
||||
|
timer.Start(); |
||||
|
return timer; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,89 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using JetBrains.Annotations; |
||||
|
using Perspex.Utilities; |
||||
|
|
||||
|
namespace Perspex.Controls.Utils |
||||
|
{ |
||||
|
class UndoRedoHelper<TState> : WeakTimer.IWeakTimerSubscriber where TState : IEquatable<TState> |
||||
|
{ |
||||
|
private readonly IUndoRedoHost _host; |
||||
|
|
||||
|
public interface IUndoRedoHost |
||||
|
{ |
||||
|
TState UndoRedoState { get; set; } |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
private readonly LinkedList<TState> _states = new LinkedList<TState>(); |
||||
|
|
||||
|
[NotNull] |
||||
|
private LinkedListNode<TState> _currentNode; |
||||
|
|
||||
|
public int Limit { get; set; } = 10; |
||||
|
|
||||
|
public UndoRedoHelper(IUndoRedoHost host) |
||||
|
{ |
||||
|
_host = host; |
||||
|
_states.AddFirst(_host.UndoRedoState); |
||||
|
_currentNode = _states.First; |
||||
|
WeakTimer.StartWeakTimer(this, new TimeSpan(0, 0, 1)); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public void Undo() |
||||
|
{ |
||||
|
_host.UndoRedoState= (_currentNode = _currentNode?.Previous ?? _currentNode).Value; |
||||
|
} |
||||
|
|
||||
|
public bool IsLastState => _currentNode.Next == null; |
||||
|
|
||||
|
public void UpdateLastState(TState state) |
||||
|
{ |
||||
|
_states.Last.Value = state; |
||||
|
} |
||||
|
|
||||
|
public void UpdateLastState() |
||||
|
{ |
||||
|
_states.Last.Value = _host.UndoRedoState; |
||||
|
} |
||||
|
|
||||
|
public TState LastState => _currentNode.Value; |
||||
|
|
||||
|
public void DiscardRedo() |
||||
|
{ |
||||
|
//Linked list sucks, so we are doing this
|
||||
|
while (_currentNode.Next != null) |
||||
|
_states.Remove(_currentNode.Next); |
||||
|
} |
||||
|
|
||||
|
public void Redo() |
||||
|
{ |
||||
|
_host.UndoRedoState = (_currentNode = _currentNode?.Next ?? _currentNode).Value; |
||||
|
} |
||||
|
|
||||
|
public void Snapshot() |
||||
|
{ |
||||
|
var current = _host.UndoRedoState; |
||||
|
if (!_currentNode.Value.Equals(current)) |
||||
|
{ |
||||
|
if(_currentNode.Next != null) |
||||
|
DiscardRedo(); |
||||
|
_states.AddLast(current); |
||||
|
_currentNode = _states.Last; |
||||
|
if(_states.Count > Limit) |
||||
|
_states.RemoveFirst(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool WeakTimer.IWeakTimerSubscriber.Tick() |
||||
|
{ |
||||
|
Snapshot(); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue