Too much to name... but damn, it got where we are needed

This commit is contained in:
TheXamlGuy
2024-01-14 15:06:30 +00:00
parent 66f4bb8757
commit 1283e8ff58
59 changed files with 511 additions and 250 deletions
@@ -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}">
<Grid Width="400">
<ItemsControl
HorizontalAlignment="Center"
ItemTemplateSelector="{Binding Converter={ui:DataTemplateConverter}}"
ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
<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;
+3
View File
@@ -0,0 +1,3 @@
namespace Hyperbar.Windows.MediaController;
public record Play : INotification;
@@ -1,3 +0,0 @@
namespace Hyperbar.Windows.Primary;
public record PlayRequest : INotification;
@@ -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 = [] }
};
}
@@ -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;
+2 -1
View File
@@ -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 =>
+6
View File
@@ -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;
}
}
+2 -1
View File
@@ -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();
}
-1
View File
@@ -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;
-3
View File
@@ -1,3 +0,0 @@
namespace Hyperbar;
public record ProcessRequest(string Process) : IRequest;
+3
View File
@@ -0,0 +1,3 @@
namespace Hyperbar;
public record StartProcess(string Process) : IRequest;
@@ -119,9 +119,13 @@ 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)
{
if (contract.Name == typeof(INotificationHandler<>).Name)
{
if (contract.GetGenericArguments() is { Length: 1 } arguments)
{
Type notificationType = arguments[0];
@@ -130,6 +134,9 @@ public static class IServiceCollectionExtensions
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
{
+12
View File
@@ -0,0 +1,12 @@
namespace Hyperbar;
public interface IFactory<TParameter, TService>
{
TService? Create(TParameter value);
}
public interface IFactory<TService>
{
TService? Create();
}
@@ -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);
}
-6
View File
@@ -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;
}
}
-6
View File
@@ -1,6 +0,0 @@
namespace Hyperbar;
public interface IMappingHandler<TFrom, TTo> : IHandler
{
TTo Handle();
}
+12 -5
View File
@@ -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;
});
}
}
}
+4
View File
@@ -0,0 +1,4 @@
namespace Hyperbar;
public record Changed<TValue>(TValue? Value = default) : INotification;
+1 -1
View File
@@ -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;
}
@@ -154,8 +155,6 @@ public partial class ObservableCollectionViewModel<TItem> :
}
public void Add(TItem item)
{
context?.Post(state =>
{
disposer.Add(this, item);
disposer.Add(item, Disposable.Create(item, args =>
@@ -168,7 +167,6 @@ public partial class ObservableCollectionViewModel<TItem> :
int index = collection.Count;
InsertItem(index, item);
}, null);
}
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;
}
+5 -1
View File
@@ -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;
}
+12 -9
View File
@@ -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; }
+23
View File
@@ -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;
}