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-08-31 20:59:22 +0300
committerEric Maupin <ermaup@microsoft.com>2018-09-05 00:02:35 +0300
commit718623c0ef4257404e44673676c8fa69f30c9000 (patch)
tree4b12a004988ef84bc4a812885e99fe63fbae4e10
parent1afb2f28df3a9516c3cd42f6c4889d2122abe454 (diff)
[Core] Add custom expression autocomplete support
-rw-r--r--Xamarin.PropertyEditing.Tests/PropertyViewModelTests.cs90
-rw-r--r--Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs91
2 files changed, 181 insertions, 0 deletions
diff --git a/Xamarin.PropertyEditing.Tests/PropertyViewModelTests.cs b/Xamarin.PropertyEditing.Tests/PropertyViewModelTests.cs
index 00d7e3b..c207b4f 100644
--- a/Xamarin.PropertyEditing.Tests/PropertyViewModelTests.cs
+++ b/Xamarin.PropertyEditing.Tests/PropertyViewModelTests.cs
@@ -1085,6 +1085,96 @@ namespace Xamarin.PropertyEditing.Tests
Assert.That (requested, Is.True, "CreateResourceRequested did not fire");
}
+ [TestCase (true, true, true)]
+ [TestCase (false, true, false)]
+ [TestCase (true, false, false)]
+ public void AutocompleteEnabled (bool customExpressions, bool hasInterface, bool expected)
+ {
+ var target = new object ();
+ var property = GetPropertyMock ();
+ var editor = new Mock<IObjectEditor> ();
+ editor.SetupGet (e => e.Target).Returns (target);
+ SetupPropertySetAndGet (editor, property.Object);
+
+ if (hasInterface) {
+ string[] results = { "Foo", "Bar", "Baz" };
+ var complete = editor.As<ICompleteValues> ();
+ complete.Setup (c => c.GetCompletionsAsync (property.Object, It.IsAny<string> (), It.IsAny<CancellationToken> ()))
+ .ReturnsAsync (results);
+ }
+
+ var mockProvider = new MockEditorProvider (editor.Object);
+ var resources = new MockResourceProvider ();
+ var platform = new TargetPlatform (mockProvider, resources) {
+ SupportsCustomExpressions = customExpressions
+ };
+
+ var vm = GetViewModel (platform, property.Object, new[] { editor.Object });
+ Assert.That (vm.SupportsAutocomplete, Is.EqualTo (expected));
+ }
+
+ [Test]
+ public void AutocompleteResults ()
+ {
+ var target = new object ();
+ var property = GetPropertyMock ();
+ var editor = new Mock<IObjectEditor> ();
+ editor.SetupGet (e => e.Target).Returns (target);
+ SetupPropertySetAndGet (editor, property.Object);
+
+ string[] results = new[] { "Foo", "Bar", "Baz" };
+ var complete = editor.As<ICompleteValues> ();
+ complete.Setup (c => c.GetCompletionsAsync (property.Object, It.IsAny<string> (), It.IsAny<CancellationToken> ())).ReturnsAsync (results);
+
+ var mockProvider = new MockEditorProvider (editor.Object);
+ var resources = new MockResourceProvider();
+ var platform = new TargetPlatform (mockProvider, resources) {
+ SupportsCustomExpressions = true
+ };
+
+ var vm = GetViewModel (platform, property.Object, new[] { editor.Object });
+ Assume.That (vm.SupportsAutocomplete, Is.True);
+ vm.PreviewCustomExpression = "preview";
+
+ CollectionAssert.AreEqual (vm.AutocompleteItems, results);
+ complete.Verify (c => c.GetCompletionsAsync (property.Object, "preview", It.IsAny<CancellationToken> ()));
+ }
+
+ [Test]
+ public void AutocompleteCancels ()
+ {
+ var target = new object ();
+ var property = GetPropertyMock ();
+ var editor = new Mock<IObjectEditor> ();
+ editor.SetupGet (e => e.Target).Returns (target);
+ SetupPropertySetAndGet (editor, property.Object);
+
+ string[] results = new[] { "Foo", "Bar", "Baz" };
+ var tcs = new TaskCompletionSource<IReadOnlyList<string>> ();
+
+ var complete = editor.As<ICompleteValues> ();
+ complete.Setup (c => c.GetCompletionsAsync (property.Object, It.IsAny<string> (), It.IsAny<CancellationToken> ()))
+ .Returns<IPropertyInfo,string,CancellationToken> ((a,b,c) => {
+ c.Register (() => {
+ tcs.TrySetCanceled ();
+ });
+ return tcs.Task;
+ });
+
+ var mockProvider = new MockEditorProvider (editor.Object);
+ var resources = new MockResourceProvider ();
+ var platform = new TargetPlatform (mockProvider, resources) {
+ SupportsCustomExpressions = true
+ };
+
+ var vm = GetViewModel (platform, property.Object, new[] { editor.Object });
+ Assume.That (vm.SupportsAutocomplete, Is.True);
+ vm.PreviewCustomExpression = "preview";
+
+ vm.PreviewCustomExpression = "attempt2";
+ Assert.That (tcs.Task.IsCanceled, Is.True);
+ }
+
protected TViewModel GetViewModel (IPropertyInfo property, IObjectEditor editor)
{
return GetViewModel (property, new[] { editor });
diff --git a/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs b/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs
index 4e353c8..605b8c5 100644
--- a/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs
+++ b/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
@@ -77,6 +78,32 @@ namespace Xamarin.PropertyEditing.ViewModels
}
}
+ public bool SupportsAutocomplete
+ {
+ get { return this.supportsAutocomplete; }
+ private set
+ {
+ if (this.supportsAutocomplete == value)
+ return;
+
+ this.supportsAutocomplete = value;
+ OnPropertyChanged();
+
+ if (!value) {
+ this.autocomplete = null;
+ this.autocompleteCancel?.Cancel();
+ this.autocompleteCancel = null;
+ }
+ }
+ }
+
+ public IReadOnlyList<string> AutocompleteItems => this.autocomplete;
+
+ public string PreviewCustomExpression
+ {
+ set { UpdateAutocomplete (value); }
+ }
+
public string CustomExpression
{
get { return this.value?.CustomExpression; }
@@ -228,6 +255,23 @@ namespace Xamarin.PropertyEditing.ViewModels
}
}
+ protected override void OnEditorsChanged (object sender, NotifyCollectionChangedEventArgs e)
+ {
+ base.OnEditorsChanged (sender, e);
+
+ if (e.Action == NotifyCollectionChangedAction.Add && SupportsAutocomplete)
+ return;
+
+ if (TargetPlatform.SupportsCustomExpressions) {
+ foreach (IObjectEditor editor in Editors) {
+ if (editor is ICompleteValues) {
+ SupportsAutocomplete = true;
+ break;
+ }
+ }
+ }
+ }
+
private readonly ICoerce<TValue> coerce;
private readonly IValidator<TValue> validator;
private readonly ICanNavigateToSource valueNavigator;
@@ -235,6 +279,10 @@ namespace Xamarin.PropertyEditing.ViewModels
private bool isNullable;
private ValueInfo<TValue> value;
+ private bool supportsAutocomplete;
+ private ObservableCollectionEx<string> autocomplete;
+ private CancellationTokenSource autocompleteCancel;
+
private void SignalValueChange ()
{
OnPropertyChanged (nameof (Value));
@@ -359,6 +407,49 @@ namespace Xamarin.PropertyEditing.ViewModels
});
}
+ private async void UpdateAutocomplete (string value)
+ {
+ if (!SupportsAutocomplete)
+ return;
+
+ if (this.autocomplete == null) {
+ this.autocomplete = new ObservableCollectionEx<string> ();
+ OnPropertyChanged (nameof(AutocompleteItems));
+ } else {
+ this.autocompleteCancel.Cancel();
+ }
+
+ this.autocompleteCancel = new CancellationTokenSource ();
+ CancellationToken cancel = this.autocompleteCancel.Token;
+
+ try {
+ HashSet<string> common = null;
+ List<Task<IReadOnlyList<string>>> tasks = new List<Task<IReadOnlyList<string>>> ();
+
+ foreach (IObjectEditor editor in Editors) {
+ if (!(editor is ICompleteValues complete))
+ continue;
+
+ tasks.Add (complete.GetCompletionsAsync (Property, value, cancel));
+ }
+
+ IReadOnlyList<string> list = null;
+ do {
+ Task<IReadOnlyList<string>> results = await Task.WhenAny (tasks);
+ tasks.Remove (results);
+
+ if (list == null) {
+ list = results.Result;
+ common = new HashSet<string> (list);
+ } else
+ common.IntersectWith (results.Result);
+ } while (tasks.Count > 0 && !cancel.IsCancellationRequested);
+
+ this.autocomplete.Reset (list.Where (common.Contains));
+ } catch (OperationCanceledException) {
+ }
+ }
+
private static TValue DefaultValue;
}