TwoFactorInput now handles paste event

This commit is contained in:
Haacked 2015-03-01 00:05:42 -08:00
Родитель e8a88ecfec
Коммит d74db93d66
4 изменённых файлов: 176 добавлений и 17 удалений

Просмотреть файл

@ -4,7 +4,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DesignTimeStyleHelper"
xmlns:ui="clr-namespace:GitHub.UI;assembly=GitHub.UI"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
@ -31,5 +31,9 @@
<StackPanel x:Name="container">
</StackPanel>
<Border Margin="20">
<ui:TwoFactorInput />
</Border>
</StackPanel>
</Window>

Просмотреть файл

@ -1,7 +1,9 @@
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using NullGuard;
namespace GitHub.UI
{
@ -13,26 +15,68 @@ namespace GitHub.UI
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(TwoFactorInput), new PropertyMetadata(""));
TextBox[] TextBoxes;
public TwoFactorInput()
{
InitializeComponent();
SetupTextBox(one);
SetupTextBox(two);
SetupTextBox(three);
SetupTextBox(four);
SetupTextBox(five);
SetupTextBox(six);
TextBoxes = new[]
{
one,
two,
three,
four,
five,
six
};
foreach(var textBox in TextBoxes)
{
SetupTextBox(textBox);
}
}
private void OnPaste(object sender, DataObjectPastingEventArgs e)
{
var isText = e.SourceDataObject.GetDataPresent(DataFormats.Text, true);
if (!isText) return;
var text = e.SourceDataObject.GetData(DataFormats.Text) as string;
if (text == null) return;
e.CancelCommand();
SetText(text);
}
void SetText(string text)
{
if (String.IsNullOrEmpty(text))
{
foreach (var textBox in TextBoxes)
{
textBox.Text = "";
}
SetValue(TextProperty, text);
return;
}
var digits = text.Where(Char.IsDigit).ToList();
for (int i = 0; i < Math.Min(6, digits.Count); i++)
{
TextBoxes[i].Text = digits[i].ToString();
}
SetValue(TextProperty, String.Join("", digits));
}
public string Text
{
get { return (string)GetValue(TextProperty); }
private set { SetValue(TextProperty, value); }
set { SetText(value); }
}
private void SetupTextBox(TextBox textBox)
{
DataObject.AddPastingHandler(textBox, new DataObjectPastingEventHandler(OnPaste));
textBox.GotFocus += (sender, args) => textBox.SelectAll();
textBox.PreviewKeyDown += (sender, args) =>
@ -48,18 +92,30 @@ namespace GitHub.UI
&& args.Key != Key.D8
&& args.Key != Key.D9
&& args.Key != Key.Tab
&& args.Key != Key.Escape)
&& args.Key != Key.Escape
&& (!(args.Key == Key.V && args.KeyboardDevice.Modifiers == ModifierKeys.Control))
&& (!(args.Key == Key.Insert && args.KeyboardDevice.Modifiers == ModifierKeys.Shift)))
{
args.Handled = true;
}
};
textBox.SelectionChanged += (sender, args) =>
{
// Make sure we can't insert additional text into a textbox.
// Each textbox should only allow one character.
if (textBox.SelectionLength == 0 && textBox.Text.Any())
{
textBox.SelectAll();
}
};
textBox.TextChanged += (sender, args) =>
{
var tRequest = new TraversalRequest(FocusNavigationDirection.Next);
var keyboardFocus = Keyboard.FocusedElement as UIElement;
Text = GetTwoFactorCode();
SetValue(TextProperty, String.Join("", GetTwoFactorCode()));
if (keyboardFocus != null)
{
@ -68,19 +124,14 @@ namespace GitHub.UI
};
}
private string GetTextBoxValue(TextBox textBox)
private static string GetTextBoxValue(TextBox textBox)
{
return String.IsNullOrEmpty(textBox.Text) ? " " : textBox.Text;
}
private string GetTwoFactorCode()
{
return GetTextBoxValue(one)
+ GetTextBoxValue(two)
+ GetTextBoxValue(three)
+ GetTextBoxValue(four)
+ GetTextBoxValue(five)
+ GetTextBoxValue(six);
return String.Join("", TextBoxes.Select(textBox => textBox.Text));
}
}
}

Просмотреть файл

@ -0,0 +1,95 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using GitHub.UI;
using Xunit;
using Xunit.Extensions;
public class TwoFactorInputTests
{
public class TheTextProperty
{
[Fact]
public void SetsTextBoxesToIndividualCharacters()
{
var twoFactorInput = new TwoFactorInput();
var textBoxes = GetChildrenRecursive(twoFactorInput).OfType<TextBox>().ToList();
twoFactorInput.Text = "012345";
Assert.Equal("012345", twoFactorInput.Text);
Assert.Equal("0", textBoxes[0].Text);
Assert.Equal("1", textBoxes[1].Text);
Assert.Equal("2", textBoxes[2].Text);
Assert.Equal("3", textBoxes[3].Text);
Assert.Equal("4", textBoxes[4].Text);
Assert.Equal("5", textBoxes[5].Text);
}
[Fact]
public void IgnoresNonDigitCharacters()
{
var twoFactorInput = new TwoFactorInput();
var textBoxes = GetChildrenRecursive(twoFactorInput).OfType<TextBox>().ToList();
twoFactorInput.Text = "01xyz2345";
Assert.Equal("012345", twoFactorInput.Text);
Assert.Equal("0", textBoxes[0].Text);
Assert.Equal("1", textBoxes[1].Text);
Assert.Equal("2", textBoxes[2].Text);
Assert.Equal("3", textBoxes[3].Text);
Assert.Equal("4", textBoxes[4].Text);
Assert.Equal("5", textBoxes[5].Text);
}
[Fact]
public void HandlesNotEnoughCharacters()
{
var twoFactorInput = new TwoFactorInput();
var textBoxes = GetChildrenRecursive(twoFactorInput).OfType<TextBox>().ToList();
twoFactorInput.Text = "012";
Assert.Equal("012", twoFactorInput.Text);
Assert.Equal("0", textBoxes[0].Text);
Assert.Equal("1", textBoxes[1].Text);
Assert.Equal("2", textBoxes[2].Text);
Assert.Equal("", textBoxes[3].Text);
Assert.Equal("", textBoxes[4].Text);
Assert.Equal("", textBoxes[5].Text);
}
[Theory]
[InlineData(null, null)]
[InlineData("", "")]
[InlineData("xxxx", "")]
public void HandlesNullAndStringsWithNoDigits(string input, string expected)
{
var twoFactorInput = new TwoFactorInput();
var textBoxes = GetChildrenRecursive(twoFactorInput).OfType<TextBox>().ToList();
twoFactorInput.Text = input;
Assert.Equal(expected, twoFactorInput.Text);
Assert.Equal("", textBoxes[0].Text);
Assert.Equal("", textBoxes[1].Text);
Assert.Equal("", textBoxes[2].Text);
Assert.Equal("", textBoxes[3].Text);
Assert.Equal("", textBoxes[4].Text);
Assert.Equal("", textBoxes[5].Text);
}
static IEnumerable<FrameworkElement> GetChildrenRecursive(FrameworkElement element)
{
yield return element;
foreach (var child in LogicalTreeHelper.GetChildren(element)
.Cast<FrameworkElement>()
.SelectMany(GetChildrenRecursive))
{
yield return child;
}
}
}
}

Просмотреть файл

@ -58,6 +58,8 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\NSubstitute.1.8.1.0\lib\net45\NSubstitute.dll</HintPath>
</Reference>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="ReactiveUI, Version=6.3.1.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\reactiveui-core.6.3.1\lib\Net45\ReactiveUI.dll</HintPath>
@ -73,11 +75,13 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll</HintPath>
</Reference>
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
<Reference Include="xunit">
<HintPath>..\..\packages\xunit.1.9.2\lib\net20\xunit.dll</HintPath>
</Reference>
@ -88,6 +92,7 @@
<ItemGroup>
<Compile Include="Args.cs" />
<Compile Include="GitHub.App\ViewModels\LoginControlViewModelTests.cs" />
<Compile Include="GitHub.UI\TwoFactorInputTests.cs" />
<Compile Include="GitHubPackageTests.cs" />
<Compile Include="Helpers\LazySubstitute.cs" />
<Compile Include="TestDoubles\FakeMenuCommandService.cs" />
@ -106,6 +111,10 @@
<Project>{158B05E8-FDBC-4D71-B871-C96E28D5ADF5}</Project>
<Name>GitHub.UI.Reactive</Name>
</ProjectReference>
<ProjectReference Include="..\GitHub.UI\GitHub.UI.csproj">
<Project>{346384dd-2445-4a28-af22-b45f3957bd89}</Project>
<Name>GitHub.UI</Name>
</ProjectReference>
<ProjectReference Include="..\GitHub.VisualStudio\GitHub.VisualStudio.csproj">
<Project>{11569514-5ae5-4b5b-92a2-f10b0967de5f}</Project>
<Name>GitHub.VisualStudio</Name>