Too much to name... but damn, it got where we are needed
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
namespace Hyperbar.Windows.MediaController;
|
||||
|
||||
public record BackwardRequest : INotification;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
namespace Hyperbar.Windows.MediaController;
|
||||
|
||||
public record FowardRequest : INotification;
|
||||
|
||||
@@ -14,29 +14,18 @@
|
||||
<None Remove="MediaInformationView.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.230913002" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.755" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI" Version="7.1.2" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls.Primitives" Version="7.1.2" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.231202003-experimental1" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.25936-preview" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Hyperbar.Windows.Controls\Hyperbar.Windows.Controls.csproj" />
|
||||
<ProjectReference Include="..\Hyperbar.Windows.UI\Hyperbar.Windows.UI.csproj" />
|
||||
<ProjectReference Include="..\Hyperbar\Hyperbar.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="MediaInformationView.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="MediaControllerView.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="MediaControllerWidgetView.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Hyperbar.Windows.MediaController;
|
||||
|
||||
public record Media;
|
||||
|
||||
|
||||
@@ -1,19 +1,34 @@
|
||||
using Windows.Media.Control;
|
||||
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
namespace Hyperbar.Windows.MediaController;
|
||||
|
||||
public class MediaController :
|
||||
INotificationHandler<PlayRequest>
|
||||
INotificationHandler<Play>,
|
||||
INotificationHandler<Pause>
|
||||
{
|
||||
private readonly IMediator mediator;
|
||||
private readonly GlobalSystemMediaTransportControlsSession session;
|
||||
|
||||
public MediaController(GlobalSystemMediaTransportControlsSession session,
|
||||
IMediator mediator)
|
||||
public MediaController(IMediator mediator,
|
||||
GlobalSystemMediaTransportControlsSession session)
|
||||
{
|
||||
this.mediator = mediator;
|
||||
this.session = session;
|
||||
|
||||
mediator.Subscribe(this);
|
||||
|
||||
session.MediaPropertiesChanged += OnMediaPropertiesChanged;
|
||||
}
|
||||
|
||||
public async ValueTask Handle(PlayRequest notification, CancellationToken cancellationToken) =>
|
||||
private void OnMediaPropertiesChanged(GlobalSystemMediaTransportControlsSession sender,
|
||||
MediaPropertiesChangedEventArgs args)
|
||||
{
|
||||
mediator.PublishAsync(new Changed<Media>());
|
||||
}
|
||||
|
||||
public async ValueTask Handle(Play notification, CancellationToken cancellationToken) =>
|
||||
await session.TryPlayAsync();
|
||||
|
||||
public async ValueTask Handle(Pause notification, CancellationToken cancellationToken) =>
|
||||
await session.TryPauseAsync();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Windows.Media.Control;
|
||||
|
||||
namespace Hyperbar.Windows.MediaController;
|
||||
|
||||
public class MediaControllerFactory(IMediator mediator,
|
||||
IServiceScopeFactory<MediaController> serviceScopeFactory) :
|
||||
IFactory<GlobalSystemMediaTransportControlsSession, MediaController?>
|
||||
{
|
||||
public MediaController? Create(GlobalSystemMediaTransportControlsSession value)
|
||||
{
|
||||
if (serviceScopeFactory.Create(value) is MediaController mediaController)
|
||||
{
|
||||
return mediaController;
|
||||
|
||||
//if (serviceScope.ServiceProvider.GetService<IServiceFactory>() is IServiceFactory serviceFactory)
|
||||
//{
|
||||
// if (serviceFactory.Create<MediaController>(value) is MediaController mediaController)
|
||||
// {
|
||||
// //if (serviceScope.ServiceProvider.GetService<IFactory<MediaControllerViewModel?>>()
|
||||
// // is IFactory<MediaControllerViewModel?> factory)
|
||||
// //{
|
||||
// // if (factory.Create() is MediaControllerViewModel mediaControllerViewModel)
|
||||
// // {
|
||||
// // _ = await mediator.PublishAsync(new Created<MediaControllerViewModel>(mediaControllerViewModel));
|
||||
// // }
|
||||
// //}
|
||||
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Hyperbar.Windows.MediaController;
|
||||
|
||||
public class MediaControllerHandler(IMediator mediator,
|
||||
IServiceScopeProvider<MediaController> scopeProvider) :
|
||||
INotificationHandler<Created<MediaController>>
|
||||
{
|
||||
public async ValueTask Handle(Created<MediaController> notification,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (scopeProvider.TryGet(notification.Value, out IServiceScope? serviceScope))
|
||||
{
|
||||
if (serviceScope is not null)
|
||||
{
|
||||
if (serviceScope.ServiceProvider.GetService<IFactory<MediaControllerViewModel?>>()
|
||||
is IFactory<MediaControllerViewModel?> factory)
|
||||
{
|
||||
if (factory.Create() is MediaControllerViewModel mediaControllerViewModel)
|
||||
{
|
||||
await mediator.PublishAsync(new Created<MediaControllerViewModel>(mediaControllerViewModel),
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +1,15 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Channels;
|
||||
using Windows.Media.Control;
|
||||
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public class MediaControllerManager :
|
||||
namespace Hyperbar.Windows.MediaController;
|
||||
public class MediaControllerManager(IMediator mediator,
|
||||
IFactory<GlobalSystemMediaTransportControlsSession, MediaController> factory) :
|
||||
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;
|
||||
@@ -50,14 +25,10 @@ public class MediaControllerManager :
|
||||
|
||||
private async Task InitializeSessionAsync(GlobalSystemMediaTransportControlsSession session)
|
||||
{
|
||||
if (serviceFactory.Create<MediaController>(session) is MediaController mediaController)
|
||||
if (factory.Create(session) is MediaController mediaController)
|
||||
{
|
||||
await d.Writer.WriteAsync(mediaController);
|
||||
|
||||
mediaControllers.Enqueue(mediaController);
|
||||
|
||||
cachedSessions.TryAdd(session, mediaController);
|
||||
await mediator.PublishAsync(new Created<MediaController>(mediaController));
|
||||
cachedSessions.TryAdd(session, mediaController);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,13 +52,4 @@ public class MediaControllerManager :
|
||||
await InitializeSessionAsync(session);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveSession(GlobalSystemMediaTransportControlsSession session)
|
||||
{
|
||||
if (serviceFactory.Create<MediaController>(session) is MediaController mediaController)
|
||||
{
|
||||
cachedSessions.TryAdd(session, mediaController);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,16 @@
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="using:Hyperbar.Windows.UI">
|
||||
<ItemsControl ItemTemplateSelector="{Binding Mode=TwoWay, Converter={ui:DataTemplateConverter}}" ItemsSource="{Binding}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
</ItemsControl>
|
||||
<Grid Width="400">
|
||||
<ItemsControl
|
||||
HorizontalAlignment="Center"
|
||||
ItemTemplateSelector="{Binding Converter={ui:DataTemplateConverter}}"
|
||||
ItemsSource="{Binding}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
</ItemsControl>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
//public class MediaControllerViewModelFactory(IServiceFactory service,
|
||||
// IMediator mediator,
|
||||
// Queue<MediaController> mediaControllers) :
|
||||
// IFactory<MediaControllerViewModel>
|
||||
//{
|
||||
// public MediaControllerViewModel Create()
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
//}
|
||||
namespace Hyperbar.Windows.MediaController;
|
||||
|
||||
public class MediaControllerViewModel :
|
||||
ObservableCollectionViewModel<WidgetComponentViewModel>,
|
||||
@@ -24,11 +13,11 @@ public class MediaControllerViewModel :
|
||||
{
|
||||
TemplateFactory = templateFactory;
|
||||
|
||||
this.Add<MediaInformationViewModel>();
|
||||
this.Add<WidgetButtonViewModel>("\uEB9E");
|
||||
this.Add<WidgetButtonViewModel>("\uE768");
|
||||
this.Add<WidgetButtonViewModel>("\uE769");
|
||||
this.Add<WidgetButtonViewModel>("\uEB9D");
|
||||
Add<MediaInformationViewModel>();
|
||||
Add<WidgetButtonViewModel>("Backward", "\uEB9E");
|
||||
Add<WidgetButtonViewModel>("Play", "\uE768", new RelayCommand(async () => await mediator.SendAsync(new Play())));
|
||||
Add<WidgetButtonViewModel>("Pause", "\uE769", new RelayCommand(async () => await mediator.PublishAsync(new Pause())));
|
||||
Add<WidgetButtonViewModel>("Forward", "\uEB9D");
|
||||
}
|
||||
|
||||
public ITemplateFactory TemplateFactory { get; set; }
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Hyperbar.Windows.MediaController;
|
||||
|
||||
public class MediaControllerViewModelFactory(IServiceFactory service) :
|
||||
IFactory<MediaControllerViewModel?>
|
||||
{
|
||||
public MediaControllerViewModel? Create()
|
||||
{
|
||||
return service.Create<MediaControllerViewModel>();
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,22 @@
|
||||
using Hyperbar.Windows.MediaController;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System.Collections.Concurrent;
|
||||
using Windows.Media.Control;
|
||||
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
namespace Hyperbar.Windows.MediaController;
|
||||
|
||||
public class MediaControllerWidgetProvider :
|
||||
IWidgetProvider
|
||||
{
|
||||
public void Create(HostBuilderContext comtext, IServiceCollection services) =>
|
||||
services.AddWidgetTemplate<MediaControllerWidgetViewModel, MediaControllerWidgetView>()
|
||||
.AddSingleton<Queue<MediaController>>()
|
||||
.AddSingleton<IInitializer, MediaControllerManager>()
|
||||
.AddContentTemplate<MediaControllerViewModel, MediaControllerView>();
|
||||
|
||||
.AddTransient<IServiceScopeFactory<MediaController>, ServiceScopeFactory<MediaController>>()
|
||||
.AddTransient<IServiceScopeProvider<MediaController>, ServiceScopeProvider<MediaController>>()
|
||||
.AddSingleton<ConcurrentDictionary<MediaController, IServiceScope>>()
|
||||
.AddTransient<IFactory<GlobalSystemMediaTransportControlsSession, MediaController?>, MediaControllerFactory>()
|
||||
.AddHandler<MediaControllerHandler>()
|
||||
.AddTransient<IFactory<MediaControllerViewModel?>, MediaControllerViewModelFactory>()
|
||||
.AddContentTemplate<MediaControllerViewModel, MediaControllerView>()
|
||||
.AddContentTemplate<MediaInformationViewModel, MediaInformationView>();
|
||||
}
|
||||
@@ -6,21 +6,8 @@
|
||||
xmlns:ui="using:Hyperbar.Windows.UI">
|
||||
<FlipView
|
||||
x:Name="FlipView"
|
||||
Width="320"
|
||||
Width="400"
|
||||
Background="Transparent"
|
||||
ItemTemplateSelector="{Binding Converter={ui:DataTemplateConverter}}"
|
||||
ItemsSource="{Binding}">
|
||||
<FlipView.ItemContainerStyle>
|
||||
<Style TargetType="FlipViewItem">
|
||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Right" />
|
||||
|
||||
</Style>
|
||||
</FlipView.ItemContainerStyle>
|
||||
<FlipView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel AreScrollSnapPointsRegular="False" Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</FlipView.ItemsPanel>
|
||||
</FlipView>
|
||||
ItemsSource="{Binding}" />
|
||||
</UserControl>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
namespace Hyperbar.Windows.MediaController;
|
||||
|
||||
public class MediaControllerWidgetViewModel(ITemplateFactory templateFactory,
|
||||
IServiceFactory serviceFactory,
|
||||
|
||||
@@ -3,5 +3,23 @@
|
||||
x:Class="Hyperbar.Windows.MediaController.MediaInformationView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Button>sdfsdf</Button>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="40" />
|
||||
<ColumnDefinition Width="8" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border
|
||||
Grid.Column="0"
|
||||
Width="40"
|
||||
Height="40"
|
||||
Background="Red" />
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Style="{ThemeResource BaseTextBlockStyle}" Text="{Binding Title}" />
|
||||
<TextBlock
|
||||
Opacity="0.7"
|
||||
Style="{ThemeResource CaptionTextBlockStyle}"
|
||||
Text="{Binding Description}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
namespace Hyperbar.Windows.MediaController;
|
||||
|
||||
public partial class MediaInformationViewModel :
|
||||
WidgetComponentViewModel
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace Hyperbar.Windows.MediaController;
|
||||
|
||||
public record Pause : INotification;
|
||||
@@ -1,3 +0,0 @@
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public record PauseRequest : INotification;
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace Hyperbar.Windows.MediaController;
|
||||
|
||||
public record Play : INotification;
|
||||
@@ -1,3 +0,0 @@
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public record PlayRequest : INotification;
|
||||
+4
@@ -9,4 +9,8 @@ public class PrimaryCommandConfiguration
|
||||
public required Guid Id { get; set; }
|
||||
|
||||
public required string Icon { get; set; }
|
||||
|
||||
public required string Text { get; set; }
|
||||
|
||||
public List<PrimaryCommandConfiguration>? Commands { get; set; } = [];
|
||||
}
|
||||
@@ -5,6 +5,6 @@ public class PrimaryWidgetConfiguration :
|
||||
{
|
||||
public static PrimaryWidgetConfiguration Defaults => new()
|
||||
{
|
||||
new KeyAcceleratorCommandConfiguration { Id = Guid.NewGuid(), Icon = "\uE720", Key = 91, Modifiers = [] }
|
||||
new KeyAcceleratorCommandConfiguration { Id = Guid.NewGuid(), Icon = "\uE720", Text = "Test", Key = 91, Modifiers = [] }
|
||||
};
|
||||
}
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public class ConfigurationChangedHandler(IMediator mediator,
|
||||
public class PrimaryWidgetConfigurationHandler(IMediator mediator,
|
||||
PrimaryWidgetConfiguration configuration,
|
||||
IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?> factory,
|
||||
IViewModelCache<Guid, IWidgetComponentViewModel> cache) :
|
||||
IFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?> factory,
|
||||
ICache<Guid, IWidgetComponentViewModel> cache) :
|
||||
INotificationHandler<ConfigurationChanged<PrimaryWidgetConfiguration>>
|
||||
{
|
||||
public async ValueTask Handle(ConfigurationChanged<PrimaryWidgetConfiguration> notification,
|
||||
@@ -8,10 +8,10 @@ public class PrimaryWidgetProvider :
|
||||
{
|
||||
public void Create(HostBuilderContext comtext, IServiceCollection services) =>
|
||||
services.AddConfiguration<PrimaryWidgetConfiguration>()
|
||||
.AddSingleton<IViewModelCache<Guid, IWidgetComponentViewModel>, ViewModelCache<Guid, IWidgetComponentViewModel>>()
|
||||
.AddTransient<IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?>, WidgetComponentViewModelFactory>()
|
||||
.AddSingleton<ICache<Guid, IWidgetComponentViewModel>, Cache<Guid, IWidgetComponentViewModel>>()
|
||||
.AddTransient<IFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?>, WidgetComponentViewModelFactory>()
|
||||
.AddTransient<IViewModelEnumerator<IWidgetComponentViewModel>, WidgetComponentViewModelEnumerator>()
|
||||
.AddWidgetTemplate<PrimaryWidgetViewModel>()
|
||||
.AddHandler<ConfigurationChangedHandler>();
|
||||
.AddHandler<PrimaryWidgetConfigurationHandler>();
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public class WidgetComponentViewModelEnumerator(PrimaryWidgetConfiguration configuration,
|
||||
IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?> factory) :
|
||||
IFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?> factory) :
|
||||
IViewModelEnumerator<IWidgetComponentViewModel>
|
||||
{
|
||||
public IEnumerable<IWidgetComponentViewModel?> Next()
|
||||
|
||||
@@ -4,31 +4,71 @@ namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public class WidgetComponentViewModelFactory(IServiceFactory service,
|
||||
IMediator mediator,
|
||||
IViewModelCache<Guid, IWidgetComponentViewModel> cache) :
|
||||
IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?>
|
||||
ICache<Guid, IWidgetComponentViewModel> cache) :
|
||||
IFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?>
|
||||
{
|
||||
public IWidgetComponentViewModel? Create(PrimaryCommandConfiguration value)
|
||||
public IWidgetComponentViewModel? Create(PrimaryCommandConfiguration configuration)
|
||||
{
|
||||
IWidgetComponentViewModel? viewModel = default;
|
||||
WidgetComponentViewModel? viewModel = default;
|
||||
|
||||
if (value is KeyAcceleratorCommandConfiguration keyAcceleratorCommand)
|
||||
if (configuration is KeyAcceleratorCommandConfiguration keyAcceleratorCommandConfiguration)
|
||||
{
|
||||
viewModel = service.Create<WidgetButtonViewModel>(keyAcceleratorCommand.Id, keyAcceleratorCommand.Icon,
|
||||
new RelayCommand(async () => await mediator.SendAsync(new KeyAcceleratorRequest((VirtualKey)
|
||||
keyAcceleratorCommand.Key, keyAcceleratorCommand.Modifiers?
|
||||
viewModel = service.Create<WidgetButtonViewModel>(keyAcceleratorCommandConfiguration.Id,
|
||||
keyAcceleratorCommandConfiguration.Text, keyAcceleratorCommandConfiguration.Icon,
|
||||
new RelayCommand(async () => await mediator.SendAsync(new KeyAccelerator((VirtualKey)
|
||||
keyAcceleratorCommandConfiguration.Key, keyAcceleratorCommandConfiguration.Modifiers?
|
||||
.Select(modifier => (VirtualKey)modifier).ToArray()))));
|
||||
}
|
||||
|
||||
if (value is ProcessCommandConfiguration commandConfiguration)
|
||||
|
||||
if (configuration is ProcessCommandConfiguration processCommandConfiguration)
|
||||
{
|
||||
viewModel = service.Create<WidgetButtonViewModel>(commandConfiguration.Id,
|
||||
commandConfiguration.Icon, new RelayCommand(async () =>
|
||||
await mediator.SendAsync(new ProcessRequest(commandConfiguration.Path))));
|
||||
if (processCommandConfiguration.Commands is { Count: > 0 } childCommandConfigurations)
|
||||
{
|
||||
List<IWidgetComponentViewModel> childViewModels = [];
|
||||
|
||||
foreach (PrimaryCommandConfiguration childCommandConfiguration in childCommandConfigurations)
|
||||
{
|
||||
WidgetComponentViewModel? childViewModel = null;
|
||||
|
||||
if (childCommandConfiguration is ProcessCommandConfiguration childProcessCommandConfiguration)
|
||||
{
|
||||
childViewModel = service.Create<WidgetMenuViewModel>(childProcessCommandConfiguration.Id,
|
||||
childProcessCommandConfiguration.Icon, childProcessCommandConfiguration.Text,
|
||||
new RelayCommand(async () => await mediator.SendAsync(new StartProcess(childProcessCommandConfiguration.Path))));
|
||||
}
|
||||
|
||||
if (childCommandConfiguration is KeyAcceleratorCommandConfiguration childKeyAcceleratorCommandConfiguration)
|
||||
{
|
||||
childViewModel = service.Create<WidgetMenuViewModel>(childKeyAcceleratorCommandConfiguration.Id,
|
||||
childKeyAcceleratorCommandConfiguration.Text, childKeyAcceleratorCommandConfiguration.Icon,
|
||||
new RelayCommand(async () =>
|
||||
await mediator.SendAsync(new KeyAccelerator((VirtualKey)childKeyAcceleratorCommandConfiguration.Key,
|
||||
childKeyAcceleratorCommandConfiguration.Modifiers?.Select(modifier => (VirtualKey)modifier).ToArray()))));
|
||||
}
|
||||
|
||||
if (childViewModel is not null)
|
||||
{
|
||||
childViewModels.Add(childViewModel);
|
||||
cache.Add(childViewModel.Id, childViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
viewModel = service.Create<WidgetSplitButtonViewModel>(childViewModels,
|
||||
processCommandConfiguration.Id, processCommandConfiguration.Text,
|
||||
processCommandConfiguration.Icon, new RelayCommand(async () =>
|
||||
await mediator.SendAsync(new StartProcess(processCommandConfiguration.Path))));
|
||||
}
|
||||
else
|
||||
{
|
||||
viewModel = service.Create<WidgetButtonViewModel>(processCommandConfiguration.Id,
|
||||
processCommandConfiguration.Text, processCommandConfiguration.Icon, new RelayCommand(async () =>
|
||||
await mediator.SendAsync(new StartProcess(processCommandConfiguration.Path))));
|
||||
}
|
||||
}
|
||||
|
||||
if (viewModel is not null)
|
||||
{
|
||||
cache.Add(value.Id, viewModel);
|
||||
cache.Add(viewModel.Id, viewModel);
|
||||
}
|
||||
|
||||
return viewModel;
|
||||
|
||||
@@ -6,4 +6,4 @@ public enum WindowPlacement
|
||||
Top = 1,
|
||||
Right = 2,
|
||||
Bottom = 3,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Hyperbar.Windows.Controls;
|
||||
using Hyperbar.Windows.MediaController;
|
||||
using Hyperbar.Windows.Primary;
|
||||
using Hyperbar.Windows.UI;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
@@ -50,7 +51,7 @@ public partial class App :
|
||||
|
||||
services.AddHandler<AppConfigurationChangedHandler>();
|
||||
|
||||
//services.AddWidgetProvider<MediaControllerWidgetProvider>();
|
||||
services.AddWidgetProvider<MediaControllerWidgetProvider>();
|
||||
services.AddWidgetProvider<PrimaryWidgetProvider>();
|
||||
|
||||
services.AddTransient(provider =>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
<ItemGroup>
|
||||
<None Remove="Views\WidgetButtonView.xaml" />
|
||||
<None Remove="Views\WidgetContainerView.xaml" />
|
||||
<None Remove="Views\WidgetSplitButtonView.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Assets\SplashScreen.scale-200.png" />
|
||||
@@ -45,6 +46,11 @@
|
||||
<ProjectReference Include="..\Hyperbar.Windows.UI\Hyperbar.Windows.UI.csproj" />
|
||||
<ProjectReference Include="..\Hyperbar\Hyperbar.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Views\WidgetSplitButtonView.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Views\WidgetContainerView.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
@@ -28,24 +28,25 @@ namespace Hyperbar.Windows
|
||||
})
|
||||
.ConfigureServices((context, isolatedServices) =>
|
||||
{
|
||||
isolatedServices.AddSingleton<IServiceFactory>(provider =>
|
||||
isolatedServices.AddScoped<IServiceFactory>(provider =>
|
||||
new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!)));
|
||||
|
||||
isolatedServices.AddHostedService<WidgetService>();
|
||||
|
||||
isolatedServices.AddTransient<ITemplateFactory, TemplateFactory>();
|
||||
isolatedServices.AddSingleton<IMediator, Mediator>();
|
||||
isolatedServices.AddSingleton<IDisposer, Disposer>();
|
||||
isolatedServices.AddScoped<IMediator, Mediator>();
|
||||
isolatedServices.AddScoped<IDisposer, Disposer>();
|
||||
|
||||
isolatedServices.AddSingleton<IVirtualKeyboard, VirtualKeyboard>();
|
||||
isolatedServices.AddScoped<IVirtualKeyboard, VirtualKeyboard>();
|
||||
|
||||
isolatedServices.AddHandler<KeyAcceleratorHandler>();
|
||||
isolatedServices.AddHandler<ProcesssAcceleratorHandler>();
|
||||
isolatedServices.AddHandler<StartProcessHandler>();
|
||||
|
||||
isolatedServices.AddTransient<IWidgetView, WidgetView>();
|
||||
|
||||
isolatedServices.AddContentTemplate<WidgetContainerViewModel, WidgetContainerView>();
|
||||
isolatedServices.AddContentTemplate<WidgetButtonViewModel, WidgetButtonView>();
|
||||
isolatedServices.AddContentTemplate<WidgetSplitButtonViewModel, WidgetSplitButtonView>();
|
||||
|
||||
builder.Create(context, isolatedServices);
|
||||
|
||||
|
||||
@@ -1,26 +1,14 @@
|
||||
using Hyperbar.Windows.Interop;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Hyperbar.Windows;
|
||||
|
||||
public class KeyAcceleratorHandler(IVirtualKeyboard virtualKeyboard) :
|
||||
IRequestHandler<KeyAcceleratorRequest>
|
||||
IRequestHandler<KeyAccelerator>
|
||||
{
|
||||
public ValueTask<Unit> Handle(KeyAcceleratorRequest request,
|
||||
public ValueTask<Unit> Handle(KeyAccelerator request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
virtualKeyboard.Send((int)request.Key, request.Modifiers?.Select(modifier => (int)modifier).ToArray() ?? []);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public class ProcesssAcceleratorHandler :
|
||||
IRequestHandler<ProcessRequest>
|
||||
{
|
||||
public ValueTask<Unit> Handle(ProcessRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
Process.Start(request.Process);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Hyperbar.Windows;
|
||||
|
||||
public class StartProcessHandler :
|
||||
IRequestHandler<StartProcess>
|
||||
{
|
||||
public ValueTask<Unit> Handle(StartProcess request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
Process.Start(request.Process);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -20,5 +20,6 @@
|
||||
Command="{Binding Click}"
|
||||
Content="{Binding Icon}"
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
FontSize="16" />
|
||||
FontSize="16"
|
||||
ToolTipService.ToolTip="{Binding Text}" />
|
||||
</UserControl>
|
||||
@@ -11,7 +11,7 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle
|
||||
Width="1"
|
||||
Margin="6,8,6,8"
|
||||
Margin="6,2,6,2"
|
||||
Fill="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||
Visibility="{Binding Alternate}" />
|
||||
<ItemsControl
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="Hyperbar.Windows.WidgetSplitButtonView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<UserControl.Resources>
|
||||
<SolidColorBrush x:Key="SplitButtonBackground" Color="Transparent" />
|
||||
<SolidColorBrush x:Key="SplitButtonBorderBrush" Color="Transparent" />
|
||||
<SolidColorBrush x:Key="SplitButtonBorderBrushPointerOver" Color="Transparent" />
|
||||
<SolidColorBrush x:Key="SplitButtonBorderBrushPressed" Color="Transparent" />
|
||||
<SolidColorBrush x:Key="SplitButtonBorderBrushDisabled" Color="Transparent" />
|
||||
<Thickness x:Key="ButtonPadding">0</Thickness>
|
||||
<x:Double x:Key="ButtonWidth">40</x:Double>
|
||||
<x:Double x:Key="ButtonHeight">38</x:Double>
|
||||
</UserControl.Resources>
|
||||
<SplitButton
|
||||
Height="{StaticResource ButtonHeight}"
|
||||
Margin="0,1,0,0"
|
||||
Command="{Binding Click}"
|
||||
Content="{Binding Icon}"
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
FontSize="16">
|
||||
<SplitButton.Flyout>
|
||||
<Flyout ShouldConstrainToRootBounds="False">
|
||||
<ItemsControl Margin="-16,-13,-16,-15" ItemsSource="{Binding}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<MenuFlyoutItem Text="{Binding Text}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Flyout>
|
||||
</SplitButton.Flyout>
|
||||
</SplitButton>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,9 @@
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Hyperbar.Windows;
|
||||
|
||||
public sealed partial class WidgetSplitButtonView :
|
||||
UserControl
|
||||
{
|
||||
public WidgetSplitButtonView() => InitializeComponent();
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
xmlns:interactions="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:ui="using:Hyperbar.Windows.UI">
|
||||
|
||||
<ItemsControl ItemTemplateSelector="{Binding Converter={ui:DataTemplateConverter}}" ItemsSource="{Binding}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public record KeyAcceleratorRequest(VirtualKey Key,
|
||||
public record KeyAccelerator(VirtualKey Key,
|
||||
VirtualKey[]? Modifiers = null) :
|
||||
IRequest;
|
||||
@@ -1,3 +0,0 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public record ProcessRequest(string Process) : IRequest;
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public record StartProcess(string Process) : IRequest;
|
||||
@@ -119,16 +119,23 @@ public static class IServiceCollectionExtensions
|
||||
where THandler :
|
||||
IHandler
|
||||
{
|
||||
if (typeof(THandler).GetInterface(typeof(INotificationHandler<>).Name) is { } notificationContract)
|
||||
if (typeof(THandler).GetInterfaces() is { } contracts)
|
||||
{
|
||||
if (notificationContract.GetGenericArguments() is { Length: 1 } arguments)
|
||||
foreach (Type contract in contracts)
|
||||
{
|
||||
Type notificationType = arguments[0];
|
||||
if (contract.Name == typeof(INotificationHandler<>).Name)
|
||||
{
|
||||
if (contract.GetGenericArguments() is { Length: 1 } arguments)
|
||||
{
|
||||
Type notificationType = arguments[0];
|
||||
|
||||
services.TryAdd(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime));
|
||||
services.Add(new ServiceDescriptor(typeof(INotificationHandler<>).MakeGenericType(notificationType),
|
||||
provider => provider.GetRequiredService<THandler>(), lifetime));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
services.TryAdd(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime));
|
||||
services.Add(new ServiceDescriptor(typeof(INotificationHandler<>).MakeGenericType(notificationType),
|
||||
provider => provider.GetRequiredService<THandler>(), lifetime));
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof(THandler).GetInterface(typeof(IHandler<,>).Name) is { } requestContract)
|
||||
@@ -150,20 +157,6 @@ public static class IServiceCollectionExtensions
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof(THandler).GetInterface(typeof(IMappingHandler<,>).Name) is { } mappingContract)
|
||||
{
|
||||
if (mappingContract.GetGenericArguments() is { Length: 2 } arguments)
|
||||
{
|
||||
Type responseType = arguments[1];
|
||||
|
||||
services.AddTransient(typeof(THandler));
|
||||
services.AddTransient(responseType, provider =>
|
||||
{
|
||||
return ((dynamic)provider.GetRequiredService<THandler>()).Handle();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
public static IServiceCollection AddWidgetTemplate<TWidgetContent>(this IServiceCollection services)
|
||||
|
||||
@@ -4,13 +4,13 @@ using System.Reactive.Disposables;
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
public class ViewModelCache<TKey, TViewModel>(IDisposer disposer) :
|
||||
IViewModelCache<TKey, TViewModel>
|
||||
public class Cache<TKey, TService>(IDisposer disposer) :
|
||||
ICache<TKey, TService>
|
||||
where TKey : notnull
|
||||
{
|
||||
private readonly IDictionary<TKey, TViewModel> cache = new Dictionary<TKey, TViewModel>();
|
||||
private readonly IDictionary<TKey, TService> cache = new Dictionary<TKey, TService>();
|
||||
|
||||
public TViewModel this[TKey key]
|
||||
public TService this[TKey key]
|
||||
{
|
||||
get => cache[key];
|
||||
set => cache[key] = value;
|
||||
@@ -18,13 +18,13 @@ public class ViewModelCache<TKey, TViewModel>(IDisposer disposer) :
|
||||
|
||||
public ICollection<TKey> Keys => cache.Keys;
|
||||
|
||||
public ICollection<TViewModel> Values => cache.Values;
|
||||
public ICollection<TService> Values => cache.Values;
|
||||
|
||||
public int Count => cache.Count;
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public void Add(TKey key, TViewModel value)
|
||||
public void Add(TKey key, TService value)
|
||||
{
|
||||
disposer.Add(value!, Disposable.Create(() =>
|
||||
{
|
||||
@@ -34,14 +34,14 @@ public class ViewModelCache<TKey, TViewModel>(IDisposer disposer) :
|
||||
cache.Add(key, value);
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<TKey, TViewModel> item)
|
||||
public void Add(KeyValuePair<TKey, TService> item)
|
||||
{
|
||||
cache.Add(item);
|
||||
}
|
||||
|
||||
public void Clear() => cache.Clear();
|
||||
|
||||
public bool Contains(KeyValuePair<TKey, TViewModel> item)
|
||||
public bool Contains(KeyValuePair<TKey, TService> item)
|
||||
{
|
||||
return cache.Contains(item);
|
||||
}
|
||||
@@ -51,12 +51,12 @@ public class ViewModelCache<TKey, TViewModel>(IDisposer disposer) :
|
||||
return cache.ContainsKey(key);
|
||||
}
|
||||
|
||||
public void CopyTo(KeyValuePair<TKey, TViewModel>[] array, int arrayIndex)
|
||||
public void CopyTo(KeyValuePair<TKey, TService>[] array, int arrayIndex)
|
||||
{
|
||||
cache.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TViewModel>> GetEnumerator()
|
||||
public IEnumerator<KeyValuePair<TKey, TService>> GetEnumerator()
|
||||
{
|
||||
return cache.GetEnumerator();
|
||||
}
|
||||
@@ -66,12 +66,12 @@ public class ViewModelCache<TKey, TViewModel>(IDisposer disposer) :
|
||||
return cache.Remove(key);
|
||||
}
|
||||
|
||||
public bool Remove(KeyValuePair<TKey, TViewModel> item)
|
||||
public bool Remove(KeyValuePair<TKey, TService> item)
|
||||
{
|
||||
return cache.Remove(item);
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TViewModel value)
|
||||
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TService value)
|
||||
{
|
||||
return cache.TryGetValue(key, out value);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public interface IViewModelCache<TKey, TViewModel> :
|
||||
public interface ICache<TKey, TViewModel> :
|
||||
IDictionary<TKey, TViewModel>
|
||||
where TKey : notnull
|
||||
{
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public interface IFactory<TParameter, TService>
|
||||
{
|
||||
TService? Create(TParameter value);
|
||||
}
|
||||
|
||||
|
||||
public interface IFactory<TService>
|
||||
{
|
||||
TService? Create();
|
||||
}
|
||||
@@ -6,4 +6,4 @@ public interface IServiceFactory
|
||||
params object?[] parameters);
|
||||
|
||||
TService Create<TService>(params object?[] parameters);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public interface IServiceScopeFactory<TService>
|
||||
{
|
||||
TService? Create(params object?[] parameters);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
public interface IServiceScopeProvider<TService>
|
||||
{
|
||||
bool TryGet(TService service, out IServiceScope? serviceScope);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public interface IViewModelFactory<TParameter, TViewModel>
|
||||
{
|
||||
TViewModel? Create(TParameter value);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
public class ServiceScopeFactory<TService>(IServiceScopeFactory serviceScopeFactory,
|
||||
ConcurrentDictionary<TService, IServiceScope> services) :
|
||||
IServiceScopeFactory<TService>
|
||||
where TService : notnull
|
||||
{
|
||||
public TService? Create(params object?[] parameters)
|
||||
{
|
||||
if (serviceScopeFactory.CreateScope() is IServiceScope serviceScope)
|
||||
{
|
||||
if (serviceScope.ServiceProvider.GetService<IServiceFactory>() is IServiceFactory serviceFactory)
|
||||
{
|
||||
if (serviceFactory.Create<TService>(parameters) is TService service)
|
||||
{
|
||||
services.TryAdd(service, serviceScope);
|
||||
return service;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
public class ServiceScopeProvider<TService>(ConcurrentDictionary<TService, IServiceScope> services) :
|
||||
IServiceScopeProvider<TService>
|
||||
where TService : notnull
|
||||
{
|
||||
public bool TryGet(TService service,
|
||||
out IServiceScope? serviceScope)
|
||||
{
|
||||
if (services.TryGetValue(service, out IServiceScope? value))
|
||||
{
|
||||
serviceScope = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
serviceScope = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public interface IMappingHandler<TFrom, TTo> : IHandler
|
||||
{
|
||||
TTo Handle();
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
@@ -8,7 +8,7 @@ public class Mediator(IServiceProvider provider) :
|
||||
{
|
||||
private readonly SynchronizationContext? context = SynchronizationContext.Current;
|
||||
|
||||
private readonly ConditionalWeakTable<Type, dynamic> subjects = [];
|
||||
private readonly ConcurrentDictionary<Type, List<dynamic>> subjects = [];
|
||||
|
||||
public ValueTask PublishAsync<TNotification>(TNotification notification,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -18,11 +18,14 @@ public class Mediator(IServiceProvider provider) :
|
||||
List<INotificationHandler<TNotification>> handlers =
|
||||
provider.GetServices<INotificationHandler<TNotification>>().ToList();
|
||||
|
||||
foreach (KeyValuePair<Type, dynamic> handler in subjects)
|
||||
foreach (KeyValuePair<Type, List<dynamic>> handler in subjects)
|
||||
{
|
||||
if (handler.Key == typeof(TNotification))
|
||||
{
|
||||
handlers.Add(handler.Value);
|
||||
foreach (dynamic value in handler.Value)
|
||||
{
|
||||
handlers.Add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +83,11 @@ public class Mediator(IServiceProvider provider) :
|
||||
if (interfaceType.GetGenericArguments() is { Length: 1 } arguments)
|
||||
{
|
||||
Type notificationType = arguments[0];
|
||||
subjects.Add(notificationType, subject);
|
||||
subjects.AddOrUpdate(notificationType, [subject], (value, collection) =>
|
||||
{
|
||||
collection.Add(subject);
|
||||
return collection;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public record Changed<TValue>(TValue? Value = default) : INotification;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public record Created<TValue>(TValue? Value = default) : INotification;
|
||||
public record Created<TValue>(TValue Value) : INotification;
|
||||
@@ -19,6 +19,7 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
private readonly IDisposer disposer;
|
||||
private readonly IViewModelEnumerator<TItem>? enumerator;
|
||||
private readonly IServiceFactory serviceFactory;
|
||||
|
||||
public ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer)
|
||||
@@ -138,7 +139,7 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
where T : TItem
|
||||
{
|
||||
T? item = serviceFactory.Create<T>(parameters);
|
||||
context?.Post(state => Add(item), null);
|
||||
Add(item);
|
||||
|
||||
return item;
|
||||
}
|
||||
@@ -155,20 +156,17 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
|
||||
public void Add(TItem item)
|
||||
{
|
||||
context?.Post(state =>
|
||||
disposer.Add(this, item);
|
||||
disposer.Add(item, Disposable.Create(item, args =>
|
||||
{
|
||||
disposer.Add(this, item);
|
||||
disposer.Add(item, Disposable.Create(item, args =>
|
||||
if (Contains(args))
|
||||
{
|
||||
if (Contains(args))
|
||||
{
|
||||
Remove(args);
|
||||
}
|
||||
}));
|
||||
Remove(args);
|
||||
}
|
||||
}));
|
||||
|
||||
int index = collection.Count;
|
||||
InsertItem(index, item);
|
||||
}, null);
|
||||
int index = collection.Count;
|
||||
InsertItem(index, item);
|
||||
}
|
||||
|
||||
int IList.Add(object? value)
|
||||
@@ -281,11 +279,7 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
int index = collection.IndexOf(item);
|
||||
if (index < 0) return false;
|
||||
|
||||
context?.Post(state =>
|
||||
{
|
||||
context?.Post(state => RemoveItem(index), null);
|
||||
|
||||
}, null);
|
||||
RemoveItem(index);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,12 +8,16 @@ public partial class WidgetButtonViewModel(IServiceFactory serviceFactory,
|
||||
IDisposer disposer,
|
||||
ITemplateFactory templateFactory,
|
||||
Guid guid = default,
|
||||
string? text = null,
|
||||
string? icon = null,
|
||||
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory, guid)
|
||||
{
|
||||
[ObservableProperty]
|
||||
private IRelayCommand? click = command;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? icon = icon;
|
||||
|
||||
[ObservableProperty]
|
||||
private IRelayCommand? click = command;
|
||||
private string? text = text;
|
||||
}
|
||||
@@ -3,29 +3,32 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public partial class WidgetComponentViewModel :
|
||||
ObservableViewModel,
|
||||
ObservableCollectionViewModel<IWidgetComponentViewModel>,
|
||||
IWidgetComponentViewModel,
|
||||
ITemplatedViewModel
|
||||
{
|
||||
private readonly IMediator mediator;
|
||||
private readonly IServiceFactory serviceFactory;
|
||||
|
||||
[ObservableProperty]
|
||||
private Guid id;
|
||||
|
||||
public WidgetComponentViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
ITemplateFactory templateFactory,
|
||||
IEnumerable<IWidgetComponentViewModel> items,
|
||||
Guid id = default) : base(serviceFactory, mediator, disposer, items)
|
||||
{
|
||||
this.id = id;
|
||||
TemplateFactory = templateFactory;
|
||||
}
|
||||
|
||||
public WidgetComponentViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
ITemplateFactory templateFactory,
|
||||
Guid id = default) : base(serviceFactory, mediator, disposer)
|
||||
{
|
||||
this.serviceFactory = serviceFactory;
|
||||
this.mediator = mediator;
|
||||
this.id = id;
|
||||
|
||||
TemplateFactory = templateFactory;
|
||||
|
||||
mediator.Subscribe(this);
|
||||
}
|
||||
|
||||
public ITemplateFactory TemplateFactory { get; private set; }
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
public partial class WidgetMenuViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
ITemplateFactory templateFactory,
|
||||
Guid guid = default,
|
||||
string? text = null,
|
||||
string? icon = null,
|
||||
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory, guid)
|
||||
{
|
||||
[ObservableProperty]
|
||||
private IRelayCommand? click = command;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? icon = icon;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? text = text;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
public partial class WidgetSplitButtonViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
ITemplateFactory templateFactory,
|
||||
IEnumerable<IWidgetComponentViewModel> items,
|
||||
Guid guid = default,
|
||||
string? text = null,
|
||||
string? icon = null,
|
||||
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory, items, guid)
|
||||
{
|
||||
[ObservableProperty]
|
||||
private IRelayCommand? click = command;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? icon = icon;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? text = text;
|
||||
}
|
||||
Reference in New Issue
Block a user