Wire up the disposer for cleaning up unused objects, i.e disposing a VM will remove it from the view
This commit is contained in:
@@ -1,18 +1,13 @@
|
|||||||
|
namespace Hyperbar.Widget.Contextual;
|
||||||
namespace Hyperbar.Widget.Contextual;
|
|
||||||
|
|
||||||
public class ContextualWidgetViewModel :
|
public class ContextualWidgetViewModel(ITemplateFactory templateFactory,
|
||||||
ObservableCollectionViewModel<IWidgetComponentViewModel>,
|
IServiceFactory serviceFactory,
|
||||||
|
IMediator mediator,
|
||||||
|
IDisposer disposer,
|
||||||
|
IEnumerable<IWidgetComponentViewModel> items) :
|
||||||
|
ObservableCollectionViewModel<IWidgetComponentViewModel>(serviceFactory, mediator, disposer, items),
|
||||||
IWidgetViewModel,
|
IWidgetViewModel,
|
||||||
ITemplatedViewModel
|
ITemplatedViewModel
|
||||||
{
|
{
|
||||||
public ContextualWidgetViewModel(ITemplateFactory templateFactory,
|
public ITemplateFactory TemplateFactory => templateFactory;
|
||||||
IServiceFactory serviceFactory,
|
|
||||||
IMediator mediator,
|
|
||||||
IEnumerable<IWidgetComponentViewModel> items) : base(serviceFactory, mediator, items)
|
|
||||||
{
|
|
||||||
TemplateFactory = templateFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ITemplateFactory TemplateFactory { get; }
|
|
||||||
}
|
}
|
||||||
@@ -3,22 +3,17 @@
|
|||||||
namespace Hyperbar.Windows.Primary;
|
namespace Hyperbar.Windows.Primary;
|
||||||
|
|
||||||
public class MediaController :
|
public class MediaController :
|
||||||
INotificationHandler<PlayRequest>,
|
INotificationHandler<PlayRequest>
|
||||||
IDisposable
|
|
||||||
{
|
{
|
||||||
public MediaController(GlobalSystemMediaTransportControlsSession session)
|
private readonly GlobalSystemMediaTransportControlsSession session;
|
||||||
{
|
|
||||||
|
|
||||||
|
public MediaController(GlobalSystemMediaTransportControlsSession session,
|
||||||
|
IMediator mediator)
|
||||||
|
{
|
||||||
|
this.session = session;
|
||||||
|
mediator.Subscribe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public async ValueTask Handle(PlayRequest notification, CancellationToken cancellationToken) =>
|
||||||
{
|
await session.TryPlayAsync();
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask Handle(PlayRequest notification,
|
|
||||||
CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
using Windows.Media.Control;
|
|
||||||
|
|
||||||
namespace Hyperbar.Windows.Primary;
|
|
||||||
|
|
||||||
public class MediaControllerInitializer :
|
|
||||||
IInitializer
|
|
||||||
{
|
|
||||||
private readonly List<GlobalSystemMediaTransportControlsSession> sessions = [];
|
|
||||||
|
|
||||||
public async Task InitializeAsync()
|
|
||||||
{
|
|
||||||
GlobalSystemMediaTransportControlsSessionManager mediaTransportControlsSessionManager =
|
|
||||||
await GlobalSystemMediaTransportControlsSessionManager.RequestAsync();
|
|
||||||
|
|
||||||
mediaTransportControlsSessionManager.SessionsChanged += OnSessionsChanged;
|
|
||||||
IReadOnlyList<GlobalSystemMediaTransportControlsSession> sessions =
|
|
||||||
mediaTransportControlsSessionManager.GetSessions();
|
|
||||||
|
|
||||||
foreach (var session in sessions)
|
|
||||||
{
|
|
||||||
this.sessions.Add(session);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSessionsChanged(GlobalSystemMediaTransportControlsSessionManager sender,
|
|
||||||
SessionsChangedEventArgs args)
|
|
||||||
{
|
|
||||||
IReadOnlyList<GlobalSystemMediaTransportControlsSession> sessions =
|
|
||||||
sender.GetSessions();
|
|
||||||
|
|
||||||
foreach (var session in this.sessions.ToList())
|
|
||||||
{
|
|
||||||
if (!sessions.Contains(session))
|
|
||||||
{
|
|
||||||
this.sessions.Remove(session);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var session in sessions)
|
|
||||||
{
|
|
||||||
if (!this.sessions.Contains(session))
|
|
||||||
{
|
|
||||||
this.sessions.Add(session);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
using Windows.Media.Control;
|
||||||
|
|
||||||
|
namespace Hyperbar.Windows.Primary;
|
||||||
|
|
||||||
|
public class MediaControllerManager :
|
||||||
|
IInitializer
|
||||||
|
{
|
||||||
|
private readonly ConcurrentDictionary<GlobalSystemMediaTransportControlsSession, MediaController> cachedSessions = [];
|
||||||
|
private readonly IMediator mediator;
|
||||||
|
private readonly Queue<MediaController> mediaControllers;
|
||||||
|
private readonly IServiceFactory serviceFactory;
|
||||||
|
|
||||||
|
public MediaControllerManager(IServiceFactory serviceFactory,
|
||||||
|
IMediator mediator,
|
||||||
|
Queue<MediaController> mediaControllers)
|
||||||
|
{
|
||||||
|
this.serviceFactory = serviceFactory;
|
||||||
|
this.mediator = mediator;
|
||||||
|
this.mediaControllers = mediaControllers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Channel<MediaController> d;
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
d = Channel.CreateUnbounded<MediaController>();
|
||||||
|
|
||||||
|
_ = Task.Run(async () => {
|
||||||
|
|
||||||
|
await foreach (var coordinates in d.Reader.ReadAllAsync())
|
||||||
|
{
|
||||||
|
Console.WriteLine(coordinates);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
GlobalSystemMediaTransportControlsSessionManager mediaTransportControlsSessionManager =
|
||||||
|
await GlobalSystemMediaTransportControlsSessionManager.RequestAsync();
|
||||||
|
mediaTransportControlsSessionManager.SessionsChanged += OnSessionsChanged;
|
||||||
|
|
||||||
|
IReadOnlyList<GlobalSystemMediaTransportControlsSession> sessions =
|
||||||
|
mediaTransportControlsSessionManager.GetSessions();
|
||||||
|
|
||||||
|
foreach (GlobalSystemMediaTransportControlsSession session in sessions)
|
||||||
|
{
|
||||||
|
await InitializeSessionAsync(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InitializeSessionAsync(GlobalSystemMediaTransportControlsSession session)
|
||||||
|
{
|
||||||
|
if (serviceFactory.Create<MediaController>(session) is MediaController mediaController)
|
||||||
|
{
|
||||||
|
await d.Writer.WriteAsync(mediaController);
|
||||||
|
|
||||||
|
mediaControllers.Enqueue(mediaController);
|
||||||
|
|
||||||
|
cachedSessions.TryAdd(session, mediaController);
|
||||||
|
await mediator.PublishAsync(new Created<MediaController>(mediaController));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnSessionsChanged(GlobalSystemMediaTransportControlsSessionManager sender,
|
||||||
|
SessionsChangedEventArgs args)
|
||||||
|
{
|
||||||
|
IReadOnlyList<GlobalSystemMediaTransportControlsSession> sessions =
|
||||||
|
sender.GetSessions();
|
||||||
|
|
||||||
|
foreach (KeyValuePair<GlobalSystemMediaTransportControlsSession, MediaController> session in
|
||||||
|
cachedSessions)
|
||||||
|
{
|
||||||
|
if (!sessions.Contains(session.Key))
|
||||||
|
{
|
||||||
|
cachedSessions.TryRemove(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (GlobalSystemMediaTransportControlsSession session in sessions)
|
||||||
|
{
|
||||||
|
await InitializeSessionAsync(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveSession(GlobalSystemMediaTransportControlsSession session)
|
||||||
|
{
|
||||||
|
if (serviceFactory.Create<MediaController>(session) is MediaController mediaController)
|
||||||
|
{
|
||||||
|
cachedSessions.TryAdd(session, mediaController);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +1,17 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
|
||||||
namespace Hyperbar.Windows.Primary;
|
namespace Hyperbar.Windows.Primary;
|
||||||
|
|
||||||
public partial class MediaInformationViewModel :
|
//public class MediaControllerViewModelFactory(IServiceFactory service,
|
||||||
WidgetComponentViewModel
|
// IMediator mediator,
|
||||||
{
|
// Queue<MediaController> mediaControllers) :
|
||||||
[ObservableProperty]
|
// IFactory<MediaControllerViewModel>
|
||||||
private string title = "this is a test";
|
//{
|
||||||
|
// public MediaControllerViewModel Create()
|
||||||
[ObservableProperty]
|
// {
|
||||||
private string description = "this is a test description";
|
// throw new NotImplementedException();
|
||||||
|
// }
|
||||||
public MediaInformationViewModel(ITemplateFactory templateFactory) : base(templateFactory)
|
//}
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MediaControllerViewModel :
|
public class MediaControllerViewModel :
|
||||||
ObservableCollectionViewModel<WidgetComponentViewModel>,
|
ObservableCollectionViewModel<WidgetComponentViewModel>,
|
||||||
@@ -22,7 +19,8 @@ public class MediaControllerViewModel :
|
|||||||
{
|
{
|
||||||
public MediaControllerViewModel(ITemplateFactory templateFactory,
|
public MediaControllerViewModel(ITemplateFactory templateFactory,
|
||||||
IServiceFactory serviceFactory,
|
IServiceFactory serviceFactory,
|
||||||
IMediator mediator) : base(serviceFactory, mediator)
|
IMediator mediator,
|
||||||
|
IDisposer disposer) : base(serviceFactory, mediator, disposer)
|
||||||
{
|
{
|
||||||
TemplateFactory = templateFactory;
|
TemplateFactory = templateFactory;
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,8 @@ public class MediaControllerWidgetProvider :
|
|||||||
{
|
{
|
||||||
public void Create(HostBuilderContext comtext, IServiceCollection services) =>
|
public void Create(HostBuilderContext comtext, IServiceCollection services) =>
|
||||||
services.AddWidgetTemplate<MediaControllerWidgetViewModel, MediaControllerWidgetView>()
|
services.AddWidgetTemplate<MediaControllerWidgetViewModel, MediaControllerWidgetView>()
|
||||||
.AddTransient<IInitializer, MediaControllerInitializer>()
|
.AddSingleton<Queue<MediaController>>()
|
||||||
.AddContentTemplate<MediaControllerViewModel, MediaControllerView>()
|
.AddSingleton<IInitializer, MediaControllerManager>()
|
||||||
.AddContentTemplate<MediaControllerViewModel, MediaControllerView>()
|
.AddContentTemplate<MediaControllerViewModel, MediaControllerView>();
|
||||||
.AddContentTemplate<MediaControllerViewModel, MediaControllerView>()
|
|
||||||
.AddContentTemplate<MediaInformationViewModel, MediaInformationView>();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -3,8 +3,8 @@
|
|||||||
public class MediaControllerWidgetViewModel(ITemplateFactory templateFactory,
|
public class MediaControllerWidgetViewModel(ITemplateFactory templateFactory,
|
||||||
IServiceFactory serviceFactory,
|
IServiceFactory serviceFactory,
|
||||||
IMediator mediator,
|
IMediator mediator,
|
||||||
IEnumerable<MediaControllerViewModel> items) :
|
IDisposer disposer) :
|
||||||
ObservableCollectionViewModel<MediaControllerViewModel>(serviceFactory, mediator, items),
|
ObservableCollectionViewModel<MediaControllerViewModel>(serviceFactory, mediator, disposer),
|
||||||
IWidgetViewModel,
|
IWidgetViewModel,
|
||||||
ITemplatedViewModel
|
ITemplatedViewModel
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
namespace Hyperbar.Windows.Primary;
|
||||||
|
|
||||||
|
public partial class MediaInformationViewModel :
|
||||||
|
WidgetComponentViewModel
|
||||||
|
{
|
||||||
|
[ObservableProperty]
|
||||||
|
private string title = "this is a test";
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string description = "this is a test description";
|
||||||
|
|
||||||
|
public MediaInformationViewModel(IServiceFactory serviceFactory, IMediator mediator, IDisposer disposer, ITemplateFactory templateFactory) : base(serviceFactory, mediator, disposer, templateFactory)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||||
|
<PackageReference Include="System.Runtime.Caching" Version="8.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Hyperbar\Hyperbar.csproj" />
|
<ProjectReference Include="..\Hyperbar\Hyperbar.csproj" />
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Hyperbar.Windows.Primary;
|
|||||||
[JsonDerivedType(typeof(ProcessCommandConfiguration), typeDiscriminator: "ProcessCommand")]
|
[JsonDerivedType(typeof(ProcessCommandConfiguration), typeDiscriminator: "ProcessCommand")]
|
||||||
public class PrimaryCommandConfiguration
|
public class PrimaryCommandConfiguration
|
||||||
{
|
{
|
||||||
public required string Id { get; set; }
|
public required Guid Id { get; set; }
|
||||||
|
|
||||||
public required string Icon { get; set; }
|
public required string Icon { get; set; }
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,6 @@ public class PrimaryWidgetConfiguration :
|
|||||||
{
|
{
|
||||||
public static PrimaryWidgetConfiguration Defaults => new()
|
public static PrimaryWidgetConfiguration Defaults => new()
|
||||||
{
|
{
|
||||||
new KeyAcceleratorCommandConfiguration { Id = $"{Guid.NewGuid()}", Icon = "\uE720", Key = 91, Modifiers = [] }
|
new KeyAcceleratorCommandConfiguration { Id = Guid.NewGuid(), Icon = "\uE720", Key = 91, Modifiers = [] }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -8,9 +8,10 @@ public class PrimaryWidgetProvider :
|
|||||||
{
|
{
|
||||||
public void Create(HostBuilderContext comtext, IServiceCollection services) =>
|
public void Create(HostBuilderContext comtext, IServiceCollection services) =>
|
||||||
services.AddConfiguration<PrimaryWidgetConfiguration>()
|
services.AddConfiguration<PrimaryWidgetConfiguration>()
|
||||||
.AddTransient<IFactory<IEnumerable<IWidgetComponentViewModel>>, WidgetComponentViewModelFactory>()
|
.AddTransient<IViewModelEnumerator<IWidgetComponentViewModel>, WidgetComponentViewModelEnumerator>()
|
||||||
|
.AddTransient<IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?>, WidgetComponentViewModelFactory>()
|
||||||
.AddWidgetTemplate<PrimaryWidgetViewModel>()
|
.AddWidgetTemplate<PrimaryWidgetViewModel>()
|
||||||
.AddNotificationPipeline<ConfigurationChanged<PrimaryWidgetConfiguration>,
|
.AddNotificationPipeline<ConfigurationChanged<PrimaryWidgetConfiguration>,
|
||||||
ValueChanging<IEnumerable<IWidgetComponentViewModel>>>();
|
CollectionChanged<IEnumerable<IWidgetComponentViewModel>>>();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,9 @@ namespace Hyperbar.Windows.Primary;
|
|||||||
public class PrimaryWidgetViewModel(ITemplateFactory templateFactory,
|
public class PrimaryWidgetViewModel(ITemplateFactory templateFactory,
|
||||||
IServiceFactory serviceFactory,
|
IServiceFactory serviceFactory,
|
||||||
IMediator mediator,
|
IMediator mediator,
|
||||||
IFactory<IEnumerable<IWidgetComponentViewModel>> factory) :
|
IDisposer disposer,
|
||||||
ObservableCollectionViewModel<IWidgetComponentViewModel>(serviceFactory, mediator, factory),
|
IViewModelEnumerator<IWidgetComponentViewModel> enumerator) :
|
||||||
|
ObservableCollectionViewModel<IWidgetComponentViewModel>(serviceFactory, mediator, disposer, enumerator),
|
||||||
IWidgetViewModel,
|
IWidgetViewModel,
|
||||||
ITemplatedViewModel
|
ITemplatedViewModel
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
|
||||||
|
namespace Hyperbar.Windows.Primary;
|
||||||
|
|
||||||
|
public class WidgetComponentViewModelFactory(IServiceFactory service,
|
||||||
|
IMediator mediator) :
|
||||||
|
IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?>
|
||||||
|
{
|
||||||
|
public async ValueTask<IWidgetComponentViewModel?> CreateAsync(PrimaryCommandConfiguration value)
|
||||||
|
{
|
||||||
|
if (value is KeyAcceleratorCommandConfiguration keyAcceleratorCommand)
|
||||||
|
{
|
||||||
|
return await ValueTask.FromResult(service.Create<WidgetButtonViewModel>(keyAcceleratorCommand.Id, keyAcceleratorCommand.Icon,
|
||||||
|
new RelayCommand(async () => await mediator.SendAsync(new KeyAcceleratorRequest((VirtualKey)
|
||||||
|
keyAcceleratorCommand.Key, keyAcceleratorCommand.Modifiers?
|
||||||
|
.Select(modifier => (VirtualKey)modifier).ToArray())))));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is ProcessCommandConfiguration commandConfiguration)
|
||||||
|
{
|
||||||
|
return await ValueTask.FromResult(service.Create<WidgetButtonViewModel>(commandConfiguration.Id,
|
||||||
|
commandConfiguration.Icon, new RelayCommand(async () =>
|
||||||
|
await mediator.SendAsync(new ProcessRequest(commandConfiguration.Path)))));
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
namespace Hyperbar.Windows.Primary;
|
||||||
|
|
||||||
|
public class WidgetComponentViewModelEnumerator(PrimaryWidgetConfiguration configuration,
|
||||||
|
IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?> factory) :
|
||||||
|
IViewModelEnumerator<IWidgetComponentViewModel>
|
||||||
|
{
|
||||||
|
public async IAsyncEnumerable<IWidgetComponentViewModel?> Next()
|
||||||
|
{
|
||||||
|
foreach (PrimaryCommandConfiguration item in configuration)
|
||||||
|
{
|
||||||
|
yield return await factory.CreateAsync(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
namespace Hyperbar.Windows.Primary;
|
|
||||||
|
|
||||||
public class WidgetComponentViewModelFactory(PrimaryWidgetConfiguration configuration,
|
|
||||||
IServiceFactory service,
|
|
||||||
IMediator mediator) :
|
|
||||||
IFactory<IEnumerable<IWidgetComponentViewModel>>
|
|
||||||
{
|
|
||||||
public IEnumerable<IWidgetComponentViewModel> Create()
|
|
||||||
{
|
|
||||||
foreach (PrimaryCommandConfiguration item in configuration)
|
|
||||||
{
|
|
||||||
if (item is KeyAcceleratorCommandConfiguration keyAcceleratorCommandConfiguration)
|
|
||||||
{
|
|
||||||
yield return service.Create<WidgetButtonViewModel>(keyAcceleratorCommandConfiguration.Icon, new Action(async () =>
|
|
||||||
await mediator.SendAsync(new KeyAcceleratorRequest((VirtualKey)keyAcceleratorCommandConfiguration.Key,
|
|
||||||
keyAcceleratorCommandConfiguration.Modifiers?.Select(modifier => (VirtualKey)modifier).ToArray()))));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item is ProcessCommandConfiguration processCommandConfiguration)
|
|
||||||
{
|
|
||||||
yield return service.Create<WidgetButtonViewModel>(processCommandConfiguration.Icon, new Action(async () =>
|
|
||||||
await mediator.SendAsync(new ProcessRequest(processCommandConfiguration.Path))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -36,6 +36,8 @@ public partial class App :
|
|||||||
new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!)));
|
new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!)));
|
||||||
|
|
||||||
services.AddSingleton<IMediator, Mediator>();
|
services.AddSingleton<IMediator, Mediator>();
|
||||||
|
services.AddSingleton<IDisposer, Disposer>();
|
||||||
|
|
||||||
services.AddHostedService<AppService>();
|
services.AddHostedService<AppService>();
|
||||||
|
|
||||||
services.AddTransient<IInitializer, AppInitializer>();
|
services.AddTransient<IInitializer, AppInitializer>();
|
||||||
@@ -45,7 +47,7 @@ public partial class App :
|
|||||||
|
|
||||||
services.AddContentTemplate<WidgetBarViewModel, WidgetBarView>();
|
services.AddContentTemplate<WidgetBarViewModel, WidgetBarView>();
|
||||||
|
|
||||||
services.AddWidgetProvider<MediaControllerWidgetProvider>();
|
//services.AddWidgetProvider<MediaControllerWidgetProvider>();
|
||||||
services.AddWidgetProvider<PrimaryWidgetProvider>();
|
services.AddWidgetProvider<PrimaryWidgetProvider>();
|
||||||
|
|
||||||
services.AddTransient(provider =>
|
services.AddTransient(provider =>
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.231202003-experimental1" />
|
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.231202003-experimental1" />
|
||||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.25936-preview" />
|
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.25936-preview" />
|
||||||
|
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
|
||||||
<Manifest Include="$(ApplicationManifest)" />
|
<Manifest Include="$(ApplicationManifest)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
|
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ namespace Hyperbar.Windows
|
|||||||
|
|
||||||
isolatedServices.AddTransient<ITemplateFactory, TemplateFactory>();
|
isolatedServices.AddTransient<ITemplateFactory, TemplateFactory>();
|
||||||
isolatedServices.AddSingleton<IMediator, Mediator>();
|
isolatedServices.AddSingleton<IMediator, Mediator>();
|
||||||
|
isolatedServices.AddSingleton<IDisposer, Disposer>();
|
||||||
|
|
||||||
isolatedServices.AddSingleton<IVirtualKeyboard, VirtualKeyboard>();
|
isolatedServices.AddSingleton<IVirtualKeyboard, VirtualKeyboard>();
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
Width="{StaticResource ButtonWidth}"
|
Width="{StaticResource ButtonWidth}"
|
||||||
Height="{StaticResource ButtonHeight}"
|
Height="{StaticResource ButtonHeight}"
|
||||||
Padding="{StaticResource ButtonPadding}"
|
Padding="{StaticResource ButtonPadding}"
|
||||||
FontSize="16"
|
|
||||||
Command="{Binding Click}"
|
Command="{Binding Click}"
|
||||||
Content="{Binding Icon}"
|
Content="{Binding Icon}"
|
||||||
FontFamily="{StaticResource SymbolThemeFontFamily}" />
|
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||||
|
FontSize="16" />
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@@ -3,12 +3,20 @@
|
|||||||
x:Class="Hyperbar.Windows.WidgetView"
|
x:Class="Hyperbar.Windows.WidgetView"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:interactions="using:Microsoft.Xaml.Interactions.Core"
|
||||||
|
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||||
xmlns:ui="using:Hyperbar.Windows.UI">
|
xmlns:ui="using:Hyperbar.Windows.UI">
|
||||||
|
|
||||||
<ItemsControl ItemTemplateSelector="{Binding Converter={ui:DataTemplateConverter}}" ItemsSource="{Binding}">
|
<ItemsControl ItemTemplateSelector="{Binding Converter={ui:DataTemplateConverter}}" ItemsSource="{Binding}">
|
||||||
<ItemsControl.ItemsPanel>
|
<ItemsControl.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
<StackPanel Orientation="Horizontal" Spacing="8" />
|
<StackPanel Orientation="Horizontal" Spacing="8" />
|
||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
</ItemsControl.ItemsPanel>
|
</ItemsControl.ItemsPanel>
|
||||||
|
<interactivity:Interaction.Behaviors>
|
||||||
|
<interactions:EventTriggerBehavior EventName="Loaded">
|
||||||
|
<interactions:InvokeCommandAction Command="{Binding Initialize}" />
|
||||||
|
</interactions:EventTriggerBehavior>
|
||||||
|
</interactivity:Interaction.Behaviors>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Microsoft.UI.Xaml.Input;
|
|
||||||
|
|
||||||
namespace Hyperbar.Windows;
|
namespace Hyperbar.Windows;
|
||||||
|
|
||||||
@@ -7,13 +6,5 @@ public sealed partial class WidgetView :
|
|||||||
UserControl,
|
UserControl,
|
||||||
IWidgetView
|
IWidgetView
|
||||||
{
|
{
|
||||||
public WidgetView()
|
public WidgetView() => InitializeComponent();
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnKeyDown(KeyRoutedEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnKeyDown(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Hyperbar;
|
|
||||||
|
|
||||||
public interface IMapping<TFrom, TTo>
|
|
||||||
{
|
|
||||||
TTo Create();
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Hyperbar;
|
||||||
|
|
||||||
|
public interface IViewModelFactory<TIn, TOut>
|
||||||
|
{
|
||||||
|
ValueTask<TOut> CreateAsync(TIn value);
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Hyperbar;
|
|
||||||
|
|
||||||
public interface IFactory<T>
|
|
||||||
{
|
|
||||||
T Create();
|
|
||||||
}
|
|
||||||
@@ -10,9 +10,6 @@ public class NotficationPipelineHandler<TFromNotification, ToNotification>(IMedi
|
|||||||
{
|
{
|
||||||
private readonly IMediator mediator = mediator;
|
private readonly IMediator mediator = mediator;
|
||||||
|
|
||||||
public ValueTask Handle(TFromNotification notification, CancellationToken cancellationToken)
|
public ValueTask Handle(TFromNotification notification, CancellationToken cancellationToken) =>
|
||||||
{
|
mediator.PublishAsync(new ToNotification(), cancellationToken);
|
||||||
return mediator.PublishAsync(new ToNotification(),
|
|
||||||
cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
namespace Hyperbar;
|
||||||
|
|
||||||
|
public record CollectionChanged<TValue> : INotification;
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
|
|
||||||
namespace Hyperbar;
|
|
||||||
|
|
||||||
public record ValueChanging<TValue> : INotification;
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
namespace Hyperbar;
|
||||||
|
|
||||||
|
public record Created<TValue>(TValue? Value = default) : INotification;
|
||||||
@@ -8,4 +8,4 @@ public interface IObservableCollectionViewModel<TItem> :
|
|||||||
IList,
|
IList,
|
||||||
IReadOnlyList<TItem>,
|
IReadOnlyList<TItem>,
|
||||||
INotifyCollectionChanged,
|
INotifyCollectionChanged,
|
||||||
INotificationHandler<ValueChanging<IEnumerable<TItem>>>;
|
IInitializer;
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Hyperbar;
|
||||||
|
|
||||||
|
public interface IViewModelEnumerator<TItem>
|
||||||
|
{
|
||||||
|
IAsyncEnumerable<TItem?> Next();
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
using System.Reactive.Disposables;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
namespace Hyperbar;
|
namespace Hyperbar;
|
||||||
|
|
||||||
@@ -11,14 +14,19 @@ public partial class ObservableCollectionViewModel<TItem> :
|
|||||||
{
|
{
|
||||||
public ObservableCollection<TItem> collection = [];
|
public ObservableCollection<TItem> collection = [];
|
||||||
private readonly SynchronizationContext? context;
|
private readonly SynchronizationContext? context;
|
||||||
|
private readonly IViewModelEnumerator<TItem>? enumerator;
|
||||||
private readonly IServiceFactory serviceFactory;
|
private readonly IServiceFactory serviceFactory;
|
||||||
|
private readonly IDisposer disposer;
|
||||||
|
|
||||||
public ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
public ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
||||||
IMediator mediator)
|
IMediator mediator,
|
||||||
|
IDisposer disposer)
|
||||||
{
|
{
|
||||||
context = SynchronizationContext.Current;
|
context = SynchronizationContext.Current;
|
||||||
|
|
||||||
this.serviceFactory = serviceFactory;
|
this.serviceFactory = serviceFactory;
|
||||||
|
this.disposer = disposer;
|
||||||
|
|
||||||
mediator.Subscribe(this);
|
mediator.Subscribe(this);
|
||||||
|
|
||||||
collection.CollectionChanged += OnCollectionChanged;
|
collection.CollectionChanged += OnCollectionChanged;
|
||||||
@@ -26,28 +34,44 @@ public partial class ObservableCollectionViewModel<TItem> :
|
|||||||
|
|
||||||
public ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
public ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
||||||
IMediator mediator,
|
IMediator mediator,
|
||||||
IFactory<IEnumerable<TItem>> factory)
|
IDisposer disposer,
|
||||||
|
IViewModelEnumerator<TItem> enumerator)
|
||||||
{
|
{
|
||||||
context = SynchronizationContext.Current;
|
context = SynchronizationContext.Current;
|
||||||
|
|
||||||
this.serviceFactory = serviceFactory;
|
this.serviceFactory = serviceFactory;
|
||||||
|
this.disposer = disposer;
|
||||||
|
this.enumerator = enumerator;
|
||||||
|
|
||||||
mediator.Subscribe(this);
|
mediator.Subscribe(this);
|
||||||
|
|
||||||
collection.CollectionChanged += OnCollectionChanged;
|
collection.CollectionChanged += OnCollectionChanged;
|
||||||
|
|
||||||
if (factory is not null && factory.Create() is { } items)
|
if (enumerator is not null)
|
||||||
{
|
{
|
||||||
AddRange(factory.Create());
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await foreach (TItem? item in enumerator.Next())
|
||||||
|
{
|
||||||
|
if (item is not null)
|
||||||
|
{
|
||||||
|
Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
public ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
||||||
IMediator mediator,
|
IMediator mediator,
|
||||||
|
IDisposer disposer,
|
||||||
IEnumerable<TItem> items)
|
IEnumerable<TItem> items)
|
||||||
{
|
{
|
||||||
context = SynchronizationContext.Current;
|
context = SynchronizationContext.Current;
|
||||||
|
|
||||||
this.serviceFactory = serviceFactory;
|
this.serviceFactory = serviceFactory;
|
||||||
|
this.disposer = disposer;
|
||||||
|
|
||||||
mediator.Subscribe(this);
|
mediator.Subscribe(this);
|
||||||
|
|
||||||
collection.CollectionChanged += OnCollectionChanged;
|
collection.CollectionChanged += OnCollectionChanged;
|
||||||
@@ -59,6 +83,10 @@ public partial class ObservableCollectionViewModel<TItem> :
|
|||||||
|
|
||||||
public int Count => collection.Count;
|
public int Count => collection.Count;
|
||||||
|
|
||||||
|
public ICommand Initialize => new AsyncRelayCommand(InitializeAsync);
|
||||||
|
|
||||||
|
public bool Initialized { get; private set; }
|
||||||
|
|
||||||
bool IList.IsFixedSize => false;
|
bool IList.IsFixedSize => false;
|
||||||
|
|
||||||
bool ICollection<TItem>.IsReadOnly => false;
|
bool ICollection<TItem>.IsReadOnly => false;
|
||||||
@@ -157,6 +185,7 @@ public partial class ObservableCollectionViewModel<TItem> :
|
|||||||
context?.Post(state => Add(item), null);
|
context?.Post(state => Add(item), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear() => ClearItems();
|
public void Clear() => ClearItems();
|
||||||
|
|
||||||
public bool Contains(TItem item) => collection.Contains(item);
|
public bool Contains(TItem item) => collection.Contains(item);
|
||||||
@@ -171,19 +200,21 @@ public partial class ObservableCollectionViewModel<TItem> :
|
|||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)collection).GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)collection).GetEnumerator();
|
||||||
|
|
||||||
//public ValueTask Handle(CollectionChanged<IEnumerable<TItem>> notification,
|
|
||||||
// CancellationToken cancellationToken)
|
|
||||||
//{
|
|
||||||
// context?.Post(state => Clear(), null);
|
|
||||||
// AddRange(notification.Items);
|
|
||||||
|
|
||||||
// return ValueTask.CompletedTask;
|
|
||||||
//}
|
|
||||||
|
|
||||||
public int IndexOf(TItem item) => collection.IndexOf(item);
|
public int IndexOf(TItem item) => collection.IndexOf(item);
|
||||||
|
|
||||||
int IList.IndexOf(object? value) => IsCompatibleObject(value) ? IndexOf((TItem)value!) : -1;
|
int IList.IndexOf(object? value) => IsCompatibleObject(value) ? IndexOf((TItem)value!) : -1;
|
||||||
|
|
||||||
|
public Task InitializeAsync()
|
||||||
|
{
|
||||||
|
if (Initialized)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
Initialized = true;
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
public void Insert(int index, TItem item) => InsertItem(index, item);
|
public void Insert(int index, TItem item) => InsertItem(index, item);
|
||||||
|
|
||||||
void IList.Insert(int index, object? value)
|
void IList.Insert(int index, object? value)
|
||||||
@@ -209,6 +240,7 @@ public partial class ObservableCollectionViewModel<TItem> :
|
|||||||
Remove((TItem)value!);
|
Remove((TItem)value!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveAt(int index) => RemoveItem(index);
|
public void RemoveAt(int index) => RemoveItem(index);
|
||||||
|
|
||||||
protected virtual void ClearItems() => collection.Clear();
|
protected virtual void ClearItems() => collection.Clear();
|
||||||
@@ -217,7 +249,15 @@ public partial class ObservableCollectionViewModel<TItem> :
|
|||||||
{
|
{
|
||||||
if (value is TItem item)
|
if (value is TItem item)
|
||||||
{
|
{
|
||||||
collection.Insert(index, item);
|
disposer.Add(item, Disposable.Create(() =>
|
||||||
|
{
|
||||||
|
if (Contains(item))
|
||||||
|
{
|
||||||
|
Remove(item);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
context?.Post(state => collection.Insert(index, item), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,13 +270,9 @@ public partial class ObservableCollectionViewModel<TItem> :
|
|||||||
|
|
||||||
private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args) =>
|
private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args) =>
|
||||||
CollectionChanged?.Invoke(this, args);
|
CollectionChanged?.Invoke(this, args);
|
||||||
|
|
||||||
public ValueTask Handle(ValueChanging<IEnumerable<TItem>> notification,
|
|
||||||
CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ObservableCollectionViewModel(IServiceFactory serviceFactory, IMediator mediator) :
|
public class ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
||||||
ObservableCollectionViewModel<object>(serviceFactory, mediator);
|
IMediator mediator,
|
||||||
|
IDisposer disposer) :
|
||||||
|
ObservableCollectionViewModel<object>(serviceFactory, mediator, disposer);
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
namespace Hyperbar;
|
||||||
|
|
||||||
|
public record Removed<TValue>(TValue Value) : INotification;
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
|
namespace Hyperbar;
|
||||||
namespace Hyperbar;
|
|
||||||
|
|
||||||
public partial class WidgetBarViewModel(ITemplateFactory templateFactory,
|
public partial class WidgetBarViewModel(ITemplateFactory templateFactory,
|
||||||
IServiceFactory serviceFactory,
|
IServiceFactory serviceFactory,
|
||||||
IMediator mediator,
|
IMediator mediator,
|
||||||
|
IDisposer disposer,
|
||||||
IEnumerable<WidgetContainerViewModel> items) :
|
IEnumerable<WidgetContainerViewModel> items) :
|
||||||
ObservableCollectionViewModel<WidgetContainerViewModel>(serviceFactory, mediator, items),
|
ObservableCollectionViewModel<WidgetContainerViewModel>(serviceFactory, mediator, disposer, items),
|
||||||
ITemplatedViewModel
|
ITemplatedViewModel
|
||||||
{
|
{
|
||||||
public ITemplateFactory TemplateFactory => templateFactory;
|
public ITemplateFactory TemplateFactory => templateFactory;
|
||||||
|
|||||||
@@ -3,23 +3,20 @@ using CommunityToolkit.Mvvm.Input;
|
|||||||
|
|
||||||
namespace Hyperbar;
|
namespace Hyperbar;
|
||||||
|
|
||||||
public partial class WidgetButtonViewModel :
|
public partial class WidgetButtonViewModel(IServiceFactory serviceFactory,
|
||||||
WidgetComponentViewModel
|
IMediator mediator,
|
||||||
|
IDisposer disposer,
|
||||||
|
ITemplateFactory templateFactory,
|
||||||
|
Guid id = default,
|
||||||
|
string? icon = null,
|
||||||
|
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory)
|
||||||
{
|
{
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string? icon;
|
private Guid id = id;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private IRelayCommand? click;
|
private string? icon = icon;
|
||||||
|
|
||||||
public WidgetButtonViewModel(ITemplateFactory templateFactory,
|
[ObservableProperty]
|
||||||
string? icon = null,
|
private IRelayCommand? click = command;
|
||||||
Action? action = null) : base(templateFactory)
|
|
||||||
{
|
|
||||||
this.icon = icon;
|
|
||||||
if (action is not null)
|
|
||||||
{
|
|
||||||
click = new RelayCommand(action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,19 @@
|
|||||||
|
|
||||||
namespace Hyperbar;
|
namespace Hyperbar;
|
||||||
|
|
||||||
public partial class WidgetComponentViewModel(ITemplateFactory templateFactory) :
|
public class ObservableViewModel(IServiceFactory serviceFactory,
|
||||||
|
IMediator mediator,
|
||||||
|
IDisposer disposer) :
|
||||||
ObservableObject,
|
ObservableObject,
|
||||||
|
IDisposable
|
||||||
|
{
|
||||||
|
public void Dispose() => disposer.Dispose(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class WidgetComponentViewModel(IServiceFactory serviceFactory,
|
||||||
|
IMediator mediator,
|
||||||
|
IDisposer disposer,
|
||||||
|
ITemplateFactory templateFactory) : ObservableViewModel(serviceFactory, mediator, disposer),
|
||||||
IWidgetComponentViewModel,
|
IWidgetComponentViewModel,
|
||||||
ITemplatedViewModel
|
ITemplatedViewModel
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ namespace Hyperbar;
|
|||||||
public partial class WidgetContainerViewModel(ITemplateFactory templateFactory,
|
public partial class WidgetContainerViewModel(ITemplateFactory templateFactory,
|
||||||
IServiceFactory serviceFactory,
|
IServiceFactory serviceFactory,
|
||||||
IMediator mediator,
|
IMediator mediator,
|
||||||
|
IDisposer disposer,
|
||||||
IEnumerable<IWidgetViewModel> items,
|
IEnumerable<IWidgetViewModel> items,
|
||||||
bool alternate) :
|
bool alternate) :
|
||||||
ObservableCollectionViewModel<IWidgetViewModel>(serviceFactory, mediator, items),
|
ObservableCollectionViewModel<IWidgetViewModel>(serviceFactory, mediator, disposer, items),
|
||||||
ITemplatedViewModel
|
ITemplatedViewModel
|
||||||
{
|
{
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
|
|||||||
Reference in New Issue
Block a user