Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/xamarin/Xamarin.PropertyEditing.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Maupin <ermaup@microsoft.com>2018-09-28 21:34:28 +0300
committerEric Maupin <ermaup@microsoft.com>2018-09-28 22:28:48 +0300
commit2e52e0bff0fac4668d810f466fa9019bd330963d (patch)
treee468fefddbe9ff00a1b35447f2082850d976eee0
parent46c79646e2cbf01526305628f6d83f37fe5a1627 (diff)
[Core/Win] Add input modes
-rw-r--r--Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs5
-rw-r--r--Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs1
-rw-r--r--Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockPropertyInfo.cs15
-rw-r--r--Xamarin.PropertyEditing.Tests/PropertyViewModelTests.cs118
-rw-r--r--Xamarin.PropertyEditing.Windows/Themes/Resources.xaml27
-rw-r--r--Xamarin.PropertyEditing/IHaveInputModes.cs38
-rw-r--r--Xamarin.PropertyEditing/ViewModels/NumericPropertyViewModel.cs1
-rw-r--r--Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs52
-rw-r--r--Xamarin.PropertyEditing/Xamarin.PropertyEditing.csproj1
9 files changed, 250 insertions, 8 deletions
diff --git a/Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs b/Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs
index 8004461..d11daf9 100644
--- a/Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs
+++ b/Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs
@@ -14,7 +14,8 @@ namespace Xamarin.PropertyEditing.Tests.MockControls
public void AddProperty<T> (string name, string category = null,
bool canWrite = true, bool flag = false,
IEnumerable<Type> converterTypes = null,
- string description = null, bool constrained = true, ValueSources valueSources = ValueSources.Local | ValueSources.Default | ValueSources.Binding)
+ string description = null, bool constrained = true, ValueSources valueSources = ValueSources.Local | ValueSources.Default | ValueSources.Binding,
+ IReadOnlyList<InputMode> inputModes = null)
{
IPropertyInfo propertyInfo;
if (typeof(T).IsEnum) {
@@ -22,6 +23,8 @@ namespace Xamarin.PropertyEditing.Tests.MockControls
var enumPropertyInfoType = typeof (MockEnumPropertyInfo<,>)
.MakeGenericType (underlyingType, typeof (T));
propertyInfo = (IPropertyInfo)Activator.CreateInstance (enumPropertyInfoType, name, description, category, canWrite, flag, converterTypes, constrained);
+ } else if (inputModes != null) {
+ propertyInfo = new MockPropertyInfoWithInputTypes<T> (name, inputModes, description, category, canWrite, converterTypes, valueSources);
} else {
propertyInfo = new MockPropertyInfo<T> (name, description, category, canWrite, converterTypes, valueSources);
}
diff --git a/Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs b/Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs
index 3fe6e51..c4d88fb 100644
--- a/Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs
+++ b/Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs
@@ -16,6 +16,7 @@ namespace Xamarin.PropertyEditing.Tests.MockControls
AddProperty<int> ("UnsetInteger", ReadWrite, valueSources: ValueSources.Local);
AddProperty<float> ("FloatingPoint", ReadWrite);
AddProperty<string> ("String", ReadWrite, valueSources: ValueSources.Local | ValueSources.Resource | ValueSources.Binding);
+ AddProperty<int> ("Width", ReadWrite, valueSources: ValueSources.Local | ValueSources.Resource | ValueSources.Binding, inputModes: new[] { new InputMode("Auto", true), new InputMode("Star"), new InputMode("Pixel"), });
AddProperty<Enumeration> ("Enumeration", ReadWrite, constrained: false);
AddProperty<FlagsNoValues> ("FlagsNoValues", ReadWrite, canWrite: true, flag: true);
AddProperty<FlagsWithValues> ("FlagsWithValues", ReadWrite, canWrite: true, flag: true);
diff --git a/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockPropertyInfo.cs b/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockPropertyInfo.cs
index 035b646..e21737e 100644
--- a/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockPropertyInfo.cs
+++ b/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockPropertyInfo.cs
@@ -6,6 +6,21 @@ using Xamarin.PropertyEditing.Tests.MockControls;
namespace Xamarin.PropertyEditing.Tests.MockPropertyInfo
{
+ public class MockPropertyInfoWithInputTypes<T>
+ : MockPropertyInfo<T>, IHaveInputModes
+ {
+ public MockPropertyInfoWithInputTypes (string name, IReadOnlyList<InputMode> inputModes, string description = null, string category = null, bool canWrite = true, IEnumerable<Type> converterTypes = null, ValueSources valueSources = ValueSources.Default | ValueSources.Local)
+ : base (name, description, category, canWrite, converterTypes, valueSources)
+ {
+ InputModes = inputModes.ToArray ();
+ }
+
+ public IReadOnlyList<InputMode> InputModes
+ {
+ get;
+ }
+ }
+
public class MockPropertyInfo<T> : IPropertyInfo, IPropertyConverter, IEquatable<MockPropertyInfo<T>>
{
public MockPropertyInfo (string name, string description = null, string category = null, bool canWrite = true, IEnumerable<Type> converterTypes = null, ValueSources valueSources = ValueSources.Local | ValueSources.Default)
diff --git a/Xamarin.PropertyEditing.Tests/PropertyViewModelTests.cs b/Xamarin.PropertyEditing.Tests/PropertyViewModelTests.cs
index c207b4f..ae2fa61 100644
--- a/Xamarin.PropertyEditing.Tests/PropertyViewModelTests.cs
+++ b/Xamarin.PropertyEditing.Tests/PropertyViewModelTests.cs
@@ -1175,6 +1175,124 @@ namespace Xamarin.PropertyEditing.Tests
Assert.That (tcs.Task.IsCanceled, Is.True);
}
+ [Test]
+ public void DoesntHaveInputModes ()
+ {
+ var vm = GetBasicTestModel ();
+ Assert.That (vm.HasInputModes, Is.False);
+ }
+
+ [Test]
+ public void HasInputModes ()
+ {
+ var modes = new[] { new InputMode ("TestMode") };
+
+ var property = GetPropertyMock ();
+ var input = property.As<IHaveInputModes> ();
+ input.SetupGet (im => im.InputModes).Returns (modes);
+
+ var vm = GetViewModel (property.Object, new MockObjectEditor (property.Object));
+
+ Assert.That (vm.HasInputModes, Is.True, "HasInputModes was false");
+ Assert.That (vm.InputModes, Contains.Item (modes[0]));
+ Assert.That (vm.InputModes.Count, Is.EqualTo (1));
+ Assert.That (vm.InputMode, Is.EqualTo (modes[0]), "InputMode not set to a default on no value set for it");
+ }
+
+ [Test]
+ public void InputModeCommits ()
+ {
+ var modes = new[] { new InputMode ("TestMode"), new InputMode("TestMode2"), };
+
+ var property = GetPropertyMock ();
+ var input = property.As<IHaveInputModes> ();
+ input.SetupGet (im => im.InputModes).Returns (modes);
+
+ var target = new object ();
+ var editor = new Mock<IObjectEditor> ();
+ editor.SetupGet (e => e.Target).Returns (target);
+ editor.SetupGet (e => e.Properties).Returns (new[] { property.Object });
+ SetupPropertySetAndGet (editor, property.Object);
+
+ var vm = GetViewModel (property.Object, editor.Object);
+ Assume.That (vm.InputMode, Is.EqualTo (modes[0]));
+
+ vm.InputMode = modes[1];
+ editor.Verify (oe => oe.SetValueAsync (property.Object, It.Is<ValueInfo<TValue>> (vi => vi.ValueDescriptor == modes[1]), It.IsAny<PropertyVariation> ()));
+ }
+
+ [Test]
+ public async Task InputModeRestores ()
+ {
+ var modes = new[] { new InputMode ("TestMode"), new InputMode ("TestMode2"), };
+
+ var property = GetPropertyMock ();
+ var input = property.As<IHaveInputModes> ();
+ input.SetupGet (im => im.InputModes).Returns (modes);
+
+ var target = new object ();
+ var editor = new Mock<IObjectEditor> ();
+ editor.SetupGet (e => e.Target).Returns (target);
+ editor.SetupGet (e => e.Properties).Returns (new[] { property.Object });
+ SetupPropertySetAndGet (editor, property.Object);
+
+ TValue value = GetRandomTestValue ();
+
+ await editor.Object.SetValueAsync (property.Object, new ValueInfo<TValue> {
+ Value = value,
+ ValueDescriptor = modes[1]
+ });
+
+ var vm = GetViewModel (property.Object, editor.Object);
+ Assert.That (vm.InputMode, Is.EqualTo (modes[1]));
+ }
+
+ [TestCase (true)]
+ [TestCase (false)]
+ public void InputEnabled (bool writeEnabled)
+ {
+ var property = GetPropertyMock ();
+ property.SetupGet (pi => pi.CanWrite).Returns (writeEnabled);
+
+ var vm = GetViewModel (property.Object, new MockObjectEditor (property.Object));
+ Assert.That (vm.IsInputEnabled, Is.EqualTo (writeEnabled));
+ }
+
+ [TestCase (true, false, true)]
+ [TestCase (true, true, false)]
+ [TestCase (false, true, false)]
+ [TestCase (false, false, false)]
+ public void InputEnabledSingleValueInputMode (bool writeEnabled, bool singleValue, bool expectation)
+ {
+ var modes = new[] { new InputMode ("TestMode"), new InputMode ("TestMode2", singleValue), };
+
+ var property = GetPropertyMock ();
+ property.SetupGet (pi => pi.CanWrite).Returns (writeEnabled);
+ var input = property.As<IHaveInputModes> ();
+ input.SetupGet (im => im.InputModes).Returns (modes);
+
+ var target = new object ();
+ var editor = new Mock<IObjectEditor> ();
+ editor.SetupGet (e => e.Target).Returns (target);
+ editor.SetupGet (e => e.Properties).Returns (new[] { property.Object });
+ SetupPropertySetAndGet (editor, property.Object);
+
+ var vm = GetViewModel (property.Object, editor.Object);
+ Assume.That (vm.InputMode, Is.EqualTo (modes[0]));
+ Assume.That (vm.IsInputEnabled, Is.EqualTo (writeEnabled), "Initial state didn't match property");
+
+ bool changed = false;
+ vm.PropertyChanged += (o, e) => {
+ if (e.PropertyName == nameof(PropertyViewModel<TValue>.IsInputEnabled))
+ changed = true;
+ };
+
+ vm.InputMode = modes[1];
+
+ Assert.That (changed, Is.EqualTo (writeEnabled != expectation));
+ Assert.That (vm.IsInputEnabled, Is.EqualTo (expectation));
+ }
+
protected TViewModel GetViewModel (IPropertyInfo property, IObjectEditor editor)
{
return GetViewModel (property, new[] { editor });
diff --git a/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml b/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml
index 3f61759..4ae48b6 100644
--- a/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml
+++ b/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml
@@ -277,7 +277,15 @@
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:StringEditorControl">
- <local:TextBoxEx x:Name="TextBox" FocusSelectsAll="True" AutomationProperties.Name="{Binding Property.Name,Mode=OneTime}" Text="{Binding Value,UpdateSourceTrigger=Explicit}" IsEnabled="{Binding Property.CanWrite,Mode=OneTime}" VerticalContentAlignment="Center" />
+ <Grid>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+
+ <local:TextBoxEx x:Name="TextBox" Grid.Column="0" FocusSelectsAll="True" AutomationProperties.Name="{Binding Property.Name,Mode=OneTime}" Text="{Binding Value,UpdateSourceTrigger=Explicit}" VerticalContentAlignment="Center" IsEnabled="{Binding IsInputEnabled}" />
+ <ComboBox Grid.Column="1" Visibility="{Binding HasInputModes,Mode=OneTime,Converter={StaticResource BoolToVisibilityConverter}}" IsEnabled="{Binding Property.CanWrite,Mode=OneTime}" ItemsSource="{Binding InputModes,Mode=OneTime}" SelectedItem="{Binding InputMode,Mode=TwoWay}" DisplayMemberPath="Identifier" />
+ </Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
@@ -2022,17 +2030,17 @@
<local:NumericTemplateSelector x:Key="NumericTemplateSelector">
<local:NumericTemplateSelector.FloatingTemplate>
<DataTemplate DataType="vm:FloatingPropertyViewModel">
- <local:DoubleUpDownControl Value="{Binding Value}" IsEnabled="{Binding Property.CanWrite,Mode=OneTime}" AutomationProperties.Name="{Binding Property.Name,Mode=OneTime}" />
+ <local:DoubleUpDownControl Value="{Binding Value}" IsEnabled="{Binding IsInputEnabled}" AutomationProperties.Name="{Binding Property.Name,Mode=OneTime}" />
</DataTemplate>
</local:NumericTemplateSelector.FloatingTemplate>
<local:NumericTemplateSelector.IntegerTemplate>
<DataTemplate DataType="vm:IntegerPropertyViewModel">
- <local:IntegerUpDownControl Value="{Binding Value}" IsEnabled="{Binding Property.CanWrite,Mode=OneTime}" AutomationProperties.Name="{Binding Property.Name,Mode=OneTime}" />
+ <local:IntegerUpDownControl Value="{Binding Value}" IsEnabled="{Binding IsInputEnabled}" AutomationProperties.Name="{Binding Property.Name,Mode=OneTime}" />
</DataTemplate>
</local:NumericTemplateSelector.IntegerTemplate>
<local:NumericTemplateSelector.ByteTemplate>
<DataTemplate DataType="vm:BytePropertyViewModel">
- <local:ByteUpDownControl Value="{Binding Value}" IsEnabled="{Binding Property.CanWrite,Mode=OneTime}" AutomationProperties.LabeledBy="{TemplateBinding AutomationProperties.LabeledBy}" />
+ <local:ByteUpDownControl Value="{Binding Value}" IsEnabled="{Binding IsInputEnabled}" AutomationProperties.LabeledBy="{TemplateBinding AutomationProperties.LabeledBy}" />
</DataTemplate>
</local:NumericTemplateSelector.ByteTemplate>
</local:NumericTemplateSelector>
@@ -2042,7 +2050,16 @@
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:NumericEditorControl">
- <ContentPresenter Content="{Binding}" ContentTemplateSelector="{StaticResource NumericTemplateSelector}" AutomationProperties.Name="{Binding Property.Name,Mode=OneTime}" />
+ <Grid>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+
+ <ContentPresenter Grid.Column="0" Content="{Binding}" ContentTemplateSelector="{StaticResource NumericTemplateSelector}" AutomationProperties.Name="{Binding Property.Name,Mode=OneTime}" />
+ <ComboBox Grid.Column="1" Visibility="{Binding HasInputModes,Mode=OneTime,Converter={StaticResource BoolToVisibilityConverter}}" IsEnabled="{Binding Property.CanWrite,Mode=OneTime}" ItemsSource="{Binding InputModes,Mode=OneTime}" SelectedItem="{Binding InputMode,Mode=TwoWay}" DisplayMemberPath="Identifier" />
+ </Grid>
+
</ControlTemplate>
</Setter.Value>
</Setter>
diff --git a/Xamarin.PropertyEditing/IHaveInputModes.cs b/Xamarin.PropertyEditing/IHaveInputModes.cs
new file mode 100644
index 0000000..10ed26c
--- /dev/null
+++ b/Xamarin.PropertyEditing/IHaveInputModes.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+
+namespace Xamarin.PropertyEditing
+{
+ public class InputMode
+ {
+ public InputMode (string identifier, bool isSingleValue = false)
+ {
+ if (identifier == null)
+ throw new ArgumentNullException (nameof(identifier));
+
+ Identifier = identifier;
+ IsSingleValue = isSingleValue;
+ }
+
+ public string Identifier
+ {
+ get;
+ }
+
+ public bool IsSingleValue
+ {
+ get;
+ }
+ }
+
+ /// <summary>
+ /// Indicates a property has input modes.
+ /// </summary>
+ /// <remarks>
+ /// Implemented on <see cref="IPropertyInfo"/> instances.
+ /// </remarks>
+ public interface IHaveInputModes
+ {
+ IReadOnlyList<InputMode> InputModes { get; }
+ }
+}
diff --git a/Xamarin.PropertyEditing/ViewModels/NumericPropertyViewModel.cs b/Xamarin.PropertyEditing/ViewModels/NumericPropertyViewModel.cs
index 2666421..710023f 100644
--- a/Xamarin.PropertyEditing/ViewModels/NumericPropertyViewModel.cs
+++ b/Xamarin.PropertyEditing/ViewModels/NumericPropertyViewModel.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Windows.Input;
namespace Xamarin.PropertyEditing.ViewModels
diff --git a/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs b/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs
index 605b8c5..20985a1 100644
--- a/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs
+++ b/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs
@@ -25,6 +25,10 @@ namespace Xamarin.PropertyEditing.ViewModels
public PropertyViewModel (TargetPlatform platform, IPropertyInfo property, IEnumerable<IObjectEditor> editors)
: base (platform, property, editors)
{
+ if (property is IHaveInputModes inputModes) {
+ InputModes = inputModes.InputModes.ToArray ();
+ }
+
this.coerce = property as ICoerce<TValue>;
this.validator = property as IValidator<TValue>;
this.valueNavigator = property as ICanNavigateToSource;
@@ -58,7 +62,8 @@ namespace Xamarin.PropertyEditing.ViewModels
SetValue (new ValueInfo<TValue> {
Source = ValueSource.Local,
- Value = value
+ Value = value,
+ ValueDescriptor = InputMode
});
}
}
@@ -121,8 +126,40 @@ namespace Xamarin.PropertyEditing.ViewModels
}
}
+ public override bool IsInputEnabled => base.IsInputEnabled && (InputMode == null || !InputMode.IsSingleValue);
+
public override bool SupportsValueSourceNavigation => this.valueNavigator != null;
+ public bool HasInputModes => InputModes != null && InputModes.Count > 0;
+
+ public IReadOnlyList<InputMode> InputModes
+ {
+ get;
+ }
+
+ public InputMode InputMode
+ {
+ get { return this.inputMode; }
+ set
+ {
+ if (this.inputMode == value)
+ return;
+
+ bool enabled = IsInputEnabled;
+
+ this.inputMode = value;
+ OnPropertyChanged ();
+ if (enabled != IsInputEnabled)
+ OnPropertyChanged (nameof (IsInputEnabled));
+
+ SetValue (new ValueInfo<TValue> {
+ Source = ValueSource.Local,
+ Value = (CurrentValue != null && !value.IsSingleValue) ? CurrentValue.Value : default(TValue),
+ ValueDescriptor = value
+ });
+ }
+ }
+
protected ValueInfo<TValue> CurrentValue
{
get { return this.value; }
@@ -276,6 +313,7 @@ namespace Xamarin.PropertyEditing.ViewModels
private readonly IValidator<TValue> validator;
private readonly ICanNavigateToSource valueNavigator;
internal const string NullableName = "Nullable`1";
+ private InputMode inputMode;
private bool isNullable;
private ValueInfo<TValue> value;
@@ -302,7 +340,16 @@ namespace Xamarin.PropertyEditing.ViewModels
return false;
this.value = newValue;
+ if (newValue != null && newValue.ValueDescriptor is InputMode newMode) {
+ this.inputMode = newMode;
+ } else if (HasInputModes) {
+ this.inputMode = InputModes.FirstOrDefault ();
+ }
+
OnValueChanged ();
+ if (HasInputModes)
+ OnPropertyChanged (nameof(InputMode));
+
SignalValueChange();
((RelayCommand) ConvertToLocalValueCommand)?.ChangeCanExecute ();
@@ -466,7 +513,6 @@ namespace Xamarin.PropertyEditing.ViewModels
SetupConstraints ();
this.requestResourceCommand = new RelayCommand (OnRequestResource, CanRequestResource);
-
}
public event EventHandler<ResourceRequestedEventArgs> ResourceRequested;
@@ -497,6 +543,8 @@ namespace Xamarin.PropertyEditing.ViewModels
public virtual bool CanDelve => false;
+ public virtual bool IsInputEnabled => Property.CanWrite;
+
public bool SupportsResources
{
get { return TargetPlatform.ResourceProvider != null && Property.CanWrite && Property.ValueSources.HasFlag (ValueSources.Resource); }
diff --git a/Xamarin.PropertyEditing/Xamarin.PropertyEditing.csproj b/Xamarin.PropertyEditing/Xamarin.PropertyEditing.csproj
index 22785d8..3dfb115 100644
--- a/Xamarin.PropertyEditing/Xamarin.PropertyEditing.csproj
+++ b/Xamarin.PropertyEditing/Xamarin.PropertyEditing.csproj
@@ -83,6 +83,7 @@
<Compile Include="ICoerce.cs" />
<Compile Include="ICompleteValues.cs" />
<Compile Include="IEditorProvider.cs" />
+ <Compile Include="IHaveInputModes.cs" />
<Compile Include="IObjectEventEditor.cs" />
<Compile Include="IEventInfo.cs" />
<Compile Include="INameableObject.cs" />