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; public record BackwardRequest : INotification;
@@ -1,3 +1,3 @@
namespace Hyperbar.Windows.Primary; namespace Hyperbar.Windows.MediaController;
public record FowardRequest : INotification; public record FowardRequest : INotification;
@@ -14,29 +14,18 @@
<None Remove="MediaInformationView.xaml" /> <None Remove="MediaInformationView.xaml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.230913002" /> <PackageReference Include="CommunityToolkit.WinUI" Version="7.1.2" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.755" /> <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>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Hyperbar.Windows.Controls\Hyperbar.Windows.Controls.csproj" />
<ProjectReference Include="..\Hyperbar.Windows.UI\Hyperbar.Windows.UI.csproj" /> <ProjectReference Include="..\Hyperbar.Windows.UI\Hyperbar.Windows.UI.csproj" />
<ProjectReference Include="..\Hyperbar\Hyperbar.csproj" /> <ProjectReference Include="..\Hyperbar\Hyperbar.csproj" />
</ItemGroup> </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> </Project>
@@ -0,0 +1,5 @@
namespace Hyperbar.Windows.MediaController;
public record Media;
@@ -1,19 +1,34 @@
using Windows.Media.Control; using Windows.Media.Control;
namespace Hyperbar.Windows.Primary; namespace Hyperbar.Windows.MediaController;
public class MediaController : public class MediaController :
INotificationHandler<PlayRequest> INotificationHandler<Play>,
INotificationHandler<Pause>
{ {
private readonly IMediator mediator;
private readonly GlobalSystemMediaTransportControlsSession session; private readonly GlobalSystemMediaTransportControlsSession session;
public MediaController(GlobalSystemMediaTransportControlsSession session, public MediaController(IMediator mediator,
IMediator mediator) GlobalSystemMediaTransportControlsSession session)
{ {
this.mediator = mediator;
this.session = session; this.session = session;
mediator.Subscribe(this); 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(); 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.Collections.Concurrent;
using System.Threading.Channels;
using Windows.Media.Control; using Windows.Media.Control;
namespace Hyperbar.Windows.Primary; namespace Hyperbar.Windows.MediaController;
public class MediaControllerManager(IMediator mediator,
public class MediaControllerManager : IFactory<GlobalSystemMediaTransportControlsSession, MediaController> factory) :
IInitializer IInitializer
{ {
private readonly ConcurrentDictionary<GlobalSystemMediaTransportControlsSession, MediaController> cachedSessions = []; 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() public async Task InitializeAsync()
{ {
d = Channel.CreateUnbounded<MediaController>();
_ = Task.Run(async () => {
await foreach (var coordinates in d.Reader.ReadAllAsync())
{
Console.WriteLine(coordinates);
}
});
GlobalSystemMediaTransportControlsSessionManager mediaTransportControlsSessionManager = GlobalSystemMediaTransportControlsSessionManager mediaTransportControlsSessionManager =
await GlobalSystemMediaTransportControlsSessionManager.RequestAsync(); await GlobalSystemMediaTransportControlsSessionManager.RequestAsync();
mediaTransportControlsSessionManager.SessionsChanged += OnSessionsChanged; mediaTransportControlsSessionManager.SessionsChanged += OnSessionsChanged;
@@ -50,14 +25,10 @@ public class MediaControllerManager :
private async Task InitializeSessionAsync(GlobalSystemMediaTransportControlsSession session) 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)); await mediator.PublishAsync(new Created<MediaController>(mediaController));
cachedSessions.TryAdd(session, mediaController);
} }
} }
@@ -81,13 +52,4 @@ public class MediaControllerManager :
await InitializeSessionAsync(session); 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="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:ui="using:Hyperbar.Windows.UI"> xmlns:ui="using:Hyperbar.Windows.UI">
<ItemsControl ItemTemplateSelector="{Binding Mode=TwoWay, Converter={ui:DataTemplateConverter}}" ItemsSource="{Binding}"> <Grid Width="400">
<ItemsControl.ItemsPanel> <ItemsControl
<ItemsPanelTemplate> HorizontalAlignment="Center"
<StackPanel Orientation="Horizontal" /> ItemTemplateSelector="{Binding Converter={ui:DataTemplateConverter}}"
</ItemsPanelTemplate> ItemsSource="{Binding}">
</ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
</ItemsControl> <ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="8" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</UserControl> </UserControl>
@@ -1,17 +1,6 @@
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
namespace Hyperbar.Windows.Primary; namespace Hyperbar.Windows.MediaController;
//public class MediaControllerViewModelFactory(IServiceFactory service,
// IMediator mediator,
// Queue<MediaController> mediaControllers) :
// IFactory<MediaControllerViewModel>
//{
// public MediaControllerViewModel Create()
// {
// throw new NotImplementedException();
// }
//}
public class MediaControllerViewModel : public class MediaControllerViewModel :
ObservableCollectionViewModel<WidgetComponentViewModel>, ObservableCollectionViewModel<WidgetComponentViewModel>,
@@ -24,11 +13,11 @@ public class MediaControllerViewModel :
{ {
TemplateFactory = templateFactory; TemplateFactory = templateFactory;
this.Add<MediaInformationViewModel>(); Add<MediaInformationViewModel>();
this.Add<WidgetButtonViewModel>("\uEB9E"); Add<WidgetButtonViewModel>("Backward", "\uEB9E");
this.Add<WidgetButtonViewModel>("\uE768"); Add<WidgetButtonViewModel>("Play", "\uE768", new RelayCommand(async () => await mediator.SendAsync(new Play())));
this.Add<WidgetButtonViewModel>("\uE769"); Add<WidgetButtonViewModel>("Pause", "\uE769", new RelayCommand(async () => await mediator.PublishAsync(new Pause())));
this.Add<WidgetButtonViewModel>("\uEB9D"); Add<WidgetButtonViewModel>("Forward", "\uEB9D");
} }
public ITemplateFactory TemplateFactory { get; set; } 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.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using System.Collections.Concurrent;
using Windows.Media.Control;
namespace Hyperbar.Windows.Primary; namespace Hyperbar.Windows.MediaController;
public class MediaControllerWidgetProvider : public class MediaControllerWidgetProvider :
IWidgetProvider IWidgetProvider
{ {
public void Create(HostBuilderContext comtext, IServiceCollection services) => public void Create(HostBuilderContext comtext, IServiceCollection services) =>
services.AddWidgetTemplate<MediaControllerWidgetViewModel, MediaControllerWidgetView>() services.AddWidgetTemplate<MediaControllerWidgetViewModel, MediaControllerWidgetView>()
.AddSingleton<Queue<MediaController>>()
.AddSingleton<IInitializer, MediaControllerManager>() .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"> xmlns:ui="using:Hyperbar.Windows.UI">
<FlipView <FlipView
x:Name="FlipView" x:Name="FlipView"
Width="320" Width="400"
Background="Transparent" Background="Transparent"
ItemTemplateSelector="{Binding Converter={ui:DataTemplateConverter}}" ItemTemplateSelector="{Binding Converter={ui:DataTemplateConverter}}"
ItemsSource="{Binding}"> 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>
</UserControl> </UserControl>
@@ -1,4 +1,4 @@
namespace Hyperbar.Windows.Primary; namespace Hyperbar.Windows.MediaController;
public class MediaControllerWidgetViewModel(ITemplateFactory templateFactory, public class MediaControllerWidgetViewModel(ITemplateFactory templateFactory,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
@@ -3,5 +3,23 @@
x:Class="Hyperbar.Windows.MediaController.MediaInformationView" x:Class="Hyperbar.Windows.MediaController.MediaInformationView"
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">
<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> </UserControl>
@@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
namespace Hyperbar.Windows.Primary; namespace Hyperbar.Windows.MediaController;
public partial class MediaInformationViewModel : public partial class MediaInformationViewModel :
WidgetComponentViewModel 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 Guid Id { get; set; }
public required string Icon { 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() 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; namespace Hyperbar.Windows.Primary;
public class ConfigurationChangedHandler(IMediator mediator, public class PrimaryWidgetConfigurationHandler(IMediator mediator,
PrimaryWidgetConfiguration configuration, PrimaryWidgetConfiguration configuration,
IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?> factory, IFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?> factory,
IViewModelCache<Guid, IWidgetComponentViewModel> cache) : ICache<Guid, IWidgetComponentViewModel> cache) :
INotificationHandler<ConfigurationChanged<PrimaryWidgetConfiguration>> INotificationHandler<ConfigurationChanged<PrimaryWidgetConfiguration>>
{ {
public async ValueTask Handle(ConfigurationChanged<PrimaryWidgetConfiguration> notification, public async ValueTask Handle(ConfigurationChanged<PrimaryWidgetConfiguration> notification,
@@ -8,10 +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>()
.AddSingleton<IViewModelCache<Guid, IWidgetComponentViewModel>, ViewModelCache<Guid, IWidgetComponentViewModel>>() .AddSingleton<ICache<Guid, IWidgetComponentViewModel>, Cache<Guid, IWidgetComponentViewModel>>()
.AddTransient<IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?>, WidgetComponentViewModelFactory>() .AddTransient<IFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?>, WidgetComponentViewModelFactory>()
.AddTransient<IViewModelEnumerator<IWidgetComponentViewModel>, WidgetComponentViewModelEnumerator>() .AddTransient<IViewModelEnumerator<IWidgetComponentViewModel>, WidgetComponentViewModelEnumerator>()
.AddWidgetTemplate<PrimaryWidgetViewModel>() .AddWidgetTemplate<PrimaryWidgetViewModel>()
.AddHandler<ConfigurationChangedHandler>(); .AddHandler<PrimaryWidgetConfigurationHandler>();
} }
@@ -1,7 +1,7 @@
namespace Hyperbar.Windows.Primary; namespace Hyperbar.Windows.Primary;
public class WidgetComponentViewModelEnumerator(PrimaryWidgetConfiguration configuration, public class WidgetComponentViewModelEnumerator(PrimaryWidgetConfiguration configuration,
IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?> factory) : IFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?> factory) :
IViewModelEnumerator<IWidgetComponentViewModel> IViewModelEnumerator<IWidgetComponentViewModel>
{ {
public IEnumerable<IWidgetComponentViewModel?> Next() public IEnumerable<IWidgetComponentViewModel?> Next()
@@ -4,31 +4,71 @@ namespace Hyperbar.Windows.Primary;
public class WidgetComponentViewModelFactory(IServiceFactory service, public class WidgetComponentViewModelFactory(IServiceFactory service,
IMediator mediator, IMediator mediator,
IViewModelCache<Guid, IWidgetComponentViewModel> cache) : ICache<Guid, IWidgetComponentViewModel> cache) :
IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?> 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, viewModel = service.Create<WidgetButtonViewModel>(keyAcceleratorCommandConfiguration.Id,
new RelayCommand(async () => await mediator.SendAsync(new KeyAcceleratorRequest((VirtualKey) keyAcceleratorCommandConfiguration.Text, keyAcceleratorCommandConfiguration.Icon,
keyAcceleratorCommand.Key, keyAcceleratorCommand.Modifiers? new RelayCommand(async () => await mediator.SendAsync(new KeyAccelerator((VirtualKey)
keyAcceleratorCommandConfiguration.Key, keyAcceleratorCommandConfiguration.Modifiers?
.Select(modifier => (VirtualKey)modifier).ToArray())))); .Select(modifier => (VirtualKey)modifier).ToArray()))));
} }
if (value is ProcessCommandConfiguration commandConfiguration) if (configuration is ProcessCommandConfiguration processCommandConfiguration)
{ {
viewModel = service.Create<WidgetButtonViewModel>(commandConfiguration.Id, if (processCommandConfiguration.Commands is { Count: > 0 } childCommandConfigurations)
commandConfiguration.Icon, new RelayCommand(async () => {
await mediator.SendAsync(new ProcessRequest(commandConfiguration.Path)))); 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) if (viewModel is not null)
{ {
cache.Add(value.Id, viewModel); cache.Add(viewModel.Id, viewModel);
} }
return viewModel; return viewModel;
@@ -6,4 +6,4 @@ public enum WindowPlacement
Top = 1, Top = 1,
Right = 2, Right = 2,
Bottom = 3, Bottom = 3,
} }
+2 -1
View File
@@ -1,4 +1,5 @@
using Hyperbar.Windows.Controls; using Hyperbar.Windows.Controls;
using Hyperbar.Windows.MediaController;
using Hyperbar.Windows.Primary; using Hyperbar.Windows.Primary;
using Hyperbar.Windows.UI; using Hyperbar.Windows.UI;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@@ -50,7 +51,7 @@ public partial class App :
services.AddHandler<AppConfigurationChangedHandler>(); services.AddHandler<AppConfigurationChangedHandler>();
//services.AddWidgetProvider<MediaControllerWidgetProvider>(); services.AddWidgetProvider<MediaControllerWidgetProvider>();
services.AddWidgetProvider<PrimaryWidgetProvider>(); services.AddWidgetProvider<PrimaryWidgetProvider>();
services.AddTransient(provider => services.AddTransient(provider =>
+6
View File
@@ -16,6 +16,7 @@
<ItemGroup> <ItemGroup>
<None Remove="Views\WidgetButtonView.xaml" /> <None Remove="Views\WidgetButtonView.xaml" />
<None Remove="Views\WidgetContainerView.xaml" /> <None Remove="Views\WidgetContainerView.xaml" />
<None Remove="Views\WidgetSplitButtonView.xaml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Assets\SplashScreen.scale-200.png" /> <Content Include="Assets\SplashScreen.scale-200.png" />
@@ -45,6 +46,11 @@
<ProjectReference Include="..\Hyperbar.Windows.UI\Hyperbar.Windows.UI.csproj" /> <ProjectReference Include="..\Hyperbar.Windows.UI\Hyperbar.Windows.UI.csproj" />
<ProjectReference Include="..\Hyperbar\Hyperbar.csproj" /> <ProjectReference Include="..\Hyperbar\Hyperbar.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Page Update="Views\WidgetSplitButtonView.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Page Update="Views\WidgetContainerView.xaml"> <Page Update="Views\WidgetContainerView.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
@@ -28,24 +28,25 @@ namespace Hyperbar.Windows
}) })
.ConfigureServices((context, isolatedServices) => .ConfigureServices((context, isolatedServices) =>
{ {
isolatedServices.AddSingleton<IServiceFactory>(provider => isolatedServices.AddScoped<IServiceFactory>(provider =>
new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!))); new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!)));
isolatedServices.AddHostedService<WidgetService>(); isolatedServices.AddHostedService<WidgetService>();
isolatedServices.AddTransient<ITemplateFactory, TemplateFactory>(); isolatedServices.AddTransient<ITemplateFactory, TemplateFactory>();
isolatedServices.AddSingleton<IMediator, Mediator>(); isolatedServices.AddScoped<IMediator, Mediator>();
isolatedServices.AddSingleton<IDisposer, Disposer>(); isolatedServices.AddScoped<IDisposer, Disposer>();
isolatedServices.AddSingleton<IVirtualKeyboard, VirtualKeyboard>(); isolatedServices.AddScoped<IVirtualKeyboard, VirtualKeyboard>();
isolatedServices.AddHandler<KeyAcceleratorHandler>(); isolatedServices.AddHandler<KeyAcceleratorHandler>();
isolatedServices.AddHandler<ProcesssAcceleratorHandler>(); isolatedServices.AddHandler<StartProcessHandler>();
isolatedServices.AddTransient<IWidgetView, WidgetView>(); isolatedServices.AddTransient<IWidgetView, WidgetView>();
isolatedServices.AddContentTemplate<WidgetContainerViewModel, WidgetContainerView>(); isolatedServices.AddContentTemplate<WidgetContainerViewModel, WidgetContainerView>();
isolatedServices.AddContentTemplate<WidgetButtonViewModel, WidgetButtonView>(); isolatedServices.AddContentTemplate<WidgetButtonViewModel, WidgetButtonView>();
isolatedServices.AddContentTemplate<WidgetSplitButtonViewModel, WidgetSplitButtonView>();
builder.Create(context, isolatedServices); builder.Create(context, isolatedServices);
@@ -1,26 +1,14 @@
using Hyperbar.Windows.Interop; using Hyperbar.Windows.Interop;
using System.Diagnostics;
namespace Hyperbar.Windows; namespace Hyperbar.Windows;
public class KeyAcceleratorHandler(IVirtualKeyboard virtualKeyboard) : public class KeyAcceleratorHandler(IVirtualKeyboard virtualKeyboard) :
IRequestHandler<KeyAcceleratorRequest> IRequestHandler<KeyAccelerator>
{ {
public ValueTask<Unit> Handle(KeyAcceleratorRequest request, public ValueTask<Unit> Handle(KeyAccelerator request,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
virtualKeyboard.Send((int)request.Key, request.Modifiers?.Select(modifier => (int)modifier).ToArray() ?? []); virtualKeyboard.Send((int)request.Key, request.Modifiers?.Select(modifier => (int)modifier).ToArray() ?? []);
return default; 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}" Command="{Binding Click}"
Content="{Binding Icon}" Content="{Binding Icon}"
FontFamily="{StaticResource SymbolThemeFontFamily}" FontFamily="{StaticResource SymbolThemeFontFamily}"
FontSize="16" /> FontSize="16"
ToolTipService.ToolTip="{Binding Text}" />
</UserControl> </UserControl>
@@ -11,7 +11,7 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Rectangle <Rectangle
Width="1" Width="1"
Margin="6,8,6,8" Margin="6,2,6,2"
Fill="{ThemeResource DividerStrokeColorDefaultBrush}" Fill="{ThemeResource DividerStrokeColorDefaultBrush}"
Visibility="{Binding Alternate}" /> Visibility="{Binding Alternate}" />
<ItemsControl <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:interactions="using:Microsoft.Xaml.Interactions.Core"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity" 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>
@@ -1,5 +1,5 @@
namespace Hyperbar; namespace Hyperbar;
public record KeyAcceleratorRequest(VirtualKey Key, public record KeyAccelerator(VirtualKey Key,
VirtualKey[]? Modifiers = null) : VirtualKey[]? Modifiers = null) :
IRequest; 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,16 +119,23 @@ public static class IServiceCollectionExtensions
where THandler : where THandler :
IHandler 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) 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; return services;
} }
public static IServiceCollection AddWidgetTemplate<TWidgetContent>(this IServiceCollection services) public static IServiceCollection AddWidgetTemplate<TWidgetContent>(this IServiceCollection services)
@@ -4,13 +4,13 @@ using System.Reactive.Disposables;
namespace Hyperbar; namespace Hyperbar;
public class ViewModelCache<TKey, TViewModel>(IDisposer disposer) : public class Cache<TKey, TService>(IDisposer disposer) :
IViewModelCache<TKey, TViewModel> ICache<TKey, TService>
where TKey : notnull 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]; get => cache[key];
set => cache[key] = value; set => cache[key] = value;
@@ -18,13 +18,13 @@ public class ViewModelCache<TKey, TViewModel>(IDisposer disposer) :
public ICollection<TKey> Keys => cache.Keys; public ICollection<TKey> Keys => cache.Keys;
public ICollection<TViewModel> Values => cache.Values; public ICollection<TService> Values => cache.Values;
public int Count => cache.Count; public int Count => cache.Count;
public bool IsReadOnly => false; public bool IsReadOnly => false;
public void Add(TKey key, TViewModel value) public void Add(TKey key, TService value)
{ {
disposer.Add(value!, Disposable.Create(() => disposer.Add(value!, Disposable.Create(() =>
{ {
@@ -34,14 +34,14 @@ public class ViewModelCache<TKey, TViewModel>(IDisposer disposer) :
cache.Add(key, value); cache.Add(key, value);
} }
public void Add(KeyValuePair<TKey, TViewModel> item) public void Add(KeyValuePair<TKey, TService> item)
{ {
cache.Add(item); cache.Add(item);
} }
public void Clear() => cache.Clear(); public void Clear() => cache.Clear();
public bool Contains(KeyValuePair<TKey, TViewModel> item) public bool Contains(KeyValuePair<TKey, TService> item)
{ {
return cache.Contains(item); return cache.Contains(item);
} }
@@ -51,12 +51,12 @@ public class ViewModelCache<TKey, TViewModel>(IDisposer disposer) :
return cache.ContainsKey(key); 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); cache.CopyTo(array, arrayIndex);
} }
public IEnumerator<KeyValuePair<TKey, TViewModel>> GetEnumerator() public IEnumerator<KeyValuePair<TKey, TService>> GetEnumerator()
{ {
return cache.GetEnumerator(); return cache.GetEnumerator();
} }
@@ -66,12 +66,12 @@ public class ViewModelCache<TKey, TViewModel>(IDisposer disposer) :
return cache.Remove(key); return cache.Remove(key);
} }
public bool Remove(KeyValuePair<TKey, TViewModel> item) public bool Remove(KeyValuePair<TKey, TService> item)
{ {
return cache.Remove(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); return cache.TryGetValue(key, out value);
} }
@@ -1,6 +1,6 @@
namespace Hyperbar; namespace Hyperbar;
public interface IViewModelCache<TKey, TViewModel> : public interface ICache<TKey, TViewModel> :
IDictionary<TKey, TViewModel> IDictionary<TKey, TViewModel>
where TKey : notnull 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();
}
+1 -1
View File
@@ -6,4 +6,4 @@ public interface IServiceFactory
params object?[] parameters); params object?[] parameters);
TService Create<TService>(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);
}
-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 Microsoft.Extensions.DependencyInjection;
using System.Runtime.CompilerServices; using System.Collections.Concurrent;
namespace Hyperbar; namespace Hyperbar;
@@ -8,7 +8,7 @@ public class Mediator(IServiceProvider provider) :
{ {
private readonly SynchronizationContext? context = SynchronizationContext.Current; 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, public ValueTask PublishAsync<TNotification>(TNotification notification,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
@@ -18,11 +18,14 @@ public class Mediator(IServiceProvider provider) :
List<INotificationHandler<TNotification>> handlers = List<INotificationHandler<TNotification>> handlers =
provider.GetServices<INotificationHandler<TNotification>>().ToList(); 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)) 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) if (interfaceType.GetGenericArguments() is { Length: 1 } arguments)
{ {
Type notificationType = arguments[0]; 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; namespace Hyperbar;
public record Created<TValue>(TValue? Value = default) : INotification; public record Created<TValue>(TValue Value) : INotification;
+11 -17
View File
@@ -19,6 +19,7 @@ public partial class ObservableCollectionViewModel<TItem> :
private readonly IDisposer disposer; private readonly IDisposer disposer;
private readonly IViewModelEnumerator<TItem>? enumerator; private readonly IViewModelEnumerator<TItem>? enumerator;
private readonly IServiceFactory serviceFactory; private readonly IServiceFactory serviceFactory;
public ObservableCollectionViewModel(IServiceFactory serviceFactory, public ObservableCollectionViewModel(IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer) IDisposer disposer)
@@ -138,7 +139,7 @@ public partial class ObservableCollectionViewModel<TItem> :
where T : TItem where T : TItem
{ {
T? item = serviceFactory.Create<T>(parameters); T? item = serviceFactory.Create<T>(parameters);
context?.Post(state => Add(item), null); Add(item);
return item; return item;
} }
@@ -155,20 +156,17 @@ public partial class ObservableCollectionViewModel<TItem> :
public void Add(TItem item) public void Add(TItem item)
{ {
context?.Post(state => disposer.Add(this, item);
disposer.Add(item, Disposable.Create(item, args =>
{ {
disposer.Add(this, item); if (Contains(args))
disposer.Add(item, Disposable.Create(item, args =>
{ {
if (Contains(args)) Remove(args);
{ }
Remove(args); }));
}
}));
int index = collection.Count; int index = collection.Count;
InsertItem(index, item); InsertItem(index, item);
}, null);
} }
int IList.Add(object? value) int IList.Add(object? value)
@@ -281,11 +279,7 @@ public partial class ObservableCollectionViewModel<TItem> :
int index = collection.IndexOf(item); int index = collection.IndexOf(item);
if (index < 0) return false; if (index < 0) return false;
context?.Post(state => RemoveItem(index);
{
context?.Post(state => RemoveItem(index), null);
}, null);
return true; return true;
} }
+5 -1
View File
@@ -8,12 +8,16 @@ public partial class WidgetButtonViewModel(IServiceFactory serviceFactory,
IDisposer disposer, IDisposer disposer,
ITemplateFactory templateFactory, ITemplateFactory templateFactory,
Guid guid = default, Guid guid = default,
string? text = null,
string? icon = null, string? icon = null,
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory, guid) RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory, guid)
{ {
[ObservableProperty]
private IRelayCommand? click = command;
[ObservableProperty] [ObservableProperty]
private string? icon = icon; private string? icon = icon;
[ObservableProperty] [ObservableProperty]
private IRelayCommand? click = command; private string? text = text;
} }
+12 -9
View File
@@ -3,29 +3,32 @@
namespace Hyperbar; namespace Hyperbar;
public partial class WidgetComponentViewModel : public partial class WidgetComponentViewModel :
ObservableViewModel, ObservableCollectionViewModel<IWidgetComponentViewModel>,
IWidgetComponentViewModel, IWidgetComponentViewModel,
ITemplatedViewModel ITemplatedViewModel
{ {
private readonly IMediator mediator;
private readonly IServiceFactory serviceFactory;
[ObservableProperty] [ObservableProperty]
private Guid id; 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, public WidgetComponentViewModel(IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer, IDisposer disposer,
ITemplateFactory templateFactory, ITemplateFactory templateFactory,
Guid id = default) : base(serviceFactory, mediator, disposer) Guid id = default) : base(serviceFactory, mediator, disposer)
{ {
this.serviceFactory = serviceFactory;
this.mediator = mediator;
this.id = id; this.id = id;
TemplateFactory = templateFactory; TemplateFactory = templateFactory;
mediator.Subscribe(this);
} }
public ITemplateFactory TemplateFactory { get; private set; } 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;
}