Browse Source

Merge branch 'master' into fixes/633-virt-horizontal-scroll

pull/758/head
Steven Kirk 10 years ago
committed by GitHub
parent
commit
bdeac95e9c
  1. 2
      .travis.yml
  2. 2
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  3. 34
      src/Avalonia.Controls/TextBox.cs
  4. 59
      src/Avalonia.Controls/Utils/UndoRedoHelper.cs
  5. 11
      src/Avalonia.SceneGraph/Media/FormattedText.cs
  6. 4
      src/Skia/Avalonia.Skia/FormattedTextImpl.cs
  7. 52
      tests/Avalonia.Controls.UnitTests/TextBoxTests.cs
  8. 1
      tests/Avalonia.SceneGraph.UnitTests/Avalonia.SceneGraph.UnitTests.csproj
  9. 28
      tests/Avalonia.SceneGraph.UnitTests/Media/FormattedTextTests.cs

2
.travis.yml

@ -3,7 +3,7 @@ os:
- linux
- osx
mono:
- latest
- 4.4.2
script:
- ./build.sh --target "Travis" --platform "Mono" --configuration "Release"
notifications:

2
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -219,7 +219,7 @@ namespace Avalonia.Controls.Presenters
{
var text = Text;
if (!string.IsNullOrWhiteSpace(text))
if (!string.IsNullOrEmpty(text))
{
return base.MeasureOverride(availableSize);
}

34
src/Avalonia.Controls/TextBox.cs

@ -148,7 +148,8 @@ namespace Avalonia.Controls
{
value = CoerceCaretIndex(value);
SetAndRaise(CaretIndexProperty, ref _caretIndex, value);
if (_undoRedoHelper.IsLastState && _undoRedoHelper.LastState.Text == Text)
UndoRedoState state;
if (_undoRedoHelper.TryGetLastState(out state) && state.Text == Text)
_undoRedoHelper.UpdateLastState();
}
}
@ -379,8 +380,20 @@ namespace Avalonia.Controls
if (!DeleteSelection() && CaretIndex > 0)
{
SetTextInternal(text.Substring(0, caretIndex - 1) + text.Substring(caretIndex));
--CaretIndex;
var removedCharacters = 1;
// handle deleting /r/n
// you don't ever want to leave a dangling /r around. So, if deleting /n, check to see if
// a /r should also be deleted.
if (CaretIndex > 1 &&
text[CaretIndex - 1] == '\n' &&
text[CaretIndex - 2] == '\r')
{
removedCharacters = 2;
}
SetTextInternal(text.Substring(0, caretIndex - removedCharacters) + text.Substring(caretIndex));
CaretIndex -= removedCharacters;
SelectionStart = SelectionEnd = CaretIndex;
}
break;
@ -393,7 +406,18 @@ namespace Avalonia.Controls
if (!DeleteSelection() && caretIndex < text.Length)
{
SetTextInternal(text.Substring(0, caretIndex) + text.Substring(caretIndex + 1));
var removedCharacters = 1;
// handle deleting /r/n
// you don't ever want to leave a dangling /r around. So, if deleting /n, check to see if
// a /r should also be deleted.
if (CaretIndex < text.Length - 1 &&
text[caretIndex + 1] == '\n' &&
text[caretIndex] == '\r')
{
removedCharacters = 2;
}
SetTextInternal(text.Substring(0, caretIndex) + text.Substring(caretIndex + removedCharacters));
}
break;
@ -695,7 +719,7 @@ namespace Avalonia.Controls
private void SelectAll()
{
SelectionStart = 0;
SelectionEnd = Text.Length;
SelectionEnd = Text?.Length ?? 0;
}
private bool DeleteSelection()

59
src/Avalonia.Controls/Utils/UndoRedoHelper.cs

@ -8,20 +8,19 @@ using Avalonia.Utilities;
namespace Avalonia.Controls.Utils
{
class UndoRedoHelper<TState> : WeakTimer.IWeakTimerSubscriber where TState : IEquatable<TState>
class UndoRedoHelper<TState> : WeakTimer.IWeakTimerSubscriber where TState : struct, IEquatable<TState>
{
private readonly IUndoRedoHost _host;
public interface IUndoRedoHost
{
TState UndoRedoState { get; set; }
TState UndoRedoState { get; set; }
}
private readonly LinkedList<TState> _states = new LinkedList<TState>();
[NotNull]
private LinkedListNode<TState> _currentNode;
public int Limit { get; set; } = 10;
@ -29,24 +28,31 @@ namespace Avalonia.Controls.Utils
public UndoRedoHelper(IUndoRedoHost host)
{
_host = host;
_states.AddFirst(_host.UndoRedoState);
_currentNode = _states.First;
WeakTimer.StartWeakTimer(this, new TimeSpan(0, 0, 1));
WeakTimer.StartWeakTimer(this, TimeSpan.FromSeconds(1));
}
public void Undo()
{
if (_currentNode?.Previous != null)
{
_currentNode = _currentNode.Previous;
}
_host.UndoRedoState = _currentNode.Value;
if (_currentNode?.Previous != null)
{
_currentNode = _currentNode.Previous;
_host.UndoRedoState = _currentNode.Value;
}
}
public bool IsLastState => _currentNode.Next == null;
public bool IsLastState => _currentNode != null && _currentNode.Next == null;
public bool TryGetLastState(out TState _state)
{
_state = default(TState);
if (!IsLastState)
return false;
_state = _currentNode.Value;
return true;
}
public bool HasState => _currentNode != null;
public void UpdateLastState(TState state)
{
_states.Last.Value = state;
@ -57,34 +63,31 @@ namespace Avalonia.Controls.Utils
_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)
while (_currentNode?.Next != null)
_states.Remove(_currentNode.Next);
}
public void Redo()
{
if (_currentNode?.Next != null) {
_currentNode = _currentNode.Next;
}
_host.UndoRedoState = _currentNode.Value;
{
if (_currentNode?.Next != null)
{
_currentNode = _currentNode.Next;
_host.UndoRedoState = _currentNode.Value;
}
}
public void Snapshot()
{
var current = _host.UndoRedoState;
if (!_currentNode.Value.Equals(current))
if (_currentNode == null || !_currentNode.Value.Equals(current))
{
if(_currentNode.Next != null)
if (_currentNode?.Next != null)
DiscardRedo();
_states.AddLast(current);
_currentNode = _states.Last;
if(_states.Count > Limit)
if (_states.Count > Limit)
_states.RemoveFirst();
}
}

11
src/Avalonia.SceneGraph/Media/FormattedText.cs

@ -33,7 +33,16 @@ namespace Avalonia.Media
{
Contract.Requires<ArgumentNullException>(text != null);
Contract.Requires<ArgumentNullException>(fontFamilyName != null);
Contract.Requires<ArgumentException>(fontSize > 0);
if (fontSize <= 0)
{
throw new ArgumentException("FontSize must be greater than 0");
}
if (fontWeight <= 0)
{
throw new ArgumentException("FontWeight must be greater than 0");
}
Text = text;
FontFamilyName = fontFamilyName;

4
src/Skia/Avalonia.Skia/FormattedTextImpl.cs

@ -28,7 +28,9 @@ namespace Avalonia.Skia
//Paint.TextEncoding = SKTextEncoding.Utf8;
_paint.TextEncoding = SKTextEncoding.Utf16;
_paint.IsStroke = false;
_paint.IsAntialias = true;
_paint.IsAntialias = true;
_paint.LcdRenderText = true;
_paint.SubpixelText = true;
_paint.Typeface = typeface;
_paint.TextSize = (float)fontSize;
_paint.TextAlign = textAlignment.ToSKTextAlign();

52
tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

@ -41,6 +41,58 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void Press_Ctrl_A_Select_All_Text()
{
using (UnitTestApplication.Start(Services))
{
var target = new TextBox
{
Template = CreateTemplate(),
Text = "1234"
};
RaiseKeyEvent(target, Key.A, InputModifiers.Control);
Assert.Equal(0, target.SelectionStart);
Assert.Equal(4, target.SelectionEnd);
}
}
[Fact]
public void Press_Ctrl_A_Select_All_Null_Text()
{
using (UnitTestApplication.Start(Services))
{
var target = new TextBox
{
Template = CreateTemplate()
};
RaiseKeyEvent(target, Key.A, InputModifiers.Control);
Assert.Equal(0, target.SelectionStart);
Assert.Equal(0, target.SelectionEnd);
}
}
[Fact]
public void Press_Ctrl_Z_Will_Not_Modify_Text()
{
using (UnitTestApplication.Start(Services))
{
var target = new TextBox
{
Template = CreateTemplate(),
Text = "1234"
};
RaiseKeyEvent(target, Key.Z, InputModifiers.Control);
Assert.Equal("1234", target.Text);
}
}
[Fact]
public void Typing_Beginning_With_0_Should_Not_Modify_Text_When_Bound_To_Int()
{

1
tests/Avalonia.SceneGraph.UnitTests/Avalonia.SceneGraph.UnitTests.csproj

@ -77,6 +77,7 @@
<Otherwise />
</Choose>
<ItemGroup>
<Compile Include="Media\FormattedTextTests.cs" />
<Compile Include="Media\PathMarkupParserTests.cs" />
<Compile Include="RelativeRectComparer.cs" />
<Compile Include="RelativeRectTests.cs" />

28
tests/Avalonia.SceneGraph.UnitTests/Media/FormattedTextTests.cs

@ -0,0 +1,28 @@
using System;
using Avalonia.Media;
using Xunit;
namespace Avalonia.SceneGraph.UnitTests.Media
{
public class FormattedTextTests
{
[Fact]
public void Exception_Should_Be_Thrown_If_FontSize_0()
{
Assert.Throws<ArgumentException>(() => new FormattedText(
"foo",
"Ariel",
0));
}
[Fact]
public void Exception_Should_Be_Thrown_If_FontWeight_0()
{
Assert.Throws<ArgumentException>(() => new FormattedText(
"foo",
"Ariel",
12,
fontWeight: 0));
}
}
}
Loading…
Cancel
Save