Skip to content

Commit a755c79

Browse files
committed
New UI integration concept for better flexibility and responsiveness
1 parent deab147 commit a755c79

38 files changed

+617
-151
lines changed

src/ExtensionManager.Manifest/IManifestService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ public interface IManifestService
77
{
88
IManifest CreateNew();
99
Task<IManifest> ReadAsync(string filePath);
10-
Task WriteAsync(string filePath, IManifest manifest);
10+
Task WriteAsync(string filePath, IManifest manifest, CancellationToken cancellationToken);
1111
}

src/ExtensionManager.Manifest/Internal/ManifestService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,12 @@ public async Task<IManifest> ReadAsync(string filePath)
4545
}
4646
}
4747

48-
public async Task WriteAsync(string filePath, IManifest manifest)
48+
public async Task WriteAsync(string filePath, IManifest manifest, CancellationToken cancellationToken)
4949
{
5050
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
5151

5252
using var stream = File.Create(filePath);
5353

54-
await ManifestVersion.Latest.WriteAsync(stream, manifest).ConfigureAwait(false);
54+
await ManifestVersion.Latest.WriteAsync(stream, manifest, cancellationToken);
5555
}
5656
}

src/ExtensionManager.Manifest/Internal/ManifestVersion.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ protected ManifestVersion(Version version)
7373
}
7474

7575
public abstract Task<IManifest> ReadAsync(Stream stream);
76-
public abstract Task WriteAsync(Stream stream, IManifest manifest);
76+
public abstract Task WriteAsync(Stream stream, IManifest manifest, CancellationToken cancellationToken);
7777

7878
protected Exception CreateVersionNotSupportedException()
7979
=> ThrowHelper.CreateManifestVersionNotSupportedException(Version);

src/ExtensionManager.Manifest/Internal/Versions/V0ManifestVersion.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ static ExtensionDto CreateExtensionDto(JsonExtension data)
6666
}
6767
}
6868

69-
public override Task WriteAsync(Stream stream, IManifest manifest)
69+
public override Task WriteAsync(Stream stream, IManifest manifest, CancellationToken cancellationToken)
7070
=> throw CreateVersionNotSupportedException();
7171
}
7272

src/ExtensionManager.Manifest/Internal/Versions/V1ManifestVersion.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ static ExtensionDto CreateExtensionDto(JsonExtension data)
6060
}
6161
}
6262

63-
public override async Task WriteAsync(Stream stream, IManifest manifest)
63+
public override async Task WriteAsync(Stream stream, IManifest manifest, CancellationToken cancellationToken)
6464
{
6565
var data = new JsonManifest(manifest);
6666

@@ -69,7 +69,7 @@ public override async Task WriteAsync(Stream stream, IManifest manifest)
6969
WriteIndented = true,
7070
};
7171

72-
await JsonSerializer.SerializeAsync(stream, data, options);
72+
await JsonSerializer.SerializeAsync(stream, data, options, cancellationToken);
7373
}
7474
}
7575

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using System.Windows.Markup;
2+
3+
namespace ExtensionManager.UI.Converters;
4+
5+
[MarkupExtensionReturnType(typeof(AndConverter))]
6+
internal sealed class AndConverter : CombineBoolConverterBase
7+
{
8+
protected override bool Combine(bool value1, bool value2) => value1 && value2;
9+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using System.Globalization;
3+
using System.Windows.Data;
4+
using System.Windows.Markup;
5+
6+
namespace ExtensionManager.UI.Converters;
7+
8+
internal abstract class CombineBoolConverterBase : MarkupExtension, IMultiValueConverter
9+
{
10+
public bool Empty { get; set; }
11+
public bool CastFallback { get; set; }
12+
13+
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
14+
{
15+
bool result;
16+
17+
if (values.Length > 0)
18+
{
19+
result = true;
20+
21+
for (var i = 0; i < values.Length; i++)
22+
{
23+
var value = values[i] as bool? ?? CastFallback;
24+
25+
result = Combine(result, value);
26+
}
27+
}
28+
else
29+
result = Empty;
30+
31+
return result;
32+
}
33+
34+
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
35+
=> throw new NotSupportedException();
36+
37+
public override object ProvideValue(IServiceProvider serviceProvider) => this;
38+
39+
protected abstract bool Combine(bool value1, bool value2);
40+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Globalization;
3+
using System.Windows.Data;
4+
using System.Windows.Markup;
5+
6+
namespace ExtensionManager.UI.Converters;
7+
8+
[ValueConversion(typeof(bool), typeof(bool))]
9+
[MarkupExtensionReturnType(typeof(InvertBoolConverter))]
10+
internal sealed class InvertBoolConverter : MarkupExtension, IValueConverter
11+
{
12+
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => Invert(value);
13+
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => Invert(value);
14+
15+
private object Invert(object value)
16+
{
17+
if (value is not bool boolValue)
18+
return value;
19+
20+
boolValue = !boolValue;
21+
22+
return boolValue;
23+
}
24+
25+
public override object ProvideValue(IServiceProvider serviceProvider) => this;
26+
}

src/ExtensionManager.UI/DialogService.cs

Lines changed: 26 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
using System.Threading.Tasks;
55

66
using ExtensionManager.Manifest;
7+
using ExtensionManager.UI.Utils;
78
using ExtensionManager.UI.ViewModels;
89
using ExtensionManager.UI.Views;
10+
using ExtensionManager.UI.Worker;
911
using ExtensionManager.VisualStudio;
1012
using ExtensionManager.VisualStudio.Extensions;
1113

@@ -43,86 +45,58 @@ internal sealed class DialogService : IDialogService
4345
}
4446
}
4547

46-
public Task<bool> ShowExportDialogAsync(IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions)
47-
=> ShowExportDialogAsync(manifest, installedExtensions, forSolution: false);
48-
public Task<bool> ShowExportForSolutionDialogAsync(IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions)
49-
=> ShowExportDialogAsync(manifest, installedExtensions, forSolution: true);
50-
private async Task<bool> ShowExportDialogAsync(IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions, bool forSolution)
48+
public Task ShowExportDialogAsync(IExportWorker worker, IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions)
49+
=> ShowExportDialogAsync(worker, manifest, installedExtensions, forSolution: false);
50+
public Task ShowExportForSolutionDialogAsync(IExportWorker worker, IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions)
51+
=> ShowExportDialogAsync(worker, manifest, installedExtensions, forSolution: true);
52+
private async Task ShowExportDialogAsync(IExportWorker worker, IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions, bool forSolution)
5153
{
52-
var vm = new InstallExportDialogViewModel(forSolution ? InstallExportDialogType.ExportSolution : InstallExportDialogType.Export);
54+
var vm = new ExportDialogViewModel(worker, manifest, forSolution);
5355

5456
foreach (var ext in installedExtensions)
5557
vm.Extensions.Add(new(ext));
5658

57-
var accept = await ShowInstallExportDialogAsync(vm).ConfigureAwait(false);
58-
59-
if (accept)
60-
{
61-
manifest.Extensions.Clear();
62-
63-
foreach (var ext in vm.SelectedExtensions)
64-
manifest.Extensions.Add(ext.Model);
65-
}
66-
67-
return accept;
59+
await ShowInstallExportDialogAsync(vm);
6860
}
6961

70-
public Task<InstallExtensionsDialogResult?> ShowInstallDialogAsync(IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions)
71-
=> ShowInstallForSolutionDialogAsync(manifest, installedExtensions, forSolution: false);
72-
public Task<InstallExtensionsDialogResult?> ShowInstallForSolutionDialogAsync(IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions)
73-
=> ShowInstallForSolutionDialogAsync(manifest, installedExtensions, forSolution: true);
74-
private async Task<InstallExtensionsDialogResult?> ShowInstallForSolutionDialogAsync(IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions, bool forSolution)
62+
public Task ShowInstallDialogAsync(IInstallWorker worker, IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions)
63+
=> ShowInstallForSolutionDialogAsync(worker, manifest, installedExtensions, forSolution: false);
64+
public Task ShowInstallForSolutionDialogAsync(IInstallWorker worker, IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions)
65+
=> ShowInstallForSolutionDialogAsync(worker, manifest, installedExtensions, forSolution: true);
66+
private async Task ShowInstallForSolutionDialogAsync(IInstallWorker worker, IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions, bool forSolution)
7567
{
76-
var vm = new InstallExportDialogViewModel(forSolution ? InstallExportDialogType.InstallSolution : InstallExportDialogType.Install);
68+
var vm = new InstallDialogViewModel(worker, manifest, forSolution);
7769

7870
foreach (var ext in manifest.Extensions)
7971
{
80-
var isInstalled = installedExtensions.Contains(ext, ExtensionEqualityComparer.Instance);
72+
var isInstalled = installedExtensions.Contains(ext, ExtensionEqualityComparerById.Instance);
8173

82-
vm.Extensions.Add(new(ext)
83-
{
84-
CanBeSelected = !isInstalled,
85-
Group = isInstalled ? "Already installed" : "Extensions",
86-
});
74+
vm.AddExtension(ext, isInstalled);
8775
}
8876

89-
var accept = await ShowInstallExportDialogAsync(vm).ConfigureAwait(false);
90-
91-
if (accept)
92-
return new(vm.SystemWide, vm.SelectedExtensions.Select(x => x.Model).ToArray());
93-
94-
return null;
77+
await ShowInstallExportDialogAsync(vm);
9578
}
9679

97-
private Task<bool> ShowInstallExportDialogAsync(object viewModel)
80+
private Task ShowInstallExportDialogAsync(object viewModel)
9881
{
9982
if (VSFacade.Threads.CheckUIThreadAccess())
100-
return Task.FromResult(OnUIThread(viewModel));
83+
{
84+
OnUIThread(viewModel);
85+
86+
return Task.CompletedTask;
87+
}
10188

10289
return VSFacade.Threads.RunOnUIThreadAsync(() => OnUIThread(viewModel));
10390

104-
static bool OnUIThread(object viewModel)
91+
static void OnUIThread(object viewModel)
10592
{
10693
var window = new InstallExportDialogWindow
10794
{
10895
Owner = WpfApplication.Current.MainWindow,
10996
DataContext = viewModel
11097
};
11198

112-
var result = window.ShowDialog();
113-
114-
return result == true;
99+
window.ShowDialog();
115100
}
116101
}
117102
}
118-
119-
file sealed class ExtensionEqualityComparer : IEqualityComparer<IVSExtension>
120-
{
121-
public static ExtensionEqualityComparer Instance { get; } = new();
122-
123-
public bool Equals(IVSExtension x, IVSExtension y)
124-
=> x?.Id == y?.Id;
125-
126-
public int GetHashCode(IVSExtension obj)
127-
=> obj?.Id?.GetHashCode() ?? 0;
128-
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
using System.Collections.Generic;
2-
using System.Threading;
32
using System.Threading.Tasks;
43

54
using ExtensionManager.Manifest;
5+
using ExtensionManager.UI.Worker;
66
using ExtensionManager.VisualStudio.Extensions;
77

88
namespace ExtensionManager.UI;
@@ -12,8 +12,8 @@ public interface IDialogService
1212
Task<string?> ShowSaveVsextFileDialogAsync();
1313
Task<string?> ShowOpenVsextFileDialogAsync();
1414

15-
Task<bool> ShowExportDialogAsync(IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions);
16-
Task<bool> ShowExportForSolutionDialogAsync(IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions);
17-
Task<InstallExtensionsDialogResult?> ShowInstallDialogAsync(IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions);
18-
Task<InstallExtensionsDialogResult?> ShowInstallForSolutionDialogAsync(IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions);
15+
Task ShowExportDialogAsync(IExportWorker worker, IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions);
16+
Task ShowExportForSolutionDialogAsync(IExportWorker worker, IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions);
17+
Task ShowInstallDialogAsync(IInstallWorker worker, IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions);
18+
Task ShowInstallForSolutionDialogAsync(IInstallWorker worker, IManifest manifest, IReadOnlyCollection<IVSExtension> installedExtensions);
1919
}

0 commit comments

Comments
 (0)