Browse Source

Merge branch 'master' of https://github.com/Perspex/Perspex

pull/346/head
Steven Kirk 10 years ago
parent
commit
768ed9d9bf
  1. 1
      src/Perspex.Base/Perspex.Base.csproj
  2. 55
      src/Perspex.Base/Utilities/WeakTimer.cs
  3. 5
      src/Perspex.Controls/Perspex.Controls.csproj
  4. 48
      src/Perspex.Controls/TextBox.cs
  5. 89
      src/Perspex.Controls/Utils/UndoRedoHelper.cs
  6. 1
      src/Perspex.Controls/packages.config

1
src/Perspex.Base/Perspex.Base.csproj

@ -85,6 +85,7 @@
<Compile Include="Utilities\MathUtilities.cs" />
<Compile Include="Utilities\TypeUtilities.cs" />
<Compile Include="Utilities\WeakSubscriptionManager.cs" />
<Compile Include="Utilities\WeakTimer.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="JetBrains.Annotations.PCL328, Version=9.2.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">

55
src/Perspex.Base/Utilities/WeakTimer.cs

@ -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;
}
}
}

5
src/Perspex.Controls/Perspex.Controls.csproj

@ -148,6 +148,7 @@
<Compile Include="Utils\StringUtils.cs" />
<Compile Include="TopLevel.cs" />
<Compile Include="Primitives\PopupRoot.cs" />
<Compile Include="Utils\UndoRedoHelper.cs" />
<Compile Include="Window.cs" />
<Compile Include="RowDefinition.cs" />
<Compile Include="RowDefinitions.cs" />
@ -173,6 +174,10 @@
<Compile Include="WrapPanel.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="JetBrains.Annotations.PCL328, Version=9.2.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
<HintPath>..\..\packages\JetBrains.Annotations.9.2.0\lib\portable-net4+sl5+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\JetBrains.Annotations.PCL328.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Reactive.Core">
<HintPath>..\..\packages\Rx-Core.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.Core.dll</HintPath>
</Reference>

48
src/Perspex.Controls/TextBox.cs

@ -17,7 +17,7 @@ using Perspex.Metadata;
namespace Perspex.Controls
{
public class TextBox : TemplatedControl
public class TextBox : TemplatedControl, UndoRedoHelper<TextBox.UndoRedoState>.IUndoRedoHost
{
public static readonly PerspexProperty<bool> AcceptsReturnProperty =
PerspexProperty.Register<TextBox, bool>("AcceptsReturn");
@ -49,7 +49,22 @@ namespace Perspex.Controls
public static readonly PerspexProperty<bool> UseFloatingWatermarkProperty =
PerspexProperty.Register<TextBox, bool>("UseFloatingWatermark");
struct UndoRedoState : IEquatable<UndoRedoState>
{
public string Text { get; }
public int CaretPosition { get; }
public UndoRedoState(string text, int caretPosition)
{
Text = text;
CaretPosition = caretPosition;
}
public bool Equals(UndoRedoState other) => ReferenceEquals(Text, other.Text) || Equals(Text, other.Text);
}
private TextPresenter _presenter;
private UndoRedoHelper<UndoRedoState> _undoRedoHelper;
static TextBox()
{
@ -73,6 +88,7 @@ namespace Perspex.Controls
ScrollViewer.HorizontalScrollBarVisibilityProperty,
horizontalScrollBarVisibility,
BindingPriority.Style);
_undoRedoHelper = new UndoRedoHelper<UndoRedoState>(this);
}
public bool AcceptsReturn
@ -90,7 +106,12 @@ namespace Perspex.Controls
public int CaretIndex
{
get { return GetValue(CaretIndexProperty); }
set { SetValue(CaretIndexProperty, value); }
set
{
SetValue(CaretIndexProperty, value);
if (_undoRedoHelper.IsLastState && _undoRedoHelper.LastState.Text == Text)
_undoRedoHelper.UpdateLastState();
}
}
public int SelectionStart
@ -173,6 +194,7 @@ namespace Perspex.Controls
Text = text.Substring(0, caretIndex) + input + text.Substring(caretIndex);
CaretIndex += input.Length;
SelectionStart = SelectionEnd = CaretIndex;
_undoRedoHelper.DiscardRedo();
}
}
@ -189,7 +211,7 @@ namespace Perspex.Controls
{
return;
}
_undoRedoHelper.Snapshot();
HandleTextInput(text);
}
@ -223,6 +245,16 @@ namespace Perspex.Controls
Paste();
}
break;
case Key.Z:
if (modifiers == InputModifiers.Control)
_undoRedoHelper.Undo();
break;
case Key.Y:
if (modifiers == InputModifiers.Control)
_undoRedoHelper.Redo();
break;
case Key.Left:
MoveHorizontal(-1, modifiers);
@ -524,5 +556,15 @@ namespace Perspex.Controls
return i;
}
UndoRedoState UndoRedoHelper<UndoRedoState>.IUndoRedoHost.UndoRedoState
{
get { return new UndoRedoState(Text, CaretIndex); }
set
{
Text = value.Text;
SelectionStart = SelectionEnd = CaretIndex = value.CaretPosition;
}
}
}
}

89
src/Perspex.Controls/Utils/UndoRedoHelper.cs

@ -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;
}
}
}

1
src/Perspex.Controls/packages.config

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="JetBrains.Annotations" version="9.2.0" targetFramework="portable45-net45+win8" />
<package id="Rx-Core" version="2.2.5" targetFramework="portable-net45+win8" />
<package id="Rx-Interfaces" version="2.2.5" targetFramework="portable-net45+win8" />
<package id="Rx-Linq" version="2.2.5" targetFramework="portable-net45+win8" />

Loading…
Cancel
Save