A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

278 lines
8.5 KiB

#nullable enable
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Harfbuzz;
using Avalonia.Headless;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Platform;
using Avalonia.Threading;
using Avalonia.UnitTests;
using Moq;
using Xunit;
namespace Avalonia.Controls.UnitTests
{
public class TextBoxTests_Input : ScopedTestBase
{
[Fact]
public void Touch_Tap_Moves_Caret()
{
using (UnitTestApplication.Start(Services))
{
var target = new TextBox
{
Template = CreateTemplate(),
Text = "12 12345678"
};
var root = new TestRoot()
{
Child = target
};
target.ApplyTemplate();
root.LayoutManager.ExecuteInitialLayoutPass();
var touch = new TouchTestHelper();
Assert.Equal(target.CaretIndex, 0);
// Move to index 8
touch.Down(target, new Point(50, 0));
touch.Up(target, new Point(50, 0));
Assert.Equal(target.CaretIndex, 8);
}
}
[Fact]
public void Touch_Double_Tap_Selects_Word()
{
using (UnitTestApplication.Start(Services))
{
var target = new TextBox
{
Template = CreateTemplate(),
Text = "12 12345678"
};
var root = new TestRoot()
{
Child = target
};
target.ApplyTemplate();
root.LayoutManager.ExecuteInitialLayoutPass();
var touch = new TouchTestHelper();
Assert.Equal(target.CaretIndex, 0);
// Move to index 8
touch.Down(target, new Point(50, 0));
touch.Up(target, new Point(50, 0));
// Double tap
touch.Down(target, new Point(50, 0));
touch.Up(target, new Point(50, 0));
Assert.Equal(target.SelectionStart, 3);
Assert.Equal(target.SelectionEnd, 11);
Assert.Equal(target.CaretIndex, 8);
}
}
[Fact]
public void Touch_Hold_Selects_Word()
{
using (UnitTestApplication.Start(Services))
{
var target = new TextBox
{
Template = CreateTemplate(),
Text = "12 12345678"
};
var root = new TestRoot()
{
Child = target
};
target.ApplyTemplate();
root.LayoutManager.ExecuteInitialLayoutPass();
var touch = new TouchTestHelper();
Assert.Equal(target.CaretIndex, 0);
// Move to index 8
touch.Down(target, new Point(50, 0));
var timer = Assert.Single(Dispatcher.SnapshotTimersForUnitTests());
timer.ForceFire();
touch.Up(target, new Point(50, 0));
Assert.Equal(target.SelectionStart, 3);
Assert.Equal(target.SelectionEnd, 11);
Assert.Equal(target.CaretIndex, 8);
}
}
[Fact]
public void Touch_Hold_On_Selection_Requests_Context()
{
using (UnitTestApplication.Start(Services))
{
var target = new TextBox
{
Template = CreateTemplate(),
Text = "12 12345678"
};
var root = new TestRoot()
{
Child = target
};
target.ApplyTemplate();
bool requested = false;
target.SelectionStart = 3;
target.SelectionEnd = 11;
target.ContextRequested += Target_ContextRequested;
root.LayoutManager.ExecuteInitialLayoutPass();
var touch = new TouchTestHelper();
Assert.Equal(target.CaretIndex, 0);
// Move to index 8
touch.Down(target, new Point(50, 0));
var timer = Assert.Single(Dispatcher.SnapshotTimersForUnitTests());
timer.ForceFire();
touch.Up(target, new Point(50, 0));
Assert.True(requested);
void Target_ContextRequested(object? sender, ContextRequestedEventArgs e)
{
requested = true;
}
}
}
private static TestServices Services => TestServices.MockThreadingInterface.With(
standardCursorFactory: Mock.Of<ICursorFactory>(),
renderInterface: new HeadlessPlatformRenderInterface(),
textShaperImpl: new HarfBuzzTextShaper(),
fontManagerImpl: new TestFontManager(),
assetLoader: new StandardAssetLoader());
internal static IControlTemplate CreateTemplate()
{
return new FuncControlTemplate<TextBox>((control, scope) =>
new ScrollViewer
{
Name = "PART_ScrollViewer",
Template = new FuncControlTemplate<ScrollViewer>(ScrollViewerTests.CreateTemplate),
Content = new TextPresenter
{
Name = "PART_TextPresenter",
[!!TextPresenter.TextProperty] = new Binding
{
Path = nameof(TextPresenter.Text),
Mode = BindingMode.TwoWay,
Priority = BindingPriority.Template,
RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
},
[!!TextPresenter.CaretIndexProperty] = new Binding
{
Path = nameof(TextPresenter.CaretIndex),
Mode = BindingMode.TwoWay,
Priority = BindingPriority.Template,
RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
}
}.RegisterInNameScope(scope)
}.RegisterInNameScope(scope));
}
private static void RaiseKeyEvent(TextBox textBox, Key key, KeyModifiers inputModifiers)
{
textBox.RaiseEvent(new KeyEventArgs
{
RoutedEvent = InputElement.KeyDownEvent,
KeyModifiers = inputModifiers,
Key = key
});
}
private static void RaiseTextEvent(TextBox textBox, string text)
{
textBox.RaiseEvent(new TextInputEventArgs
{
RoutedEvent = InputElement.TextInputEvent,
Text = text
});
}
private class Class1 : NotifyingBase
{
private int _foo;
private string? _bar;
public int Foo
{
get { return _foo; }
set { _foo = value; RaisePropertyChanged(); }
}
public string? Bar
{
get { return _bar; }
set { _bar = value; RaisePropertyChanged(); }
}
}
private class TestTopLevel(ITopLevelImpl impl) : TopLevel(impl)
{
}
private static Mock<ITopLevelImpl> CreateMockTopLevelImpl()
{
var clipboard = new Mock<ITopLevelImpl>();
clipboard.Setup(x => x.Compositor).Returns(RendererMocks.CreateDummyCompositor());
clipboard.Setup(r => r.TryGetFeature(typeof(IClipboard)))
.Returns(new Clipboard(new HeadlessClipboardImplStub()));
clipboard.SetupGet(x => x.RenderScaling).Returns(1);
return clipboard;
}
private static FuncControlTemplate<TestTopLevel> CreateTopLevelTemplate()
{
return new FuncControlTemplate<TestTopLevel>((x, scope) =>
new ContentPresenter
{
Name = "PART_ContentPresenter",
[!ContentPresenter.ContentProperty] = x[!ContentControl.ContentProperty],
}.RegisterInNameScope(scope));
}
private class TestContextMenu : ContextMenu
{
public TestContextMenu()
{
IsOpen = true;
}
}
}
}