diff options
3 files changed, 166 insertions, 34 deletions
diff --git a/Xamarin.PropertyEditing.Tests/PropertiesViewModelTests.cs b/Xamarin.PropertyEditing.Tests/PropertiesViewModelTests.cs index 824e9fb..ace3fb2 100644 --- a/Xamarin.PropertyEditing.Tests/PropertiesViewModelTests.cs +++ b/Xamarin.PropertyEditing.Tests/PropertiesViewModelTests.cs @@ -936,6 +936,7 @@ namespace Xamarin.PropertyEditing.Tests var stringVms = vm.Properties.OfType<StringPropertyViewModel> ().ToList(); Assert.That (stringVms.Count, Is.EqualTo (3), "Not including correct number of properties with variants"); Assert.That (stringVms.Count (svm => svm.Variation == null), Is.EqualTo (1), "Did not include neutral property"); + Assert.That (stringVms.First (svm => svm.Variation == null).HasVariantChildren, Is.True, "Did not report variant children"); Assert.That (stringVms.Count (svm => svm.Variation == variants[0]), Is.EqualTo (1), "Missing variant property"); Assert.That (stringVms.Count (svm => svm.Variation == variants[1]), Is.EqualTo (1), "Missing variant property"); } @@ -968,12 +969,17 @@ namespace Xamarin.PropertyEditing.Tests editor.SetupGet (oe => oe.TargetType).Returns (typeof (object).ToTypeInfo ()); editor.SetupGet (oe => oe.Target).Returns (target); editor.SetupGet (oe => oe.Properties).Returns (new[] { property.Object }); - editor.Setup (oe => oe.GetPropertyVariantsAsync (property.Object)).ReturnsAsync (new[] { variants[1] }); + editor.Setup (oe => oe.GetPropertyVariantsAsync (property.Object)).ReturnsAsync (new[] { variants[0], variants[1] }); editor.Setup (oe => oe.GetValueAsync<string> (property.Object, null)).ReturnsAsync ( new ValueInfo<string> { Value = "Any", Source = ValueSource.Local }); + editor.Setup (oe => oe.GetValueAsync<string> (property.Object, variants[0])).ReturnsAsync ( + new ValueInfo<string> { + Value = "Compact", + Source = ValueSource.Local + }); editor.Setup (oe => oe.GetValueAsync<string> (property.Object, variants[1])).ReturnsAsync ( new ValueInfo<string> { Value = "Compact+P3", @@ -1005,9 +1011,11 @@ namespace Xamarin.PropertyEditing.Tests vm.SelectedObjects.Add (target); var stringVms = vm.Properties.OfType<StringPropertyViewModel> ().ToList (); - Assume.That (stringVms.Count, Is.EqualTo (2), "Not including correct number of properties with variants"); - Assume.That (stringVms.Count (svm => svm.Variation == null), Is.EqualTo (1), "Did not include neutral property"); - Assume.That (stringVms.Count (svm => svm.Variation == variants[1]), Is.EqualTo (1), "Missing variant property"); + Assert.That (stringVms.Count, Is.EqualTo (3), "Not including correct number of properties with variants"); + Assert.That (stringVms.Count (svm => svm.Variation == null), Is.EqualTo (1), "Did not include neutral property"); + Assert.That (stringVms.First (svm => svm.Variation == null).HasVariantChildren, Is.True, "Did not report variant children"); + Assert.That (stringVms.Count (svm => svm.Variation == variants[0]), Is.EqualTo (1), "Missing variant property"); + Assert.That (stringVms.Count (svm => svm.Variation == variants[1]), Is.EqualTo (1), "Missing variant property"); vm.SelectedObjects.Add (target2); @@ -1105,6 +1113,84 @@ namespace Xamarin.PropertyEditing.Tests } [Test] + public void VariantsUpdated () + { + var variations = new[] { + new PropertyVariationOption ("Width", "Compact"), + new PropertyVariationOption ("Width", "Regular"), + new PropertyVariationOption ("Gamut", "P3"), + new PropertyVariationOption ("Gamut", "sRGB"), + }; + + var property = new Mock<IPropertyInfo> (); + property.SetupGet (p => p.Name).Returns ("Variation"); + property.SetupGet (p => p.Type).Returns (typeof (string)); + property.SetupGet (p => p.RealType).Returns (typeof (string).ToTypeInfo ()); + property.SetupGet (p => p.CanWrite).Returns (true); + property.SetupGet (p => p.ValueSources).Returns (ValueSources.Default | ValueSources.Local); + property.SetupGet (p => p.Variations).Returns (variations); + + var properties = new ObservableCollection<IPropertyInfo> { property.Object }; + + var variation0 = new PropertyVariation (variations[0]); + var variation1 = new PropertyVariation (variations[0], variations[2]); + var variants = new List<PropertyVariation> { + variation0, + variation1 + }; + + var target = new object (); + var editor = new Mock<IObjectEditor> (); + editor.SetupGet (oe => oe.TargetType).Returns (typeof (object).ToTypeInfo ()); + editor.SetupGet (oe => oe.Target).Returns (target); + editor.SetupGet (oe => oe.Properties).Returns (properties); + editor.Setup (oe => oe.GetPropertyVariantsAsync (property.Object)).ReturnsAsync (variants); + editor.Setup (oe => oe.GetValueAsync<string> (property.Object, null)).ReturnsAsync ( + new ValueInfo<string> { + Value = "Any", + Source = ValueSource.Local + }); + editor.Setup (oe => oe.GetValueAsync<string> (property.Object, variation0)).ReturnsAsync ( + new ValueInfo<string> { + Value = "Compact", + Source = ValueSource.Local + }); + editor.Setup (oe => oe.GetValueAsync<string> (property.Object, variation1)).ReturnsAsync ( + new ValueInfo<string> { + Value = "Compact+P3", + Source = ValueSource.Local + }); + + var provider = new Mock<IEditorProvider> (); + provider.Setup (p => p.GetObjectEditorAsync (target)).ReturnsAsync (editor.Object); + + var vm = CreateVm (provider.Object); + vm.SelectedObjects.Add (target); + + var stringVms = vm.Properties.OfType<StringPropertyViewModel> ().ToList (); + Assume.That (stringVms.Count, Is.EqualTo (3), "Not including correct number of properties with variants"); + Assume.That (stringVms.Count (svm => svm.Variation == null), Is.EqualTo (1), "Did not include neutral property"); + Assume.That (stringVms.First (svm => svm.Variation == null).HasVariantChildren, Is.True, "Did not report variant children"); + Assume.That (stringVms.Count (svm => svm.Variation == variants[0]), Is.EqualTo (1), "Missing variant property"); + Assume.That (stringVms.Count (svm => svm.Variation == variants[1]), Is.EqualTo (1), "Missing variant property"); + + variants.RemoveAt (1); + editor.Raise (oe => oe.PropertyChanged += null, new EditorPropertyChangedEventArgs (property.Object)); + + Assert.That (vm.Properties.Count, Is.EqualTo (2)); + Assert.That (stringVms.Count (svm => svm.Variation == null), Is.EqualTo (1), "Did not include neutral property"); + Assert.That (stringVms.First (svm => svm.Variation == null).HasVariantChildren, Is.True, "Did not report variant children"); + Assert.That (stringVms.Count (svm => svm.Variation == variants[0]), Is.EqualTo (1), "Missing variant property"); + + variants.RemoveAt (0); + editor.Raise (oe => oe.PropertyChanged += null, new EditorPropertyChangedEventArgs (property.Object)); + + Assert.That (vm.Properties.Count, Is.EqualTo (1)); + Assert.That (stringVms.Count (svm => svm.Variation == null), Is.EqualTo (1), "Did not include neutral property"); + Assert.That (stringVms.First (svm => svm.Variation == null).HasVariantChildren, Is.False, "Did not update variant children"); + } + + [Test] public void VariantsAddedWhenPropertyIs () { var variations = new[] { @@ -1231,6 +1317,7 @@ namespace Xamarin.PropertyEditing.Tests stringVms = vm.Properties.OfType<StringPropertyViewModel> ().ToList (); Assert.That (stringVms.Count, Is.EqualTo (3), "Not including correct number of properties with variants"); Assert.That (stringVms.Count (svm => svm.Variation == null), Is.EqualTo (1), "Did not include neutral property"); + Assert.That (stringVms.First (svm => svm.Variation == null).HasVariantChildren, Is.True, "Did not report variant children"); Assert.That (stringVms.Count (svm => svm.Variation == variants[0]), Is.EqualTo (1), "Missing variant property"); Assert.That (stringVms.Count (svm => svm.Variation == variants[1]), Is.EqualTo (1), "Missing variant property"); } diff --git a/Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs b/Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs index e97647b..9bf6bf1 100644 --- a/Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs +++ b/Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs @@ -167,6 +167,7 @@ namespace Xamarin.PropertyEditing.ViewModels removedEditors = new IObjectEditor[e.OldItems.Count]; for (int i = 0; i < e.OldItems.Count; i++) { IObjectEditor editor = this.objEditors.First (oe => oe?.Target == e.OldItems[i]); + editor.PropertyChanged -= OnObjectEditorPropertyChanged; INotifyCollectionChanged notifier = editor.Properties as INotifyCollectionChanged; if (notifier != null) notifier.CollectionChanged -= OnObjectEditorPropertiesChanged; @@ -181,8 +182,13 @@ namespace Xamarin.PropertyEditing.ViewModels case NotifyCollectionChangedAction.Reset: { removedEditors = new IObjectEditor[this.objEditors.Count]; for (int i = 0; i < removedEditors.Length; i++) { - removedEditors[i] = this.objEditors[i]; - INotifyCollectionChanged notifier = removedEditors[i]?.Properties as INotifyCollectionChanged; + IObjectEditor editor = this.objEditors[i]; + if (editor == null) + continue; + + removedEditors[i] = editor; + editor.PropertyChanged -= OnObjectEditorPropertyChanged; + INotifyCollectionChanged notifier = editor.Properties as INotifyCollectionChanged; if (notifier != null) notifier.CollectionChanged -= OnObjectEditorPropertiesChanged; } @@ -226,20 +232,45 @@ namespace Xamarin.PropertyEditing.ViewModels ErrorsChanged?.Invoke (this, e); } + private void OnObjectEditorPropertyChanged (object sender, EditorPropertyChangedEventArgs e) + { + if (!e.Property.HasVariations ()) + return; + + OnVariationsChanged (e.Property, EventArgs.Empty); + } + private async void OnVariationsChanged (object sender, EventArgs e) { + IPropertyInfo property = sender as IPropertyInfo; + if (property == null) + property = ((PropertyViewModel)sender).Property; + using (await AsyncWork.RequestAsyncWork (this)) { - PropertyViewModel pvm = (PropertyViewModel) sender; - var variations = (await GetVariationsAsync (pvm.Property)).SelectMany (vs => vs).Distinct (); - var properties = this.editors - .OfType<PropertyViewModel> () - .Where (evm => Equals (evm.Property, pvm.Property) && evm.Variation != null) - .ToDictionary (evm => evm.Variation); + var variationsTask = GetVariationsAsync (property); + + PropertyViewModel baseVm = null; + var properties = new Dictionary<PropertyVariation, PropertyViewModel> (); + foreach (PropertyViewModel pvm in this.editors.OfType<PropertyViewModel> ()) { + if (!Equals (property, pvm.Property)) + continue; + + if (pvm.Variation == null) + baseVm = pvm; + else + properties.Add (pvm.Variation, pvm); + } + + if (baseVm == null) + throw new InvalidOperationException ("Base property view model couldn't be found"); + + var variations = await variationsTask; + baseVm.HasVariantChildren = variations.Count > 0; List<PropertyViewModel> toAdd = new List<PropertyViewModel> (); foreach (PropertyVariation variation in variations) { if (!properties.Remove (variation)) { - toAdd.Add (GetViewModel (pvm.Property, variation)); + toAdd.Add (CreateViewModel (property, variation)); } } @@ -472,12 +503,11 @@ namespace Xamarin.PropertyEditing.ViewModels List<EditorViewModel> newVms = new List<EditorViewModel> (); foreach (IPropertyInfo property in newSet) { if (variations != null && variations.TryGetValue (property, out Dictionary<PropertyVariation, PropertyViewModel> propertyVariations)) { - var setVariations = (await GetVariationsAsync (property)).SelectMany (vs => vs).Distinct (); - foreach (PropertyVariation variation in setVariations) { + foreach (PropertyVariation variation in await GetVariationsAsync (property)) { if (propertyVariations.Remove (variation)) continue; - newVms.Add (GetViewModel (property, variation)); + newVms.Add (CreateViewModel (property, variation)); } foreach (var kvp in propertyVariations) { @@ -486,7 +516,7 @@ namespace Xamarin.PropertyEditing.ViewModels } else if (property.HasVariations()) { newVms.AddRange (await GetViewModelsAsync (property)); } else { - newVms.Add (GetViewModel (property)); + newVms.Add (CreateViewModel (property)); } } @@ -511,6 +541,7 @@ namespace Xamarin.PropertyEditing.ViewModels if (editor == null) continue; + editor.PropertyChanged += OnObjectEditorPropertyChanged; var notifier = editor.Properties as INotifyCollectionChanged; if (notifier != null) notifier.CollectionChanged += OnObjectEditorPropertiesChanged; @@ -532,35 +563,28 @@ namespace Xamarin.PropertyEditing.ViewModels tcs.SetResult (true); } - private Task<IReadOnlyCollection<PropertyVariation>[]> GetVariationsAsync (IPropertyInfo property) + private async Task<IReadOnlyCollection<PropertyVariation>> GetVariationsAsync (IPropertyInfo property) { var variantTasks = new List<Task<IReadOnlyCollection<PropertyVariation>>> (ObjectEditors.Count); for (int i = 0; i < ObjectEditors.Count; i++) { variantTasks.Add (ObjectEditors[i].GetPropertyVariantsAsync (property)); } - return Task.WhenAll (variantTasks); + return (await Task.WhenAll (variantTasks)).SelectMany (vs => vs).Distinct ().ToList (); } - private async Task<IReadOnlyList<PropertyViewModel>> GetViewModelsAsync (IPropertyInfo property, IEnumerable<PropertyVariation> variantsToSkip = null) + private async Task<IReadOnlyList<PropertyViewModel>> GetViewModelsAsync (IPropertyInfo property) { - PropertyViewModel baseVm = GetViewModel (property); - List<PropertyViewModel> vms = new List<PropertyViewModel> (); - vms.Add (baseVm); - - HashSet<PropertyVariation> skipped = - (variantsToSkip != null) ? new HashSet<PropertyVariation> (variantsToSkip) : null; + PropertyViewModel baseVm = CreateViewModel (property); + var vms = new List<PropertyViewModel> { baseVm }; if (baseVm.HasVariations) { using (await AsyncWork.RequestAsyncWork (this)) { var variants = await GetVariationsAsync (property); - for (int i = 0; i < variants.Length; i++) { - foreach (PropertyVariation variant in variants[i]) { - if (skipped != null && skipped.Contains (variant)) - continue; + baseVm.HasVariantChildren = variants.Count > 0; - vms.Add (GetViewModel (property, variant)); - } + foreach (PropertyVariation variant in variants) { + vms.Add (CreateViewModel (property, variant)); } } } @@ -568,7 +592,7 @@ namespace Xamarin.PropertyEditing.ViewModels return vms; } - private PropertyViewModel GetViewModel (IPropertyInfo property, PropertyVariation variant = null) + private PropertyViewModel CreateViewModel (IPropertyInfo property, PropertyVariation variant = null) { PropertyViewModel vm; Type[] interfaces = property.GetType ().GetInterfaces (); diff --git a/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs b/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs index 951e848..2d6319b 100644 --- a/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs +++ b/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs @@ -516,7 +516,7 @@ namespace Xamarin.PropertyEditing.ViewModels private bool CanCreateVariation () { - return HasVariations; + return HasVariations && !IsVariant; } private async void OnCreateVariation () @@ -611,8 +611,26 @@ namespace Xamarin.PropertyEditing.ViewModels get { return Property.CanWrite && TargetPlatform.BindingProvider != null && Property.ValueSources.HasFlag (ValueSources.Binding); } } + /// <summary> + /// Gets whether the property has possible variations. + /// </summary> public bool HasVariations => Property.HasVariations(); + public bool HasVariantChildren + { + get { return this.hasVariantChildren; } + internal set + { + if (this.hasVariantChildren == value) + return; + + this.hasVariantChildren = value; + OnPropertyChanged(); + } + } + + public bool IsVariant => Variation != null; + public abstract Resource Resource { get; @@ -737,6 +755,8 @@ namespace Xamarin.PropertyEditing.ViewModels try { if (e.Property != null && !Equals (e.Property, Property)) return; + if (!Equals (Variation, e.Variation)) + return; // TODO: Smarter querying, can query the single editor and check against MultipleValues await UpdateCurrentValueAsync (); @@ -800,6 +820,7 @@ namespace Xamarin.PropertyEditing.ViewModels private HashSet<IPropertyInfo> constraintProperties; private string error; private Task<bool> isAvailable; + private bool hasVariantChildren; private void OnErrorsChanged (DataErrorsChangedEventArgs e) { |