diff options
-rw-r--r-- | Xamarin.PropertyEditing.Tests/PanelViewModelTests.cs | 38 | ||||
-rw-r--r-- | Xamarin.PropertyEditing/OrderedDictionary.cs | 2 | ||||
-rw-r--r-- | Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs | 185 |
3 files changed, 156 insertions, 69 deletions
diff --git a/Xamarin.PropertyEditing.Tests/PanelViewModelTests.cs b/Xamarin.PropertyEditing.Tests/PanelViewModelTests.cs index 6d39f22..0626626 100644 --- a/Xamarin.PropertyEditing.Tests/PanelViewModelTests.cs +++ b/Xamarin.PropertyEditing.Tests/PanelViewModelTests.cs @@ -74,10 +74,10 @@ namespace Xamarin.PropertyEditing.Tests vm.SelectedObjects.Add (obj); Assume.That (vm.ArrangedEditors, Is.Not.Empty); - Assume.That (vm.ArrangedEditors[0].Count, Is.EqualTo (2)); + Assume.That (vm.ArrangedEditors[0].Editors.Count, Is.EqualTo (2)); vm.FilterText = "sub"; - Assert.That (vm.ArrangedEditors[0].Count, Is.EqualTo (1)); + Assert.That (vm.ArrangedEditors[0].Editors.Count, Is.EqualTo (1)); } [Test] @@ -93,7 +93,7 @@ namespace Xamarin.PropertyEditing.Tests vm.SelectedObjects.Add (obj); Assume.That (vm.ArrangedEditors, Is.Not.Empty); - Assume.That (vm.ArrangedEditors[0].Count, Is.EqualTo (2)); + Assume.That (vm.ArrangedEditors[0].Editors.Count, Is.EqualTo (2)); Assume.That (vm.IsFiltering, Is.False); bool changed = false; @@ -104,7 +104,7 @@ namespace Xamarin.PropertyEditing.Tests }; vm.FilterText = "sub"; - Assume.That (vm.ArrangedEditors[0].Count, Is.EqualTo (1)); + Assume.That (vm.ArrangedEditors[0].Editors.Count, Is.EqualTo (1)); Assert.That (vm.IsFiltering, Is.True); Assert.That (changed, Is.True); changed = false; @@ -128,13 +128,13 @@ namespace Xamarin.PropertyEditing.Tests vm.SelectedObjects.Add (obj); Assume.That (vm.ArrangedEditors, Is.Not.Empty); - Assume.That (vm.ArrangedEditors[0].Count, Is.EqualTo (2)); + Assume.That (vm.ArrangedEditors[0].Editors.Count, Is.EqualTo (2)); vm.FilterText = "sub"; - Assume.That (vm.ArrangedEditors[0].Count, Is.EqualTo (1)); + Assume.That (vm.ArrangedEditors[0].Editors.Count, Is.EqualTo (1)); vm.FilterText = String.Empty; - Assert.That (vm.ArrangedEditors[0].Count, Is.EqualTo (2)); + Assert.That (vm.ArrangedEditors[0].Editors.Count, Is.EqualTo (2)); } [Test] @@ -149,7 +149,7 @@ namespace Xamarin.PropertyEditing.Tests vm.SelectedObjects.Add (obj); Assume.That (vm.ArrangedEditors, Is.Not.Empty); - Assert.That (vm.ArrangedEditors.FirstOrDefault (g => g.Key == "Sub"), Is.Not.Null); + Assert.That (vm.ArrangedEditors.FirstOrDefault (g => g.Category == "Sub"), Is.Not.Null); } [Test] @@ -180,9 +180,9 @@ namespace Xamarin.PropertyEditing.Tests vm.ArrangeMode = PropertyArrangeMode.Category; vm.SelectedObjects.Add (editor.Target); - Assert.That (vm.ArrangedEditors[0].Key, Is.EqualTo ("A")); - Assert.That (vm.ArrangedEditors[1].Key, Is.EqualTo ("B")); - Assert.That (vm.ArrangedEditors[2].Key, Is.EqualTo ("C")); + Assert.That (vm.ArrangedEditors[0].Category, Is.EqualTo ("A")); + Assert.That (vm.ArrangedEditors[1].Category, Is.EqualTo ("B")); + Assert.That (vm.ArrangedEditors[2].Category, Is.EqualTo ("C")); } [Test] @@ -201,9 +201,9 @@ namespace Xamarin.PropertyEditing.Tests vm.FilterText = "sub"; Assert.That (vm.ArrangedEditors.Count, Is.EqualTo (1)); - var group = vm.ArrangedEditors.FirstOrDefault (g => g.Key == "Sub"); + var group = vm.ArrangedEditors.FirstOrDefault (g => g.Category == "Sub"); Assert.That (group, Is.Not.Null); - Assert.That (group.Count, Is.EqualTo (1)); + Assert.That (group.Editors.Count, Is.EqualTo (1)); } [Test] @@ -236,8 +236,8 @@ namespace Xamarin.PropertyEditing.Tests vm.SelectedObjects.Add (target); Assert.That (vm.ArrangedEditors.Count, Is.EqualTo (2)); - Assert.That (vm.ArrangedEditors[0].Key, Is.EqualTo ("ints"), "Grouped group not found or out of order"); - Assert.That (vm.ArrangedEditors[1].Key, Is.Null); + Assert.That (vm.ArrangedEditors[0].Category, Is.EqualTo ("ints"), "Grouped group not found or out of order"); + Assert.That (vm.ArrangedEditors[1].Category, Is.Empty); } [Test] @@ -272,7 +272,7 @@ namespace Xamarin.PropertyEditing.Tests vm.FilterText = "name"; Assert.That (vm.ArrangedEditors.Count, Is.EqualTo (1)); - var group = vm.ArrangedEditors.FirstOrDefault (g => g.Key == "ints"); + var group = vm.ArrangedEditors.FirstOrDefault (g => g.Category == "ints"); Assert.That (group, Is.Null); } @@ -332,7 +332,7 @@ namespace Xamarin.PropertyEditing.Tests }; vm.SelectedObjects.Add (target); - Assert.That (vm.ArrangedEditors.Any (g => g.Key == "ints"), Is.True, "Does not have grouped editors category"); + Assert.That (vm.ArrangedEditors.Any (g => g.Category == "ints"), Is.True, "Does not have grouped editors category"); } [Test] @@ -350,7 +350,7 @@ namespace Xamarin.PropertyEditing.Tests vm.SelectedObjects.Add (obj); Assume.That (vm.ArrangedEditors, Is.Not.Empty); - Assert.That (vm.GetIsExpanded (vm.ArrangedEditors[0].Key), Is.True); + Assert.That (vm.GetIsExpanded (vm.ArrangedEditors[0].Category), Is.True); } [Test] @@ -383,7 +383,7 @@ namespace Xamarin.PropertyEditing.Tests vm.SelectedObjects.Add (target); Assume.That (vm.ArrangedEditors, Is.Not.Empty); - Assume.That (vm.ArrangedEditors.Any (g => g.Key == "ints"), Is.True, "Does not have grouped editors category"); + Assume.That (vm.ArrangedEditors.Any (g => g.Category == "ints"), Is.True, "Does not have grouped editors category"); Assert.That (vm.GetIsExpanded ("ints"), Is.True); } diff --git a/Xamarin.PropertyEditing/OrderedDictionary.cs b/Xamarin.PropertyEditing/OrderedDictionary.cs index 69df576..dc0a388 100644 --- a/Xamarin.PropertyEditing/OrderedDictionary.cs +++ b/Xamarin.PropertyEditing/OrderedDictionary.cs @@ -378,7 +378,7 @@ namespace Cadenza.Collections private readonly List<TKey> keyOrder; private class ReadOnlyValueCollection - : IList<TValue> + : IList<TValue>, IReadOnlyList<TValue> { public ReadOnlyValueCollection (OrderedDictionary<TKey, TValue> dict) { diff --git a/Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs b/Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs index ce8ebd7..4b2b47d 100644 --- a/Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs +++ b/Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs @@ -1,11 +1,99 @@ -using System; +using System; using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Threading.Tasks; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Cadenza.Collections; namespace Xamarin.PropertyEditing.ViewModels { + internal class PanelGroupViewModel + { + public PanelGroupViewModel (string category, IEnumerable<EditorViewModel> editors) + { + if (editors == null) + throw new ArgumentNullException (nameof(editors)); + + Category = category; + Add (editors); + } + + public string Category + { + get; + } + + public IReadOnlyList<EditorViewModel> Editors => this.editors; + + public IReadOnlyList<EditorViewModel> UncommonEditors => this.uncommonEditors; + + public bool HasChildElements => Editors.Count > 0 || UncommonEditors.Count > 0; + + public bool UncommonShown + { + get; + set; + } + + public void Add (IEnumerable<EditorViewModel> editors) + { + if (editors == null) + throw new ArgumentNullException (nameof(editors)); + + foreach (EditorViewModel evm in editors) + Add (evm); + } + + public void Add (EditorViewModel editor) + { + if (editor == null) + throw new ArgumentNullException (nameof(editor)); + + GetList (editor).Add (editor); + } + + public bool Remove (EditorViewModel editor) + { + if (editor == null) + throw new ArgumentNullException (nameof(editor)); + + return GetList (editor).Remove (editor); + } + + public bool GetIsExpanded (PropertyArrangeMode mode) + { + if (this.isExpanded == null) + return false; + + this.isExpanded.TryGetValue (mode, out bool expanded); + return expanded; + } + + public void SetIsExpanded (PropertyArrangeMode mode, bool expanded) + { + if (this.isExpanded == null) { + if (!expanded) + return; + + this.isExpanded = new Dictionary<PropertyArrangeMode, bool> (); + } + + this.isExpanded[mode] = expanded; + } + + private Dictionary<PropertyArrangeMode, bool> isExpanded; + private readonly ObservableCollectionEx<EditorViewModel> editors = new ObservableCollectionEx<EditorViewModel> (); + private readonly ObservableCollectionEx<EditorViewModel> uncommonEditors = new ObservableCollectionEx<EditorViewModel> (); + + private IList<EditorViewModel> GetList (EditorViewModel evm) + { + if (evm is PropertyViewModel pvm) + return pvm.Property.IsUncommon ? this.uncommonEditors : this.editors; + else + return this.editors; + } + } + internal class PanelViewModel : PropertiesViewModel, IFilterable { @@ -20,8 +108,11 @@ namespace Xamarin.PropertyEditing.ViewModels public event EventHandler ArrangedPropertiesChanged; - public IReadOnlyList<IGroupingList<string, EditorViewModel>> ArrangedEditors => this.arranged; + public IReadOnlyList<PanelGroupViewModel> ArrangedEditors => (IReadOnlyList<PanelGroupViewModel>)this.arranged.Values; + /// <summary> + /// Gets or sets whether all categories should automatically expand. + /// </summary> public bool AutoExpand { get { return this.autoExpand; } @@ -78,11 +169,10 @@ namespace Xamarin.PropertyEditing.ViewModels public bool GetIsExpanded (string group) { - HashSet<string> groups; - if (!this.expandedGroups.TryGetValue (ArrangeMode, out groups)) + if (!this.arranged.TryGetValue (group, out PanelGroupViewModel panelGroup)) return false; - return groups.Contains (group); + return panelGroup.GetIsExpanded (ArrangeMode); } public void SetIsExpanded (string group, bool isExpanded) @@ -131,37 +221,37 @@ namespace Xamarin.PropertyEditing.ViewModels } } - AutoExpandGroup (grouping.Key); - if (remainingItems != null) - this.arranged.Add (grouping.Key, remainingItems); + string key = grouping.Key ?? String.Empty; + if (remainingItems != null) // TODO: pretty sure this was out of order before, add test + this.arranged.Add (key, new PanelGroupViewModel (key, grouping.Where (evm => remainingItems.Contains (evm)))); else - this.arranged.Add (grouping); + this.arranged.Add (key, new PanelGroupViewModel (key, grouping)); + + AutoExpandGroup (key); } if (groupedTypeProperties != null) { // Insert type-grouped properties back in sorted. int i = 0; foreach (var kvp in groupedTypeProperties.OrderBy (kvp => kvp.Key, CategoryComparer.Instance)) { - var group = new ObservableGrouping<string, EditorViewModel> (kvp.Key) { - new PropertyGroupViewModel (TargetPlatform, kvp.Key, kvp.Value, ObjectEditors) - }; - - AutoExpandGroup (group.Key); + var group = new PanelGroupViewModel (kvp.Key, new[] { new PropertyGroupViewModel (TargetPlatform, kvp.Key, kvp.Value, ObjectEditors) }); bool added = false; for (; i < this.arranged.Count; i++) { - var g = (IGrouping<string, EditorViewModel>) this.arranged[i]; + var g = this.arranged[i]; // TODO: Are we translating categories? If so this needs to lookup the resource and be culture specific // nulls go on the bottom. - if (g.Key == null || String.Compare (g.Key, kvp.Key, StringComparison.Ordinal) > 0) { + if (String.IsNullOrEmpty (g.Category) || String.Compare (g.Category, kvp.Key, StringComparison.Ordinal) > 0) { added = true; - this.arranged.Insert (i, group); + this.arranged.Insert (i, group.Category, group); break; } } if (!added) - this.arranged.Add (group); + this.arranged.Add (group.Category, group); + + AutoExpandGroup (group.Category); } } @@ -172,10 +262,13 @@ namespace Xamarin.PropertyEditing.ViewModels { foreach (EditorViewModel vm in editors) { string g = GetGroup (vm); - var grouping = this.arranged[g] as ObservableGrouping<string, EditorViewModel>; - if (grouping != null) { - this.arranged.Remove (g, vm); - } + PanelGroupViewModel group = this.arranged[g]; + if (group == null) + continue; + + group.Remove (vm); + if (!group.HasChildElements) + this.arranged.Remove (group.Category); } ArrangedPropertiesChanged?.Invoke (this, EventArgs.Empty); @@ -188,10 +281,7 @@ namespace Xamarin.PropertyEditing.ViewModels ArrangedPropertiesChanged?.Invoke (this, EventArgs.Empty); } - private readonly Dictionary<PropertyArrangeMode, HashSet<string>> expandedGroups = new Dictionary<PropertyArrangeMode, HashSet<string>> (); - private readonly ObservableLookup<string, EditorViewModel> arranged = new ObservableLookup<string, EditorViewModel> { - ReuseGroups = true - }; + private readonly OrderedDictionary<string, PanelGroupViewModel> arranged = new OrderedDictionary<string, PanelGroupViewModel> (); private PropertyArrangeMode arrangeMode; private string filterText; @@ -199,38 +289,35 @@ namespace Xamarin.PropertyEditing.ViewModels private void AutoExpandGroup (string group) { - if (AutoExpand || (group != null && TargetPlatform.AutoExpandGroups != null && TargetPlatform.AutoExpandGroups.Contains (group))) - UpdateExpanded (new[] { group }, true); + if (group == null || !this.arranged.TryGetValue (group, out PanelGroupViewModel panelGroup)) + return; + if (!AutoExpand && (TargetPlatform.AutoExpandGroups == null || !TargetPlatform.AutoExpandGroups.Contains (group))) + return; + + UpdateExpanded (new[] { panelGroup }, true); } private void SetIsExpanded (PropertyArrangeMode mode, string group, bool isExpanded) { - if (!this.expandedGroups.TryGetValue (mode, out HashSet<string> groups)) { - if (!isExpanded) - return; + if (!this.arranged.TryGetValue (group, out PanelGroupViewModel panelGroup) || mode == PropertyArrangeMode.Name) + return; - this.expandedGroups[mode] = groups = new HashSet<string> (); - } - - if (isExpanded) - groups.Add (group); - else - groups.Remove (group); + panelGroup.SetIsExpanded (mode, isExpanded); } private void UpdateExpanded (bool expanded) { - UpdateExpanded (this.arranged.Select<IGroupingList<string, EditorViewModel>, string> (g => g.Key), expanded); + UpdateExpanded (this.arranged.Values, expanded); } - private void UpdateExpanded (IEnumerable<string> groups, bool expanded) + private void UpdateExpanded (IEnumerable<PanelGroupViewModel> groups, bool expanded) { - foreach (string group in groups) { + foreach (PanelGroupViewModel group in groups) { foreach (var mode in ArrangeModes) { if (mode.ArrangeMode == PropertyArrangeMode.Name) continue; - SetIsExpanded (mode.ArrangeMode, group, expanded); + group.SetIsExpanded (mode.ArrangeMode, expanded); } } } @@ -248,8 +335,8 @@ namespace Xamarin.PropertyEditing.ViewModels if (FilterText != null && (String.IsNullOrWhiteSpace (oldFilter) || FilterText.StartsWith (oldFilter, StringComparison.OrdinalIgnoreCase))) { var toRemove = new List<EditorViewModel> (); - foreach (var g in this.arranged) { - foreach (var vm in g) { + foreach (PanelGroupViewModel g in this.arranged.Values) { + foreach (EditorViewModel vm in g.Editors.Concat (g.UncommonEditors)) { if (!MatchesFilter (vm)) toRemove.Add (vm); else if (vm is IFilterable) { @@ -272,14 +359,14 @@ namespace Xamarin.PropertyEditing.ViewModels private string GetGroup (EditorViewModel vm) { - return (ArrangeMode == PropertyArrangeMode.Name) ? "0" : vm.Category; + return (ArrangeMode == PropertyArrangeMode.Name) ? "0" : (vm.Category ?? String.Empty); } private bool MatchesFilter (EditorViewModel vm) { if (String.IsNullOrWhiteSpace (FilterText)) return true; - if (ArrangeMode == PropertyArrangeMode.Category && vm.Category != null && vm.Category.Contains (FilterText, StringComparison.OrdinalIgnoreCase)) + if (ArrangeMode == PropertyArrangeMode.Category && !String.IsNullOrEmpty (vm.Category) && vm.Category.Contains (FilterText, StringComparison.OrdinalIgnoreCase)) return true; if (String.IsNullOrWhiteSpace (vm.Name)) return false; |