From 705d84e56dfe47af0fb863f2ef163f8fa1ce252c Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sat, 13 Apr 2024 14:55:33 +0100 Subject: [PATCH 001/228] Toolkit.Avalonia --- Toolkit.Avalonia/AvaloniaDispatcher.cs | 13 ++ .../ClassicDesktopStyleApplicationHandler.cs | 28 +++ Toolkit.Avalonia/ContentControlHandler.cs | 60 ++++++ Toolkit.Avalonia/ContentDialogHandler.cs | 113 ++++++++++ Toolkit.Avalonia/ContentTemplate.cs | 70 ++++++ Toolkit.Avalonia/FrameHandler.cs | 203 ++++++++++++++++++ Toolkit.Avalonia/INavigationContext.cs | 8 + .../IServiceCollectionExtensions.cs | 160 ++++++++++++++ Toolkit.Avalonia/NavigationContext.cs | 38 ++++ Toolkit.Avalonia/NavigationPageFactory.cs | 18 ++ .../SingleViewApplicationHandler.cs | 28 +++ Toolkit.Avalonia/Toolkit.Avalonia.csproj | 15 ++ Toolkit.sln | 12 +- 13 files changed, 763 insertions(+), 3 deletions(-) create mode 100644 Toolkit.Avalonia/AvaloniaDispatcher.cs create mode 100644 Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs create mode 100644 Toolkit.Avalonia/ContentControlHandler.cs create mode 100644 Toolkit.Avalonia/ContentDialogHandler.cs create mode 100644 Toolkit.Avalonia/ContentTemplate.cs create mode 100644 Toolkit.Avalonia/FrameHandler.cs create mode 100644 Toolkit.Avalonia/INavigationContext.cs create mode 100644 Toolkit.Avalonia/IServiceCollectionExtensions.cs create mode 100644 Toolkit.Avalonia/NavigationContext.cs create mode 100644 Toolkit.Avalonia/NavigationPageFactory.cs create mode 100644 Toolkit.Avalonia/SingleViewApplicationHandler.cs create mode 100644 Toolkit.Avalonia/Toolkit.Avalonia.csproj diff --git a/Toolkit.Avalonia/AvaloniaDispatcher.cs b/Toolkit.Avalonia/AvaloniaDispatcher.cs new file mode 100644 index 0000000..0870623 --- /dev/null +++ b/Toolkit.Avalonia/AvaloniaDispatcher.cs @@ -0,0 +1,13 @@ +using Avalonia.Threading; +using IDispatcher = Toolkit.Foundation.IDispatcher; + +namespace Toolkit.Avalonia; + +public class AvaloniaDispatcher : + IDispatcher +{ + public async Task InvokeAsync(Action action) + { + await Dispatcher.UIThread.InvokeAsync(action); + } +} \ No newline at end of file diff --git a/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs b/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs new file mode 100644 index 0000000..42d67c0 --- /dev/null +++ b/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs @@ -0,0 +1,28 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Toolkit.Foundation; + +namespace Toolkit.Avalonia; + +public class ClassicDesktopStyleApplicationHandler(INavigationContext navigationContext) : + INavigateHandler +{ + public Task Handle(Navigate args, + CancellationToken cancellationToken = default) + { + if (Application.Current?.ApplicationLifetime is + IClassicDesktopStyleApplicationLifetime lifeTime) + { + if (args.Template is Window window) + { + lifeTime.MainWindow = window; + window.DataContext = args.Content; + + navigationContext.Set(window); + } + } + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Toolkit.Avalonia/ContentControlHandler.cs b/Toolkit.Avalonia/ContentControlHandler.cs new file mode 100644 index 0000000..ccef36e --- /dev/null +++ b/Toolkit.Avalonia/ContentControlHandler.cs @@ -0,0 +1,60 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Toolkit.Foundation; + +namespace Toolkit.Avalonia; + +public class ContentControlHandler(INavigationContext navigationContext) : + INavigateHandler +{ + public async Task Handle(Navigate args, + CancellationToken cancellationToken) + { + if (args.Context is ContentControl contentControl) + { + if (args.Template is Control control) + { + TaskCompletionSource taskCompletionSource = new(); + async void HandleLoaded(object? sender, RoutedEventArgs args) + { + control.Loaded -= HandleLoaded; + if (control.DataContext is object content) + { + if (content is IInitializer initializer) + { + await initializer.Initialize(); + } + + if (content is IActivated activated) + { + await activated.Activated(); + } + } + + taskCompletionSource.SetResult(); + } + + async void HandleUnloaded(object? sender, RoutedEventArgs args) + { + control.Unloaded -= HandleLoaded; + if (control.DataContext is object content) + { + if (content is IDeactivated deactivated) + { + await deactivated.Deactivated(); + } + } + } + + control.Loaded += HandleLoaded; + control.Unloaded += HandleUnloaded; + + contentControl.Content = control; + contentControl.DataContext = args.Content; + + navigationContext.Set(control); + await taskCompletionSource.Task; + } + } + } +} diff --git a/Toolkit.Avalonia/ContentDialogHandler.cs b/Toolkit.Avalonia/ContentDialogHandler.cs new file mode 100644 index 0000000..33394ae --- /dev/null +++ b/Toolkit.Avalonia/ContentDialogHandler.cs @@ -0,0 +1,113 @@ +using Toolkit.Foundation; +using Toolkit.UI.Controls.Avalonia; + +namespace Toolkit.Avalonia; + +public class ContentDialogHandler(IDispatcher dispatcher) : + INavigateHandler +{ + public async Task Handle(Navigate args, + CancellationToken cancellationToken) + { + if (args.Context is ContentDialog contentDialog) + { + contentDialog.DataContext = args.Content; + + async void HandlePrimaryButtonClick(FluentAvalonia.UI.Controls.ContentDialog sender, + FluentAvalonia.UI.Controls.ContentDialogButtonClickEventArgs args) + { + contentDialog.PrimaryButtonClick -= HandlePrimaryButtonClick; + if (contentDialog.DataContext is object content) + { + if (content is IPrimaryConfirmation primaryConfirmation) + { + if (!await primaryConfirmation.Confirm()) + { + args.Cancel = true; + contentDialog.PrimaryButtonClick += HandlePrimaryButtonClick; + } + } + } + } + + async void HandleSecondaryButtonClick(FluentAvalonia.UI.Controls.ContentDialog sender, + FluentAvalonia.UI.Controls.ContentDialogButtonClickEventArgs args) + { + contentDialog.SecondaryButtonClick -= HandleSecondaryButtonClick; + if (contentDialog.DataContext is object content) + { + if (content is ISecondaryConfirmation secondaryConfirmation) + { + if (!await secondaryConfirmation.Confirm()) + { + args.Cancel = true; + contentDialog.SecondaryButtonClick += HandleSecondaryButtonClick; + } + } + } + } + + async void HandleClosing(FluentAvalonia.UI.Controls.ContentDialog sender, + FluentAvalonia.UI.Controls.ContentDialogClosingEventArgs args) + { + if (args.Result == FluentAvalonia.UI.Controls.ContentDialogResult.Primary || + args.Result == FluentAvalonia.UI.Controls.ContentDialogResult.Secondary) + { + contentDialog.Closing -= HandleClosing; + if (contentDialog.DataContext is object content) + { + if (content is IConfirmation confirmation) + { + if (!await confirmation.Confirm()) + { + args.Cancel = true; + contentDialog.Closing += HandleClosing; + } + } + } + } + } + + async void HandleOpened(FluentAvalonia.UI.Controls.ContentDialog sender, + EventArgs args) + { + contentDialog.Opened -= HandleOpened; + if (contentDialog.DataContext is object content) + { + if (content is IDeactivatable deactivatable) + { + async void DeactivateHandler(object? sender, EventArgs args) + { + deactivatable.DeactivateHandler -= DeactivateHandler; + await dispatcher.InvokeAsync(contentDialog.Hide); + } + + deactivatable.DeactivateHandler += DeactivateHandler; + } + + // A hack to wait for the dialog to finish loading up to make it appear more responsive + await Task.Delay(250, cancellationToken); + if (content is IInitializer initializer) + { + await initializer.Initialize(); + } + + if (content is IActivated activated) + { + await activated.Activated(); + } + } + } + + contentDialog.Opened += HandleOpened; + contentDialog.Closing += HandleClosing; + contentDialog.PrimaryButtonClick += HandlePrimaryButtonClick; + contentDialog.SecondaryButtonClick += HandleSecondaryButtonClick; + + await contentDialog.ShowAsync(); + + contentDialog.PrimaryButtonClick += HandlePrimaryButtonClick; + contentDialog.SecondaryButtonClick += HandleSecondaryButtonClick; + } + } +} \ No newline at end of file diff --git a/Toolkit.Avalonia/ContentTemplate.cs b/Toolkit.Avalonia/ContentTemplate.cs new file mode 100644 index 0000000..86a7144 --- /dev/null +++ b/Toolkit.Avalonia/ContentTemplate.cs @@ -0,0 +1,70 @@ +using Avalonia.Controls; +using Avalonia.Controls.Templates; +using Avalonia.Interactivity; +using Microsoft.Extensions.DependencyInjection; +using Toolkit.Foundation; + +namespace Toolkit.Avalonia; + +public class ContentTemplate : + IContentTemplate, + IDataTemplate +{ + public Control? Build(object? item) + { + if (item is IObservableViewModel observableViewModel) + { + if (observableViewModel.ServiceProvider is IServiceProvider provider) + { + IContentTemplateDescriptorProvider? contentTemplateProvider = provider.GetService(); + INavigationContext? viewModelContentBinder = provider.GetService(); + + if (contentTemplateProvider?.Get(item.GetType().Name) is IContentTemplateDescriptor descriptor) + { + if (provider.GetRequiredKeyedService(descriptor.TemplateType, descriptor.Key) is Control control) + { + async void HandleLoaded(object? sender, RoutedEventArgs args) + { + control.Loaded -= HandleLoaded; + if (control.DataContext is object content) + { + if (content is IInitializer initializer) + { + await initializer.Initialize(); + } + + if (content is IActivated activated) + { + await activated.Activated(); + } + } + } + + async void HandleUnloaded(object? sender, RoutedEventArgs args) + { + control.Unloaded -= HandleLoaded; + if (control.DataContext is object content) + { + if (content is IDeactivated deactivated) + { + await deactivated.Deactivated(); + } + } + } + + control.Loaded += HandleLoaded; + control.Unloaded += HandleUnloaded; + + viewModelContentBinder?.Set(control); + + return control; + } + } + } + } + + return default; + } + + public bool Match(object? data) => true; +} diff --git a/Toolkit.Avalonia/FrameHandler.cs b/Toolkit.Avalonia/FrameHandler.cs new file mode 100644 index 0000000..fa5a17d --- /dev/null +++ b/Toolkit.Avalonia/FrameHandler.cs @@ -0,0 +1,203 @@ +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using FluentAvalonia.UI.Navigation; +using System.Reflection; +using Toolkit.Foundation; +using Toolkit.UI.Controls.Avalonia; + +namespace Toolkit.Avalonia; + +public class FrameHandler(INavigationContext navigationContext) : + INavigateHandler, + INavigateBackHandler +{ + public Task Handle(Navigate args, + CancellationToken cancellationToken) + { + if (args.Context is Frame frame) + { + frame.NavigationPageFactory ??= new NavigationPageFactory(); + if (args.Template is Control control) + { + void NavigatingFrom(object? sender, + Control control) + { + async void HandleNavigatingFrom(object? _, + NavigatingCancelEventArgs args) + { + Dictionary results = []; + + control.RemoveHandler(Frame.NavigatingFromEvent, HandleNavigatingFrom); + NavigatedFrom(sender, control, () => results); + + if (control.DataContext is object content) + { + if (content is IPrimaryConfirmation confirmNavigation && + !await confirmNavigation.Confirm()) + { + args.Cancel = true; + } + + if (!args.Cancel) + { + if (content is IDeactivating deactivating) + { + await deactivating.Deactivating(); + } + + Type contentType = content.GetType(); + if (contentType.GetInterfaces() is Type[] contracts) + { + foreach (Type contract in contracts) + { + if (contract.Name == typeof(IDeactivating<>).Name && + contract.GetGenericArguments() is { Length: 1 } arguments) + { + if (contentType.GetMethods().FirstOrDefault(x => + x.Name == "Deactivating" && x.ReturnType == typeof(Task<>) + .MakeGenericType(arguments[0])) + is MethodInfo methodInfo) + { + if (methodInfo.GetCustomAttribute() + is NavigationContextAttribute attribute) + { + if (await methodInfo.InvokeAsync(content) is object result) + { + results.Add(attribute.Name, result); + } + } + } + } + } + } + } + } + } + + control.AddHandler(Frame.NavigatingFromEvent, HandleNavigatingFrom); + } + + void NavigatedFrom(object? sender, + Control control, + Func> resultCallBack) + { + async void HandleNavigatedFrom(object? _, + NavigationEventArgs args) + { + control.RemoveHandler(Frame.NavigatedFromEvent, HandleNavigatedFrom); + if (args.NavigationMode == NavigationMode.New) + { + NavigatedTo(sender, control); + } + + Dictionary results = resultCallBack.Invoke(); + async Task DoNavigatedFromAsync(object? content) + { + if (content is not null) + { + if (content is IDeactivated deactivated) + { + await deactivated.Deactivated(); + } + + Type contentType = content.GetType(); + if (contentType.GetInterfaces() is Type[] contracts) + { + foreach (Type contract in contracts) + { + if (contract.Name == typeof(IActivated<>).Name && + contract.GetGenericArguments() is { Length: 1 } arguments) + { + if (contentType.GetMethods().FirstOrDefault(x => + x.Name == "NavigatedToAsync" && + x.GetCustomAttribute() + is NavigationContextAttribute attribute && results.ContainsKey(attribute.Name)) + is MethodInfo methodInfo) + { + if (methodInfo.GetCustomAttribute() + is NavigationContextAttribute attribute) + { + if (results.TryGetValue(attribute.Name, out object? value)) + { + await methodInfo.InvokeAsync(content, value); + } + } + } + } + } + } + } + } + + if (args.Source is TemplatedControl sourceTemplate) + { + if (sourceTemplate.DataContext is object content) + { + await DoNavigatedFromAsync(content); + } + } + + if (sender is TemplatedControl senderTemplate) + { + if (senderTemplate.DataContext is object content) + { + await DoNavigatedFromAsync(content); + } + } + else + { + await DoNavigatedFromAsync(sender); + } + } + + control.AddHandler(Frame.NavigatedFromEvent, HandleNavigatedFrom); + } + + void NavigatedTo(object? sender, + Control control) + { + async void HandleNavigatedTo(object? _, + NavigationEventArgs __) + { + control.RemoveHandler(Frame.NavigatedToEvent, HandleNavigatedTo); + NavigatingFrom(sender, control); + + if (control.DataContext is object content) + { + if (content is IInitializer initializer) + { + await initializer.Initialize(); + } + + if (content is IActivated activated) + { + await activated.Activated(); + } + } + } + + control.AddHandler(Frame.NavigatedToEvent, HandleNavigatedTo); + } + + control.DataContext = args.Content; + navigationContext.Set(control); + + NavigatedTo(args.Sender, control); + frame.NavigateFromObject(control); + } + } + + return Task.CompletedTask; + } + + public Task Handle(NavigateBack args, + CancellationToken cancellationToken = default) + { + if (args.Context is Frame frame) + { + frame.GoBack(); + } + + return Task.CompletedTask; + } +} diff --git a/Toolkit.Avalonia/INavigationContext.cs b/Toolkit.Avalonia/INavigationContext.cs new file mode 100644 index 0000000..53d3792 --- /dev/null +++ b/Toolkit.Avalonia/INavigationContext.cs @@ -0,0 +1,8 @@ +using Avalonia.Controls; + +namespace Toolkit.Avalonia; + +public interface INavigationContext +{ + void Set(Control control); +} \ No newline at end of file diff --git a/Toolkit.Avalonia/IServiceCollectionExtensions.cs b/Toolkit.Avalonia/IServiceCollectionExtensions.cs new file mode 100644 index 0000000..f1338cf --- /dev/null +++ b/Toolkit.Avalonia/IServiceCollectionExtensions.cs @@ -0,0 +1,160 @@ +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Toolkit.Foundation; + +namespace Toolkit.Avalonia; + +public static class IServiceCollectionExtensions +{ + public static IServiceCollection AddComponentConfigurationTemplate(this IServiceCollection services, + params object[]? parameters) + where TConfiguration : class + where THeader : class + where TDescription : class + where TAction : class + { + Type viewModelType = typeof(ComponentConfigurationViewModel); + Type viewType = typeof(Button); + + object key = viewModelType.Name.Replace("ViewModel", ""); + + services.AddTransient>(provider => + provider.GetRequiredService() + .Create>(parameters)!); + + services.TryAddTransient(viewType); + + services.AddKeyedTransient>(key, (provider, key) => + provider.GetRequiredService() + .Create>(parameters)!); + + services.TryAddKeyedTransient(viewType, key); + + services.AddTransient(provider => + new ContentTemplateDescriptor(key, viewModelType, viewType, parameters)); + + services.TryAddTransient(); + services.TryAddTransient(); + services.TryAddTransient(); + + return services; + } + + public static IServiceCollection AddComponentConfigurationTemplate(this IServiceCollection services, + Func valueDelegate, + object header, + object description, + params object[]? parameters) + where TConfiguration : class + where TAction : class + { + Type viewModelType = typeof(ComponentConfigurationViewModel); + Type viewType = typeof(Button); + + object key = viewModelType.Name.Replace("ViewModel", ""); + + parameters = [valueDelegate, header, description, .. parameters ?? Enumerable.Empty()]; + + services.AddTransient>(provider => + provider.GetRequiredService() + .Create>(parameters)!); + + services.TryAddTransient(viewType); + + services.AddKeyedTransient>(key, (provider, key) => + provider.GetRequiredService() + .Create>(parameters)!); + + services.TryAddKeyedTransient(viewType, key); + + services.AddTransient(provider => + new ContentTemplateDescriptor(key, viewModelType, viewType, parameters)); + + services.TryAddTransient(); + + return services; + } + + public static IServiceCollection AddComponentConfigurationTemplate(this IServiceCollection services, + Func valueDelegate, + object description, + params object[]? parameters) + where TConfiguration : class + where TDescription : class + where TAction : class + { + Type viewModelType = typeof(ComponentConfigurationViewModel); + Type viewType = typeof(Button); + + object key = viewModelType.Name.Replace("ViewModel", ""); + + parameters = [valueDelegate, description, .. parameters ?? Enumerable.Empty()]; + + services.AddTransient>(provider => + provider.GetRequiredService() + .Create>(parameters)!); + + services.TryAddTransient(viewType); + + services.AddKeyedTransient>(key, (provider, key) => + provider.GetRequiredService() + .Create>(parameters)!); + + services.TryAddKeyedTransient(viewType, key); + + services.AddTransient(provider => + new ContentTemplateDescriptor(key, viewModelType, viewType, parameters)); + + services.TryAddTransient(); + services.TryAddTransient(); + + return services; + } + + public static IServiceCollection AddAvalonia(this IServiceCollection services) + { + services.AddTransient(); + + services.AddTransient(); + services.AddTransient(); + + services.AddNavigateHandler(); + services.AddNavigateHandler(); + services.AddNavigateHandler(); + services.AddNavigateHandler(); + services.AddNavigateHandler(); + + services.AddScoped(provider => new NavigationContextCollection + { + { typeof(IClassicDesktopStyleApplicationLifetime), typeof(IClassicDesktopStyleApplicationLifetime) }, + { typeof(ISingleViewApplicationLifetime), typeof(ISingleViewApplicationLifetime) } + }); + + services.AddTransient((Func>)(provider => + new ProxyServiceCollection(services => + { + services.AddSingleton(provider.GetRequiredService()); + + services.AddTransient(); + services.AddTransient(); + + services.AddTransient(); + + services.AddNavigateHandler(); + services.AddNavigateHandler(); + services.AddNavigateHandler(); + }))); + + return services; + } +} \ No newline at end of file diff --git a/Toolkit.Avalonia/NavigationContext.cs b/Toolkit.Avalonia/NavigationContext.cs new file mode 100644 index 0000000..538cc92 --- /dev/null +++ b/Toolkit.Avalonia/NavigationContext.cs @@ -0,0 +1,38 @@ +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Interactivity; +using HyperX.UI.Windows; +using System.Reflection; +using Toolkit.Foundation; + +namespace Toolkit.Avalonia; + +public class NavigationContext(INavigationContextCollection contexts) : + INavigationContext +{ + public void Set(Control control) + { + if (control.GetType().GetCustomAttributes() + is IEnumerable attributes) + { + foreach (NavigationTargetAttribute attribute in attributes) + { + if (!contexts.ContainsKey(attribute.Name)) + { + if (control.Find(attribute.Name) is TemplatedControl content) + { + contexts.Add(attribute.Name, content); + void HandleUnloaded(object? sender, RoutedEventArgs args) + { + control.Unloaded -= HandleUnloaded; + contexts.Remove(attribute.Name); + } + + control.Unloaded += HandleUnloaded; + } + } + } + } + } +} + diff --git a/Toolkit.Avalonia/NavigationPageFactory.cs b/Toolkit.Avalonia/NavigationPageFactory.cs new file mode 100644 index 0000000..511d563 --- /dev/null +++ b/Toolkit.Avalonia/NavigationPageFactory.cs @@ -0,0 +1,18 @@ +using Avalonia.Controls; +using FluentAvalonia.UI.Controls; + +namespace Toolkit.Avalonia; + +public class NavigationPageFactory : + INavigationPageFactory +{ + public Control? GetPage(Type srcType) + { + return default; + } + + public Control GetPageFromObject(object target) + { + return (Control)target; + } +} diff --git a/Toolkit.Avalonia/SingleViewApplicationHandler.cs b/Toolkit.Avalonia/SingleViewApplicationHandler.cs new file mode 100644 index 0000000..530bade --- /dev/null +++ b/Toolkit.Avalonia/SingleViewApplicationHandler.cs @@ -0,0 +1,28 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Toolkit.Foundation; + +namespace Toolkit.Avalonia; + +public class SingleViewApplicationHandler(INavigationContext navigationContext) : + INavigateHandler +{ + public Task Handle(Navigate args, + CancellationToken cancellationToken = default) + { + if (Application.Current?.ApplicationLifetime is + ISingleViewApplicationLifetime lifeTime) + { + if (args.Template is Control control) + { + lifeTime.MainView = control; + control.DataContext = args.Content; + + navigationContext.Set(control); + } + } + + return Task.CompletedTask; + } +} diff --git a/Toolkit.Avalonia/Toolkit.Avalonia.csproj b/Toolkit.Avalonia/Toolkit.Avalonia.csproj new file mode 100644 index 0000000..2670c0f --- /dev/null +++ b/Toolkit.Avalonia/Toolkit.Avalonia.csproj @@ -0,0 +1,15 @@ + + + net8.0 + enable + enable + + + + + + + + + + \ No newline at end of file diff --git a/Toolkit.sln b/Toolkit.sln index 8c89dac..3afc236 100644 --- a/Toolkit.sln +++ b/Toolkit.sln @@ -3,11 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.4.33110.190 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Toolkit.Foundation", "Toolkit.Foundation\Toolkit.Foundation.csproj", "{66968F8D-689E-49D8-9370-DFF099C56202}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Toolkit.Foundation", "Toolkit.Foundation\Toolkit.Foundation.csproj", "{66968F8D-689E-49D8-9370-DFF099C56202}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Toolkit.UI.Avalonia", "Toolkit.UI.Avalonia\Toolkit.UI.Avalonia.csproj", "{E091FA94-2F15-403A-98D1-4557C2FF9A02}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Toolkit.UI.Avalonia", "Toolkit.UI.Avalonia\Toolkit.UI.Avalonia.csproj", "{E091FA94-2F15-403A-98D1-4557C2FF9A02}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Toolkit.UI.Controls.Avalonia", "Toolkit.UI.Controls.Avalonia\Toolkit.UI.Controls.Avalonia.csproj", "{8841990D-A246-495D-9A40-C39BA4347505}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Toolkit.UI.Controls.Avalonia", "Toolkit.UI.Controls.Avalonia\Toolkit.UI.Controls.Avalonia.csproj", "{8841990D-A246-495D-9A40-C39BA4347505}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Toolkit.Avalonia", "Toolkit.Avalonia\Toolkit.Avalonia.csproj", "{9585A317-4405-4E39-BDBE-23EF822FBF34}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,6 +29,10 @@ Global {8841990D-A246-495D-9A40-C39BA4347505}.Debug|Any CPU.Build.0 = Debug|Any CPU {8841990D-A246-495D-9A40-C39BA4347505}.Release|Any CPU.ActiveCfg = Release|Any CPU {8841990D-A246-495D-9A40-C39BA4347505}.Release|Any CPU.Build.0 = Release|Any CPU + {9585A317-4405-4E39-BDBE-23EF822FBF34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9585A317-4405-4E39-BDBE-23EF822FBF34}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9585A317-4405-4E39-BDBE-23EF822FBF34}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9585A317-4405-4E39-BDBE-23EF822FBF34}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 8b52affc241e2ae146b1ddd92bcf5c52ed27a4f1 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sat, 13 Apr 2024 15:07:52 +0100 Subject: [PATCH 002/228] update nugets --- Toolkit.Foundation/Toolkit.Foundation.csproj | 2 +- Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj | 2 +- .../Toolkit.UI.Controls.Avalonia.csproj | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Toolkit.Foundation/Toolkit.Foundation.csproj b/Toolkit.Foundation/Toolkit.Foundation.csproj index e27f1b3..ef44a00 100644 --- a/Toolkit.Foundation/Toolkit.Foundation.csproj +++ b/Toolkit.Foundation/Toolkit.Foundation.csproj @@ -6,7 +6,7 @@ - + diff --git a/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj b/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj index e80fef1..e84927b 100644 --- a/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj +++ b/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj @@ -6,7 +6,7 @@ - + diff --git a/Toolkit.UI.Controls.Avalonia/Toolkit.UI.Controls.Avalonia.csproj b/Toolkit.UI.Controls.Avalonia/Toolkit.UI.Controls.Avalonia.csproj index 18560b4..4671d61 100644 --- a/Toolkit.UI.Controls.Avalonia/Toolkit.UI.Controls.Avalonia.csproj +++ b/Toolkit.UI.Controls.Avalonia/Toolkit.UI.Controls.Avalonia.csproj @@ -7,7 +7,7 @@ - - + + \ No newline at end of file From 99d79cd45f43d7900425f2ccf2f72681b6b45186 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sat, 13 Apr 2024 15:33:11 +0100 Subject: [PATCH 003/228] Added Toolkit.UI.Avalonia XmlnsDefinition --- Toolkit.UI.Avalonia/Properties/Assembly.cs | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Toolkit.UI.Avalonia/Properties/Assembly.cs diff --git a/Toolkit.UI.Avalonia/Properties/Assembly.cs b/Toolkit.UI.Avalonia/Properties/Assembly.cs new file mode 100644 index 0000000..eaaf8a8 --- /dev/null +++ b/Toolkit.UI.Avalonia/Properties/Assembly.cs @@ -0,0 +1,3 @@ +using Avalonia.Metadata; + +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Toolkit.UI.Avalonia")] \ No newline at end of file From 51bb7d4943bfd7f5e5d3049fff69e7bb4b93a717 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sat, 13 Apr 2024 15:46:02 +0100 Subject: [PATCH 004/228] Old ns --- Toolkit.Avalonia/NavigationContext.cs | 1 - Toolkit.Foundation/Cache.cs | 3 +-- Toolkit.Foundation/NavigationTargetAttribute.cs | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Toolkit.Avalonia/NavigationContext.cs b/Toolkit.Avalonia/NavigationContext.cs index 538cc92..e87034d 100644 --- a/Toolkit.Avalonia/NavigationContext.cs +++ b/Toolkit.Avalonia/NavigationContext.cs @@ -1,7 +1,6 @@ using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Interactivity; -using HyperX.UI.Windows; using System.Reflection; using Toolkit.Foundation; diff --git a/Toolkit.Foundation/Cache.cs b/Toolkit.Foundation/Cache.cs index a5c0cfd..f86b219 100644 --- a/Toolkit.Foundation/Cache.cs +++ b/Toolkit.Foundation/Cache.cs @@ -1,5 +1,4 @@ -using HyperX; -using System.Collections; +using System.Collections; using System.Collections.Concurrent; using System.Reactive.Disposables; diff --git a/Toolkit.Foundation/NavigationTargetAttribute.cs b/Toolkit.Foundation/NavigationTargetAttribute.cs index 29bc568..c4e3f75 100644 --- a/Toolkit.Foundation/NavigationTargetAttribute.cs +++ b/Toolkit.Foundation/NavigationTargetAttribute.cs @@ -1,4 +1,4 @@ -namespace HyperX.UI.Windows; +namespace Toolkit.Foundation; [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] public class NavigationTargetAttribute(string name) : From 5b055bb2d7b240ef30b72184eb58e7eb05986400 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sat, 13 Apr 2024 19:49:51 +0100 Subject: [PATCH 005/228] Rename AddConfigurationTemplate --- Toolkit.Avalonia/IServiceCollectionExtensions.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Toolkit.Avalonia/IServiceCollectionExtensions.cs b/Toolkit.Avalonia/IServiceCollectionExtensions.cs index f1338cf..6f3ae03 100644 --- a/Toolkit.Avalonia/IServiceCollectionExtensions.cs +++ b/Toolkit.Avalonia/IServiceCollectionExtensions.cs @@ -8,7 +8,7 @@ namespace Toolkit.Avalonia; public static class IServiceCollectionExtensions { - public static IServiceCollection AddComponentConfigurationTemplate(this IServiceCollection services, params object[]? parameters) where TConfiguration : class @@ -45,7 +45,7 @@ public static class IServiceCollectionExtensions return services; } - public static IServiceCollection AddComponentConfigurationTemplate(this IServiceCollection services, + public static IServiceCollection AddConfigurationTemplate(this IServiceCollection services, Func valueDelegate, object header, object description, @@ -82,7 +82,7 @@ public static class IServiceCollectionExtensions return services; } - public static IServiceCollection AddComponentConfigurationTemplate(this IServiceCollection services, Func valueDelegate, object description, From e0f49832f82896291e5216adc2538c2b4d788a14 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 21 Apr 2024 19:38:38 +0100 Subject: [PATCH 006/228] Amend configuration to support writing to nested sections --- Toolkit.Foundation/ComponentConfiguration.cs | 9 +- .../ConfigurationInitializer.cs | 6 +- Toolkit.Foundation/ConfigurationSource.cs | 83 ++++++----- Toolkit.Foundation/DefaultBuilder.cs | 1 - Toolkit.Foundation/INavigationViewModel.cs | 7 - .../IServiceCollectionExtensions.cs | 130 +++++++++++------- Toolkit.Foundation/NavigationViewModel.cs | 31 ----- 7 files changed, 135 insertions(+), 132 deletions(-) delete mode 100644 Toolkit.Foundation/INavigationViewModel.cs delete mode 100644 Toolkit.Foundation/NavigationViewModel.cs diff --git a/Toolkit.Foundation/ComponentConfiguration.cs b/Toolkit.Foundation/ComponentConfiguration.cs index 72feb26..f16b093 100644 --- a/Toolkit.Foundation/ComponentConfiguration.cs +++ b/Toolkit.Foundation/ComponentConfiguration.cs @@ -1,13 +1,6 @@ -using System.Text.Json.Serialization; - -namespace Toolkit.Foundation; +namespace Toolkit.Foundation; public record ComponentConfiguration { - public string? Description { get; set; } - public string? Name { get; set; } - - [JsonInclude] - internal Guid Id { get; set; } = Guid.NewGuid(); } \ No newline at end of file diff --git a/Toolkit.Foundation/ConfigurationInitializer.cs b/Toolkit.Foundation/ConfigurationInitializer.cs index 29977cb..537f9a5 100644 --- a/Toolkit.Foundation/ConfigurationInitializer.cs +++ b/Toolkit.Foundation/ConfigurationInitializer.cs @@ -1,9 +1,10 @@ namespace Toolkit.Foundation; -public class ConfigurationInitializer(IPublisher publisher, +public class ConfigurationInitializer(string section, IConfigurationReader reader, IConfigurationWriter writer, - IConfigurationFactory factory) : + IConfigurationFactory factory, + IPublisher publisher) : IConfigurationInitializer, IInitializer where TConfiguration : @@ -11,6 +12,7 @@ public class ConfigurationInitializer(IPublisher publisher, { public async Task Initialize() { + var d = section; if (!reader.TryRead(out TConfiguration? configuration)) { if (factory.Create() is object defaultConfiguration) diff --git a/Toolkit.Foundation/ConfigurationSource.cs b/Toolkit.Foundation/ConfigurationSource.cs index 8484f2b..2cc7e35 100644 --- a/Toolkit.Foundation/ConfigurationSource.cs +++ b/Toolkit.Foundation/ConfigurationSource.cs @@ -1,6 +1,8 @@ using Microsoft.Extensions.FileProviders; +using System; using System.Text.Encodings.Web; using System.Text.Json; +using System.Text.Json.Nodes; using System.Text.Json.Serialization; namespace Toolkit.Foundation; @@ -35,7 +37,6 @@ public class ConfigurationSource(IConfigurationFile(IConfigurationFile(this IServiceCollection services, + string section) + where TConfiguration : class => + services.AddConfiguration(section, "Settings.json", null); + public static IServiceCollection AddConfiguration(this IServiceCollection services) where TConfiguration : class => services.AddConfiguration(typeof(TConfiguration).Name, "Settings.json", null); @@ -63,6 +68,17 @@ public static class IServiceCollectionExtensions return services.AddConfiguration(typeof(TConfiguration).Name, "Settings.json", configuration); } + public static IServiceCollection AddConfiguration(this IServiceCollection services, + Action configurationDelegate, + string section) + where TConfiguration : class, new() + { + TConfiguration configuration = new(); + configurationDelegate.Invoke(configuration); + + return services.AddConfiguration(section, "Settings.json", configuration); + } + public static IServiceCollection AddConfiguration(this IServiceCollection services, TConfiguration configuration) where TConfiguration : class => @@ -81,20 +97,7 @@ public static class IServiceCollectionExtensions Action? serializerDelegate = null) where TConfiguration : class { - services.AddSingleton>(provider => - { - JsonSerializerOptions? defaultSerializer = null; - if (serializerDelegate is not null) - { - defaultSerializer = new JsonSerializerOptions(); - serializerDelegate.Invoke(defaultSerializer); - } - - return new ConfigurationSource(provider.GetRequiredService>(), - section, defaultSerializer); - }); - - services.AddSingleton>(provider => + services.TryAddSingleton>(provider => { IFileInfo? fileInfo = null; if (provider.GetService() is IHostEnvironment hostEnvironment) @@ -107,15 +110,39 @@ public static class IServiceCollectionExtensions return new ConfigurationFile(fileInfo); }); - services.AddHostedService>(); - services.AddSingleton, ConfigurationReader>(); - services.AddSingleton, ConfigurationWriter>(); + services.TryAddKeyedSingleton>(section, (provider, KeyAccelerator) => + { + JsonSerializerOptions? defaultSerializer = null; + if (serializerDelegate is not null) + { + defaultSerializer = new JsonSerializerOptions(); + serializerDelegate.Invoke(defaultSerializer); + } - services.AddTransient>(provider => new ConfigurationFactory(() => - configuration ?? provider.GetRequiredService())); + return new ConfigurationSource(provider.GetRequiredService>(), + section, defaultSerializer); + }); - services.AddTransient>(); - services.AddTransient, ConfigurationInitializer>(); + //services.AddHostedService>(); + services.TryAddKeyedTransient>(section, (provider, key) => + new ConfigurationReader(provider.GetRequiredKeyedService>(key), + provider.GetRequiredKeyedService>(key))); + + services.TryAddKeyedTransient>(section, (provider, key) => + new ConfigurationWriter(provider.GetRequiredKeyedService>(key))); + + services.TryAddKeyedTransient>(section, (provider, key) => + new ConfigurationFactory(() => configuration ?? provider.GetRequiredService())); + + services.AddTransient>(provider => + new ConfigurationInitializer(section, + provider.GetRequiredKeyedService>(section), + provider.GetRequiredKeyedService>(section), + provider.GetRequiredKeyedService>(section), + provider.GetRequiredService())); + + services.AddTransient, ConfigurationInitializer>(provider => + provider.GetRequiredService().Create>(section)); services.AddTransient, WritableConfiguration>(); @@ -125,31 +152,6 @@ public static class IServiceCollectionExtensions return services; } - public static IServiceCollection AddTemplate(this IServiceCollection services, - object? key = null, - params object[]? parameters) - { - Type viewModelType = typeof(TViewModel); - Type viewType = typeof(TView); - - key ??= viewModelType.Name.Replace("ViewModel", ""); - - services.AddTransient(viewModelType, provider => - provider.GetRequiredService().Create(parameters)!); - - services.AddTransient(viewType); - - services.AddKeyedTransient(viewModelType, key, (provider, key) => - provider.GetRequiredService().Create(parameters)!); - - services.AddKeyedTransient(viewType, key); - - services.AddTransient(provider => - new ContentTemplateDescriptor(key, viewModelType, viewType, parameters)); - - return services; - } - public static IServiceCollection AddHandler(this IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Transient) where THandler : IHandler @@ -169,7 +171,7 @@ public static class IServiceCollectionExtensions services.TryAdd(new ServiceDescriptor(typeof(INotificationHandler<>) .MakeGenericType(notificationType), typeof(THandler), lifetime)); - services.Add(new ServiceDescriptor(wrapperType, provider => + services.Add(new ServiceDescriptor(wrapperType, provider => provider.GetService()?.Create(wrapperType, provider.GetRequiredService(typeof(INotificationHandler<>).MakeGenericType(notificationType)), provider.GetServices(typeof(IPipelineBehavior<>) @@ -185,10 +187,10 @@ public static class IServiceCollectionExtensions Type wrapperType = typeof(HandlerWrapper<,>) .MakeGenericType(requestType, responseType); - services.TryAdd(new ServiceDescriptor(typeof(THandler), + services.TryAdd(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime)); - services.Add(new ServiceDescriptor(wrapperType, provider => + services.Add(new ServiceDescriptor(wrapperType, provider => provider.GetService()?.Create(wrapperType, provider.GetRequiredService(), provider.GetServices(typeof(IPipelineBehavior<,>) @@ -203,7 +205,7 @@ public static class IServiceCollectionExtensions } public static IServiceCollection AddInitializer(this IServiceCollection services) - where TInitializer : class, + where TInitializer : class, IInitializer { services.AddTransient(); @@ -211,7 +213,7 @@ public static class IServiceCollectionExtensions } public static IServiceCollection AddNavigateHandler(this IServiceCollection services) - where THandler : INavigateHandler, + where THandler : INavigateHandler, IHandler { IEnumerable contracts = typeof(THandler).GetInterfaces() @@ -231,6 +233,7 @@ public static class IServiceCollectionExtensions services.AddHandler(); return services; } + public static IServiceCollection AddRange(this IServiceCollection services, IServiceCollection fromServices) { @@ -241,4 +244,29 @@ public static class IServiceCollectionExtensions return services; } + + public static IServiceCollection AddTemplate(this IServiceCollection services, + object? key = null, + params object[]? parameters) + { + Type viewModelType = typeof(TViewModel); + Type viewType = typeof(TView); + + key ??= viewModelType.Name.Replace("ViewModel", ""); + + services.AddTransient(viewModelType, provider => + provider.GetRequiredService().Create(parameters)!); + + services.AddTransient(viewType); + + services.AddKeyedTransient(viewModelType, key, (provider, key) => + provider.GetRequiredService().Create(parameters)!); + + services.AddKeyedTransient(viewType, key); + + services.AddTransient(provider => + new ContentTemplateDescriptor(key, viewModelType, viewType, parameters)); + + return services; + } } \ No newline at end of file diff --git a/Toolkit.Foundation/NavigationViewModel.cs b/Toolkit.Foundation/NavigationViewModel.cs deleted file mode 100644 index f9052e7..0000000 --- a/Toolkit.Foundation/NavigationViewModel.cs +++ /dev/null @@ -1,31 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; - -namespace Toolkit.Foundation; - -public partial class NavigationViewModel(IServiceProvider serviceProvider, - IServiceFactory serviceFactory, - IPublisher publisher, - ISubscriber subscriber, - IDisposer disposer, - string text) : - ObservableViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer), - INavigationViewModel -{ - [ObservableProperty] - private string? text = text; -} - -public partial class NavigationViewModel(IServiceProvider serviceProvider, - IServiceFactory serviceFactory, - IPublisher publisher, - ISubscriber subscriber, - IDisposer disposer, - string text) : - ObservableViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer), - INavigationViewModel - where TNavigationViewModel : - INavigationViewModel -{ - [ObservableProperty] - private string? text = text; -} From 21481a82d889566d51cba116a9d9159cc0584e90 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 21 Apr 2024 19:53:47 +0100 Subject: [PATCH 007/228] Amend reading config --- Toolkit.Foundation/ConfigurationSource.cs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Toolkit.Foundation/ConfigurationSource.cs b/Toolkit.Foundation/ConfigurationSource.cs index 2cc7e35..18800d6 100644 --- a/Toolkit.Foundation/ConfigurationSource.cs +++ b/Toolkit.Foundation/ConfigurationSource.cs @@ -135,11 +135,26 @@ public class ConfigurationSource(IConfigurationFile(sectionValue.ToString(), serializerOptions ?? defaultSerializerOptions()); + if (currentNode is null) + { + value = default; + return false; + } + + currentNode = currentNode[segments[i]]; + } + + if (currentNode is not null) + { + value = JsonSerializer.Deserialize(currentNode[segments[lastIndex]], serializerOptions ?? defaultSerializerOptions()); return true; } } From 969c740f2d9850eac2114245f0455a519cf48e19 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 21 Apr 2024 21:10:26 +0100 Subject: [PATCH 008/228] WIP --- Toolkit.Foundation/ComponentBuilder.cs | 33 ++++++++++++++----- Toolkit.Foundation/Configuration.cs | 5 ++- .../ConfigurationInitializer.cs | 4 +-- Toolkit.Foundation/ConfigurationSource.cs | 4 +-- Toolkit.Foundation/IComponentBuilder.cs | 7 ++++ Toolkit.Foundation/IConfiguration.cs | 2 ++ .../IServiceCollectionExtensions.cs | 24 +++++++------- 7 files changed, 54 insertions(+), 25 deletions(-) diff --git a/Toolkit.Foundation/ComponentBuilder.cs b/Toolkit.Foundation/ComponentBuilder.cs index eef2252..3afcb6e 100644 --- a/Toolkit.Foundation/ComponentBuilder.cs +++ b/Toolkit.Foundation/ComponentBuilder.cs @@ -52,6 +52,15 @@ public class ComponentBuilder : public IComponentBuilder AddConfiguration(Action configurationDelegate) where TConfiguration : ComponentConfiguration, new() + { + AddConfiguration(typeof(TConfiguration).Name, configurationDelegate); + return this; + } + + public IComponentBuilder AddConfiguration(string section, + Action? configurationDelegate = null) + where TConfiguration : + ComponentConfiguration, new() { if (configurationRegistered) { @@ -68,8 +77,8 @@ public class ComponentBuilder : hostBuilder.ConfigureServices(services => { - services.AddConfiguration(section: configuration.GetType().Name, - configuration: configuration); + services.AddConfiguration(section: section, + defaultConfiguration: configuration); services.AddConfiguration(configuration); }); @@ -77,15 +86,23 @@ public class ComponentBuilder : return this; } + public IComponentBuilder AddConfiguration(string section) + where TConfiguration : + ComponentConfiguration, new() + { + AddConfiguration(section, null); + return this; + } + + public IComponentBuilder AddServices(Action configureDelegate) + { + hostBuilder.ConfigureServices(configureDelegate); + return this; + } + public IComponentHost Build() { IHost host = hostBuilder.Build(); return host.Services.GetRequiredService(); } - - public IComponentBuilder AddServices(Action configureDelegate) - { - hostBuilder.ConfigureServices(configureDelegate); - return this; - } } \ No newline at end of file diff --git a/Toolkit.Foundation/Configuration.cs b/Toolkit.Foundation/Configuration.cs index f577181..fc9249d 100644 --- a/Toolkit.Foundation/Configuration.cs +++ b/Toolkit.Foundation/Configuration.cs @@ -1,9 +1,12 @@ namespace Toolkit.Foundation; -public class Configuration(IConfigurationReader reader) : +public class Configuration(string section, + IConfigurationReader reader) : IConfiguration where TConfiguration : class { public TConfiguration Value => reader.Read(); + + public string Section => section; } diff --git a/Toolkit.Foundation/ConfigurationInitializer.cs b/Toolkit.Foundation/ConfigurationInitializer.cs index 537f9a5..f969c89 100644 --- a/Toolkit.Foundation/ConfigurationInitializer.cs +++ b/Toolkit.Foundation/ConfigurationInitializer.cs @@ -1,7 +1,6 @@ namespace Toolkit.Foundation; -public class ConfigurationInitializer(string section, - IConfigurationReader reader, +public class ConfigurationInitializer(IConfigurationReader reader, IConfigurationWriter writer, IConfigurationFactory factory, IPublisher publisher) : @@ -12,7 +11,6 @@ public class ConfigurationInitializer(string section, { public async Task Initialize() { - var d = section; if (!reader.TryRead(out TConfiguration? configuration)) { if (factory.Create() is object defaultConfiguration) diff --git a/Toolkit.Foundation/ConfigurationSource.cs b/Toolkit.Foundation/ConfigurationSource.cs index 18800d6..f9fd923 100644 --- a/Toolkit.Foundation/ConfigurationSource.cs +++ b/Toolkit.Foundation/ConfigurationSource.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.FileProviders; -using System; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Nodes; @@ -154,7 +153,8 @@ public class ConfigurationSource(IConfigurationFile(currentNode[segments[lastIndex]], serializerOptions ?? defaultSerializerOptions()); + value = JsonSerializer.Deserialize(currentNode[segments[lastIndex]], + serializerOptions ?? defaultSerializerOptions()); return true; } } diff --git a/Toolkit.Foundation/IComponentBuilder.cs b/Toolkit.Foundation/IComponentBuilder.cs index d65586e..571f207 100644 --- a/Toolkit.Foundation/IComponentBuilder.cs +++ b/Toolkit.Foundation/IComponentBuilder.cs @@ -7,6 +7,13 @@ public interface IComponentBuilder IComponentBuilder AddConfiguration(Action configurationDelegate) where TConfiguration : ComponentConfiguration, new(); + IComponentBuilder AddConfiguration(string section, + Action? configurationDelegate = null) + where TConfiguration : ComponentConfiguration, new(); + + IComponentBuilder AddConfiguration(string section) + where TConfiguration : ComponentConfiguration, new(); + IComponentHost Build(); IComponentBuilder AddServices(Action configureDelegate); diff --git a/Toolkit.Foundation/IConfiguration.cs b/Toolkit.Foundation/IConfiguration.cs index cf4ef25..0af78a7 100644 --- a/Toolkit.Foundation/IConfiguration.cs +++ b/Toolkit.Foundation/IConfiguration.cs @@ -5,4 +5,6 @@ public interface IConfiguration class { TConfiguration Value { get; } + + string Section { get; } } diff --git a/Toolkit.Foundation/IServiceCollectionExtensions.cs b/Toolkit.Foundation/IServiceCollectionExtensions.cs index 10e5def..cba129e 100644 --- a/Toolkit.Foundation/IServiceCollectionExtensions.cs +++ b/Toolkit.Foundation/IServiceCollectionExtensions.cs @@ -51,11 +51,11 @@ public static class IServiceCollectionExtensions public static IServiceCollection AddConfiguration(this IServiceCollection services, string section) - where TConfiguration : class => + where TConfiguration : class, new() => services.AddConfiguration(section, "Settings.json", null); public static IServiceCollection AddConfiguration(this IServiceCollection services) - where TConfiguration : class => + where TConfiguration : class, new() => services.AddConfiguration(typeof(TConfiguration).Name, "Settings.json", null); public static IServiceCollection AddConfiguration(this IServiceCollection services, @@ -81,21 +81,21 @@ public static class IServiceCollectionExtensions public static IServiceCollection AddConfiguration(this IServiceCollection services, TConfiguration configuration) - where TConfiguration : class => + where TConfiguration : class, new() => services.AddConfiguration(configuration.GetType().Name, "Settings.json", configuration); public static IServiceCollection AddConfiguration(this IServiceCollection services, object configuration) - where TConfiguration : class => + where TConfiguration : class, new() => services.AddConfiguration(configuration.GetType().Name, "Settings.json", (TConfiguration?)configuration); public static IServiceCollection AddConfiguration(this IServiceCollection services, string section, string path = "Settings.json", - TConfiguration? configuration = null, + TConfiguration? defaultConfiguration = null, Action? serializerDelegate = null) - where TConfiguration : class + where TConfiguration : class, new() { services.TryAddSingleton>(provider => { @@ -132,11 +132,10 @@ public static class IServiceCollectionExtensions new ConfigurationWriter(provider.GetRequiredKeyedService>(key))); services.TryAddKeyedTransient>(section, (provider, key) => - new ConfigurationFactory(() => configuration ?? provider.GetRequiredService())); + new ConfigurationFactory(() => defaultConfiguration ?? new TConfiguration())); services.AddTransient>(provider => - new ConfigurationInitializer(section, - provider.GetRequiredKeyedService>(section), + new ConfigurationInitializer(provider.GetRequiredKeyedService>(section), provider.GetRequiredKeyedService>(section), provider.GetRequiredKeyedService>(section), provider.GetRequiredService())); @@ -146,8 +145,11 @@ public static class IServiceCollectionExtensions services.AddTransient, WritableConfiguration>(); - services.AddTransient, Configuration>(); - services.AddTransient(provider => provider.GetRequiredService>().Value); + services.TryAddKeyedTransient>(section, (provider, key) => + new Configuration(section, provider.GetRequiredKeyedService>(key))); + + services.AddTransient(provider => provider.GetRequiredKeyedService>(section)); + services.AddTransient(provider => provider.GetRequiredKeyedService>(section).Value); return services; } From 5cb7ea538ebd1e32f3fcd61f12a18e03965e3409 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 21 Apr 2024 22:29:48 +0100 Subject: [PATCH 009/228] get vaults rendering on the screen --- Toolkit.Avalonia/ContentControlHandler.cs | 2 +- Toolkit.Foundation/ComponentBuilder.cs | 23 ++++++++++++----------- Toolkit.Foundation/IComponentBuilder.cs | 2 +- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Toolkit.Avalonia/ContentControlHandler.cs b/Toolkit.Avalonia/ContentControlHandler.cs index ccef36e..f159b64 100644 --- a/Toolkit.Avalonia/ContentControlHandler.cs +++ b/Toolkit.Avalonia/ContentControlHandler.cs @@ -49,8 +49,8 @@ public class ContentControlHandler(INavigationContext navigationContext) : control.Loaded += HandleLoaded; control.Unloaded += HandleUnloaded; + control.DataContext = args.Content; contentControl.Content = control; - contentControl.DataContext = args.Content; navigationContext.Set(control); await taskCompletionSource.Task; diff --git a/Toolkit.Foundation/ComponentBuilder.cs b/Toolkit.Foundation/ComponentBuilder.cs index 3afcb6e..35292c5 100644 --- a/Toolkit.Foundation/ComponentBuilder.cs +++ b/Toolkit.Foundation/ComponentBuilder.cs @@ -53,12 +53,19 @@ public class ComponentBuilder : public IComponentBuilder AddConfiguration(Action configurationDelegate) where TConfiguration : ComponentConfiguration, new() { - AddConfiguration(typeof(TConfiguration).Name, configurationDelegate); + TConfiguration configuration = new(); + + if (configurationDelegate is not null) + { + configurationDelegate(configuration); + } + + AddConfiguration(typeof(TConfiguration).Name, configuration); return this; } public IComponentBuilder AddConfiguration(string section, - Action? configurationDelegate = null) + TConfiguration? configuration = null) where TConfiguration : ComponentConfiguration, new() { @@ -68,27 +75,21 @@ public class ComponentBuilder : } configurationRegistered = true; - TConfiguration configuration = new(); - - if (configurationDelegate is not null) - { - configurationDelegate(configuration); - } hostBuilder.ConfigureServices(services => { services.AddConfiguration(section: section, defaultConfiguration: configuration); - services.AddConfiguration(configuration); + services.AddConfiguration(section: section, + defaultConfiguration: configuration); }); return this; } public IComponentBuilder AddConfiguration(string section) - where TConfiguration : - ComponentConfiguration, new() + where TConfiguration : ComponentConfiguration, new() { AddConfiguration(section, null); return this; diff --git a/Toolkit.Foundation/IComponentBuilder.cs b/Toolkit.Foundation/IComponentBuilder.cs index 571f207..a383377 100644 --- a/Toolkit.Foundation/IComponentBuilder.cs +++ b/Toolkit.Foundation/IComponentBuilder.cs @@ -8,7 +8,7 @@ public interface IComponentBuilder where TConfiguration : ComponentConfiguration, new(); IComponentBuilder AddConfiguration(string section, - Action? configurationDelegate = null) + TConfiguration? configuration = null) where TConfiguration : ComponentConfiguration, new(); IComponentBuilder AddConfiguration(string section) From 7fa80371a4bb7d8404242c23652d2c619b2665ed Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Mon, 22 Apr 2024 22:30:57 +0100 Subject: [PATCH 010/228] Add KeyBindingBehavior --- Toolkit.Foundation/ComponentInitializer.cs | 9 +++-- Toolkit.Foundation/ComponentScope.cs | 3 ++ .../ComponentScopeCollection.cs | 2 +- .../ComponentScopeDescriptor.cs | 3 ++ Toolkit.Foundation/ComponentScopeProvider.cs | 9 ++--- ...guration.cs => ConfigurationDescriptor.cs} | 4 +- .../ContentTemplateDescriptorProvider.cs | 4 +- Toolkit.Foundation/DefaultBuilder.cs | 5 ++- .../IComponentScopeCollection.cs | 2 +- Toolkit.Foundation/IComponentScopeProvider.cs | 2 +- ...uration.cs => IConfigurationDescriptor.cs} | 2 +- .../IServiceCollectionExtensions.cs | 10 ++--- Toolkit.Foundation/NavigateBackHandler.cs | 6 +-- Toolkit.Foundation/NavigateHandler.cs | 9 +++-- Toolkit.UI.Avalonia/ComparisonCondition.cs | 1 + Toolkit.UI.Avalonia/KeyBindingBehavior.cs | 38 +++++++++++++++++++ 16 files changed, 77 insertions(+), 32 deletions(-) create mode 100644 Toolkit.Foundation/ComponentScope.cs create mode 100644 Toolkit.Foundation/ComponentScopeDescriptor.cs rename Toolkit.Foundation/{Configuration.cs => ConfigurationDescriptor.cs} (65%) rename Toolkit.Foundation/{IConfiguration.cs => IConfigurationDescriptor.cs} (69%) create mode 100644 Toolkit.UI.Avalonia/KeyBindingBehavior.cs diff --git a/Toolkit.Foundation/ComponentInitializer.cs b/Toolkit.Foundation/ComponentInitializer.cs index a758541..be18151 100644 --- a/Toolkit.Foundation/ComponentInitializer.cs +++ b/Toolkit.Foundation/ComponentInitializer.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; namespace Toolkit.Foundation; @@ -35,12 +36,14 @@ public class ComponentInitializer(IEnumerable components, provider.GetRequiredService()); services.AddRange(typedServices.Services); + + services.AddSingleton(new ComponentScope(component.GetType().Name)); }); IComponentHost host = builder.Build(); - scopes.Add(component.GetType().Name, - host.Services.GetRequiredService()); + scopes.Add(new ComponentScopeDescriptor(component.GetType().Name, + provider.GetRequiredService())); hosts.Add(host); await host.StartAsync(); diff --git a/Toolkit.Foundation/ComponentScope.cs b/Toolkit.Foundation/ComponentScope.cs new file mode 100644 index 0000000..c403c54 --- /dev/null +++ b/Toolkit.Foundation/ComponentScope.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record ComponentScope(string Name); diff --git a/Toolkit.Foundation/ComponentScopeCollection.cs b/Toolkit.Foundation/ComponentScopeCollection.cs index d944e5d..b9a42a7 100644 --- a/Toolkit.Foundation/ComponentScopeCollection.cs +++ b/Toolkit.Foundation/ComponentScopeCollection.cs @@ -1,4 +1,4 @@ namespace Toolkit.Foundation; -public class ComponentScopeCollection : Dictionary, +public class ComponentScopeCollection : List, IComponentScopeCollection; diff --git a/Toolkit.Foundation/ComponentScopeDescriptor.cs b/Toolkit.Foundation/ComponentScopeDescriptor.cs new file mode 100644 index 0000000..23380c7 --- /dev/null +++ b/Toolkit.Foundation/ComponentScopeDescriptor.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record ComponentScopeDescriptor(string Key, IServiceProvider Services); \ No newline at end of file diff --git a/Toolkit.Foundation/ComponentScopeProvider.cs b/Toolkit.Foundation/ComponentScopeProvider.cs index f0b28f4..e29b8d4 100644 --- a/Toolkit.Foundation/ComponentScopeProvider.cs +++ b/Toolkit.Foundation/ComponentScopeProvider.cs @@ -3,10 +3,9 @@ public class ComponentScopeProvider(IComponentScopeCollection scopes) : IComponentScopeProvider { - public IServiceProvider? Get(string name) + public ComponentScopeDescriptor? Get(string key) { - return scopes.TryGetValue(name, - out IServiceProvider? scope) ? scope : default; + return scopes.FirstOrDefault(x => x.Key == key) is ComponentScopeDescriptor + descriptor ? descriptor : default; } -} - +} \ No newline at end of file diff --git a/Toolkit.Foundation/Configuration.cs b/Toolkit.Foundation/ConfigurationDescriptor.cs similarity index 65% rename from Toolkit.Foundation/Configuration.cs rename to Toolkit.Foundation/ConfigurationDescriptor.cs index fc9249d..347195c 100644 --- a/Toolkit.Foundation/Configuration.cs +++ b/Toolkit.Foundation/ConfigurationDescriptor.cs @@ -1,8 +1,8 @@ namespace Toolkit.Foundation; -public class Configuration(string section, +public class ConfigurationDescriptor(string section, IConfigurationReader reader) : - IConfiguration + IConfigurationDescriptor where TConfiguration : class { diff --git a/Toolkit.Foundation/ContentTemplateDescriptorProvider.cs b/Toolkit.Foundation/ContentTemplateDescriptorProvider.cs index a4f1404..fef28cb 100644 --- a/Toolkit.Foundation/ContentTemplateDescriptorProvider.cs +++ b/Toolkit.Foundation/ContentTemplateDescriptorProvider.cs @@ -1,6 +1,4 @@ -using System.Xml.Linq; - -namespace Toolkit.Foundation; +namespace Toolkit.Foundation; public class ContentTemplateDescriptorProvider(IEnumerable contentTemplates) : IContentTemplateDescriptorProvider diff --git a/Toolkit.Foundation/DefaultBuilder.cs b/Toolkit.Foundation/DefaultBuilder.cs index 92535f8..948e896 100644 --- a/Toolkit.Foundation/DefaultBuilder.cs +++ b/Toolkit.Foundation/DefaultBuilder.cs @@ -49,10 +49,11 @@ public class DefaultBuilder : services.AddTransient(); services.AddTransient(); - + + services.AddSingleton(new ComponentScope("Root")); services.AddScoped(provider => new ComponentScopeCollection { - { "Default", provider.GetRequiredService() } + new ComponentScopeDescriptor("Root", provider.GetRequiredService()) }); services.AddTransient(); diff --git a/Toolkit.Foundation/IComponentScopeCollection.cs b/Toolkit.Foundation/IComponentScopeCollection.cs index 72eb836..1dc26fa 100644 --- a/Toolkit.Foundation/IComponentScopeCollection.cs +++ b/Toolkit.Foundation/IComponentScopeCollection.cs @@ -1,5 +1,5 @@ namespace Toolkit.Foundation; public interface IComponentScopeCollection : - IDictionary; + IList; diff --git a/Toolkit.Foundation/IComponentScopeProvider.cs b/Toolkit.Foundation/IComponentScopeProvider.cs index bf5cfc2..220446e 100644 --- a/Toolkit.Foundation/IComponentScopeProvider.cs +++ b/Toolkit.Foundation/IComponentScopeProvider.cs @@ -2,6 +2,6 @@ public interface IComponentScopeProvider { - IServiceProvider? Get(string name); + ComponentScopeDescriptor? Get(string key); } diff --git a/Toolkit.Foundation/IConfiguration.cs b/Toolkit.Foundation/IConfigurationDescriptor.cs similarity index 69% rename from Toolkit.Foundation/IConfiguration.cs rename to Toolkit.Foundation/IConfigurationDescriptor.cs index 0af78a7..cad357a 100644 --- a/Toolkit.Foundation/IConfiguration.cs +++ b/Toolkit.Foundation/IConfigurationDescriptor.cs @@ -1,6 +1,6 @@ namespace Toolkit.Foundation; -public interface IConfiguration +public interface IConfigurationDescriptor where TConfiguration : class { diff --git a/Toolkit.Foundation/IServiceCollectionExtensions.cs b/Toolkit.Foundation/IServiceCollectionExtensions.cs index cba129e..e482ed6 100644 --- a/Toolkit.Foundation/IServiceCollectionExtensions.cs +++ b/Toolkit.Foundation/IServiceCollectionExtensions.cs @@ -3,9 +3,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders.Physical; using Microsoft.Extensions.Hosting; -using System; using System.Text.Json; -using Toolkit.Foundation; namespace Toolkit.Foundation; @@ -145,11 +143,11 @@ public static class IServiceCollectionExtensions services.AddTransient, WritableConfiguration>(); - services.TryAddKeyedTransient>(section, (provider, key) => - new Configuration(section, provider.GetRequiredKeyedService>(key))); + services.TryAddKeyedTransient>(section, (provider, key) => + new ConfigurationDescriptor(section, provider.GetRequiredKeyedService>(key))); - services.AddTransient(provider => provider.GetRequiredKeyedService>(section)); - services.AddTransient(provider => provider.GetRequiredKeyedService>(section).Value); + services.AddTransient(provider => provider.GetRequiredKeyedService>(section)); + services.AddTransient(provider => provider.GetRequiredKeyedService>(section).Value); return services; } diff --git a/Toolkit.Foundation/NavigateBackHandler.cs b/Toolkit.Foundation/NavigateBackHandler.cs index d66c7e4..2efa23d 100644 --- a/Toolkit.Foundation/NavigateBackHandler.cs +++ b/Toolkit.Foundation/NavigateBackHandler.cs @@ -8,10 +8,10 @@ public class NavigateBackHandler(IComponentScopeProvider provider) : public async Task Handle(NavigateBack args, CancellationToken cancellationToken) { - if (provider.Get(args.Scope ?? "Default") - is IServiceProvider scope) + if (provider.Get(args.Scope ?? "Root") + is ComponentScopeDescriptor descriptor) { - if (scope.GetService() is INavigationScope navigationScope) + if (descriptor?.Services?.GetService() is INavigationScope navigationScope) { await navigationScope.NavigateBackAsync(args.Context, cancellationToken); } diff --git a/Toolkit.Foundation/NavigateHandler.cs b/Toolkit.Foundation/NavigateHandler.cs index a634319..e91cfd3 100644 --- a/Toolkit.Foundation/NavigateHandler.cs +++ b/Toolkit.Foundation/NavigateHandler.cs @@ -2,16 +2,17 @@ namespace Toolkit.Foundation; -public class NavigateHandler(IComponentScopeProvider provider) : +public class NavigateHandler(ComponentScope scope, + IComponentScopeProvider provider) : INotificationHandler { public async Task Handle(Navigate args, CancellationToken cancellationToken) { - if (provider.Get(args.Scope ?? "Default") - is IServiceProvider scope) + if (provider.Get(args.Scope ?? scope.Name) + is ComponentScopeDescriptor descriptor) { - if (scope.GetService() is INavigationScope navigationScope) + if (descriptor?.Services?.GetService() is INavigationScope navigationScope) { await navigationScope.NavigateAsync(args.Route, args.Sender, args.Context, args.Navigated, args.Parameters, cancellationToken); diff --git a/Toolkit.UI.Avalonia/ComparisonCondition.cs b/Toolkit.UI.Avalonia/ComparisonCondition.cs index 6d112be..bde4777 100644 --- a/Toolkit.UI.Avalonia/ComparisonCondition.cs +++ b/Toolkit.UI.Avalonia/ComparisonCondition.cs @@ -2,6 +2,7 @@ using Avalonia.Xaml.Interactivity; namespace Toolkit.UI.Avalonia; + public class ComparisonCondition : AvaloniaObject, ICondition diff --git a/Toolkit.UI.Avalonia/KeyBindingBehavior.cs b/Toolkit.UI.Avalonia/KeyBindingBehavior.cs new file mode 100644 index 0000000..20f2117 --- /dev/null +++ b/Toolkit.UI.Avalonia/KeyBindingBehavior.cs @@ -0,0 +1,38 @@ +using Avalonia; +using Avalonia.Input; +using Avalonia.Xaml.Interactivity; +using System.Windows.Input; + +namespace Toolkit.UI.Avalonia; + +public class KeyBindingBehavior : + Trigger, + ICommand +{ + public static readonly StyledProperty KeyBindingProperty = + AvaloniaProperty.Register(nameof(KeyBinding)); + + public KeyBinding KeyBinding + { + get => GetValue(KeyBindingProperty); + set => SetValue(KeyBindingProperty, value); + } + + public event EventHandler? CanExecuteChanged; + + protected override void OnAttached() + { + if (KeyBinding != null) + { + KeyBinding.Command = this; + AssociatedObject?.KeyBindings.Add(KeyBinding); + } + + base.OnAttached(); + } + + public bool CanExecute(object? parameter) => true; + + public void Execute(object? parameter) => + Interaction.ExecuteActions(AssociatedObject, Actions, null); +} From 5ded324d1a9a5a6ec7e90d87d54c5dd286020599 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Mon, 22 Apr 2024 22:49:04 +0100 Subject: [PATCH 011/228] Added KeyBindingTriggerBehavior --- Toolkit.UI.Avalonia/KeyBindingBehavior.cs | 38 ---------------- .../KeyBindingTriggerBehavior.cs | 43 +++++++++++++++++++ 2 files changed, 43 insertions(+), 38 deletions(-) delete mode 100644 Toolkit.UI.Avalonia/KeyBindingBehavior.cs create mode 100644 Toolkit.UI.Avalonia/KeyBindingTriggerBehavior.cs diff --git a/Toolkit.UI.Avalonia/KeyBindingBehavior.cs b/Toolkit.UI.Avalonia/KeyBindingBehavior.cs deleted file mode 100644 index 20f2117..0000000 --- a/Toolkit.UI.Avalonia/KeyBindingBehavior.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Avalonia; -using Avalonia.Input; -using Avalonia.Xaml.Interactivity; -using System.Windows.Input; - -namespace Toolkit.UI.Avalonia; - -public class KeyBindingBehavior : - Trigger, - ICommand -{ - public static readonly StyledProperty KeyBindingProperty = - AvaloniaProperty.Register(nameof(KeyBinding)); - - public KeyBinding KeyBinding - { - get => GetValue(KeyBindingProperty); - set => SetValue(KeyBindingProperty, value); - } - - public event EventHandler? CanExecuteChanged; - - protected override void OnAttached() - { - if (KeyBinding != null) - { - KeyBinding.Command = this; - AssociatedObject?.KeyBindings.Add(KeyBinding); - } - - base.OnAttached(); - } - - public bool CanExecute(object? parameter) => true; - - public void Execute(object? parameter) => - Interaction.ExecuteActions(AssociatedObject, Actions, null); -} diff --git a/Toolkit.UI.Avalonia/KeyBindingTriggerBehavior.cs b/Toolkit.UI.Avalonia/KeyBindingTriggerBehavior.cs new file mode 100644 index 0000000..68c54c9 --- /dev/null +++ b/Toolkit.UI.Avalonia/KeyBindingTriggerBehavior.cs @@ -0,0 +1,43 @@ +using Avalonia; +using Avalonia.Input; +using Avalonia.Xaml.Interactivity; +using System.Windows.Input; + +namespace Toolkit.UI.Avalonia; + +public class KeyBindingTriggerBehavior : + Trigger, + ICommand +{ + public static readonly StyledProperty GestureProperty = + AvaloniaProperty.Register(nameof(Gesture)); + + public KeyGesture Gesture + { + get => GetValue(GestureProperty); + set => SetValue(GestureProperty, value); + } + + public event EventHandler? CanExecuteChanged; + + protected override void OnAttached() + { + if (Gesture is not null) + { + KeyBinding keyBinding = new KeyBinding + { + Gesture = Gesture, + Command = this + }; + + AssociatedObject?.KeyBindings.Add(keyBinding); + } + + base.OnAttached(); + } + + public bool CanExecute(object? parameter) => true; + + public void Execute(object? parameter) => + Interaction.ExecuteActions(AssociatedObject, Actions, null); +} From d799eab511a93f145c81bc66a2b7a6edad0d450b Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Wed, 24 Apr 2024 23:08:58 +0100 Subject: [PATCH 012/228] Added the abilty to load configuration sections using * pattern --- Toolkit.Avalonia/FrameHandler.cs | 4 +- Toolkit.Avalonia/NavigationPageFactory.cs | 10 +- Toolkit.Foundation/Create.cs | 9 +- Toolkit.Foundation/DefaultBuilder.cs | 148 +++++++++++++++++- .../IServiceCollectionExtensions.cs | 10 +- 5 files changed, 166 insertions(+), 15 deletions(-) diff --git a/Toolkit.Avalonia/FrameHandler.cs b/Toolkit.Avalonia/FrameHandler.cs index fa5a17d..ffe6781 100644 --- a/Toolkit.Avalonia/FrameHandler.cs +++ b/Toolkit.Avalonia/FrameHandler.cs @@ -32,8 +32,8 @@ public class FrameHandler(INavigationContext navigationContext) : if (control.DataContext is object content) { - if (content is IPrimaryConfirmation confirmNavigation && - !await confirmNavigation.Confirm()) + if (content is IConfirmation confirmation && + !await confirmation.Confirm()) { args.Cancel = true; } diff --git a/Toolkit.Avalonia/NavigationPageFactory.cs b/Toolkit.Avalonia/NavigationPageFactory.cs index 511d563..e7d1657 100644 --- a/Toolkit.Avalonia/NavigationPageFactory.cs +++ b/Toolkit.Avalonia/NavigationPageFactory.cs @@ -6,13 +6,7 @@ namespace Toolkit.Avalonia; public class NavigationPageFactory : INavigationPageFactory { - public Control? GetPage(Type srcType) - { - return default; - } + public Control? GetPage(Type srcType) => default; - public Control GetPageFromObject(object target) - { - return (Control)target; - } + public Control GetPageFromObject(object target) => (Control)target; } diff --git a/Toolkit.Foundation/Create.cs b/Toolkit.Foundation/Create.cs index 9483a29..45b2bb9 100644 --- a/Toolkit.Foundation/Create.cs +++ b/Toolkit.Foundation/Create.cs @@ -2,4 +2,11 @@ namespace Toolkit.Foundation; public record Create(TValue Value) : - INotification; \ No newline at end of file + INotification; + +public record Create +{ + public static Create As(TValue value) => new(value); + + public static Create As() where TValue : new() => new(new TValue()); +} \ No newline at end of file diff --git a/Toolkit.Foundation/DefaultBuilder.cs b/Toolkit.Foundation/DefaultBuilder.cs index 948e896..55eb938 100644 --- a/Toolkit.Foundation/DefaultBuilder.cs +++ b/Toolkit.Foundation/DefaultBuilder.cs @@ -1,9 +1,155 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.FileProviders.Physical; using Microsoft.Extensions.Hosting; +using System.Text.Json; namespace Toolkit.Foundation; +public static class Test +{ + public static IHostBuilder AddConfiguration(this IHostBuilder builder, + string section) + where TConfiguration : class, new() => + builder.AddConfiguration(section, "Settings.json", null); + + public static IHostBuilder AddConfiguration(this IHostBuilder services) + where TConfiguration : class, new() => + services.AddConfiguration(typeof(TConfiguration).Name, "Settings.json", null); + + public static IHostBuilder AddConfiguration(this IHostBuilder builder, + Action configurationDelegate) + where TConfiguration : class, new() + { + TConfiguration configuration = new(); + configurationDelegate.Invoke(configuration); + + return builder.AddConfiguration(typeof(TConfiguration).Name, "Settings.json", configuration); + } + + public static IHostBuilder AddConfiguration(this IHostBuilder builder, + Action configurationDelegate, + string section) + where TConfiguration : class, new() + { + TConfiguration configuration = new(); + configurationDelegate.Invoke(configuration); + + return builder.AddConfiguration(section, "Settings.json", configuration); + } + + public static IHostBuilder AddConfiguration(this IHostBuilder builder, + TConfiguration configuration) + where TConfiguration : class, new() => + builder.AddConfiguration(configuration.GetType().Name, "Settings.json", configuration); + + public static IHostBuilder AddConfiguration(this IHostBuilder builder, + object configuration) + where TConfiguration : class, new() => + builder.AddConfiguration(configuration.GetType().Name, + "Settings.json", (TConfiguration?)configuration); + + public static IHostBuilder AddConfiguration(this IHostBuilder builder, string section, + string path = "Settings.json", + TConfiguration? defaultConfiguration = null, + Action? serializerDelegate = null) + where TConfiguration : class, new() + { + builder.ConfigureServices((context, services) => + { + HashSet sections = []; + + if (section.EndsWith(":*")) + { + section = section[..^1]; + if (context.Configuration is ConfigurationRoot root) + { + foreach (KeyValuePair configuration in root.AsEnumerable()) + { + string[] segments = configuration.Key.Split(':'); + if (segments.Length > 2) + { + string keyPrefix = string.Join(':', segments.Take(2)); + if (!keyPrefix.EndsWith(":*")) + { + sections.Add(keyPrefix); + } + } + } + } + } + else + { + sections.Add(section); + } + + foreach (string section in sections) + { + services.TryAddSingleton>(provider => + { + IFileInfo? fileInfo = null; + if (provider.GetService() is IHostEnvironment hostEnvironment) + { + IFileProvider fileProvider = hostEnvironment.ContentRootFileProvider; + fileInfo = fileProvider.GetFileInfo(path); + } + + fileInfo ??= new PhysicalFileInfo(new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path))); + return new ConfigurationFile(fileInfo); + }); + + services.TryAddKeyedSingleton>(section, (provider, KeyAccelerator) => + { + JsonSerializerOptions? defaultSerializer = null; + if (serializerDelegate is not null) + { + defaultSerializer = new JsonSerializerOptions(); + serializerDelegate.Invoke(defaultSerializer); + } + + return new ConfigurationSource(provider.GetRequiredService>(), + section, defaultSerializer); + }); + + //services.AddHostedService>(); + services.TryAddKeyedTransient>(section, (provider, key) => + new ConfigurationReader(provider.GetRequiredKeyedService>(key), + provider.GetRequiredKeyedService>(key))); + + services.TryAddKeyedTransient>(section, (provider, key) => + new ConfigurationWriter(provider.GetRequiredKeyedService>(key))); + + services.TryAddKeyedTransient>(section, (provider, key) => + new ConfigurationFactory(() => defaultConfiguration ?? new TConfiguration())); + + services.AddTransient>(provider => + new ConfigurationInitializer(provider.GetRequiredKeyedService>(section), + provider.GetRequiredKeyedService>(section), + provider.GetRequiredKeyedService>(section), + provider.GetRequiredService())); + + services.AddTransient, ConfigurationInitializer>(provider => + provider.GetRequiredService().Create>(section)); + + services.AddTransient, WritableConfiguration>(); + + services.TryAddKeyedTransient>(section, (provider, key) => + new ConfigurationDescriptor(section, provider.GetRequiredKeyedService>(key))); + + services.AddTransient(provider => + provider.GetRequiredKeyedService>(section)); + + services.AddTransient(provider => + provider.GetRequiredKeyedService>(section).Value); + } + + }); + + return builder; + } +} public class DefaultBuilder : HostBuilder { @@ -11,7 +157,7 @@ public class DefaultBuilder : { return new HostBuilder() .UseContentRoot("Local", true) - .ConfigureAppConfiguration(config => + .ConfigureAppConfiguration((context, config) => { config.AddJsonFile("Settings.json", true, true); }) diff --git a/Toolkit.Foundation/IServiceCollectionExtensions.cs b/Toolkit.Foundation/IServiceCollectionExtensions.cs index e482ed6..e067f42 100644 --- a/Toolkit.Foundation/IServiceCollectionExtensions.cs +++ b/Toolkit.Foundation/IServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Configuration.Json; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders.Physical; @@ -146,8 +147,11 @@ public static class IServiceCollectionExtensions services.TryAddKeyedTransient>(section, (provider, key) => new ConfigurationDescriptor(section, provider.GetRequiredKeyedService>(key))); - services.AddTransient(provider => provider.GetRequiredKeyedService>(section)); - services.AddTransient(provider => provider.GetRequiredKeyedService>(section).Value); + services.AddTransient(provider => + provider.GetRequiredKeyedService>(section)); + + services.AddTransient(provider => + provider.GetRequiredKeyedService>(section).Value); return services; } From 9f90ef693d56edfe01f02707160fc9664250f3db Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Fri, 26 Apr 2024 22:27:31 +0100 Subject: [PATCH 013/228] Refactor --- Toolkit.Avalonia/ContentTemplate.cs | 2 +- Toolkit.Foundation/Changed.cs | 9 ++- Toolkit.Foundation/CommandValueViewModel.cs | 7 +- Toolkit.Foundation/CommandViewModel.cs | 7 +- .../ComponentConfigurationViewModel.cs | 21 +++--- Toolkit.Foundation/ComponentHost.cs | 2 +- Toolkit.Foundation/Create.cs | 9 +-- Toolkit.Foundation/Enumerate.cs | 2 +- Toolkit.Foundation/HandlerDelegate.cs | 5 +- Toolkit.Foundation/HandlerWrapper.cs | 20 +++--- Toolkit.Foundation/IHandler.cs | 6 +- Toolkit.Foundation/IMediator.cs | 7 +- Toolkit.Foundation/INotification.cs | 4 -- Toolkit.Foundation/INotificationHandler.cs | 6 +- Toolkit.Foundation/IObservableViewModel.cs | 4 +- Toolkit.Foundation/IPipelineBehavior.cs | 10 ++- Toolkit.Foundation/IPublisher.cs | 51 +++++--------- .../IServiceCollectionExtensions.cs | 4 +- Toolkit.Foundation/Initialized.cs | 10 +++ Toolkit.Foundation/Insert.cs | 11 ++- Toolkit.Foundation/Mediator.cs | 5 +- Toolkit.Foundation/Move.cs | 11 ++- Toolkit.Foundation/Navigate.cs | 6 +- Toolkit.Foundation/NavigateBack.cs | 6 +- Toolkit.Foundation/NavigationChanged.cs | 3 +- Toolkit.Foundation/NavigationScope.cs | 10 +-- .../NotificationHandlerDelegate.cs | 5 +- .../NotificationHandlerWrapper.cs | 19 +++-- .../ObservableCollectionViewModel.cs | 43 +++++++----- Toolkit.Foundation/ObservableViewModel.cs | 20 +++--- Toolkit.Foundation/Publisher.cs | 70 +++++++++---------- Toolkit.Foundation/Remove.cs | 12 +++- Toolkit.Foundation/Replace.cs | 11 ++- Toolkit.Foundation/Request.cs | 8 +-- Toolkit.Foundation/Response.cs | 3 +- Toolkit.Foundation/ServiceScopeFactory.cs | 4 +- Toolkit.Foundation/Started.cs | 3 +- Toolkit.Foundation/ValueViewModel.cs | 7 +- .../Themes/ThemeResources.axaml.cs | 4 +- 39 files changed, 236 insertions(+), 211 deletions(-) delete mode 100644 Toolkit.Foundation/INotification.cs create mode 100644 Toolkit.Foundation/Initialized.cs diff --git a/Toolkit.Avalonia/ContentTemplate.cs b/Toolkit.Avalonia/ContentTemplate.cs index 86a7144..95b753c 100644 --- a/Toolkit.Avalonia/ContentTemplate.cs +++ b/Toolkit.Avalonia/ContentTemplate.cs @@ -14,7 +14,7 @@ public class ContentTemplate : { if (item is IObservableViewModel observableViewModel) { - if (observableViewModel.ServiceProvider is IServiceProvider provider) + if (observableViewModel.Provider is IServiceProvider provider) { IContentTemplateDescriptorProvider? contentTemplateProvider = provider.GetService(); INavigationContext? viewModelContentBinder = provider.GetService(); diff --git a/Toolkit.Foundation/Changed.cs b/Toolkit.Foundation/Changed.cs index 82fd0df..e8a009b 100644 --- a/Toolkit.Foundation/Changed.cs +++ b/Toolkit.Foundation/Changed.cs @@ -1,3 +1,10 @@ namespace Toolkit.Foundation; -public record Changed(TValue? Value = default) : INotification; \ No newline at end of file +public record Changed(TValue? Value = default); + +public record Changed +{ + public static Changed As(TValue value) => new(value); + + public static Changed As() where TValue : new() => new(new TValue()); +} \ No newline at end of file diff --git a/Toolkit.Foundation/CommandValueViewModel.cs b/Toolkit.Foundation/CommandValueViewModel.cs index f2b7717..dd8bbb4 100644 --- a/Toolkit.Foundation/CommandValueViewModel.cs +++ b/Toolkit.Foundation/CommandValueViewModel.cs @@ -2,12 +2,13 @@ namespace Toolkit.Foundation; -public partial class CommandValueViewModel(IServiceProvider serviceProvider, - IServiceFactory serviceFactory, +public partial class CommandValueViewModel(IServiceProvider provider, + IServiceFactory factory, + IMediator mediator, IPublisher publisher, ISubscriber subscriber, IDisposer disposer) : - ValueViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer) + ValueViewModel(provider, factory, mediator, publisher, subscriber, disposer) { public IRelayCommand InvokeCommand => new AsyncRelayCommand(InvokeAsync); diff --git a/Toolkit.Foundation/CommandViewModel.cs b/Toolkit.Foundation/CommandViewModel.cs index 740f078..20e3596 100644 --- a/Toolkit.Foundation/CommandViewModel.cs +++ b/Toolkit.Foundation/CommandViewModel.cs @@ -2,12 +2,13 @@ namespace Toolkit.Foundation; -public partial class CommandViewModel(IServiceProvider serviceProvider, - IServiceFactory serviceFactory, +public partial class CommandViewModel(IServiceProvider provider, + IServiceFactory factory, + IMediator mediator, IPublisher publisher, ISubscriber subscriber, IDisposer disposer) : - ObservableViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer) + ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) { public IRelayCommand InvokeCommand => new AsyncRelayCommand(InvokeAsync); diff --git a/Toolkit.Foundation/ComponentConfigurationViewModel.cs b/Toolkit.Foundation/ComponentConfigurationViewModel.cs index 4eafe1e..76c09da 100644 --- a/Toolkit.Foundation/ComponentConfigurationViewModel.cs +++ b/Toolkit.Foundation/ComponentConfigurationViewModel.cs @@ -8,14 +8,15 @@ public partial class ComponentConfigurationViewModel> where TConfiguration : class { - public ComponentConfigurationViewModel(IServiceProvider serviceProvider, - IServiceFactory serviceFactory, + public ComponentConfigurationViewModel(IServiceProvider provider, + IServiceFactory factory, + IMediator mediator, IPublisher publisher, ISubscriber subscriber, IDisposer disposer, THeader header, TDescription description, - TAction action) : base(serviceProvider, serviceFactory, publisher, subscriber, disposer) + TAction action) : base(provider, factory, mediator, publisher, subscriber, disposer) { } @@ -27,8 +28,9 @@ public partial class ComponentConfigurationViewModel(IServiceProvider serviceProvider, - IServiceFactory serviceFactory, +public partial class ComponentConfigurationViewModel(IServiceProvider provider, + IServiceFactory factory, + IMediator mediator, IPublisher publisher, ISubscriber subscriber, IDisposer disposer, @@ -37,7 +39,7 @@ public partial class ComponentConfigurationViewModel valueDelegate, object header, object description) : - ValueViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer), + ValueViewModel(provider, factory, mediator, publisher, subscriber, disposer), IComponentConfigurationViewModel, INotificationHandler> where TConfiguration : class @@ -68,8 +70,9 @@ public partial class ComponentConfigurationViewModel(IServiceProvider serviceProvider, - IServiceFactory serviceFactory, +public partial class ComponentConfigurationViewModel(IServiceProvider provider, + IServiceFactory factory, + IMediator mediator, IPublisher publisher, ISubscriber subscriber, IDisposer disposer, @@ -78,7 +81,7 @@ public partial class ComponentConfigurationViewModel valueDelegate, object header) : - ValueViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer), + ValueViewModel(provider, factory, mediator, publisher, subscriber, disposer), IComponentConfigurationViewModel, INotificationHandler> where TConfiguration : class diff --git a/Toolkit.Foundation/ComponentHost.cs b/Toolkit.Foundation/ComponentHost.cs index f660820..907a694 100644 --- a/Toolkit.Foundation/ComponentHost.cs +++ b/Toolkit.Foundation/ComponentHost.cs @@ -3,7 +3,7 @@ using Microsoft.Extensions.Hosting; namespace Toolkit.Foundation; -public sealed class ComponentHost(IServiceProvider services, +public class ComponentHost(IServiceProvider services, IEnumerable initializers, IEnumerable hostedServices) : IComponentHost diff --git a/Toolkit.Foundation/Create.cs b/Toolkit.Foundation/Create.cs index 45b2bb9..667aa9a 100644 --- a/Toolkit.Foundation/Create.cs +++ b/Toolkit.Foundation/Create.cs @@ -1,12 +1,13 @@  namespace Toolkit.Foundation; -public record Create(TValue Value) : - INotification; +public record Create(TValue Value); public record Create { - public static Create As(TValue value) => new(value); + public static Create As(TValue value) => + new(value); - public static Create As() where TValue : new() => new(new TValue()); + public static Create As() where TValue : new() => + new(new TValue()); } \ No newline at end of file diff --git a/Toolkit.Foundation/Enumerate.cs b/Toolkit.Foundation/Enumerate.cs index 14d385f..63aebe8 100644 --- a/Toolkit.Foundation/Enumerate.cs +++ b/Toolkit.Foundation/Enumerate.cs @@ -1,3 +1,3 @@ namespace Toolkit.Foundation; -public record Enumerate(object? Key = null) : INotification; \ No newline at end of file +public record Enumerate(object? Key = null); \ No newline at end of file diff --git a/Toolkit.Foundation/HandlerDelegate.cs b/Toolkit.Foundation/HandlerDelegate.cs index 73ce94b..455c50c 100644 --- a/Toolkit.Foundation/HandlerDelegate.cs +++ b/Toolkit.Foundation/HandlerDelegate.cs @@ -1,5 +1,4 @@ namespace Toolkit.Foundation; -public delegate Task HandlerDelegate(TMessage message, - CancellationToken cancellationToken) - where TMessage : IMessage; \ No newline at end of file +public delegate Task HandlerDelegate(TRequest request, + CancellationToken cancellationToken); \ No newline at end of file diff --git a/Toolkit.Foundation/HandlerWrapper.cs b/Toolkit.Foundation/HandlerWrapper.cs index f5ba764..9cc8ae0 100644 --- a/Toolkit.Foundation/HandlerWrapper.cs +++ b/Toolkit.Foundation/HandlerWrapper.cs @@ -1,25 +1,25 @@ namespace Toolkit.Foundation; -public class HandlerWrapper(IHandler handler, - IEnumerable> pipelineBehaviours) - where TMessage : class, IRequest +public class HandlerWrapper(IHandler handler, + IEnumerable> pipelineBehaviours) + where TRequest : class { - private readonly IEnumerable> pipelineBehaviours = + private readonly IEnumerable> pipelineBehaviours = pipelineBehaviours.Reverse(); - public async Task Handle(TMessage message, + public async Task Handle(TRequest request, CancellationToken cancellationToken) { - HandlerDelegate currentHandler = handler.Handle; - foreach (IPipelineBehavior behavior in pipelineBehaviours) + HandlerDelegate currentHandler = handler.Handle; + foreach (IPipelineBehaviour behaviour in pipelineBehaviours) { - HandlerDelegate previousHandler = currentHandler; + HandlerDelegate previousHandler = currentHandler; currentHandler = async (args, token) => { - return await behavior.Handle(args, previousHandler, token); + return await behaviour.Handle(args, previousHandler, token); }; } - return await currentHandler(message, cancellationToken); + return await currentHandler(request, cancellationToken); } } \ No newline at end of file diff --git a/Toolkit.Foundation/IHandler.cs b/Toolkit.Foundation/IHandler.cs index 9bfb4ee..2263715 100644 --- a/Toolkit.Foundation/IHandler.cs +++ b/Toolkit.Foundation/IHandler.cs @@ -4,14 +4,10 @@ public interface IHandler; public interface IHandler : IHandler - where TRequest : - IRequest { Task Handle(TRequest args, CancellationToken cancellationToken); } public interface IHandler : - IHandler - where TRequest : - IRequest; \ No newline at end of file + IHandler; \ No newline at end of file diff --git a/Toolkit.Foundation/IMediator.cs b/Toolkit.Foundation/IMediator.cs index 74fcb71..e87d071 100644 --- a/Toolkit.Foundation/IMediator.cs +++ b/Toolkit.Foundation/IMediator.cs @@ -2,9 +2,10 @@ public interface IMediator { - Task SendAsync(IRequest request, - CancellationToken cancellationToken = default); + Task Handle(TRequest request, + CancellationToken cancellationToken = default) + where TRequest : notnull; - Task SendAsync(object message, CancellationToken + Task Handle(object request, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/Toolkit.Foundation/INotification.cs b/Toolkit.Foundation/INotification.cs deleted file mode 100644 index 1bc2226..0000000 --- a/Toolkit.Foundation/INotification.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Toolkit.Foundation; - -public interface INotification : - IMessage; \ No newline at end of file diff --git a/Toolkit.Foundation/INotificationHandler.cs b/Toolkit.Foundation/INotificationHandler.cs index abe34bd..3da0f29 100644 --- a/Toolkit.Foundation/INotificationHandler.cs +++ b/Toolkit.Foundation/INotificationHandler.cs @@ -1,10 +1,8 @@ namespace Toolkit.Foundation; -public interface INotificationHandler : +public interface INotificationHandler : IHandler - where TNotification : - INotification { - Task Handle(TNotification args, + Task Handle(TMessage args, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/Toolkit.Foundation/IObservableViewModel.cs b/Toolkit.Foundation/IObservableViewModel.cs index 7d688f4..333fa16 100644 --- a/Toolkit.Foundation/IObservableViewModel.cs +++ b/Toolkit.Foundation/IObservableViewModel.cs @@ -7,7 +7,7 @@ public interface IObservableViewModel : public IPublisher Publisher { get; } - public IServiceFactory ServiceFactory { get; } + public IServiceFactory Factory { get; } - public IServiceProvider ServiceProvider { get; } + public IServiceProvider Provider { get; } } \ No newline at end of file diff --git a/Toolkit.Foundation/IPipelineBehavior.cs b/Toolkit.Foundation/IPipelineBehavior.cs index 7bd8b32..9f4999e 100644 --- a/Toolkit.Foundation/IPipelineBehavior.cs +++ b/Toolkit.Foundation/IPipelineBehavior.cs @@ -1,17 +1,15 @@ namespace Toolkit.Foundation; -public interface IPipelineBehavior - where TMessage : IMessage +public interface IPipelineBehaviour { Task Handle(TMessage message, HandlerDelegate next, CancellationToken cancellationToken = default); } -public interface IPipelineBehavior - where TNotification : INotification +public interface IPipelineBehaviour { - Task Handle(TNotification notification, - NotificationHandlerDelegate next, + Task Handle(TMessage message, + NotificationHandlerDelegate next, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/Toolkit.Foundation/IPublisher.cs b/Toolkit.Foundation/IPublisher.cs index 6289413..4f0feec 100644 --- a/Toolkit.Foundation/IPublisher.cs +++ b/Toolkit.Foundation/IPublisher.cs @@ -2,57 +2,44 @@ public interface IPublisher { - public Task Publish(object key, + public Task Publish(object key, CancellationToken cancellationToken = default) - where TNotification : - INotification, - new(); + where TMessage : new(); - public Task Publish(TNotification notification, + public Task Publish(TMessage message, CancellationToken cancellationToken = default) - where TNotification : - INotification; + where TMessage : notnull; - public Task Publish(TNotification notification, + public Task Publish(TMessage message, object key, CancellationToken cancellationToken = default) - where TNotification : - INotification; + where TMessage : notnull; - Task PublishUI(TNotification notification, + Task PublishUI(TMessage message, object key, CancellationToken cancellationToken = default) - where TNotification : - INotification; - - Task PublishUI(object key, + where TMessage : notnull; + Task PublishUI(object key, CancellationToken cancellationToken = default) - where TNotification : - INotification, - new(); + where TMessage : new(); - Task PublishUI(TNotification notification, + Task PublishUI(TMessage message, CancellationToken cancellationToken = default) - where TNotification : - INotification; + where TMessage : notnull; - Task PublishUI(object notification, + Task PublishUI(object message, CancellationToken cancellationToken = default); - Task Publish(object notification, + Task Publish(object message, Func, Task> marshal, object? key = null, CancellationToken cancellationToken = default); - Task PublishUIAsync(CancellationToken cancellationToken = default) - where TNotification : - INotification, - new(); + Task PublishUI(CancellationToken cancellationToken = default) + where TMessage : new(); - Task Publish(CancellationToken cancellationToken = default) - where TNotification : - INotification, - new(); + Task Publish(CancellationToken cancellationToken = default) + where TMessage : new(); - public Task Publish(object notification, CancellationToken cancellationToken = default); + public Task Publish(object message, CancellationToken cancellationToken = default); } diff --git a/Toolkit.Foundation/IServiceCollectionExtensions.cs b/Toolkit.Foundation/IServiceCollectionExtensions.cs index e067f42..20eeebb 100644 --- a/Toolkit.Foundation/IServiceCollectionExtensions.cs +++ b/Toolkit.Foundation/IServiceCollectionExtensions.cs @@ -178,7 +178,7 @@ public static class IServiceCollectionExtensions services.Add(new ServiceDescriptor(wrapperType, provider => provider.GetService()?.Create(wrapperType, provider.GetRequiredService(typeof(INotificationHandler<>).MakeGenericType(notificationType)), - provider.GetServices(typeof(IPipelineBehavior<>) + provider.GetServices(typeof(IPipelineBehaviour<>) .MakeGenericType(notificationType)))!, lifetime)); } @@ -197,7 +197,7 @@ public static class IServiceCollectionExtensions services.Add(new ServiceDescriptor(wrapperType, provider => provider.GetService()?.Create(wrapperType, provider.GetRequiredService(), - provider.GetServices(typeof(IPipelineBehavior<,>) + provider.GetServices(typeof(IPipelineBehaviour<,>) .MakeGenericType(requestType, responseType)))!, lifetime)); } } diff --git a/Toolkit.Foundation/Initialized.cs b/Toolkit.Foundation/Initialized.cs new file mode 100644 index 0000000..6302662 --- /dev/null +++ b/Toolkit.Foundation/Initialized.cs @@ -0,0 +1,10 @@ +namespace Toolkit.Foundation; + +public record Initialized(TValue Value); + +public record Initialized +{ + public static Initialized As(TValue value) => new(value); + + public static Initialized As() where TValue : new() => new(new TValue()); +} \ No newline at end of file diff --git a/Toolkit.Foundation/Insert.cs b/Toolkit.Foundation/Insert.cs index e97b8eb..a5ac4d7 100644 --- a/Toolkit.Foundation/Insert.cs +++ b/Toolkit.Foundation/Insert.cs @@ -1,3 +1,12 @@ namespace Toolkit.Foundation; -public record Insert(int Index, TValue Value) : INotification; +public record Insert(int Index, TValue Value); + +public record Insert +{ + public static Insert As(int index, TValue value) => + new(index, value); + + public static Insert As(int index) where TValue : new() => + new(index, new TValue()); +} diff --git a/Toolkit.Foundation/Mediator.cs b/Toolkit.Foundation/Mediator.cs index c710ed3..f6d649d 100644 --- a/Toolkit.Foundation/Mediator.cs +++ b/Toolkit.Foundation/Mediator.cs @@ -5,8 +5,9 @@ namespace Toolkit.Foundation; public class Mediator(IServiceProvider provider) : IMediator { - public Task SendAsync(IRequest request, + public Task Handle(TRequest request, CancellationToken cancellationToken = default) + where TRequest : notnull { Type handlerType = typeof(HandlerWrapper<,>).MakeGenericType(request.GetType(), typeof(TResponse)); @@ -23,7 +24,7 @@ public class Mediator(IServiceProvider provider) : return Task.FromResult(default); } - public Task SendAsync(object message, + public Task Handle(object message, CancellationToken cancellationToken = default) { if (message.GetType().GetInterface(typeof(IRequest<>).Name) is Type requestType && diff --git a/Toolkit.Foundation/Move.cs b/Toolkit.Foundation/Move.cs index f3c66d9..67e4d77 100644 --- a/Toolkit.Foundation/Move.cs +++ b/Toolkit.Foundation/Move.cs @@ -1,3 +1,12 @@ namespace Toolkit.Foundation; -public record Move(int Index, TValue Value) : INotification; \ No newline at end of file +public record Move(int Index, TValue Value); + +public record Move +{ + public static Move As(int index, TValue value) => + new(index, value); + + public static Insert As(int index) where TValue : new() => + new(index, new TValue()); +} \ No newline at end of file diff --git a/Toolkit.Foundation/Navigate.cs b/Toolkit.Foundation/Navigate.cs index 30dce1c..9b889c2 100644 --- a/Toolkit.Foundation/Navigate.cs +++ b/Toolkit.Foundation/Navigate.cs @@ -5,12 +5,10 @@ public record Navigate(string Route, string? Scope = null, object? Sender = null, EventHandler? Navigated = null, - object[]? Parameters = null) : - INotification; + object[]? Parameters = null); public record Navigate(object Context, object Template, object Content, object? Sender = null, - object[]? Parameters = null) : - INotification; \ No newline at end of file + object[]? Parameters = null); \ No newline at end of file diff --git a/Toolkit.Foundation/NavigateBack.cs b/Toolkit.Foundation/NavigateBack.cs index 25be518..224de35 100644 --- a/Toolkit.Foundation/NavigateBack.cs +++ b/Toolkit.Foundation/NavigateBack.cs @@ -1,7 +1,5 @@ namespace Toolkit.Foundation; -public record NavigateBack(object? Context = null, string? Scope = null) : - INotification; +public record NavigateBack(object? Context = null, string? Scope = null); -public record NavigateBack(object? Context) : - INotification; +public record NavigateBack(object? Context); diff --git a/Toolkit.Foundation/NavigationChanged.cs b/Toolkit.Foundation/NavigationChanged.cs index 6545fd0..b6df381 100644 --- a/Toolkit.Foundation/NavigationChanged.cs +++ b/Toolkit.Foundation/NavigationChanged.cs @@ -1,4 +1,3 @@ namespace Toolkit.Foundation; -public record NavigationChanged(TValue? Value) : - INotification; \ No newline at end of file +public record NavigationChanged(TValue? Value); \ No newline at end of file diff --git a/Toolkit.Foundation/NavigationScope.cs b/Toolkit.Foundation/NavigationScope.cs index 374cb41..5748090 100644 --- a/Toolkit.Foundation/NavigationScope.cs +++ b/Toolkit.Foundation/NavigationScope.cs @@ -3,8 +3,8 @@ namespace Toolkit.Foundation; public class NavigationScope(IPublisher publisher, - IServiceProvider serviceProvider, - IServiceFactory serviceFactory, + IServiceProvider provider, + IServiceFactory factory, INavigationProvider navigationProvider, INavigationContextProvider navigationContextProvider, IContentTemplateDescriptorProvider contentTemplateDescriptorProvider) : @@ -39,11 +39,11 @@ public class NavigationScope(IPublisher publisher, Enumerable.Empty(), .. mappedParameters ?? Enumerable.Empty()]; - if (serviceProvider.GetRequiredKeyedService(descriptor.TemplateType, segment) is object view) + if (provider.GetRequiredKeyedService(descriptor.TemplateType, segment) is object view) { if ((parameters is { Length: > 0 } - ? serviceFactory.Create(descriptor.ContentType, parameters) - : serviceProvider.GetRequiredKeyedService(descriptor.ContentType, segment)) is object viewModel) + ? factory.Create(descriptor.ContentType, parameters) + : provider.GetRequiredKeyedService(descriptor.ContentType, segment)) is object viewModel) { if (context is not null) { diff --git a/Toolkit.Foundation/NotificationHandlerDelegate.cs b/Toolkit.Foundation/NotificationHandlerDelegate.cs index 2237a3a..01c0dea 100644 --- a/Toolkit.Foundation/NotificationHandlerDelegate.cs +++ b/Toolkit.Foundation/NotificationHandlerDelegate.cs @@ -1,5 +1,4 @@ namespace Toolkit.Foundation; -public delegate Task NotificationHandlerDelegate(TNotification notification, - CancellationToken cancellationToken) - where TNotification : INotification; +public delegate Task NotificationHandlerDelegate(TMessage message, + CancellationToken cancellationToken); diff --git a/Toolkit.Foundation/NotificationHandlerWrapper.cs b/Toolkit.Foundation/NotificationHandlerWrapper.cs index 100165a..91a67c4 100644 --- a/Toolkit.Foundation/NotificationHandlerWrapper.cs +++ b/Toolkit.Foundation/NotificationHandlerWrapper.cs @@ -1,25 +1,24 @@ namespace Toolkit.Foundation; -public class NotificationHandlerWrapper(INotificationHandler handler, - IEnumerable> pipelineBehaviours) - where TNotification : INotification +public class NotificationHandlerWrapper(INotificationHandler handler, + IEnumerable> pipelineBehaviours) { - private readonly IEnumerable> pipelineBehaviours = + private readonly IEnumerable> pipelineBehaviours = pipelineBehaviours.Reverse(); - public async Task Handle(TNotification notification, + public async Task Handle(TMessage message, CancellationToken cancellationToken) { - NotificationHandlerDelegate currentHandler = handler.Handle; - foreach (IPipelineBehavior behavior in pipelineBehaviours) + NotificationHandlerDelegate currentHandler = handler.Handle; + foreach (IPipelineBehaviour behaviour in pipelineBehaviours) { - NotificationHandlerDelegate previousHandler = currentHandler; + NotificationHandlerDelegate previousHandler = currentHandler; currentHandler = async (args, token) => { - await behavior.Handle(args, previousHandler, token); + await behaviour.Handle(args, previousHandler, token); }; } - await currentHandler(notification, cancellationToken); + await currentHandler(message, cancellationToken); } } diff --git a/Toolkit.Foundation/ObservableCollectionViewModel.cs b/Toolkit.Foundation/ObservableCollectionViewModel.cs index 81c2712..7ef445d 100644 --- a/Toolkit.Foundation/ObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/ObservableCollectionViewModel.cs @@ -30,14 +30,16 @@ public partial class ObservableCollectionViewModel : [ObservableProperty] private bool isInitialized; - public ObservableCollectionViewModel(IServiceProvider serviceProvider, - IServiceFactory serviceFactory, + public ObservableCollectionViewModel(IServiceProvider provider, + IServiceFactory factory, + IMediator mediator, IPublisher publisher, ISubscriber subscriber, IDisposer disposer) { - ServiceProvider = serviceProvider; - ServiceFactory = serviceFactory; + Provider = provider; + Factory = factory; + Mediator = mediator; Publisher = publisher; Disposer = disposer; @@ -46,15 +48,17 @@ public partial class ObservableCollectionViewModel : collection.CollectionChanged += OnCollectionChanged; } - public ObservableCollectionViewModel(IServiceProvider serviceProvider, - IServiceFactory serviceFactory, + public ObservableCollectionViewModel(IServiceProvider provider, + IServiceFactory factory, + IMediator mediator, IPublisher publisher, ISubscriber subscriber, IDisposer disposer, IEnumerable items) { - ServiceProvider = serviceProvider; - ServiceFactory = serviceFactory; + Provider = provider; + Factory = factory; + Mediator = mediator; Publisher = publisher; Disposer = disposer; @@ -72,6 +76,8 @@ public partial class ObservableCollectionViewModel : public IDisposer Disposer { get; private set; } + public IServiceFactory Factory { get; private set; } + bool IList.IsFixedSize => false; bool ICollection.IsReadOnly => false; @@ -80,12 +86,12 @@ public partial class ObservableCollectionViewModel : bool ICollection.IsSynchronized => false; + public IMediator Mediator { get; } + + public IServiceProvider Provider { get; private set; } + public IPublisher Publisher { get; private set; } - public IServiceFactory ServiceFactory { get; private set; } - - public IServiceProvider ServiceProvider { get; private set; } - object ICollection.SyncRoot => this; public TViewModel this[int index] @@ -119,7 +125,7 @@ public partial class ObservableCollectionViewModel : public TViewModel Add() { - TViewModel? item = ServiceFactory.Create(); + TViewModel? item = Factory.Create(); Add(item); return item; @@ -128,7 +134,7 @@ public partial class ObservableCollectionViewModel : public TViewModel Add(params object?[] parameters) where T : TViewModel { - T? item = ServiceFactory.Create(parameters); + T? item = Factory.Create(parameters); Add(item); return item; @@ -138,7 +144,7 @@ public partial class ObservableCollectionViewModel : where T : TViewModel { - T? item = ServiceFactory.Create(); + T? item = Factory.Create(); Add(item); return item; @@ -407,9 +413,10 @@ public partial class ObservableCollectionViewModel : CollectionChanged?.Invoke(this, args); } -public class ObservableCollectionViewModel(IServiceProvider serviceProvider, - IServiceFactory serviceFactory, +public class ObservableCollectionViewModel(IServiceProvider provider, + IServiceFactory factory, + IMediator mediator, IPublisher publisher, ISubscriber subscriber, IDisposer disposer) : - ObservableCollectionViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer); \ No newline at end of file + ObservableCollectionViewModel(provider, factory, mediator, publisher, subscriber, disposer); \ No newline at end of file diff --git a/Toolkit.Foundation/ObservableViewModel.cs b/Toolkit.Foundation/ObservableViewModel.cs index 2f73260..87f0ec8 100644 --- a/Toolkit.Foundation/ObservableViewModel.cs +++ b/Toolkit.Foundation/ObservableViewModel.cs @@ -14,14 +14,16 @@ public partial class ObservableViewModel : [ObservableProperty] private bool isInitialized; - public ObservableViewModel(IServiceProvider serviceProvider, - IServiceFactory serviceFactory, + public ObservableViewModel(IServiceProvider provider, + IServiceFactory factory, + IMediator mediator, IPublisher publisher, ISubscriber subscriber, IDisposer disposer) { - ServiceProvider = serviceProvider; - ServiceFactory = serviceFactory; + Provider = provider; + Factory = factory; + Mediator = mediator; Publisher = publisher; Disposer = disposer; @@ -32,12 +34,14 @@ public partial class ObservableViewModel : public IDisposer Disposer { get; } + public IServiceFactory Factory { get; } + + public IMediator Mediator { get; } + + public IServiceProvider Provider { get; } + public IPublisher Publisher { get; } - public IServiceFactory ServiceFactory { get; } - - public IServiceProvider ServiceProvider { get; } - public virtual Task Activated() => Task.CompletedTask; diff --git a/Toolkit.Foundation/Publisher.cs b/Toolkit.Foundation/Publisher.cs index 12622bc..172608c 100644 --- a/Toolkit.Foundation/Publisher.cs +++ b/Toolkit.Foundation/Publisher.cs @@ -8,32 +8,31 @@ public class Publisher(ISubscriptionManager subscriptionManager, IDispatcher dispatcher) : IPublisher { - public Task Publish(object key, + public Task Publish(object key, CancellationToken cancellationToken = default) - where TNotification : - INotification, - new() => Publish(new TNotification(), async args => await args(), + where TMessage : new() => + Publish(new TMessage(), async args => await args(), key, cancellationToken); - public Task Publish(TNotification notification, + public Task Publish(TMessage message, CancellationToken cancellationToken = default) - where TNotification : - INotification => Publish(notification, async args => await args(), + where TMessage : notnull => + Publish(message, async args => await args(), null, cancellationToken); - public Task Publish(TNotification notification, + public Task Publish(TMessage message, object key, CancellationToken cancellationToken = default) - where TNotification : - INotification => Publish(notification, - async args => await args(), key, cancellationToken); + where TMessage : notnull => + Publish(message, async args => await args(), + key, cancellationToken); - public async Task Publish(object notification, + public async Task Publish(object message, Func, Task> marshal, object? key = null, CancellationToken cancellationToken = default) { - Type notificationType = notification.GetType(); + Type notificationType = message.GetType(); List handlers = provider.GetServices(typeof(NotificationHandlerWrapper<>) .MakeGenericType(notificationType)).ToList(); @@ -55,52 +54,47 @@ public class Publisher(ISubscriptionManager subscriptionManager, if (handleMethod is not null) { await marshal(() => (Task)handleMethod.Invoke(handler, new object[] - { notification, cancellationToken })!); + { message, cancellationToken })!); } } } } - public Task Publish(object notification, - CancellationToken cancellationToken = default) => Publish(notification, + public Task Publish(object message, + CancellationToken cancellationToken = default) => Publish(message, async args => await args(), null, cancellationToken); - public Task Publish(CancellationToken cancellationToken = default) - where TNotification : - INotification, new() => Publish(new TNotification(), - async args => await args(), + public Task Publish(CancellationToken cancellationToken = default) + where TMessage : new() => + Publish(new TMessage(), async args => await args(), null, cancellationToken); - public Task PublishUI(object key, + public Task PublishUI(object key, CancellationToken cancellationToken = default) - where TNotification : - INotification, new() => Publish(new TNotification(), - args => dispatcher.InvokeAsync(async () => await args()), + where TMessage : new() => + Publish(new TMessage(), args => dispatcher.InvokeAsync(async () => await args()), key, cancellationToken); - public Task PublishUI(TNotification notification, + public Task PublishUI(TMessage message, CancellationToken cancellationToken = default) - where TNotification : - INotification => Publish(notification, - args => dispatcher.InvokeAsync(async () => await args()), + where TMessage : notnull => + Publish(message, args => dispatcher.InvokeAsync(async () => await args()), null, cancellationToken); - public Task PublishUI(TNotification notification, + public Task PublishUI(TMessage message, object key, CancellationToken cancellationToken = default) - where TNotification : - INotification => Publish(notification, - args => dispatcher.InvokeAsync(async () => await args()), + where TMessage : notnull => + Publish(message, args => dispatcher.InvokeAsync(async () => await args()), key, cancellationToken); - public Task PublishUIAsync(CancellationToken cancellationToken = default) - where TNotification : - INotification, new() => Publish(new TNotification(), - args => dispatcher.InvokeAsync(async () => await args()), + public Task PublishUI(CancellationToken cancellationToken = default) + where TMessage : new() => + Publish(new TMessage(), args => dispatcher.InvokeAsync(async () => await args()), null, cancellationToken); - public Task PublishUI(object notification, - CancellationToken cancellationToken = default) => Publish(notification, args => + public Task PublishUI(object message, + CancellationToken cancellationToken = default) => Publish(message, args => dispatcher.InvokeAsync(async () => await args()), null, cancellationToken); } diff --git a/Toolkit.Foundation/Remove.cs b/Toolkit.Foundation/Remove.cs index 54d190d..4b63bd7 100644 --- a/Toolkit.Foundation/Remove.cs +++ b/Toolkit.Foundation/Remove.cs @@ -1,5 +1,13 @@  namespace Toolkit.Foundation; -public record Remove(TValue Value) : - INotification; \ No newline at end of file +public record Remove(TValue Value); + +public record Remove +{ + public static Remove As(TValue value) => + new(value); + + public static Remove As() where TValue : new() => + new(new TValue()); +} \ No newline at end of file diff --git a/Toolkit.Foundation/Replace.cs b/Toolkit.Foundation/Replace.cs index 6441149..919c49e 100644 --- a/Toolkit.Foundation/Replace.cs +++ b/Toolkit.Foundation/Replace.cs @@ -1,6 +1,13 @@  namespace Toolkit.Foundation; -public record Replace(int Index, TValue Value, object? Target = null) : - INotification; +public record Replace(int Index, TValue Value); +public record Replace +{ + public static Replace As(int index, TValue value) => + new(index, value); + + public static Replace As(int index) where TValue : new() => + new(index, new TValue()); +} \ No newline at end of file diff --git a/Toolkit.Foundation/Request.cs b/Toolkit.Foundation/Request.cs index a5d5fb7..c7f50b9 100644 --- a/Toolkit.Foundation/Request.cs +++ b/Toolkit.Foundation/Request.cs @@ -1,12 +1,8 @@ namespace Toolkit.Foundation; -public record Request : - INotification -{ - -} +public record Request; public class Request { - public static Request Create() => new(); + public static Request As() => new(); } \ No newline at end of file diff --git a/Toolkit.Foundation/Response.cs b/Toolkit.Foundation/Response.cs index f1a7248..8197b75 100644 --- a/Toolkit.Foundation/Response.cs +++ b/Toolkit.Foundation/Response.cs @@ -1,4 +1,3 @@ namespace Toolkit.Foundation; -public record Response(TValue Value) : - INotification; \ No newline at end of file +public record Response(TValue Value); \ No newline at end of file diff --git a/Toolkit.Foundation/ServiceScopeFactory.cs b/Toolkit.Foundation/ServiceScopeFactory.cs index e8b114e..c86aec1 100644 --- a/Toolkit.Foundation/ServiceScopeFactory.cs +++ b/Toolkit.Foundation/ServiceScopeFactory.cs @@ -11,9 +11,9 @@ public class ServiceScopeFactory(IServiceScopeFactory serviceScopeFact { if (serviceScopeFactory.CreateScope() is IServiceScope serviceScope) { - if (serviceScope.ServiceProvider.GetService() is IServiceFactory serviceFactory) + if (serviceScope.ServiceProvider.GetService() is IServiceFactory factory) { - if (serviceFactory.Create(parameters) is TService service) + if (factory.Create(parameters) is TService service) { cache.Add(service, serviceScope); return service; diff --git a/Toolkit.Foundation/Started.cs b/Toolkit.Foundation/Started.cs index 9180311..3df6262 100644 --- a/Toolkit.Foundation/Started.cs +++ b/Toolkit.Foundation/Started.cs @@ -1,4 +1,3 @@ namespace Toolkit.Foundation; -public record Started : - INotification; \ No newline at end of file +public record Started; \ No newline at end of file diff --git a/Toolkit.Foundation/ValueViewModel.cs b/Toolkit.Foundation/ValueViewModel.cs index da03c02..a43ba96 100644 --- a/Toolkit.Foundation/ValueViewModel.cs +++ b/Toolkit.Foundation/ValueViewModel.cs @@ -2,12 +2,13 @@ namespace Toolkit.Foundation; -public partial class ValueViewModel(IServiceProvider serviceProvider, - IServiceFactory serviceFactory, +public partial class ValueViewModel(IServiceProvider provider, + IServiceFactory factory, + IMediator mediator, IPublisher publisher, ISubscriber subscriber, IDisposer disposer) : - ObservableViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer) + ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) { [ObservableProperty] private TValue? value; diff --git a/Toolkit.UI.Controls.Avalonia/Themes/ThemeResources.axaml.cs b/Toolkit.UI.Controls.Avalonia/Themes/ThemeResources.axaml.cs index 0f3df7f..6989d3e 100644 --- a/Toolkit.UI.Controls.Avalonia/Themes/ThemeResources.axaml.cs +++ b/Toolkit.UI.Controls.Avalonia/Themes/ThemeResources.axaml.cs @@ -6,8 +6,8 @@ namespace Toolkit.UI.Controls.Avalonia; public class ThemeResources : Styles { - public ThemeResources(IServiceProvider? serviceProvider = null) + public ThemeResources(IServiceProvider? provider = null) { - AvaloniaXamlLoader.Load(serviceProvider, this); + AvaloniaXamlLoader.Load(provider, this); } } From bc55c4649b42409c6cbd9d8d77e0a3bf4c0e4583 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Fri, 26 Apr 2024 23:05:36 +0100 Subject: [PATCH 014/228] tidy --- .../ClassicDesktopStyleApplicationHandler.cs | 2 +- Toolkit.Avalonia/ContentControlHandler.cs | 4 +- Toolkit.Avalonia/ContentDialogHandler.cs | 8 +- Toolkit.Avalonia/ContentTemplate.cs | 4 +- Toolkit.Avalonia/FrameHandler.cs | 22 +- Toolkit.Avalonia/NavigationContext.cs | 3 +- Toolkit.Avalonia/NavigationPageFactory.cs | 4 +- .../SingleViewApplicationHandler.cs | 2 +- Toolkit.Foundation/AsyncLock.cs | 6 +- Toolkit.Foundation/CommandViewModel.cs | 4 +- Toolkit.Foundation/ComponentBuilder.cs | 6 +- Toolkit.Foundation/ComponentConfiguration.cs | 1 - .../ComponentConfigurationViewModel.cs | 6 +- Toolkit.Foundation/ComponentHost.cs | 3 +- Toolkit.Foundation/ComponentHostCollection.cs | 2 +- Toolkit.Foundation/ComponentInitializer.cs | 17 +- Toolkit.Foundation/ComponentScope.cs | 2 +- .../ComponentScopeCollection.cs | 4 +- .../ConfigurationChangedHandler.cs | 1 - Toolkit.Foundation/ConfigurationDescriptor.cs | 2 +- Toolkit.Foundation/ConfigurationFactory.cs | 4 +- Toolkit.Foundation/ConfigurationFile.cs | 4 +- .../ConfigurationInitializer.cs | 4 +- Toolkit.Foundation/ConfigurationMonitor.cs | 2 +- Toolkit.Foundation/ConfigurationReader.cs | 2 +- Toolkit.Foundation/ConfigurationSource.cs | 2 +- Toolkit.Foundation/ConfigurationValue.cs | 4 +- .../ContentTemplateDescriptor.cs | 2 +- .../ContentTemplateDescriptorProvider.cs | 2 +- Toolkit.Foundation/Create.cs | 5 +- Toolkit.Foundation/DefaultBuilder.cs | 8 +- .../DictionaryStringObjectJsonConverter.cs | 15 +- Toolkit.Foundation/Disposer.cs | 16 +- Toolkit.Foundation/HandlerWrapper.cs | 4 +- Toolkit.Foundation/ICache.cs | 6 +- .../IComponentConfigurationViewModel.cs | 3 +- Toolkit.Foundation/IComponentHost.cs | 2 +- .../IComponentScopeCollection.cs | 5 +- Toolkit.Foundation/IComponentScopeProvider.cs | 3 +- Toolkit.Foundation/IConfigurationChanged.cs | 2 +- .../IConfigurationDescriptor.cs | 2 +- Toolkit.Foundation/IConfigurationFactory.cs | 4 +- Toolkit.Foundation/IConfigurationFile.cs | 2 +- .../IConfigurationInitializer.cs | 2 +- Toolkit.Foundation/IConfigurationMonitor.cs | 5 +- Toolkit.Foundation/IConfigurationReader.cs | 2 +- Toolkit.Foundation/IConfigurationSource.cs | 2 +- Toolkit.Foundation/IContentTemplate.cs | 1 - .../IContentTemplateDescriptorProvider.cs | 2 +- Toolkit.Foundation/IDeactivating.cs | 2 +- Toolkit.Foundation/IDispatcher.cs | 2 +- Toolkit.Foundation/IDisposer.cs | 4 +- Toolkit.Foundation/IFactory.cs | 3 +- Toolkit.Foundation/IHostBuilderExtensions.cs | 6 +- Toolkit.Foundation/IMediator.cs | 16 +- Toolkit.Foundation/INavigateHandler.cs | 2 +- Toolkit.Foundation/INavigation.cs | 3 +- .../INavigationContextCollection.cs | 2 +- .../INavigationContextProvider.cs | 2 +- Toolkit.Foundation/INavigationProvider.cs | 2 +- Toolkit.Foundation/INavigationScope.cs | 5 +- .../IObservableCollectionViewModel.cs | 4 +- Toolkit.Foundation/IPrimaryConfirmation.cs | 3 +- Toolkit.Foundation/IProxyService.cs | 2 +- Toolkit.Foundation/IPublisher.cs | 3 +- Toolkit.Foundation/IRequest.cs | 6 - .../IServiceCollectionExtensions.cs | 19 +- Toolkit.Foundation/IServiceFactory.cs | 2 +- Toolkit.Foundation/IServiceScopeFactory.cs | 2 +- Toolkit.Foundation/IServiceScopeProvider.cs | 2 +- Toolkit.Foundation/ISubscriptionManager.cs | 2 +- Toolkit.Foundation/IWritableConfiguration.cs | 2 +- Toolkit.Foundation/Insert.cs | 4 +- Toolkit.Foundation/KeyAccelerator.cs | 3 +- Toolkit.Foundation/Mediator.cs | 8 +- Toolkit.Foundation/MethodInfoExtensions.cs | 2 +- Toolkit.Foundation/Navigate.cs | 10 +- Toolkit.Foundation/NavigateBack.cs | 2 +- Toolkit.Foundation/NavigateBackHandler.cs | 4 +- Toolkit.Foundation/NavigateHandler.cs | 6 +- Toolkit.Foundation/NavigatingFrom.cs | 7 - Toolkit.Foundation/Navigation.cs | 5 +- .../NavigationContextAttribute.cs | 2 +- .../NavigationContextCollection.cs | 4 +- .../NavigationContextProvider.cs | 4 +- Toolkit.Foundation/NavigationProvider.cs | 2 +- Toolkit.Foundation/NavigationScope.cs | 11 +- .../NavigationTargetAttribute.cs | 4 +- .../NotificationHandlerDelegate.cs | 2 +- .../NotificationHandlerWrapper.cs | 4 +- Toolkit.Foundation/ObjectExtensions.cs | 2 +- .../ObservableCollectionViewModel.cs | 13 +- Toolkit.Foundation/ObservableViewModel.cs | 4 +- Toolkit.Foundation/Publisher.cs | 25 +- Toolkit.Foundation/Remove.cs | 3 +- Toolkit.Foundation/Replace.cs | 3 +- Toolkit.Foundation/ServiceFactory.cs | 2 +- Toolkit.Foundation/ServiceScopeFactory.cs | 2 +- Toolkit.Foundation/ServiceScopeProvider.cs | 4 +- Toolkit.Foundation/SetupRequiredAttribute.cs | 2 +- Toolkit.Foundation/StartProcess.cs | 2 +- Toolkit.Foundation/Subscriber.cs | 6 +- Toolkit.Foundation/SubscriptionCollection.cs | 2 +- Toolkit.Foundation/SubscriptionManager.cs | 10 +- Toolkit.Foundation/TypeExtensions.cs | 2 +- Toolkit.Foundation/ValueViewModel.cs | 3 +- Toolkit.Foundation/VirtualKey.cs | 2 +- Toolkit.Foundation/WritableConfiguration.cs | 2 +- Toolkit.UI.Avalonia/AttachedBehavior.cs | 2 +- Toolkit.UI.Avalonia/ComparisonCondition.cs | 6 +- Toolkit.UI.Avalonia/ComparisonLogic.cs | 21 +- Toolkit.UI.Avalonia/ConditionAction.cs | 2 +- Toolkit.UI.Avalonia/ConditionCollection.cs | 2 +- Toolkit.UI.Avalonia/ConditionalExpression.cs | 7 +- Toolkit.UI.Avalonia/ForwardChaining.cs | 2 +- Toolkit.UI.Avalonia/ICondition.cs | 2 +- .../KeyBindingTriggerBehavior.cs | 8 +- Toolkit.UI.Avalonia/NavigateAction.cs | 8 +- Toolkit.UI.Avalonia/NavigateBackAction.cs | 2 +- Toolkit.UI.Avalonia/ParameterBinding.cs | 3 +- .../AsyncImage/AsyncImage.cs | 2 +- .../BlurBehind/BlurBehind.cs | 45 +- .../CarouselView/CarouselView.cs | 18 +- .../CarouselView/CarouselViewItem.cs | 2 +- .../FastNoiseBackgroundRenderer.cs | 16 +- .../FastNoiseRendererOptions.cs | 2 +- .../FastRendererBackground.cs | 8 +- Toolkit.UI.Controls.Avalonia/Frame/Frame.cs | 6 +- .../Helpers/ScopedBatchHelper.cs | 1 + .../NavigationView/NavigationView.cs | 4 +- .../NavigationView/NavigationViewItem.cs | 2 +- .../QrCode/Encoding/BitList.cs | 207 +++--- .../QrCode/Encoding/BitMatrix.cs | 34 +- .../QrCode/Encoding/BitMatrixBase.cs | 44 +- .../DataEncodation/CharCountIndicatorTable.cs | 80 ++- .../Encoding/DataEncodation/DataEncode.cs | 79 ++- .../QrCode/Encoding/DataEncodation/ECISet.cs | 425 ++++++------ .../DataEncodation/EightBitByteEncoder.cs | 106 ++- .../DataEncodation/EncodationStruct.cs | 16 +- .../Encoding/DataEncodation/EncoderBase.cs | 74 +-- .../InputRecognition/InputRecognise.cs | 81 ++- .../InputRecognition/ModeEncodeCheck.cs | 122 ++-- .../InputRecognition/RecognitionStruct.cs | 14 +- .../Encoding/EncodingRegion/BCHCalculator.cs | 106 +-- .../Encoding/EncodingRegion/Codeword.cs | 112 ++-- .../EncodingRegion/FormatInformation.cs | 181 +++-- .../EncodingRegion/VersionInformation.cs | 98 ++- .../Encoding/ErrorCorrection/ECGenerator.cs | 130 ++-- .../QrCode/Encoding/ErrorCorrectionLevel.cs | 10 +- .../Encoding/InputOutOfBoundaryException.cs | 16 +- .../Encoding/Masking/MaskPatternType.cs | 18 +- .../Encoding/Masking/MatrixExtensions.cs | 65 +- .../QrCode/Encoding/Masking/Pattern.cs | 12 +- .../QrCode/Encoding/Masking/Pattern0.cs | 16 +- .../QrCode/Encoding/Masking/Pattern1.cs | 16 +- .../QrCode/Encoding/Masking/Pattern2.cs | 16 +- .../QrCode/Encoding/Masking/Pattern3.cs | 16 +- .../QrCode/Encoding/Masking/Pattern4.cs | 16 +- .../QrCode/Encoding/Masking/Pattern5.cs | 16 +- .../QrCode/Encoding/Masking/Pattern6.cs | 15 +- .../QrCode/Encoding/Masking/Pattern7.cs | 16 +- .../QrCode/Encoding/Masking/PatternFactory.cs | 49 +- .../Masking/Scoring/MatrixScoreCalculator.cs | 58 +- .../Encoding/Masking/Scoring/Penalty.cs | 4 +- .../Encoding/Masking/Scoring/Penalty1.cs | 142 ++-- .../Encoding/Masking/Scoring/Penalty2.cs | 82 +-- .../Encoding/Masking/Scoring/Penalty3.cs | 252 +++---- .../Encoding/Masking/Scoring/Penalty4.cs | 48 +- .../Masking/Scoring/PenaltyFactory.cs | 41 +- .../Encoding/Masking/Scoring/PenaltyRules.cs | 10 +- .../QrCode/Encoding/MatrixPoint.cs | 24 +- .../QrCode/Encoding/MatrixRectangle.cs | 43 +- .../QrCode/Encoding/MatrixSize.cs | 26 +- .../QrCode/Encoding/MatrixStatus.cs | 8 +- .../Positioning/PositioninngPatternBuilder.cs | 16 +- .../Positioning/Stencils/AlignmentPattern.cs | 184 +++--- .../Stencils/DarkDotAtLeftBottom.cs | 20 +- .../Stencils/PatternStencilBase.cs | 38 +- .../Stencils/PositionDetectionPattern.cs | 60 +- .../Positioning/Stencils/TimingPattern.cs | 52 +- .../QrCode/Encoding/QRCodeConstantVariable.cs | 70 +- .../QrCode/Encoding/QRCodeEncode.cs | 32 +- .../QrCode/Encoding/QrCode.cs | 32 +- .../QrCode/Encoding/QrEncoder.cs | 64 +- .../Encoding/ReedSolomon/GaloisField256.cs | 186 +++--- .../ReedSolomon/GeneratorPolynomial.cs | 96 ++- .../Encoding/ReedSolomon/PolyDivideStruct.cs | 18 +- .../QrCode/Encoding/ReedSolomon/Polynomial.cs | 404 ++++++------ .../ReedSolomon/ReedSolomonEncoder.cs | 134 ++-- .../QrCode/Encoding/StateMatrix.cs | 38 +- .../QrCode/Encoding/Terminate/Terminator.cs | 122 ++-- .../QrCode/Encoding/TriStateMatrix.cs | 70 +- .../QrCode/Encoding/VersionDetail.cs | 42 +- .../Encoding/Versions/ErrorCorrectionBlock.cs | 18 +- .../Versions/ErrorCorrectionBlocks.cs | 70 +- .../QrCode/Encoding/Versions/QRCodeVersion.cs | 50 +- .../Encoding/Versions/VersionControl.cs | 211 +++--- .../Encoding/Versions/VersionControlStruct.cs | 8 +- .../QrCode/Encoding/Versions/VersionTable.cs | 616 +++++++++--------- Toolkit.UI.Controls.Avalonia/QrCode/QrCode.cs | 41 +- .../SettingsExpander/SettingsExpander.cs | 4 +- .../SpacedGrid/ISpacingDefinition.cs | 2 +- .../SpacedGrid/SpacedGrid.cs | 2 +- .../SpacedGrid/SpacingColumnDefinition.cs | 2 +- .../SpacedGrid/SpacingRowDefinition.cs | 2 +- .../Themes/ThemeResources.axaml.cs | 2 +- 206 files changed, 3106 insertions(+), 3204 deletions(-) delete mode 100644 Toolkit.Foundation/IRequest.cs delete mode 100644 Toolkit.Foundation/NavigatingFrom.cs diff --git a/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs b/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs index 42d67c0..94d3087 100644 --- a/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs +++ b/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs @@ -11,7 +11,7 @@ public class ClassicDesktopStyleApplicationHandler(INavigationContext navigation public Task Handle(Navigate args, CancellationToken cancellationToken = default) { - if (Application.Current?.ApplicationLifetime is + if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifeTime) { if (args.Template is Window window) diff --git a/Toolkit.Avalonia/ContentControlHandler.cs b/Toolkit.Avalonia/ContentControlHandler.cs index f159b64..3905680 100644 --- a/Toolkit.Avalonia/ContentControlHandler.cs +++ b/Toolkit.Avalonia/ContentControlHandler.cs @@ -4,7 +4,7 @@ using Toolkit.Foundation; namespace Toolkit.Avalonia; -public class ContentControlHandler(INavigationContext navigationContext) : +public class ContentControlHandler(INavigationContext navigationContext) : INavigateHandler { public async Task Handle(Navigate args, @@ -57,4 +57,4 @@ public class ContentControlHandler(INavigationContext navigationContext) : } } } -} +} \ No newline at end of file diff --git a/Toolkit.Avalonia/ContentDialogHandler.cs b/Toolkit.Avalonia/ContentDialogHandler.cs index 33394ae..18c20d8 100644 --- a/Toolkit.Avalonia/ContentDialogHandler.cs +++ b/Toolkit.Avalonia/ContentDialogHandler.cs @@ -3,7 +3,7 @@ using Toolkit.UI.Controls.Avalonia; namespace Toolkit.Avalonia; -public class ContentDialogHandler(IDispatcher dispatcher) : +public class ContentDialogHandler(IDispatcher dispatcher) : INavigateHandler { public async Task Handle(Navigate args, @@ -50,7 +50,7 @@ public class ContentDialogHandler(IDispatcher dispatcher) : async void HandleClosing(FluentAvalonia.UI.Controls.ContentDialog sender, FluentAvalonia.UI.Controls.ContentDialogClosingEventArgs args) { - if (args.Result == FluentAvalonia.UI.Controls.ContentDialogResult.Primary || + if (args.Result == FluentAvalonia.UI.Controls.ContentDialogResult.Primary || args.Result == FluentAvalonia.UI.Controls.ContentDialogResult.Secondary) { contentDialog.Closing -= HandleClosing; @@ -93,12 +93,12 @@ public class ContentDialogHandler(IDispatcher dispatcher) : } if (content is IActivated activated) - { + { await activated.Activated(); } } } - + contentDialog.Opened += HandleOpened; contentDialog.Closing += HandleClosing; contentDialog.PrimaryButtonClick += HandlePrimaryButtonClick; diff --git a/Toolkit.Avalonia/ContentTemplate.cs b/Toolkit.Avalonia/ContentTemplate.cs index 95b753c..5a02417 100644 --- a/Toolkit.Avalonia/ContentTemplate.cs +++ b/Toolkit.Avalonia/ContentTemplate.cs @@ -54,7 +54,7 @@ public class ContentTemplate : control.Loaded += HandleLoaded; control.Unloaded += HandleUnloaded; - + viewModelContentBinder?.Set(control); return control; @@ -67,4 +67,4 @@ public class ContentTemplate : } public bool Match(object? data) => true; -} +} \ No newline at end of file diff --git a/Toolkit.Avalonia/FrameHandler.cs b/Toolkit.Avalonia/FrameHandler.cs index ffe6781..68b0bd8 100644 --- a/Toolkit.Avalonia/FrameHandler.cs +++ b/Toolkit.Avalonia/FrameHandler.cs @@ -7,7 +7,7 @@ using Toolkit.UI.Controls.Avalonia; namespace Toolkit.Avalonia; -public class FrameHandler(INavigationContext navigationContext) : +public class FrameHandler(INavigationContext navigationContext) : INavigateHandler, INavigateBackHandler { @@ -19,13 +19,13 @@ public class FrameHandler(INavigationContext navigationContext) : frame.NavigationPageFactory ??= new NavigationPageFactory(); if (args.Template is Control control) { - void NavigatingFrom(object? sender, + void NavigatingFrom(object? sender, Control control) { - async void HandleNavigatingFrom(object? _, + async void HandleNavigatingFrom(object? _, NavigatingCancelEventArgs args) { - Dictionary results = []; + Dictionary results = []; control.RemoveHandler(Frame.NavigatingFromEvent, HandleNavigatingFrom); NavigatedFrom(sender, control, () => results); @@ -71,13 +71,13 @@ public class FrameHandler(INavigationContext navigationContext) : } } } - } + } } control.AddHandler(Frame.NavigatingFromEvent, HandleNavigatingFrom); } - void NavigatedFrom(object? sender, + void NavigatedFrom(object? sender, Control control, Func> resultCallBack) { @@ -109,9 +109,9 @@ public class FrameHandler(INavigationContext navigationContext) : contract.GetGenericArguments() is { Length: 1 } arguments) { if (contentType.GetMethods().FirstOrDefault(x => - x.Name == "NavigatedToAsync" && + x.Name == "NavigatedToAsync" && x.GetCustomAttribute() - is NavigationContextAttribute attribute && results.ContainsKey(attribute.Name)) + is NavigationContextAttribute attribute && results.ContainsKey(attribute.Name)) is MethodInfo methodInfo) { if (methodInfo.GetCustomAttribute() @@ -153,10 +153,10 @@ public class FrameHandler(INavigationContext navigationContext) : control.AddHandler(Frame.NavigatedFromEvent, HandleNavigatedFrom); } - void NavigatedTo(object? sender, + void NavigatedTo(object? sender, Control control) { - async void HandleNavigatedTo(object? _, + async void HandleNavigatedTo(object? _, NavigationEventArgs __) { control.RemoveHandler(Frame.NavigatedToEvent, HandleNavigatedTo); @@ -200,4 +200,4 @@ public class FrameHandler(INavigationContext navigationContext) : return Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/Toolkit.Avalonia/NavigationContext.cs b/Toolkit.Avalonia/NavigationContext.cs index e87034d..4ea4635 100644 --- a/Toolkit.Avalonia/NavigationContext.cs +++ b/Toolkit.Avalonia/NavigationContext.cs @@ -33,5 +33,4 @@ public class NavigationContext(INavigationContextCollection contexts) : } } } -} - +} \ No newline at end of file diff --git a/Toolkit.Avalonia/NavigationPageFactory.cs b/Toolkit.Avalonia/NavigationPageFactory.cs index e7d1657..ab16eb3 100644 --- a/Toolkit.Avalonia/NavigationPageFactory.cs +++ b/Toolkit.Avalonia/NavigationPageFactory.cs @@ -3,10 +3,10 @@ using FluentAvalonia.UI.Controls; namespace Toolkit.Avalonia; -public class NavigationPageFactory : +public class NavigationPageFactory : INavigationPageFactory { public Control? GetPage(Type srcType) => default; public Control GetPageFromObject(object target) => (Control)target; -} +} \ No newline at end of file diff --git a/Toolkit.Avalonia/SingleViewApplicationHandler.cs b/Toolkit.Avalonia/SingleViewApplicationHandler.cs index 530bade..5dc4d4f 100644 --- a/Toolkit.Avalonia/SingleViewApplicationHandler.cs +++ b/Toolkit.Avalonia/SingleViewApplicationHandler.cs @@ -25,4 +25,4 @@ public class SingleViewApplicationHandler(INavigationContext navigationContext) return Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/AsyncLock.cs b/Toolkit.Foundation/AsyncLock.cs index 56dc549..5646f49 100644 --- a/Toolkit.Foundation/AsyncLock.cs +++ b/Toolkit.Foundation/AsyncLock.cs @@ -2,8 +2,8 @@ namespace Toolkit.Foundation; -public class AsyncLock(int initial = 1, - int maximum = 1) : +public class AsyncLock(int initial = 1, + int maximum = 1) : IDisposable { private readonly SemaphoreSlim semaphore = new(initial, maximum); @@ -20,4 +20,4 @@ public class AsyncLock(int initial = 1, await semaphore.WaitAsync(); return this; } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/CommandViewModel.cs b/Toolkit.Foundation/CommandViewModel.cs index 20e3596..8727673 100644 --- a/Toolkit.Foundation/CommandViewModel.cs +++ b/Toolkit.Foundation/CommandViewModel.cs @@ -10,9 +10,9 @@ public partial class CommandViewModel(IServiceProvider provider, IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) { - public IRelayCommand InvokeCommand => + public IRelayCommand InvokeCommand => new AsyncRelayCommand(InvokeAsync); - protected virtual Task InvokeAsync() => + protected virtual Task InvokeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/Toolkit.Foundation/ComponentBuilder.cs b/Toolkit.Foundation/ComponentBuilder.cs index 35292c5..dde604e 100644 --- a/Toolkit.Foundation/ComponentBuilder.cs +++ b/Toolkit.Foundation/ComponentBuilder.cs @@ -4,7 +4,7 @@ using Microsoft.Extensions.Hosting; namespace Toolkit.Foundation; -public class ComponentBuilder : +public class ComponentBuilder : IComponentBuilder { private readonly IHostBuilder hostBuilder; @@ -65,7 +65,7 @@ public class ComponentBuilder : } public IComponentBuilder AddConfiguration(string section, - TConfiguration? configuration = null) + TConfiguration? configuration = null) where TConfiguration : ComponentConfiguration, new() { @@ -88,7 +88,7 @@ public class ComponentBuilder : return this; } - public IComponentBuilder AddConfiguration(string section) + public IComponentBuilder AddConfiguration(string section) where TConfiguration : ComponentConfiguration, new() { AddConfiguration(section, null); diff --git a/Toolkit.Foundation/ComponentConfiguration.cs b/Toolkit.Foundation/ComponentConfiguration.cs index f16b093..1bf75b0 100644 --- a/Toolkit.Foundation/ComponentConfiguration.cs +++ b/Toolkit.Foundation/ComponentConfiguration.cs @@ -2,5 +2,4 @@ public record ComponentConfiguration { - } \ No newline at end of file diff --git a/Toolkit.Foundation/ComponentConfigurationViewModel.cs b/Toolkit.Foundation/ComponentConfigurationViewModel.cs index 76c09da..b74dd71 100644 --- a/Toolkit.Foundation/ComponentConfigurationViewModel.cs +++ b/Toolkit.Foundation/ComponentConfigurationViewModel.cs @@ -8,17 +8,16 @@ public partial class ComponentConfigurationViewModel> where TConfiguration : class { - public ComponentConfigurationViewModel(IServiceProvider provider, + public ComponentConfigurationViewModel(IServiceProvider provider, IServiceFactory factory, IMediator mediator, IPublisher publisher, - ISubscriber subscriber, + ISubscriber subscriber, IDisposer disposer, THeader header, TDescription description, TAction action) : base(provider, factory, mediator, publisher, subscriber, disposer) { - } public Task Handle(Changed args, @@ -58,6 +57,7 @@ public partial class ComponentConfigurationViewModel args, CancellationToken cancellationToken = default) { diff --git a/Toolkit.Foundation/ComponentHost.cs b/Toolkit.Foundation/ComponentHost.cs index 907a694..686b6ef 100644 --- a/Toolkit.Foundation/ComponentHost.cs +++ b/Toolkit.Foundation/ComponentHost.cs @@ -15,7 +15,6 @@ public class ComponentHost(IServiceProvider services, public void Dispose() { - } public async Task StartAsync(CancellationToken cancellationToken = default) @@ -38,4 +37,4 @@ public class ComponentHost(IServiceProvider services, await service.StopAsync(cancellationToken); } } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/ComponentHostCollection.cs b/Toolkit.Foundation/ComponentHostCollection.cs index c55e2d7..08f2d7f 100644 --- a/Toolkit.Foundation/ComponentHostCollection.cs +++ b/Toolkit.Foundation/ComponentHostCollection.cs @@ -18,4 +18,4 @@ public class ComponentHostCollection : IEnumerator IEnumerable.GetEnumerator() => hosts.GetEnumerator(); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/ComponentInitializer.cs b/Toolkit.Foundation/ComponentInitializer.cs index be18151..3221117 100644 --- a/Toolkit.Foundation/ComponentInitializer.cs +++ b/Toolkit.Foundation/ComponentInitializer.cs @@ -1,5 +1,4 @@ -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; namespace Toolkit.Foundation; @@ -17,22 +16,22 @@ public class ComponentInitializer(IEnumerable components, IComponentBuilder builder = component.Create(); builder.AddServices(services => { - services.AddTransient(_ => + services.AddTransient(_ => provider.GetRequiredService>()); services.AddTransient(_ => provider.GetRequiredService>()); - services.AddScoped(_ => + services.AddScoped(_ => provider.GetRequiredService()); - - services.AddScoped(_ => + + services.AddScoped(_ => provider.GetRequiredService()); - services.AddScoped(_ => + services.AddScoped(_ => provider.GetRequiredService()); - services.AddTransient(_ => + services.AddTransient(_ => provider.GetRequiredService()); services.AddRange(typedServices.Services); @@ -49,4 +48,4 @@ public class ComponentInitializer(IEnumerable components, await host.StartAsync(); } } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/ComponentScope.cs b/Toolkit.Foundation/ComponentScope.cs index c403c54..1c50570 100644 --- a/Toolkit.Foundation/ComponentScope.cs +++ b/Toolkit.Foundation/ComponentScope.cs @@ -1,3 +1,3 @@ namespace Toolkit.Foundation; -public record ComponentScope(string Name); +public record ComponentScope(string Name); \ No newline at end of file diff --git a/Toolkit.Foundation/ComponentScopeCollection.cs b/Toolkit.Foundation/ComponentScopeCollection.cs index b9a42a7..e9d40a2 100644 --- a/Toolkit.Foundation/ComponentScopeCollection.cs +++ b/Toolkit.Foundation/ComponentScopeCollection.cs @@ -1,4 +1,4 @@ namespace Toolkit.Foundation; -public class ComponentScopeCollection : List, - IComponentScopeCollection; +public class ComponentScopeCollection : List, + IComponentScopeCollection; \ No newline at end of file diff --git a/Toolkit.Foundation/ConfigurationChangedHandler.cs b/Toolkit.Foundation/ConfigurationChangedHandler.cs index de6d8cf..e142bcc 100644 --- a/Toolkit.Foundation/ConfigurationChangedHandler.cs +++ b/Toolkit.Foundation/ConfigurationChangedHandler.cs @@ -12,7 +12,6 @@ public class ConfigurationChangedHandler(ConfigurationVa { if (configurationValue.TryUpdate(configuration, out TValue value)) { - } } diff --git a/Toolkit.Foundation/ConfigurationDescriptor.cs b/Toolkit.Foundation/ConfigurationDescriptor.cs index 347195c..79d0079 100644 --- a/Toolkit.Foundation/ConfigurationDescriptor.cs +++ b/Toolkit.Foundation/ConfigurationDescriptor.cs @@ -9,4 +9,4 @@ public class ConfigurationDescriptor(string section, public TConfiguration Value => reader.Read(); public string Section => section; -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/ConfigurationFactory.cs b/Toolkit.Foundation/ConfigurationFactory.cs index d41debd..cbf981b 100644 --- a/Toolkit.Foundation/ConfigurationFactory.cs +++ b/Toolkit.Foundation/ConfigurationFactory.cs @@ -1,9 +1,9 @@ namespace Toolkit.Foundation; public class ConfigurationFactory(Func factory) : - IConfigurationFactory + IConfigurationFactory where TConfiguration : class { public object Create() => factory.Invoke(); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/ConfigurationFile.cs b/Toolkit.Foundation/ConfigurationFile.cs index 50fcd4c..c0c19d4 100644 --- a/Toolkit.Foundation/ConfigurationFile.cs +++ b/Toolkit.Foundation/ConfigurationFile.cs @@ -2,10 +2,10 @@ namespace Toolkit.Foundation; -public class ConfigurationFile(IFileInfo fileInfo) : +public class ConfigurationFile(IFileInfo fileInfo) : IConfigurationFile where TConfiguration : class { public IFileInfo FileInfo => fileInfo; -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/ConfigurationInitializer.cs b/Toolkit.Foundation/ConfigurationInitializer.cs index f969c89..b7240eb 100644 --- a/Toolkit.Foundation/ConfigurationInitializer.cs +++ b/Toolkit.Foundation/ConfigurationInitializer.cs @@ -1,6 +1,6 @@ namespace Toolkit.Foundation; -public class ConfigurationInitializer(IConfigurationReader reader, +public class ConfigurationInitializer(IConfigurationReader reader, IConfigurationWriter writer, IConfigurationFactory factory, IPublisher publisher) : @@ -22,4 +22,4 @@ public class ConfigurationInitializer(IConfigurationReader(configuration)); } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/ConfigurationMonitor.cs b/Toolkit.Foundation/ConfigurationMonitor.cs index cfe6228..df9b908 100644 --- a/Toolkit.Foundation/ConfigurationMonitor.cs +++ b/Toolkit.Foundation/ConfigurationMonitor.cs @@ -43,4 +43,4 @@ public class ConfigurationMonitor(IConfigurationFile(IConfigurationSource(IConfigurationFile(currentNode[segments[lastIndex]], + value = JsonSerializer.Deserialize(currentNode[segments[lastIndex]], serializerOptions ?? defaultSerializerOptions()); return true; } diff --git a/Toolkit.Foundation/ConfigurationValue.cs b/Toolkit.Foundation/ConfigurationValue.cs index 4efe3ab..2eff43f 100644 --- a/Toolkit.Foundation/ConfigurationValue.cs +++ b/Toolkit.Foundation/ConfigurationValue.cs @@ -1,12 +1,12 @@ namespace Toolkit.Foundation; public class ConfigurationValue(Func> changed) - where TValue : + where TValue : class, new() { private TValue? currentValue; - public bool TryUpdate(TConfiguration configuration, + public bool TryUpdate(TConfiguration configuration, out TValue value) { TValue newValue = new(); diff --git a/Toolkit.Foundation/ContentTemplateDescriptor.cs b/Toolkit.Foundation/ContentTemplateDescriptor.cs index 427d71e..565fd18 100644 --- a/Toolkit.Foundation/ContentTemplateDescriptor.cs +++ b/Toolkit.Foundation/ContentTemplateDescriptor.cs @@ -1,7 +1,7 @@ namespace Toolkit.Foundation; public class ContentTemplateDescriptor(object key, - Type viewModelType, + Type viewModelType, Type viewType, params object[]? parameters) : IContentTemplateDescriptor diff --git a/Toolkit.Foundation/ContentTemplateDescriptorProvider.cs b/Toolkit.Foundation/ContentTemplateDescriptorProvider.cs index fef28cb..fd30799 100644 --- a/Toolkit.Foundation/ContentTemplateDescriptorProvider.cs +++ b/Toolkit.Foundation/ContentTemplateDescriptorProvider.cs @@ -13,4 +13,4 @@ public class ContentTemplateDescriptorProvider(IEnumerable(TValue Value); @@ -8,6 +7,6 @@ public record Create public static Create As(TValue value) => new(value); - public static Create As() where TValue : new() => + public static Create As() where TValue : new() => new(new TValue()); } \ No newline at end of file diff --git a/Toolkit.Foundation/DefaultBuilder.cs b/Toolkit.Foundation/DefaultBuilder.cs index 55eb938..2d0309f 100644 --- a/Toolkit.Foundation/DefaultBuilder.cs +++ b/Toolkit.Foundation/DefaultBuilder.cs @@ -144,13 +144,13 @@ public static class Test services.AddTransient(provider => provider.GetRequiredKeyedService>(section).Value); } - }); return builder; } } -public class DefaultBuilder : + +public class DefaultBuilder : HostBuilder { public static IHostBuilder Create() @@ -166,7 +166,7 @@ public class DefaultBuilder : services.AddScoped(provider => new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!))); - services.AddSingleton(); services.AddScoped(); @@ -211,4 +211,4 @@ public class DefaultBuilder : services.AddHostedService(); }); } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/DictionaryStringObjectJsonConverter.cs b/Toolkit.Foundation/DictionaryStringObjectJsonConverter.cs index 595fb6e..8ab6df9 100644 --- a/Toolkit.Foundation/DictionaryStringObjectJsonConverter.cs +++ b/Toolkit.Foundation/DictionaryStringObjectJsonConverter.cs @@ -3,15 +3,15 @@ using System.Text.Json.Serialization; namespace Toolkit.Foundation; -public class DictionaryStringObjectJsonConverter : +public class DictionaryStringObjectJsonConverter : JsonConverter> { public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Dictionary) || typeToConvert == typeof(Dictionary); - public override Dictionary Read(ref Utf8JsonReader reader, - Type typeToConvert, + public override Dictionary Read(ref Utf8JsonReader reader, + Type typeToConvert, JsonSerializerOptions options) { Dictionary dictionary = []; @@ -42,7 +42,7 @@ public class DictionaryStringObjectJsonConverter : public override void Write(Utf8JsonWriter writer, Dictionary value, - JsonSerializerOptions options) => + JsonSerializerOptions options) => JsonSerializer.Serialize(writer, (IDictionary)value, options); private object? ExtractValue(ref Utf8JsonReader reader, @@ -56,20 +56,26 @@ public class DictionaryStringObjectJsonConverter : return date; } return reader.GetString(); + case JsonTokenType.False: return false; + case JsonTokenType.True: return true; + case JsonTokenType.Null: return null; + case JsonTokenType.Number: if (reader.TryGetInt64(out var result)) { return result; } return reader.GetDecimal(); + case JsonTokenType.StartObject: return Read(ref reader, null!, options); + case JsonTokenType.StartArray: List list = []; while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) @@ -77,6 +83,7 @@ public class DictionaryStringObjectJsonConverter : list.Add(ExtractValue(ref reader, options)); } return list; + default: return default; } diff --git a/Toolkit.Foundation/Disposer.cs b/Toolkit.Foundation/Disposer.cs index 093120c..c7d03fc 100644 --- a/Toolkit.Foundation/Disposer.cs +++ b/Toolkit.Foundation/Disposer.cs @@ -1,10 +1,10 @@ -using System.Reactive.Disposables; -using System.Collections; +using System.Collections; using System.Collections.Concurrent; +using System.Reactive.Disposables; namespace Toolkit.Foundation; -public class Disposer : +public class Disposer : IDisposer { private readonly ConcurrentDictionary subjects = []; @@ -45,10 +45,10 @@ public class Disposer : } } - public TDisposable Replace(object subject, - IDisposable disposer, + public TDisposable Replace(object subject, + IDisposable disposer, TDisposable replacement) - where TDisposable : + where TDisposable : IDisposable { CompositeDisposable disposables = subjects.GetOrAdd(subject, args => new CompositeDisposable()); @@ -61,7 +61,7 @@ public class Disposer : return replacement; } - public void Remove(object subject, + public void Remove(object subject, IDisposable disposer) { CompositeDisposable disposables = subjects.GetOrAdd(subject, args => new CompositeDisposable()); @@ -78,4 +78,4 @@ public class Disposer : disposables?.Dispose(); } } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/HandlerWrapper.cs b/Toolkit.Foundation/HandlerWrapper.cs index 9cc8ae0..e1f464d 100644 --- a/Toolkit.Foundation/HandlerWrapper.cs +++ b/Toolkit.Foundation/HandlerWrapper.cs @@ -4,10 +4,10 @@ public class HandlerWrapper(IHandler h IEnumerable> pipelineBehaviours) where TRequest : class { - private readonly IEnumerable> pipelineBehaviours = + private readonly IEnumerable> pipelineBehaviours = pipelineBehaviours.Reverse(); - public async Task Handle(TRequest request, + public async Task Handle(TRequest request, CancellationToken cancellationToken) { HandlerDelegate currentHandler = handler.Handle; diff --git a/Toolkit.Foundation/ICache.cs b/Toolkit.Foundation/ICache.cs index de9420c..2143da6 100644 --- a/Toolkit.Foundation/ICache.cs +++ b/Toolkit.Foundation/ICache.cs @@ -14,10 +14,10 @@ public interface ICache : IEnumerable> where TKey : notnull - where TValue : + where TValue : notnull { - void Add(TKey key, + void Add(TKey key, TValue value); void Clear(); @@ -27,4 +27,4 @@ public interface ICache : bool Remove(TKey key); bool TryGetValue(TKey key, out TValue? value); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/IComponentConfigurationViewModel.cs b/Toolkit.Foundation/IComponentConfigurationViewModel.cs index f7eb4cd..56ff184 100644 --- a/Toolkit.Foundation/IComponentConfigurationViewModel.cs +++ b/Toolkit.Foundation/IComponentConfigurationViewModel.cs @@ -2,5 +2,4 @@ public interface IComponentConfigurationViewModel { - -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/IComponentHost.cs b/Toolkit.Foundation/IComponentHost.cs index 3990386..260a74c 100644 --- a/Toolkit.Foundation/IComponentHost.cs +++ b/Toolkit.Foundation/IComponentHost.cs @@ -2,7 +2,7 @@ namespace Toolkit.Foundation; -public interface IComponentHost : +public interface IComponentHost : IHost { ComponentConfiguration? Configuration { get; } diff --git a/Toolkit.Foundation/IComponentScopeCollection.cs b/Toolkit.Foundation/IComponentScopeCollection.cs index 1dc26fa..d198369 100644 --- a/Toolkit.Foundation/IComponentScopeCollection.cs +++ b/Toolkit.Foundation/IComponentScopeCollection.cs @@ -1,5 +1,4 @@ namespace Toolkit.Foundation; -public interface IComponentScopeCollection : - IList; - +public interface IComponentScopeCollection : + IList; \ No newline at end of file diff --git a/Toolkit.Foundation/IComponentScopeProvider.cs b/Toolkit.Foundation/IComponentScopeProvider.cs index 220446e..062cca8 100644 --- a/Toolkit.Foundation/IComponentScopeProvider.cs +++ b/Toolkit.Foundation/IComponentScopeProvider.cs @@ -3,5 +3,4 @@ public interface IComponentScopeProvider { ComponentScopeDescriptor? Get(string key); -} - +} \ No newline at end of file diff --git a/Toolkit.Foundation/IConfigurationChanged.cs b/Toolkit.Foundation/IConfigurationChanged.cs index f00fe22..d0f8eee 100644 --- a/Toolkit.Foundation/IConfigurationChanged.cs +++ b/Toolkit.Foundation/IConfigurationChanged.cs @@ -1,4 +1,4 @@ namespace Toolkit.Foundation; -public interface IConfigurationChanged : +public interface IConfigurationChanged : IInitializer; \ No newline at end of file diff --git a/Toolkit.Foundation/IConfigurationDescriptor.cs b/Toolkit.Foundation/IConfigurationDescriptor.cs index cad357a..ac80a0a 100644 --- a/Toolkit.Foundation/IConfigurationDescriptor.cs +++ b/Toolkit.Foundation/IConfigurationDescriptor.cs @@ -7,4 +7,4 @@ public interface IConfigurationDescriptor TConfiguration Value { get; } string Section { get; } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/IConfigurationFactory.cs b/Toolkit.Foundation/IConfigurationFactory.cs index 916812a..52702dd 100644 --- a/Toolkit.Foundation/IConfigurationFactory.cs +++ b/Toolkit.Foundation/IConfigurationFactory.cs @@ -1,7 +1,7 @@ namespace Toolkit.Foundation; -public interface IConfigurationFactory - where TConfiguration : +public interface IConfigurationFactory + where TConfiguration : class { object Create(); diff --git a/Toolkit.Foundation/IConfigurationFile.cs b/Toolkit.Foundation/IConfigurationFile.cs index baec244..80fb81a 100644 --- a/Toolkit.Foundation/IConfigurationFile.cs +++ b/Toolkit.Foundation/IConfigurationFile.cs @@ -7,4 +7,4 @@ public interface IConfigurationFile class { IFileInfo FileInfo { get; } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/IConfigurationInitializer.cs b/Toolkit.Foundation/IConfigurationInitializer.cs index 597609f..a573f89 100644 --- a/Toolkit.Foundation/IConfigurationInitializer.cs +++ b/Toolkit.Foundation/IConfigurationInitializer.cs @@ -1,6 +1,6 @@ namespace Toolkit.Foundation; -public interface IConfigurationInitializer +public interface IConfigurationInitializer where TConfiguration : class { diff --git a/Toolkit.Foundation/IConfigurationMonitor.cs b/Toolkit.Foundation/IConfigurationMonitor.cs index 3a18e99..c3aafac 100644 --- a/Toolkit.Foundation/IConfigurationMonitor.cs +++ b/Toolkit.Foundation/IConfigurationMonitor.cs @@ -1,9 +1,8 @@ - -using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Hosting; namespace Toolkit.Foundation; -public interface IConfigurationMonitor : +public interface IConfigurationMonitor : IHostedService where TConfiguration : class; \ No newline at end of file diff --git a/Toolkit.Foundation/IConfigurationReader.cs b/Toolkit.Foundation/IConfigurationReader.cs index 7e2f5e5..e703305 100644 --- a/Toolkit.Foundation/IConfigurationReader.cs +++ b/Toolkit.Foundation/IConfigurationReader.cs @@ -7,4 +7,4 @@ public interface IConfigurationReader bool TryRead(out TConfiguration? configuration); TConfiguration Read(); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/IConfigurationSource.cs b/Toolkit.Foundation/IConfigurationSource.cs index 626e754..13d46f1 100644 --- a/Toolkit.Foundation/IConfigurationSource.cs +++ b/Toolkit.Foundation/IConfigurationSource.cs @@ -9,4 +9,4 @@ public interface IConfigurationSource void Set(TConfiguration value); void Set(object value); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/IContentTemplate.cs b/Toolkit.Foundation/IContentTemplate.cs index 6eb73fb..b3ce145 100644 --- a/Toolkit.Foundation/IContentTemplate.cs +++ b/Toolkit.Foundation/IContentTemplate.cs @@ -2,5 +2,4 @@ public interface IContentTemplate { - } \ No newline at end of file diff --git a/Toolkit.Foundation/IContentTemplateDescriptorProvider.cs b/Toolkit.Foundation/IContentTemplateDescriptorProvider.cs index 9475494..aa0da56 100644 --- a/Toolkit.Foundation/IContentTemplateDescriptorProvider.cs +++ b/Toolkit.Foundation/IContentTemplateDescriptorProvider.cs @@ -3,4 +3,4 @@ public interface IContentTemplateDescriptorProvider { IContentTemplateDescriptor? Get(object key); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/IDeactivating.cs b/Toolkit.Foundation/IDeactivating.cs index 032b5fd..0c9a25a 100644 --- a/Toolkit.Foundation/IDeactivating.cs +++ b/Toolkit.Foundation/IDeactivating.cs @@ -5,7 +5,7 @@ public interface IDeactivating Task Deactivating(); } -public interface IDeactivating +public interface IDeactivating { Task Deactivating(); } \ No newline at end of file diff --git a/Toolkit.Foundation/IDispatcher.cs b/Toolkit.Foundation/IDispatcher.cs index d85772d..7a4f6ce 100644 --- a/Toolkit.Foundation/IDispatcher.cs +++ b/Toolkit.Foundation/IDispatcher.cs @@ -3,4 +3,4 @@ public interface IDispatcher { Task InvokeAsync(Action action); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/IDisposer.cs b/Toolkit.Foundation/IDisposer.cs index aff06d6..212f697 100644 --- a/Toolkit.Foundation/IDisposer.cs +++ b/Toolkit.Foundation/IDisposer.cs @@ -2,14 +2,14 @@ public interface IDisposer { - void Add(object subject, + void Add(object subject, params object[] objects); TDisposable Replace(object subject, IDisposable disposer, TDisposable replacement) where TDisposable : IDisposable; - void Remove(object subject, + void Remove(object subject, IDisposable disposer); void Dispose(object subject); diff --git a/Toolkit.Foundation/IFactory.cs b/Toolkit.Foundation/IFactory.cs index f6837e1..f17c2ed 100644 --- a/Toolkit.Foundation/IFactory.cs +++ b/Toolkit.Foundation/IFactory.cs @@ -5,8 +5,7 @@ public interface IFactory TService? Create(TParameter value); } - public interface IFactory { TService? Create(); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/IHostBuilderExtensions.cs b/Toolkit.Foundation/IHostBuilderExtensions.cs index cb72ce3..bf3ed92 100644 --- a/Toolkit.Foundation/IHostBuilderExtensions.cs +++ b/Toolkit.Foundation/IHostBuilderExtensions.cs @@ -4,16 +4,16 @@ namespace Toolkit.Foundation; public static class IHostBuilderExtensions { - public static IHostBuilder UseContentRoot(this IHostBuilder hostBuilder, + public static IHostBuilder UseContentRoot(this IHostBuilder hostBuilder, string contentRoot, bool createDirectory) { if (createDirectory) - { + { Directory.CreateDirectory(contentRoot); } hostBuilder.UseContentRoot(contentRoot); return hostBuilder; } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/IMediator.cs b/Toolkit.Foundation/IMediator.cs index e87d071..df84e29 100644 --- a/Toolkit.Foundation/IMediator.cs +++ b/Toolkit.Foundation/IMediator.cs @@ -1,11 +1,9 @@ -namespace Toolkit.Foundation; - -public interface IMediator + +namespace Toolkit.Foundation { - Task Handle(TRequest request, - CancellationToken cancellationToken = default) - where TRequest : notnull; - - Task Handle(object request, CancellationToken - cancellationToken = default); + public interface IMediator + { + Task Handle(object message, CancellationToken cancellationToken = default); + Task Handle(TRequest request, CancellationToken cancellationToken = default) where TRequest : notnull; + } } \ No newline at end of file diff --git a/Toolkit.Foundation/INavigateHandler.cs b/Toolkit.Foundation/INavigateHandler.cs index 199faad..0cc5571 100644 --- a/Toolkit.Foundation/INavigateHandler.cs +++ b/Toolkit.Foundation/INavigateHandler.cs @@ -4,4 +4,4 @@ public interface INavigateHandler; public interface INavigateHandler : INotificationHandler>, - INavigateHandler; + INavigateHandler; \ No newline at end of file diff --git a/Toolkit.Foundation/INavigation.cs b/Toolkit.Foundation/INavigation.cs index 4298399..3d63189 100644 --- a/Toolkit.Foundation/INavigation.cs +++ b/Toolkit.Foundation/INavigation.cs @@ -3,5 +3,4 @@ public interface INavigation { Type Type { get; set; } -} - +} \ No newline at end of file diff --git a/Toolkit.Foundation/INavigationContextCollection.cs b/Toolkit.Foundation/INavigationContextCollection.cs index e867d10..9bdae1f 100644 --- a/Toolkit.Foundation/INavigationContextCollection.cs +++ b/Toolkit.Foundation/INavigationContextCollection.cs @@ -1,4 +1,4 @@ namespace Toolkit.Foundation; public interface INavigationContextCollection : - IDictionary; + IDictionary; \ No newline at end of file diff --git a/Toolkit.Foundation/INavigationContextProvider.cs b/Toolkit.Foundation/INavigationContextProvider.cs index 08ddc03..fb44955 100644 --- a/Toolkit.Foundation/INavigationContextProvider.cs +++ b/Toolkit.Foundation/INavigationContextProvider.cs @@ -4,6 +4,6 @@ public interface INavigationContextProvider { object? Get(object key); - bool TryGet(object key, + bool TryGet(object key, out object? value); } \ No newline at end of file diff --git a/Toolkit.Foundation/INavigationProvider.cs b/Toolkit.Foundation/INavigationProvider.cs index b6255cb..227bb91 100644 --- a/Toolkit.Foundation/INavigationProvider.cs +++ b/Toolkit.Foundation/INavigationProvider.cs @@ -3,4 +3,4 @@ public interface INavigationProvider { INavigation? Get(Type type); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/INavigationScope.cs b/Toolkit.Foundation/INavigationScope.cs index 6fa029b..bdab8e8 100644 --- a/Toolkit.Foundation/INavigationScope.cs +++ b/Toolkit.Foundation/INavigationScope.cs @@ -2,9 +2,8 @@ public interface INavigationScope { - Task NavigateAsync(string route, object? sender = null, object? context = null, + Task NavigateAsync(string route, object? sender = null, object? context = null, EventHandler? navigated = null, object[]? parameters = null, CancellationToken cancellationToken = default); Task NavigateBackAsync(object? context, CancellationToken cancellationToken = default); -} - +} \ No newline at end of file diff --git a/Toolkit.Foundation/IObservableCollectionViewModel.cs b/Toolkit.Foundation/IObservableCollectionViewModel.cs index 9e12d1e..9e4de44 100644 --- a/Toolkit.Foundation/IObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/IObservableCollectionViewModel.cs @@ -1,4 +1,4 @@ namespace Toolkit.Foundation; -public interface IObservableCollectionViewModel : - IObservableViewModel; +public interface IObservableCollectionViewModel : + IObservableViewModel; \ No newline at end of file diff --git a/Toolkit.Foundation/IPrimaryConfirmation.cs b/Toolkit.Foundation/IPrimaryConfirmation.cs index 975ed5c..efce945 100644 --- a/Toolkit.Foundation/IPrimaryConfirmation.cs +++ b/Toolkit.Foundation/IPrimaryConfirmation.cs @@ -8,5 +8,4 @@ public interface IPrimaryConfirmation public interface IConfirmation { Task Confirm(); -} - +} \ No newline at end of file diff --git a/Toolkit.Foundation/IProxyService.cs b/Toolkit.Foundation/IProxyService.cs index bb1747e..4689185 100644 --- a/Toolkit.Foundation/IProxyService.cs +++ b/Toolkit.Foundation/IProxyService.cs @@ -3,4 +3,4 @@ public interface IProxyService { TService Proxy { get; } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/IPublisher.cs b/Toolkit.Foundation/IPublisher.cs index 4f0feec..36d973a 100644 --- a/Toolkit.Foundation/IPublisher.cs +++ b/Toolkit.Foundation/IPublisher.cs @@ -19,6 +19,7 @@ public interface IPublisher object key, CancellationToken cancellationToken = default) where TMessage : notnull; + Task PublishUI(object key, CancellationToken cancellationToken = default) where TMessage : new(); @@ -42,4 +43,4 @@ public interface IPublisher where TMessage : new(); public Task Publish(object message, CancellationToken cancellationToken = default); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/IRequest.cs b/Toolkit.Foundation/IRequest.cs deleted file mode 100644 index 33f891a..0000000 --- a/Toolkit.Foundation/IRequest.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Toolkit.Foundation; - -public interface IRequest : - IMessage; - -public interface IRequest : IRequest; \ No newline at end of file diff --git a/Toolkit.Foundation/IServiceCollectionExtensions.cs b/Toolkit.Foundation/IServiceCollectionExtensions.cs index 20eeebb..bee5f85 100644 --- a/Toolkit.Foundation/IServiceCollectionExtensions.cs +++ b/Toolkit.Foundation/IServiceCollectionExtensions.cs @@ -1,5 +1,4 @@ -using Microsoft.Extensions.Configuration.Json; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders.Physical; @@ -29,7 +28,7 @@ public static class IServiceCollectionExtensions } public static IServiceCollection AddComponent(this IServiceCollection services) - where TComponent : class, + where TComponent : class, IComponent { services.AddTransient(); @@ -48,13 +47,13 @@ public static class IServiceCollectionExtensions return services; } - public static IServiceCollection AddConfiguration(this IServiceCollection services, + public static IServiceCollection AddConfiguration(this IServiceCollection services, string section) where TConfiguration : class, new() => services.AddConfiguration(section, "Settings.json", null); public static IServiceCollection AddConfiguration(this IServiceCollection services) - where TConfiguration : class, new() => + where TConfiguration : class, new() => services.AddConfiguration(typeof(TConfiguration).Name, "Settings.json", null); public static IServiceCollection AddConfiguration(this IServiceCollection services, @@ -85,7 +84,7 @@ public static class IServiceCollectionExtensions public static IServiceCollection AddConfiguration(this IServiceCollection services, object configuration) - where TConfiguration : class, new() => + where TConfiguration : class, new() => services.AddConfiguration(configuration.GetType().Name, "Settings.json", (TConfiguration?)configuration); @@ -130,7 +129,7 @@ public static class IServiceCollectionExtensions services.TryAddKeyedTransient>(section, (provider, key) => new ConfigurationWriter(provider.GetRequiredKeyedService>(key))); - services.TryAddKeyedTransient>(section, (provider, key) => + services.TryAddKeyedTransient>(section, (provider, key) => new ConfigurationFactory(() => defaultConfiguration ?? new TConfiguration())); services.AddTransient>(provider => @@ -147,7 +146,7 @@ public static class IServiceCollectionExtensions services.TryAddKeyedTransient>(section, (provider, key) => new ConfigurationDescriptor(section, provider.GetRequiredKeyedService>(key))); - services.AddTransient(provider => + services.AddTransient(provider => provider.GetRequiredKeyedService>(section)); services.AddTransient(provider => @@ -258,7 +257,7 @@ public static class IServiceCollectionExtensions key ??= viewModelType.Name.Replace("ViewModel", ""); - services.AddTransient(viewModelType, provider => + services.AddTransient(viewModelType, provider => provider.GetRequiredService().Create(parameters)!); services.AddTransient(viewType); @@ -268,7 +267,7 @@ public static class IServiceCollectionExtensions services.AddKeyedTransient(viewType, key); - services.AddTransient(provider => + services.AddTransient(provider => new ContentTemplateDescriptor(key, viewModelType, viewType, parameters)); return services; diff --git a/Toolkit.Foundation/IServiceFactory.cs b/Toolkit.Foundation/IServiceFactory.cs index be6aee8..c69fb20 100644 --- a/Toolkit.Foundation/IServiceFactory.cs +++ b/Toolkit.Foundation/IServiceFactory.cs @@ -5,4 +5,4 @@ public interface IServiceFactory object Create(Type type, params object?[]? parameters); TService Create(params object?[]? parameters); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/IServiceScopeFactory.cs b/Toolkit.Foundation/IServiceScopeFactory.cs index dc3bae6..891139d 100644 --- a/Toolkit.Foundation/IServiceScopeFactory.cs +++ b/Toolkit.Foundation/IServiceScopeFactory.cs @@ -3,4 +3,4 @@ public interface IServiceScopeFactory { TService? Create(params object?[] parameters); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/IServiceScopeProvider.cs b/Toolkit.Foundation/IServiceScopeProvider.cs index d0aa9ab..46dff06 100644 --- a/Toolkit.Foundation/IServiceScopeProvider.cs +++ b/Toolkit.Foundation/IServiceScopeProvider.cs @@ -5,4 +5,4 @@ namespace Toolkit.Foundation; public interface IServiceScopeProvider { bool TryGet(TService service, out IServiceScope? serviceScope); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/ISubscriptionManager.cs b/Toolkit.Foundation/ISubscriptionManager.cs index 71177e0..c6866b8 100644 --- a/Toolkit.Foundation/ISubscriptionManager.cs +++ b/Toolkit.Foundation/ISubscriptionManager.cs @@ -7,4 +7,4 @@ public interface ISubscriptionManager void Remove(object subscriber); void Add(object subscriber); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/IWritableConfiguration.cs b/Toolkit.Foundation/IWritableConfiguration.cs index 0c537e1..6b261d1 100644 --- a/Toolkit.Foundation/IWritableConfiguration.cs +++ b/Toolkit.Foundation/IWritableConfiguration.cs @@ -5,4 +5,4 @@ public interface IWritableConfiguration class { void Write(Action updateDelegate); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/Insert.cs b/Toolkit.Foundation/Insert.cs index a5ac4d7..08f8301 100644 --- a/Toolkit.Foundation/Insert.cs +++ b/Toolkit.Foundation/Insert.cs @@ -7,6 +7,6 @@ public record Insert public static Insert As(int index, TValue value) => new(index, value); - public static Insert As(int index) where TValue : new() => + public static Insert As(int index) where TValue : new() => new(index, new TValue()); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/KeyAccelerator.cs b/Toolkit.Foundation/KeyAccelerator.cs index 0d3d045..2b7f265 100644 --- a/Toolkit.Foundation/KeyAccelerator.cs +++ b/Toolkit.Foundation/KeyAccelerator.cs @@ -1,5 +1,4 @@ namespace Toolkit.Foundation; public record KeyAccelerator(VirtualKey Key, - VirtualKey[]? Modifiers = null) : - IRequest; + VirtualKey[]? Modifiers = null); \ No newline at end of file diff --git a/Toolkit.Foundation/Mediator.cs b/Toolkit.Foundation/Mediator.cs index f6d649d..3e9d354 100644 --- a/Toolkit.Foundation/Mediator.cs +++ b/Toolkit.Foundation/Mediator.cs @@ -12,7 +12,7 @@ public class Mediator(IServiceProvider provider) : Type handlerType = typeof(HandlerWrapper<,>).MakeGenericType(request.GetType(), typeof(TResponse)); - if (provider.GetService(handlerType) + if (provider.GetService(handlerType) is object handler) { if (handlerType.GetMethod("Handle") is MethodInfo handleMethod) @@ -27,14 +27,14 @@ public class Mediator(IServiceProvider provider) : public Task Handle(object message, CancellationToken cancellationToken = default) { - if (message.GetType().GetInterface(typeof(IRequest<>).Name) is Type requestType && + if (message.GetType().GetInterface(message.GetType().Name) is Type requestType && requestType.GetGenericArguments().Length == 1) { Type responseType = requestType.GetGenericArguments()[0]; - Type handlerType = typeof(HandlerWrapper<,>).MakeGenericType(message.GetType(), + Type handlerType = typeof(HandlerWrapper<,>).MakeGenericType(message.GetType(), responseType); - if (provider.GetService(handlerType) + if (provider.GetService(handlerType) is object handler) { if (handlerType.GetMethod("Handle") is MethodInfo handleMethod) diff --git a/Toolkit.Foundation/MethodInfoExtensions.cs b/Toolkit.Foundation/MethodInfoExtensions.cs index 374a4f7..4a64a03 100644 --- a/Toolkit.Foundation/MethodInfoExtensions.cs +++ b/Toolkit.Foundation/MethodInfoExtensions.cs @@ -12,7 +12,7 @@ public static class MethodInfoExtensions } public static async Task InvokeAsync(this MethodInfo methodInfo, - object? obj, + object? obj, params object[] parameters) { await (dynamic?)methodInfo.Invoke(obj, parameters); diff --git a/Toolkit.Foundation/Navigate.cs b/Toolkit.Foundation/Navigate.cs index 9b889c2..0b2cec9 100644 --- a/Toolkit.Foundation/Navigate.cs +++ b/Toolkit.Foundation/Navigate.cs @@ -1,14 +1,14 @@ namespace Toolkit.Foundation; -public record Navigate(string Route, - object? Context = null, - string? Scope = null, +public record Navigate(string Route, + object? Context = null, + string? Scope = null, object? Sender = null, EventHandler? Navigated = null, object[]? Parameters = null); -public record Navigate(object Context, - object Template, +public record Navigate(object Context, + object Template, object Content, object? Sender = null, object[]? Parameters = null); \ No newline at end of file diff --git a/Toolkit.Foundation/NavigateBack.cs b/Toolkit.Foundation/NavigateBack.cs index 224de35..48feb09 100644 --- a/Toolkit.Foundation/NavigateBack.cs +++ b/Toolkit.Foundation/NavigateBack.cs @@ -2,4 +2,4 @@ public record NavigateBack(object? Context = null, string? Scope = null); -public record NavigateBack(object? Context); +public record NavigateBack(object? Context); \ No newline at end of file diff --git a/Toolkit.Foundation/NavigateBackHandler.cs b/Toolkit.Foundation/NavigateBackHandler.cs index 2efa23d..fdd07a3 100644 --- a/Toolkit.Foundation/NavigateBackHandler.cs +++ b/Toolkit.Foundation/NavigateBackHandler.cs @@ -17,6 +17,4 @@ public class NavigateBackHandler(IComponentScopeProvider provider) : } } } -} - - +} \ No newline at end of file diff --git a/Toolkit.Foundation/NavigateHandler.cs b/Toolkit.Foundation/NavigateHandler.cs index e91cfd3..f1a4905 100644 --- a/Toolkit.Foundation/NavigateHandler.cs +++ b/Toolkit.Foundation/NavigateHandler.cs @@ -6,10 +6,10 @@ public class NavigateHandler(ComponentScope scope, IComponentScopeProvider provider) : INotificationHandler { - public async Task Handle(Navigate args, + public async Task Handle(Navigate args, CancellationToken cancellationToken) { - if (provider.Get(args.Scope ?? scope.Name) + if (provider.Get(args.Scope ?? scope.Name) is ComponentScopeDescriptor descriptor) { if (descriptor?.Services?.GetService() is INavigationScope navigationScope) @@ -19,4 +19,4 @@ public class NavigateHandler(ComponentScope scope, } } } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/NavigatingFrom.cs b/Toolkit.Foundation/NavigatingFrom.cs deleted file mode 100644 index 8d8e611..0000000 --- a/Toolkit.Foundation/NavigatingFrom.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Toolkit.Foundation; - -public record NavigatingFrom(object Content) : - IRequest>; - -public record NavigatingTo(object Content) : - IRequest>; diff --git a/Toolkit.Foundation/Navigation.cs b/Toolkit.Foundation/Navigation.cs index 4a1c6e0..6e6835a 100644 --- a/Toolkit.Foundation/Navigation.cs +++ b/Toolkit.Foundation/Navigation.cs @@ -1,8 +1,7 @@ namespace Toolkit.Foundation; -public record Navigation : +public record Navigation : INavigation { public required Type Type { get; set; } -} - +} \ No newline at end of file diff --git a/Toolkit.Foundation/NavigationContextAttribute.cs b/Toolkit.Foundation/NavigationContextAttribute.cs index 651c12d..62a889b 100644 --- a/Toolkit.Foundation/NavigationContextAttribute.cs +++ b/Toolkit.Foundation/NavigationContextAttribute.cs @@ -9,4 +9,4 @@ public class NavigationContextAttribute : Attribute } public string Name { get; } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/NavigationContextCollection.cs b/Toolkit.Foundation/NavigationContextCollection.cs index a36d908..0cdf540 100644 --- a/Toolkit.Foundation/NavigationContextCollection.cs +++ b/Toolkit.Foundation/NavigationContextCollection.cs @@ -1,4 +1,4 @@ namespace Toolkit.Foundation; -public class NavigationContextCollection : Dictionary, - INavigationContextCollection; +public class NavigationContextCollection : Dictionary, + INavigationContextCollection; \ No newline at end of file diff --git a/Toolkit.Foundation/NavigationContextProvider.cs b/Toolkit.Foundation/NavigationContextProvider.cs index 74865bb..a0173ef 100644 --- a/Toolkit.Foundation/NavigationContextProvider.cs +++ b/Toolkit.Foundation/NavigationContextProvider.cs @@ -6,7 +6,7 @@ public class NavigationContextProvider(INavigationContextCollection contexts) : public object? Get(object key) => contexts.TryGetValue(key, out object? target) ? target : default; - public bool TryGet(object name, + public bool TryGet(object name, out object? value) { if (contexts.TryGetValue(name, @@ -21,4 +21,4 @@ public class NavigationContextProvider(INavigationContextCollection contexts) : return false; } } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/NavigationProvider.cs b/Toolkit.Foundation/NavigationProvider.cs index 2135e07..8a23026 100644 --- a/Toolkit.Foundation/NavigationProvider.cs +++ b/Toolkit.Foundation/NavigationProvider.cs @@ -13,4 +13,4 @@ public class NavigationProvider(IEnumerable navigations) : return default; } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/NavigationScope.cs b/Toolkit.Foundation/NavigationScope.cs index 5748090..bc8320e 100644 --- a/Toolkit.Foundation/NavigationScope.cs +++ b/Toolkit.Foundation/NavigationScope.cs @@ -7,16 +7,16 @@ public class NavigationScope(IPublisher publisher, IServiceFactory factory, INavigationProvider navigationProvider, INavigationContextProvider navigationContextProvider, - IContentTemplateDescriptorProvider contentTemplateDescriptorProvider) : + IContentTemplateDescriptorProvider contentTemplateDescriptorProvider) : INavigationScope { - public async Task NavigateAsync(string route, object? sender = null, object? context = null, + public async Task NavigateAsync(string route, object? sender = null, object? context = null, EventHandler? navigated = null, object[]? parameters = null, CancellationToken cancellationToken = default) { string[] segments = route.Split('/'); int segmentCount = segments.Length; int currentSegmentIndex = 0; - + foreach (object segment in segments) { currentSegmentIndex++; @@ -79,7 +79,7 @@ public class NavigationScope(IPublisher publisher, } } - public async Task NavigateBackAsync(object? context, + public async Task NavigateBackAsync(object? context, CancellationToken cancellationToken = default) { if (context is not null) @@ -100,5 +100,4 @@ public class NavigationScope(IPublisher publisher, } } } -} - +} \ No newline at end of file diff --git a/Toolkit.Foundation/NavigationTargetAttribute.cs b/Toolkit.Foundation/NavigationTargetAttribute.cs index c4e3f75..4529d86 100644 --- a/Toolkit.Foundation/NavigationTargetAttribute.cs +++ b/Toolkit.Foundation/NavigationTargetAttribute.cs @@ -1,8 +1,8 @@ namespace Toolkit.Foundation; [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] -public class NavigationTargetAttribute(string name) : +public class NavigationTargetAttribute(string name) : Attribute { public string Name => name; -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/NotificationHandlerDelegate.cs b/Toolkit.Foundation/NotificationHandlerDelegate.cs index 01c0dea..448ac77 100644 --- a/Toolkit.Foundation/NotificationHandlerDelegate.cs +++ b/Toolkit.Foundation/NotificationHandlerDelegate.cs @@ -1,4 +1,4 @@ namespace Toolkit.Foundation; public delegate Task NotificationHandlerDelegate(TMessage message, - CancellationToken cancellationToken); + CancellationToken cancellationToken); \ No newline at end of file diff --git a/Toolkit.Foundation/NotificationHandlerWrapper.cs b/Toolkit.Foundation/NotificationHandlerWrapper.cs index 91a67c4..b6391bf 100644 --- a/Toolkit.Foundation/NotificationHandlerWrapper.cs +++ b/Toolkit.Foundation/NotificationHandlerWrapper.cs @@ -18,7 +18,7 @@ public class NotificationHandlerWrapper(INotificationHandler await behaviour.Handle(args, previousHandler, token); }; } - + await currentHandler(message, cancellationToken); } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/ObjectExtensions.cs b/Toolkit.Foundation/ObjectExtensions.cs index 9cad647..1bd67f1 100644 --- a/Toolkit.Foundation/ObjectExtensions.cs +++ b/Toolkit.Foundation/ObjectExtensions.cs @@ -18,7 +18,7 @@ public static class ObjectExtensions return null; } - public static TAttribute? GetAttribute(this object obj) + public static TAttribute? GetAttribute(this object obj) where TAttribute : Attribute { Type type = obj.GetType(); diff --git a/Toolkit.Foundation/ObservableCollectionViewModel.cs b/Toolkit.Foundation/ObservableCollectionViewModel.cs index 7ef445d..569f831 100644 --- a/Toolkit.Foundation/ObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/ObservableCollectionViewModel.cs @@ -5,6 +5,7 @@ using System.Collections.Specialized; using System.Reactive.Disposables; namespace Toolkit.Foundation; + public partial class ObservableCollectionViewModel : ObservableObject, IObservableCollectionViewModel, @@ -22,7 +23,7 @@ public partial class ObservableCollectionViewModel : INotificationHandler>, INotificationHandler>, INotificationHandler> - where TViewModel : + where TViewModel : notnull { private readonly ObservableCollection collection = []; @@ -113,7 +114,6 @@ public partial class ObservableCollectionViewModel : } catch (InvalidCastException) { - } this[index] = item!; @@ -172,7 +172,6 @@ public partial class ObservableCollectionViewModel : } catch (InvalidCastException) { - } Add(item!); @@ -280,7 +279,7 @@ public partial class ObservableCollectionViewModel : return Task.CompletedTask; } - public Task Handle(Replace args, + public Task Handle(Replace args, CancellationToken cancellationToken) { if (args.Value is TViewModel item) @@ -366,7 +365,7 @@ public partial class ObservableCollectionViewModel : public void RemoveAt(int index) => RemoveItem(index); - public bool Replace(int index, + public bool Replace(int index, TViewModel item) { if (index <= Count - 1) @@ -409,12 +408,12 @@ public partial class ObservableCollectionViewModel : private static bool IsCompatibleObject(object? value) => (value is TViewModel) || (value == null && default(TViewModel) == null); - private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args) => + private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args) => CollectionChanged?.Invoke(this, args); } public class ObservableCollectionViewModel(IServiceProvider provider, - IServiceFactory factory, + IServiceFactory factory, IMediator mediator, IPublisher publisher, ISubscriber subscriber, diff --git a/Toolkit.Foundation/ObservableViewModel.cs b/Toolkit.Foundation/ObservableViewModel.cs index 87f0ec8..60b3585 100644 --- a/Toolkit.Foundation/ObservableViewModel.cs +++ b/Toolkit.Foundation/ObservableViewModel.cs @@ -2,7 +2,7 @@ namespace Toolkit.Foundation; -public partial class ObservableViewModel : +public partial class ObservableViewModel : ObservableObject, IObservableViewModel, IInitializer, @@ -73,4 +73,4 @@ public partial class ObservableViewModel : IsInitialized = true; return Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/Publisher.cs b/Toolkit.Foundation/Publisher.cs index 172608c..ac17c94 100644 --- a/Toolkit.Foundation/Publisher.cs +++ b/Toolkit.Foundation/Publisher.cs @@ -5,18 +5,18 @@ namespace Toolkit.Foundation; public class Publisher(ISubscriptionManager subscriptionManager, IServiceProvider provider, - IDispatcher dispatcher) : + IDispatcher dispatcher) : IPublisher { public Task Publish(object key, CancellationToken cancellationToken = default) - where TMessage : new() => + where TMessage : new() => Publish(new TMessage(), async args => await args(), key, cancellationToken); public Task Publish(TMessage message, CancellationToken cancellationToken = default) - where TMessage : notnull => + where TMessage : notnull => Publish(message, async args => await args(), null, cancellationToken); @@ -24,7 +24,7 @@ public class Publisher(ISubscriptionManager subscriptionManager, object key, CancellationToken cancellationToken = default) where TMessage : notnull => - Publish(message, async args => await args(), + Publish(message, async args => await args(), key, cancellationToken); public async Task Publish(object message, @@ -65,31 +65,32 @@ public class Publisher(ISubscriptionManager subscriptionManager, async args => await args(), null, cancellationToken); - public Task Publish(CancellationToken cancellationToken = default) - where TMessage : new() => + public Task Publish(CancellationToken cancellationToken = default) + where TMessage : new() => Publish(new TMessage(), async args => await args(), null, cancellationToken); public Task PublishUI(object key, CancellationToken cancellationToken = default) - where TMessage : new() => - Publish(new TMessage(), args => dispatcher.InvokeAsync(async () => await args()), + where TMessage : new() => + Publish(new TMessage(), args => dispatcher.InvokeAsync(async () => await args()), key, cancellationToken); public Task PublishUI(TMessage message, CancellationToken cancellationToken = default) - where TMessage : notnull => + where TMessage : notnull => Publish(message, args => dispatcher.InvokeAsync(async () => await args()), null, cancellationToken); public Task PublishUI(TMessage message, object key, CancellationToken cancellationToken = default) - where TMessage : notnull => + where TMessage : notnull => Publish(message, args => dispatcher.InvokeAsync(async () => await args()), key, cancellationToken); + public Task PublishUI(CancellationToken cancellationToken = default) - where TMessage : new() => + where TMessage : new() => Publish(new TMessage(), args => dispatcher.InvokeAsync(async () => await args()), null, cancellationToken); @@ -97,4 +98,4 @@ public class Publisher(ISubscriptionManager subscriptionManager, CancellationToken cancellationToken = default) => Publish(message, args => dispatcher.InvokeAsync(async () => await args()), null, cancellationToken); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/Remove.cs b/Toolkit.Foundation/Remove.cs index 4b63bd7..6cf37d8 100644 --- a/Toolkit.Foundation/Remove.cs +++ b/Toolkit.Foundation/Remove.cs @@ -1,5 +1,4 @@ - -namespace Toolkit.Foundation; +namespace Toolkit.Foundation; public record Remove(TValue Value); diff --git a/Toolkit.Foundation/Replace.cs b/Toolkit.Foundation/Replace.cs index 919c49e..b784976 100644 --- a/Toolkit.Foundation/Replace.cs +++ b/Toolkit.Foundation/Replace.cs @@ -1,5 +1,4 @@ - -namespace Toolkit.Foundation; +namespace Toolkit.Foundation; public record Replace(int Index, TValue Value); diff --git a/Toolkit.Foundation/ServiceFactory.cs b/Toolkit.Foundation/ServiceFactory.cs index 39e892e..d98b2bc 100644 --- a/Toolkit.Foundation/ServiceFactory.cs +++ b/Toolkit.Foundation/ServiceFactory.cs @@ -6,6 +6,6 @@ public class ServiceFactory(Func factory) : public TService Create(params object?[]? parameters) => (TService)factory(typeof(TService), parameters); - public object Create(Type type, params object?[]? parameters) => + public object Create(Type type, params object?[]? parameters) => factory(type, parameters); } \ No newline at end of file diff --git a/Toolkit.Foundation/ServiceScopeFactory.cs b/Toolkit.Foundation/ServiceScopeFactory.cs index c86aec1..2ea2332 100644 --- a/Toolkit.Foundation/ServiceScopeFactory.cs +++ b/Toolkit.Foundation/ServiceScopeFactory.cs @@ -2,7 +2,7 @@ namespace Toolkit.Foundation; -public class ServiceScopeFactory(IServiceScopeFactory serviceScopeFactory, +public class ServiceScopeFactory(IServiceScopeFactory serviceScopeFactory, ICache cache) : IServiceScopeFactory where TService : notnull diff --git a/Toolkit.Foundation/ServiceScopeProvider.cs b/Toolkit.Foundation/ServiceScopeProvider.cs index 11ccc9b..639db09 100644 --- a/Toolkit.Foundation/ServiceScopeProvider.cs +++ b/Toolkit.Foundation/ServiceScopeProvider.cs @@ -6,7 +6,7 @@ public class ServiceScopeProvider(ICache cach IServiceScopeProvider where TService : notnull { - public bool TryGet(TService service, + public bool TryGet(TService service, out IServiceScope? serviceScope) { if (cache.TryGetValue(service, out IServiceScope? value)) @@ -18,4 +18,4 @@ public class ServiceScopeProvider(ICache cach serviceScope = null; return false; } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/SetupRequiredAttribute.cs b/Toolkit.Foundation/SetupRequiredAttribute.cs index 6f2c67d..d2f4cf5 100644 --- a/Toolkit.Foundation/SetupRequiredAttribute.cs +++ b/Toolkit.Foundation/SetupRequiredAttribute.cs @@ -4,4 +4,4 @@ public class NavigationRouteAttribute(string route) : Attribute { public string Route => route; -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/StartProcess.cs b/Toolkit.Foundation/StartProcess.cs index c2cae16..ed075a5 100644 --- a/Toolkit.Foundation/StartProcess.cs +++ b/Toolkit.Foundation/StartProcess.cs @@ -1,3 +1,3 @@ namespace Toolkit.Foundation; -public record StartProcess(string Process) : IRequest; \ No newline at end of file +public record StartProcess(string Process); \ No newline at end of file diff --git a/Toolkit.Foundation/Subscriber.cs b/Toolkit.Foundation/Subscriber.cs index 8d13d8f..83898a2 100644 --- a/Toolkit.Foundation/Subscriber.cs +++ b/Toolkit.Foundation/Subscriber.cs @@ -1,11 +1,11 @@ namespace Toolkit.Foundation; -public class Subscriber(ISubscriptionManager subscriptionManager) : +public class Subscriber(ISubscriptionManager subscriptionManager) : ISubscriber { - public void Remove(object subscriber) => + public void Remove(object subscriber) => subscriptionManager.Remove(subscriber); - public void Add(object subscriber) => + public void Add(object subscriber) => subscriptionManager.Add(subscriber); } \ No newline at end of file diff --git a/Toolkit.Foundation/SubscriptionCollection.cs b/Toolkit.Foundation/SubscriptionCollection.cs index 67cbbc4..82f4572 100644 --- a/Toolkit.Foundation/SubscriptionCollection.cs +++ b/Toolkit.Foundation/SubscriptionCollection.cs @@ -3,4 +3,4 @@ namespace Toolkit.Foundation; public class SubscriptionCollection : - ConcurrentDictionary>; + ConcurrentDictionary>; \ No newline at end of file diff --git a/Toolkit.Foundation/SubscriptionManager.cs b/Toolkit.Foundation/SubscriptionManager.cs index 2d8eae3..d6f92a5 100644 --- a/Toolkit.Foundation/SubscriptionManager.cs +++ b/Toolkit.Foundation/SubscriptionManager.cs @@ -1,13 +1,11 @@ -using System.Reflection; - -namespace Toolkit.Foundation; +namespace Toolkit.Foundation; public class SubscriptionManager(SubscriptionCollection subscriptions) : ISubscriptionManager { public IEnumerable GetHandlers(Type notificationType, object key) { - if (subscriptions.TryGetValue($"{(key is not null ? $"{key}:" : "")}{notificationType}", + if (subscriptions.TryGetValue($"{(key is not null ? $"{key}:" : "")}{notificationType}", out List? subscribers)) { foreach (WeakReference weakRef in subscribers.ToArray()) @@ -72,7 +70,7 @@ public class SubscriptionManager(SubscriptionCollection subscriptions) : : null; } - private static IEnumerable GetHandlerInterfaces(Type handlerType) => + private static IEnumerable GetHandlerInterfaces(Type handlerType) => handlerType.GetInterfaces().Where(interfaceType => interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(INotificationHandler<>)); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/TypeExtensions.cs b/Toolkit.Foundation/TypeExtensions.cs index 76ea051..791b0ae 100644 --- a/Toolkit.Foundation/TypeExtensions.cs +++ b/Toolkit.Foundation/TypeExtensions.cs @@ -14,4 +14,4 @@ public static class TypeExtensions return null; } -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/ValueViewModel.cs b/Toolkit.Foundation/ValueViewModel.cs index a43ba96..aed8932 100644 --- a/Toolkit.Foundation/ValueViewModel.cs +++ b/Toolkit.Foundation/ValueViewModel.cs @@ -15,8 +15,7 @@ public partial class ValueViewModel(IServiceProvider provider, protected virtual void OnChanged(TValue? value) { - } partial void OnValueChanged(TValue? value) => OnChanged(value); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/VirtualKey.cs b/Toolkit.Foundation/VirtualKey.cs index 67ca644..d02ae56 100644 --- a/Toolkit.Foundation/VirtualKey.cs +++ b/Toolkit.Foundation/VirtualKey.cs @@ -172,4 +172,4 @@ public enum VirtualKey GamepadRightThumbstickDown = 216, GamepadRightThumbstickRight = 217, GamepadRightThumbstickLeft = 218 -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/WritableConfiguration.cs b/Toolkit.Foundation/WritableConfiguration.cs index 4f1f4f1..11f5727 100644 --- a/Toolkit.Foundation/WritableConfiguration.cs +++ b/Toolkit.Foundation/WritableConfiguration.cs @@ -6,4 +6,4 @@ public class WritableConfiguration(IConfigurationWriter updateDelegate) => writer.Write(updateDelegate); -} +} \ No newline at end of file diff --git a/Toolkit.UI.Avalonia/AttachedBehavior.cs b/Toolkit.UI.Avalonia/AttachedBehavior.cs index eae5e7c..d9d61ae 100644 --- a/Toolkit.UI.Avalonia/AttachedBehavior.cs +++ b/Toolkit.UI.Avalonia/AttachedBehavior.cs @@ -9,4 +9,4 @@ public class AttachedBehavior : Trigger Interaction.ExecuteActions(AssociatedObject, Actions, null); base.OnAttachedToVisualTree(); } -} +} \ No newline at end of file diff --git a/Toolkit.UI.Avalonia/ComparisonCondition.cs b/Toolkit.UI.Avalonia/ComparisonCondition.cs index bde4777..83e271c 100644 --- a/Toolkit.UI.Avalonia/ComparisonCondition.cs +++ b/Toolkit.UI.Avalonia/ComparisonCondition.cs @@ -3,8 +3,8 @@ using Avalonia.Xaml.Interactivity; namespace Toolkit.UI.Avalonia; -public class ComparisonCondition : - AvaloniaObject, +public class ComparisonCondition : + AvaloniaObject, ICondition { public static readonly StyledProperty LeftOperandProperty = @@ -36,4 +36,4 @@ public class ComparisonCondition : public bool Evaluate() => ComparisonLogic.Evaluate(LeftOperand, Operator, RightOperand); -} +} \ No newline at end of file diff --git a/Toolkit.UI.Avalonia/ComparisonLogic.cs b/Toolkit.UI.Avalonia/ComparisonLogic.cs index 96415a3..2e1f281 100644 --- a/Toolkit.UI.Avalonia/ComparisonLogic.cs +++ b/Toolkit.UI.Avalonia/ComparisonLogic.cs @@ -6,8 +6,8 @@ namespace Toolkit.UI.Avalonia; internal static class ComparisonLogic { - internal static bool Evaluate(object leftOperand, - ComparisonConditionType operatorType, + internal static bool Evaluate(object leftOperand, + ComparisonConditionType operatorType, object? rightOperand) { bool result = false; @@ -17,14 +17,13 @@ internal static class ComparisonLogic Type leftType = leftOperand.GetType(); if (rightOperand != null) - { + { TypeConverter typeConverter = TypeDescriptor.GetConverter(leftType); rightOperand = typeConverter.ConvertFrom(rightOperand); } } - - if (leftOperand is IComparable leftComparableOperand && + if (leftOperand is IComparable leftComparableOperand && rightOperand is IComparable rightComparableOperand) { return EvaluateComparable(leftComparableOperand, operatorType, rightComparableOperand); @@ -35,6 +34,7 @@ internal static class ComparisonLogic case ComparisonConditionType.Equal: result = Equals(leftOperand, rightOperand); break; + case ComparisonConditionType.NotEqual: result = !Equals(leftOperand, rightOperand); break; @@ -43,7 +43,7 @@ internal static class ComparisonLogic } private static bool EvaluateComparable(IComparable leftOperand, - ComparisonConditionType operatorType, + ComparisonConditionType operatorType, IComparable rightOperand) { object? convertedOperand = null; @@ -54,11 +54,9 @@ internal static class ComparisonLogic } catch (FormatException) { - } catch (InvalidCastException) { - } if (convertedOperand == null) @@ -74,18 +72,23 @@ internal static class ComparisonLogic case ComparisonConditionType.Equal: result = comparison == 0; break; + case ComparisonConditionType.GreaterThan: result = comparison > 0; break; + case ComparisonConditionType.GreaterThanOrEqual: result = comparison >= 0; break; + case ComparisonConditionType.LessThan: result = comparison < 0; break; + case ComparisonConditionType.LessThanOrEqual: result = comparison <= 0; break; + case ComparisonConditionType.NotEqual: result = comparison != 0; break; @@ -93,4 +96,4 @@ internal static class ComparisonLogic return result; } -} +} \ No newline at end of file diff --git a/Toolkit.UI.Avalonia/ConditionAction.cs b/Toolkit.UI.Avalonia/ConditionAction.cs index 29a25a1..23cf491 100644 --- a/Toolkit.UI.Avalonia/ConditionAction.cs +++ b/Toolkit.UI.Avalonia/ConditionAction.cs @@ -4,7 +4,7 @@ using Avalonia.Xaml.Interactivity; namespace Toolkit.UI.Avalonia; -public class ConditionAction : +public class ConditionAction : AvaloniaObject, IAction { diff --git a/Toolkit.UI.Avalonia/ConditionCollection.cs b/Toolkit.UI.Avalonia/ConditionCollection.cs index 36f0c69..d8bee8c 100644 --- a/Toolkit.UI.Avalonia/ConditionCollection.cs +++ b/Toolkit.UI.Avalonia/ConditionCollection.cs @@ -5,4 +5,4 @@ namespace Toolkit.UI.Avalonia; public class ConditionCollection : ObservableCollection { -} +} \ No newline at end of file diff --git a/Toolkit.UI.Avalonia/ConditionalExpression.cs b/Toolkit.UI.Avalonia/ConditionalExpression.cs index 8b6d28d..14c35ab 100644 --- a/Toolkit.UI.Avalonia/ConditionalExpression.cs +++ b/Toolkit.UI.Avalonia/ConditionalExpression.cs @@ -1,10 +1,9 @@ using Avalonia; using Avalonia.Metadata; -using Toolkit.UI.Avalonia; namespace Toolkit.UI.Avalonia; -public class ConditionalExpression : +public class ConditionalExpression : AvaloniaObject, ICondition { @@ -18,7 +17,7 @@ public class ConditionalExpression : SetValue(ConditionsProperty, []); [Content] - public ConditionCollection Conditions => + public ConditionCollection Conditions => GetValue(ConditionsProperty); public ForwardChaining ForwardChaining @@ -47,4 +46,4 @@ public class ConditionalExpression : return result; } -} +} \ No newline at end of file diff --git a/Toolkit.UI.Avalonia/ForwardChaining.cs b/Toolkit.UI.Avalonia/ForwardChaining.cs index 7dac9ee..d716d8b 100644 --- a/Toolkit.UI.Avalonia/ForwardChaining.cs +++ b/Toolkit.UI.Avalonia/ForwardChaining.cs @@ -4,4 +4,4 @@ public enum ForwardChaining { And, Or -} +} \ No newline at end of file diff --git a/Toolkit.UI.Avalonia/ICondition.cs b/Toolkit.UI.Avalonia/ICondition.cs index 5f95e0b..f64733a 100644 --- a/Toolkit.UI.Avalonia/ICondition.cs +++ b/Toolkit.UI.Avalonia/ICondition.cs @@ -3,4 +3,4 @@ public interface ICondition { bool Evaluate(); -} +} \ No newline at end of file diff --git a/Toolkit.UI.Avalonia/KeyBindingTriggerBehavior.cs b/Toolkit.UI.Avalonia/KeyBindingTriggerBehavior.cs index 68c54c9..50c0775 100644 --- a/Toolkit.UI.Avalonia/KeyBindingTriggerBehavior.cs +++ b/Toolkit.UI.Avalonia/KeyBindingTriggerBehavior.cs @@ -5,8 +5,8 @@ using System.Windows.Input; namespace Toolkit.UI.Avalonia; -public class KeyBindingTriggerBehavior : - Trigger, +public class KeyBindingTriggerBehavior : + Trigger, ICommand { public static readonly StyledProperty GestureProperty = @@ -38,6 +38,6 @@ public class KeyBindingTriggerBehavior : public bool CanExecute(object? parameter) => true; - public void Execute(object? parameter) => + public void Execute(object? parameter) => Interaction.ExecuteActions(AssociatedObject, Actions, null); -} +} \ No newline at end of file diff --git a/Toolkit.UI.Avalonia/NavigateAction.cs b/Toolkit.UI.Avalonia/NavigateAction.cs index 1ff15ef..eb1ce4c 100644 --- a/Toolkit.UI.Avalonia/NavigateAction.cs +++ b/Toolkit.UI.Avalonia/NavigateAction.cs @@ -51,6 +51,7 @@ public class NavigateAction : get => GetValue(RouteProperty); set => SetValue(RouteProperty, value); } + public string Scope { get => GetValue(ScopeProperty); @@ -62,12 +63,13 @@ public class NavigateAction : { if (sender is TemplatedControl control) { - Dictionary arguments = + Dictionary arguments = new(StringComparer.InvariantCultureIgnoreCase); if (control.DataContext is IObservableViewModel observableViewModel) { - object[] parameters = [.. Parameters ?? Enumerable.Empty(), .. + object[] parameters = [.. Parameters ?? Enumerable.Empty(), + .. ParameterBindings is { Count: > 0 } ? ParameterBindings.Select(binding => new KeyValuePair(binding.Key, binding.Value)).ToArray() : Enumerable.Empty>()]; @@ -79,4 +81,4 @@ public class NavigateAction : return true; } -} +} \ No newline at end of file diff --git a/Toolkit.UI.Avalonia/NavigateBackAction.cs b/Toolkit.UI.Avalonia/NavigateBackAction.cs index 93d66db..10bd27c 100644 --- a/Toolkit.UI.Avalonia/NavigateBackAction.cs +++ b/Toolkit.UI.Avalonia/NavigateBackAction.cs @@ -41,4 +41,4 @@ public class NavigateBackAction : return true; } -} +} \ No newline at end of file diff --git a/Toolkit.UI.Avalonia/ParameterBinding.cs b/Toolkit.UI.Avalonia/ParameterBinding.cs index abb7803..16ec964 100644 --- a/Toolkit.UI.Avalonia/ParameterBinding.cs +++ b/Toolkit.UI.Avalonia/ParameterBinding.cs @@ -16,9 +16,10 @@ public class ParameterBinding : get => GetValue(KeyProperty); set => SetValue(KeyProperty, value); } + public object Value { get => GetValue(ValueProperty); set => SetValue(ValueProperty, value); } -} +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/AsyncImage/AsyncImage.cs b/Toolkit.UI.Controls.Avalonia/AsyncImage/AsyncImage.cs index bacad06..a0da572 100644 --- a/Toolkit.UI.Controls.Avalonia/AsyncImage/AsyncImage.cs +++ b/Toolkit.UI.Controls.Avalonia/AsyncImage/AsyncImage.cs @@ -5,4 +5,4 @@ public class AsyncImage : { protected override Type StyleKeyOverride => typeof(global::Avalonia.Labs.Controls.AsyncImage); -} +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/BlurBehind/BlurBehind.cs b/Toolkit.UI.Controls.Avalonia/BlurBehind/BlurBehind.cs index 8284e95..35fc4eb 100644 --- a/Toolkit.UI.Controls.Avalonia/BlurBehind/BlurBehind.cs +++ b/Toolkit.UI.Controls.Avalonia/BlurBehind/BlurBehind.cs @@ -9,29 +9,29 @@ using SkiaSharp; namespace Toolkit.UI.Controls.Avalonia; -public class BlurBehind : +public class BlurBehind : Control { public static readonly StyledProperty MaterialProperty = AvaloniaProperty.Register("Material"); - public static readonly ImmutableExperimentalAcrylicMaterial DefaultAcrylicMaterialDark = + public static readonly ImmutableExperimentalAcrylicMaterial DefaultAcrylicMaterialDark = (ImmutableExperimentalAcrylicMaterial)new ExperimentalAcrylicMaterial() - { - MaterialOpacity = 0.25, - TintColor = Colors.Black, - TintOpacity = 0.7, - PlatformTransparencyCompensationLevel = 0 - }.ToImmutable(); + { + MaterialOpacity = 0.25, + TintColor = Colors.Black, + TintOpacity = 0.7, + PlatformTransparencyCompensationLevel = 0 + }.ToImmutable(); - public static readonly ImmutableExperimentalAcrylicMaterial DefaultAcrylicMaterialLight = + public static readonly ImmutableExperimentalAcrylicMaterial DefaultAcrylicMaterialLight = (ImmutableExperimentalAcrylicMaterial)new ExperimentalAcrylicMaterial() - { - MaterialOpacity = 0.0, - TintColor = Colors.White, - TintOpacity = 0.3, - PlatformTransparencyCompensationLevel = 0 - }.ToImmutable(); + { + MaterialOpacity = 0.0, + TintColor = Colors.White, + TintOpacity = 0.3, + PlatformTransparencyCompensationLevel = 0 + }.ToImmutable(); static BlurBehind() { @@ -49,7 +49,7 @@ public class BlurBehind : ImmutableExperimentalAcrylicMaterial material = Material is not null ? (ImmutableExperimentalAcrylicMaterial)Material.ToImmutable() : Application.Current?.ActualThemeVariant == ThemeVariant.Dark ? DefaultAcrylicMaterialDark : DefaultAcrylicMaterialLight; - + context.Custom(new BlurBehindRenderOperation(material, new Rect(default, Bounds.Size))); } @@ -63,18 +63,17 @@ public class BlurBehind : public void Dispose() { - } - public bool Equals(ICustomDrawOperation? other) => - other is BlurBehindRenderOperation behindRenderOperation && + public bool Equals(ICustomDrawOperation? other) => + other is BlurBehindRenderOperation behindRenderOperation && behindRenderOperation.bounds == bounds && behindRenderOperation.material.Equals(material); public bool HitTest(Point point) => bounds.Contains(point); public void Render(ImmediateDrawingContext context) { - if (context.TryGetFeature() is ISkiaSharpApiLeaseFeature leaseFeature) + if (context.TryGetFeature() is ISkiaSharpApiLeaseFeature leaseFeature) { using ISkiaSharpApiLease? lease = leaseFeature.Lease(); if (lease.SkCanvas is SKCanvas canvas) @@ -92,7 +91,7 @@ public class BlurBehind : SKImageInfo.PlatformColorType, SKAlphaType.Premul)); using (SKImageFilter filter = SKImageFilter.CreateBlur(8, 8, SKShaderTileMode.Clamp)) - using (SKPaint blurPaint = new() { Shader = backdropShader, ImageFilter = filter }) + using (SKPaint blurPaint = new() { Shader = backdropShader, ImageFilter = filter }) blurred.Canvas.DrawRect(5, 5, (float)bounds.Width - 20, (float)bounds.Height - 20, blurPaint); using SKImage blurSnap = blurred.Snapshot(); @@ -105,11 +104,9 @@ public class BlurBehind : canvas.DrawRect(0, 0, (float)bounds.Width, (float)bounds.Height, blurSnapPaint); } - } } } } } -} - +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/CarouselView/CarouselView.cs b/Toolkit.UI.Controls.Avalonia/CarouselView/CarouselView.cs index 23de510..2887ac5 100644 --- a/Toolkit.UI.Controls.Avalonia/CarouselView/CarouselView.cs +++ b/Toolkit.UI.Controls.Avalonia/CarouselView/CarouselView.cs @@ -78,7 +78,7 @@ public class CarouselView : indicatorVisual = ElementComposition.GetElementVisual(indicator); touchAreaVisual = ElementComposition.GetElementVisual(container); if (touchAreaVisual is not null) - { + { compositor = touchAreaVisual.Compositor; } @@ -136,7 +136,7 @@ public class CarouselView : protected override void OnPointerReleased(PointerReleasedEventArgs args) { - if (isPressed && container is not null + if (isPressed && container is not null && items is not null && indicatorVisual is not null) { @@ -167,12 +167,12 @@ public class CarouselView : } private void ArrangeItems(int newIndex, - int oldIndex = -1, + int oldIndex = -1, bool isAnimating = false) { - if (compositor is not null - && container is not null - && items is not null + if (compositor is not null + && container is not null + && items is not null && indicatorVisual is not null) { double containerHeight = Bounds.Height; @@ -237,15 +237,14 @@ public class CarouselView : scopedBatch.Completed += () => { - this.isAnimating = false; + this.isAnimating = false; for (int i = 0; i < columnCount; i++) { itemVisuals[(newIndex + i - 2 + columnCount) % columnCount].Offset = new Vector3((float)(offsets[i] - centreOffset), 0, 0); - } }; - + indicatorVisual.StartAnimation("Offset", indicatorAnimation); scopedBatch.Start(animationDuration); @@ -253,6 +252,7 @@ public class CarouselView : } } } + private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args) => ArrangeItems(newIndex); diff --git a/Toolkit.UI.Controls.Avalonia/CarouselView/CarouselViewItem.cs b/Toolkit.UI.Controls.Avalonia/CarouselView/CarouselViewItem.cs index ed75a5f..a593889 100644 --- a/Toolkit.UI.Controls.Avalonia/CarouselView/CarouselViewItem.cs +++ b/Toolkit.UI.Controls.Avalonia/CarouselView/CarouselViewItem.cs @@ -9,4 +9,4 @@ public class CarouselViewItem : { PseudoClasses.Set(":selected", selected); } -} +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/FastNoiseBackground/FastNoiseBackgroundRenderer.cs b/Toolkit.UI.Controls.Avalonia/FastNoiseBackground/FastNoiseBackgroundRenderer.cs index 58a9cf8..76d854e 100644 --- a/Toolkit.UI.Controls.Avalonia/FastNoiseBackground/FastNoiseBackgroundRenderer.cs +++ b/Toolkit.UI.Controls.Avalonia/FastNoiseBackground/FastNoiseBackgroundRenderer.cs @@ -1,9 +1,9 @@ -using Avalonia.Media.Imaging; -using Avalonia.Styling; -using Avalonia; -using SukiUI.Utilities.Background; -using Avalonia.Platform; +using Avalonia; using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Platform; +using Avalonia.Styling; +using SukiUI.Utilities.Background; namespace Toolkit.UI.Controls.Avalonia; @@ -28,7 +28,7 @@ public class FastNoiseBackgroundRenderer public FastNoiseBackgroundRenderer(FastNoiseRendererOptions? options = null) { - FastNoiseRendererOptions opt = options ?? + FastNoiseRendererOptions opt = options ?? new FastNoiseRendererOptions(FastNoiseLite.NoiseType.OpenSimplex2); NoiseGen.SetNoiseType(opt.Type); @@ -130,7 +130,7 @@ public class FastNoiseBackgroundRenderer return ARGB(A(back), resultR, resultG, resultB); } - private static byte G(uint col) => + private static byte G(uint col) => (byte)(col >> 8); private static uint GetBackgroundColour(Color input) @@ -159,7 +159,7 @@ public class FastNoiseBackgroundRenderer private static byte R(uint col) => (byte)(col >> 16); - private static uint ToUInt32(Color colour) => + private static uint ToUInt32(Color colour) => (uint)(colour.A << 24 | colour.R << 16 | colour.G << 8 | colour.B); private static uint WithAlpha(uint col, byte a) => col & 0x00FFFFFF | (uint)(a << 24); diff --git a/Toolkit.UI.Controls.Avalonia/FastNoiseBackground/FastNoiseRendererOptions.cs b/Toolkit.UI.Controls.Avalonia/FastNoiseBackground/FastNoiseRendererOptions.cs index 48052e6..b496aa5 100644 --- a/Toolkit.UI.Controls.Avalonia/FastNoiseBackground/FastNoiseRendererOptions.cs +++ b/Toolkit.UI.Controls.Avalonia/FastNoiseBackground/FastNoiseRendererOptions.cs @@ -20,4 +20,4 @@ public readonly struct FastNoiseRendererOptions( public float XSeed { get; } = xSeed * seedScale; public float YSeed { get; } = ySeed * seedScale; -} +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/FastNoiseBackground/FastRendererBackground.cs b/Toolkit.UI.Controls.Avalonia/FastNoiseBackground/FastRendererBackground.cs index 33dcef4..195894b 100644 --- a/Toolkit.UI.Controls.Avalonia/FastNoiseBackground/FastRendererBackground.cs +++ b/Toolkit.UI.Controls.Avalonia/FastNoiseBackground/FastRendererBackground.cs @@ -7,13 +7,13 @@ using Avalonia.Styling; namespace Toolkit.UI.Controls.Avalonia; -public class FastRendererBackground : +public class FastRendererBackground : Image, IDisposable { private const int ImageWidth = 100; private const int ImageHeight = 100; - private readonly WriteableBitmap bitmap = new(new PixelSize(ImageWidth, ImageHeight), + private readonly WriteableBitmap bitmap = new(new PixelSize(ImageWidth, ImageHeight), new Vector(96, 96), PixelFormat.Bgra8888); private readonly FastNoiseBackgroundRenderer renderer = new(); @@ -29,7 +29,7 @@ public class FastRendererBackground : base.EndInit(); if (Application.Current?.ActualThemeVariant is ThemeVariant theme) { - renderer.UpdateValues((Color)Application.Current.FindResource("SystemAccentColorLight3"), + renderer.UpdateValues((Color)Application.Current.FindResource("SystemAccentColorLight3"), (Color)Application.Current.FindResource("SystemAccentColorDark3"), theme); } @@ -41,4 +41,4 @@ public class FastRendererBackground : GC.SuppressFinalize(this); bitmap.Dispose(); } -} +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/Frame/Frame.cs b/Toolkit.UI.Controls.Avalonia/Frame/Frame.cs index 9d60880..2fbe63d 100644 --- a/Toolkit.UI.Controls.Avalonia/Frame/Frame.cs +++ b/Toolkit.UI.Controls.Avalonia/Frame/Frame.cs @@ -1,8 +1,8 @@ namespace Toolkit.UI.Controls.Avalonia; -public class Frame : +public class Frame : FluentAvalonia.UI.Controls.Frame { - protected override Type StyleKeyOverride => + protected override Type StyleKeyOverride => typeof(FluentAvalonia.UI.Controls.Frame); -} +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/Helpers/ScopedBatchHelper.cs b/Toolkit.UI.Controls.Avalonia/Helpers/ScopedBatchHelper.cs index 5b7b451..adbf661 100644 --- a/Toolkit.UI.Controls.Avalonia/Helpers/ScopedBatchHelper.cs +++ b/Toolkit.UI.Controls.Avalonia/Helpers/ScopedBatchHelper.cs @@ -1,6 +1,7 @@ using Avalonia.Threading; namespace Toolkit.UI.Controls.Avalonia; + public class ScopedBatchHelper { private DispatcherTimer? timer; diff --git a/Toolkit.UI.Controls.Avalonia/NavigationView/NavigationView.cs b/Toolkit.UI.Controls.Avalonia/NavigationView/NavigationView.cs index 6594341..369fee4 100644 --- a/Toolkit.UI.Controls.Avalonia/NavigationView/NavigationView.cs +++ b/Toolkit.UI.Controls.Avalonia/NavigationView/NavigationView.cs @@ -3,6 +3,6 @@ public class NavigationView : FluentAvalonia.UI.Controls.NavigationView { - protected override Type StyleKeyOverride => + protected override Type StyleKeyOverride => typeof(FluentAvalonia.UI.Controls.NavigationView); -} +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/NavigationView/NavigationViewItem.cs b/Toolkit.UI.Controls.Avalonia/NavigationView/NavigationViewItem.cs index cd86847..f65fade 100644 --- a/Toolkit.UI.Controls.Avalonia/NavigationView/NavigationViewItem.cs +++ b/Toolkit.UI.Controls.Avalonia/NavigationView/NavigationViewItem.cs @@ -5,4 +5,4 @@ public class NavigationViewItem : { protected override Type StyleKeyOverride => typeof(FluentAvalonia.UI.Controls.NavigationViewItem); -} +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/BitList.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/BitList.cs index ec8cd02..d725ab4 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/BitList.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/BitList.cs @@ -1,127 +1,124 @@ -using System; using System.Collections; -using System.Collections.Generic; -using System.Linq; namespace Gma.QrCodeNet.Encoding; internal sealed class BitList : IEnumerable { - internal BitList() - { - Count = 0; - List = new List(32); - } + internal BitList() + { + Count = 0; + List = new List(32); + } - internal BitList(IEnumerable byteArray) - { - Count = byteArray.Count(); - List = byteArray.ToList(); - } + internal BitList(IEnumerable byteArray) + { + Count = byteArray.Count(); + List = byteArray.ToList(); + } - internal List List { get; } + internal List List { get; } - internal int Count { get; private set; } + internal int Count { get; private set; } - internal bool this[int index] - { - get - { - if (index < 0 || index >= Count) - { - throw new ArgumentOutOfRangeException(nameof(index), "Index out of range"); - } + internal bool this[int index] + { + get + { + if (index < 0 || index >= Count) + { + throw new ArgumentOutOfRangeException(nameof(index), "Index out of range"); + } - int value_Renamed = List[index >> 3] & 0xff; - return ((value_Renamed >> (7 - (index & 0x7))) & 1) == 1; - } - } + int value_Renamed = List[index >> 3] & 0xff; + return ((value_Renamed >> (7 - (index & 0x7))) & 1) == 1; + } + } - public IEnumerator GetEnumerator() - { - int numBytes = Count >> 3; - int remainder = Count & 0x7; - byte value; - for (int index = 0; index < numBytes; index++) - { - value = List[index]; - for (int shiftNum = 7; shiftNum >= 0; shiftNum--) - { - yield return ((value >> shiftNum) & 1) == 1; - } - } - if (remainder > 0) - { - value = List[numBytes]; - for (int index = 0; index < remainder; index++) - { - yield return ((value >> (7 - index)) & 1) == 1; - } - } - } + public IEnumerator GetEnumerator() + { + int numBytes = Count >> 3; + int remainder = Count & 0x7; + byte value; + for (int index = 0; index < numBytes; index++) + { + value = List[index]; + for (int shiftNum = 7; shiftNum >= 0; shiftNum--) + { + yield return ((value >> shiftNum) & 1) == 1; + } + } + if (remainder > 0) + { + value = List[numBytes]; + for (int index = 0; index < remainder; index++) + { + yield return ((value >> (7 - index)) & 1) == 1; + } + } + } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } - private int ToBit(bool item) - { - return item ? 1 : 0; - } + private int ToBit(bool item) + { + return item ? 1 : 0; + } - internal void Add(bool item) - { - int numBitsinLastByte = Count & 0x7; + internal void Add(bool item) + { + int numBitsinLastByte = Count & 0x7; - // Add one more byte to List when we have no bits in the last byte. - if (numBitsinLastByte == 0) - { - List.Add(0); - } + // Add one more byte to List when we have no bits in the last byte. + if (numBitsinLastByte == 0) + { + List.Add(0); + } - List[Count >> 3] |= (byte)(ToBit(item) << (7 - numBitsinLastByte)); - Count++; - } + List[Count >> 3] |= (byte)(ToBit(item) << (7 - numBitsinLastByte)); + Count++; + } - internal void Add(IEnumerable items) - { - foreach (bool item in items) - { - Add(item); - } - } + internal void Add(IEnumerable items) + { + foreach (bool item in items) + { + Add(item); + } + } - internal void Add(int value, int bitCount) - { - if (bitCount is < 0 or > 32) - { - throw new ArgumentOutOfRangeException(nameof(bitCount), $"{nameof(bitCount)} must be greater than or equal to 0"); - } + internal void Add(int value, int bitCount) + { + if (bitCount is < 0 or > 32) + { + throw new ArgumentOutOfRangeException(nameof(bitCount), $"{nameof(bitCount)} must be greater than or equal to 0"); + } - int numBitsLeft = bitCount; + int numBitsLeft = bitCount; - while (numBitsLeft > 0) - { - if ((Count & 0x7) == 0 && numBitsLeft >= 8) - { - // Add one more byte to List. - byte newByte = (byte)((value >> (numBitsLeft - 8)) & 0xFF); - AppendByte(newByte); - numBitsLeft -= 8; - } - else - { - bool bit = ((value >> (numBitsLeft - 1)) & 1) == 1; - Add(bit); - numBitsLeft--; - } - } - } + while (numBitsLeft > 0) + { + if ((Count & 0x7) == 0 && numBitsLeft >= 8) + { + // Add one more byte to List. + byte newByte = (byte)((value >> (numBitsLeft - 8)) & 0xFF); + AppendByte(newByte); + numBitsLeft -= 8; + } + else + { + bool bit = ((value >> (numBitsLeft - 1)) & 1) == 1; + Add(bit); + numBitsLeft--; + } + } + } - private void AppendByte(byte item) - { - List.Add(item); - Count += 8; - } -} + private void AppendByte(byte item) + { + List.Add(item); + Count += 8; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/BitMatrix.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/BitMatrix.cs index ae0fb52..ed44f68 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/BitMatrix.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/BitMatrix.cs @@ -2,23 +2,23 @@ namespace Gma.QrCodeNet.Encoding; public abstract class BitMatrix { - public abstract int Width { get; } - public abstract int Height { get; } - public abstract bool[,] InternalArray { get; } + public abstract int Width { get; } + public abstract int Height { get; } + public abstract bool[,] InternalArray { get; } - public abstract bool this[int i, int j] { get; set; } + public abstract bool this[int i, int j] { get; set; } - internal void CopyTo(TriStateMatrix target, MatrixRectangle sourceArea, MatrixPoint targetPoint, MatrixStatus mstatus) - { - for (int j = 0; j < sourceArea.Size.Height; j++) - { - for (int i = 0; i < sourceArea.Size.Width; i++) - { - bool value = this[sourceArea.Location.X + i, sourceArea.Location.Y + j]; - target[targetPoint.X + i, targetPoint.Y + j, mstatus] = value; - } - } - } + internal void CopyTo(TriStateMatrix target, MatrixRectangle sourceArea, MatrixPoint targetPoint, MatrixStatus mstatus) + { + for (int j = 0; j < sourceArea.Size.Height; j++) + { + for (int i = 0; i < sourceArea.Size.Width; i++) + { + bool value = this[sourceArea.Location.X + i, sourceArea.Location.Y + j]; + target[targetPoint.X + i, targetPoint.Y + j, mstatus] = value; + } + } + } - internal void CopyTo(TriStateMatrix target, MatrixPoint targetPoint, MatrixStatus mstatus) => CopyTo(target, new MatrixRectangle(new MatrixPoint(0, 0), new MatrixSize(Width, Height)), targetPoint, mstatus); -} + internal void CopyTo(TriStateMatrix target, MatrixPoint targetPoint, MatrixStatus mstatus) => CopyTo(target, new MatrixRectangle(new MatrixPoint(0, 0), new MatrixSize(Width, Height)), targetPoint, mstatus); +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/BitMatrixBase.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/BitMatrixBase.cs index aaa4ba5..6a577e2 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/BitMatrixBase.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/BitMatrixBase.cs @@ -2,30 +2,30 @@ namespace Gma.QrCodeNet.Encoding; public abstract class BitMatrixBase : BitMatrix { - protected BitMatrixBase(int width, bool[,] internalArray) - { - Width = width; - InternalArray = internalArray; - } + protected BitMatrixBase(int width, bool[,] internalArray) + { + Width = width; + InternalArray = internalArray; + } - protected BitMatrixBase(bool[,] internalArray) - { - InternalArray = internalArray; - int width = internalArray.GetLength(0); - Width = width; - } + protected BitMatrixBase(bool[,] internalArray) + { + InternalArray = internalArray; + int width = internalArray.GetLength(0); + Width = width; + } - public override bool[,] InternalArray { get; } + public override bool[,] InternalArray { get; } - public override int Width { get; } + public override int Width { get; } - public static bool CanCreate(bool[,] internalArray) - { - if (internalArray is null) - { - return false; - } + public static bool CanCreate(bool[,] internalArray) + { + if (internalArray is null) + { + return false; + } - return internalArray.GetLength(0) == internalArray.GetLength(1); - } -} + return internalArray.GetLength(0) == internalArray.GetLength(1); + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/CharCountIndicatorTable.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/CharCountIndicatorTable.cs index 9a6c752..c66d6b2 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/CharCountIndicatorTable.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/CharCountIndicatorTable.cs @@ -1,48 +1,46 @@ -using System; - namespace Gma.QrCodeNet.Encoding.DataEncodation; public static class CharCountIndicatorTable { - /// ISO/IEC 18004:2000 Table 3 Page 18 - public static int[] GetCharCountIndicatorSet() - { - return new int[] { 8, 16, 16 }; - } + /// ISO/IEC 18004:2000 Table 3 Page 18 + public static int[] GetCharCountIndicatorSet() + { + return new int[] { 8, 16, 16 }; + } - public static int GetBitCountInCharCountIndicator(int version) - { - int[] charCountIndicatorSet = GetCharCountIndicatorSet(); - int versionGroup = GetVersionGroup(version); + public static int GetBitCountInCharCountIndicator(int version) + { + int[] charCountIndicatorSet = GetCharCountIndicatorSet(); + int versionGroup = GetVersionGroup(version); - return charCountIndicatorSet[versionGroup]; - } + return charCountIndicatorSet[versionGroup]; + } - /// - /// Used to define length of the Character Count Indicator - /// - /// Returns the 0 based index of the row from Chapter 8.4 Data encodation, Table 3 — Number of bits in Character Count Indicator. - private static int GetVersionGroup(int version) - { - if (version > 40) - { - throw new InvalidOperationException($"Unexpected version: {version}."); - } - else if (version >= 27) - { - return 2; - } - else if (version >= 10) - { - return 1; - } - else if (version > 0) - { - return 0; - } - else - { - throw new InvalidOperationException($"Unexpected version: {version}."); - } - } -} + /// + /// Used to define length of the Character Count Indicator + /// + /// Returns the 0 based index of the row from Chapter 8.4 Data encodation, Table 3 — Number of bits in Character Count Indicator. + private static int GetVersionGroup(int version) + { + if (version > 40) + { + throw new InvalidOperationException($"Unexpected version: {version}."); + } + else if (version >= 27) + { + return 2; + } + else if (version >= 10) + { + return 1; + } + else if (version > 0) + { + return 0; + } + else + { + throw new InvalidOperationException($"Unexpected version: {version}."); + } + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/DataEncode.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/DataEncode.cs index 2a3c222..f36ec90 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/DataEncode.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/DataEncode.cs @@ -1,4 +1,3 @@ -using System; using Gma.QrCodeNet.Encoding.DataEncodation.InputRecognition; using Gma.QrCodeNet.Encoding.Terminate; using Gma.QrCodeNet.Encoding.Versions; @@ -10,53 +9,53 @@ namespace Gma.QrCodeNet.Encoding.DataEncodation; /// Which uses sub functions under several different namespaces internal static class DataEncode { - internal static EncodationStruct Encode(string content, ErrorCorrectionLevel ecLevel) - { - RecognitionStruct recognitionResult = InputRecognise.Recognise(content); - EncoderBase encoderBase = CreateEncoder(recognitionResult.EncodingName); + internal static EncodationStruct Encode(string content, ErrorCorrectionLevel ecLevel) + { + RecognitionStruct recognitionResult = InputRecognise.Recognise(content); + EncoderBase encoderBase = CreateEncoder(recognitionResult.EncodingName); - BitList encodeContent = encoderBase.GetDataBits(content); + BitList encodeContent = encoderBase.GetDataBits(content); - int encodeContentLength = encodeContent.Count; + int encodeContentLength = encodeContent.Count; - VersionControlStruct vcStruct = - VersionControl.InitialSetup(encodeContentLength, ecLevel, recognitionResult.EncodingName); + VersionControlStruct vcStruct = + VersionControl.InitialSetup(encodeContentLength, ecLevel, recognitionResult.EncodingName); - BitList dataCodewords = new(); + BitList dataCodewords = new(); - // Eci header - if (vcStruct.IsContainECI && vcStruct.ECIHeader is { }) - { - dataCodewords.Add(vcStruct.ECIHeader); - } + // Eci header + if (vcStruct.IsContainECI && vcStruct.ECIHeader is { }) + { + dataCodewords.Add(vcStruct.ECIHeader); + } - // Header - dataCodewords.Add(encoderBase.GetModeIndicator()); - int numLetter = encodeContentLength >> 3; - dataCodewords.Add(encoderBase.GetCharCountIndicator(numLetter, vcStruct.VersionDetail.Version)); + // Header + dataCodewords.Add(encoderBase.GetModeIndicator()); + int numLetter = encodeContentLength >> 3; + dataCodewords.Add(encoderBase.GetCharCountIndicator(numLetter, vcStruct.VersionDetail.Version)); - // Data - dataCodewords.Add(encodeContent); + // Data + dataCodewords.Add(encodeContent); - // Terminator Padding - dataCodewords.TerminateBites(dataCodewords.Count, vcStruct.VersionDetail.NumDataBytes); + // Terminator Padding + dataCodewords.TerminateBites(dataCodewords.Count, vcStruct.VersionDetail.NumDataBytes); - int dataCodewordsCount = dataCodewords.Count; - if ((dataCodewordsCount & 0x7) != 0) - { - throw new ArgumentException($"{nameof(dataCodewords)} is not byte sized."); - } - else if (dataCodewordsCount >> 3 != vcStruct.VersionDetail.NumDataBytes) - { - throw new ArgumentException($"{nameof(dataCodewords)} num of bytes not equal to {nameof(vcStruct.VersionDetail.NumDataBytes)} for current version"); - } + int dataCodewordsCount = dataCodewords.Count; + if ((dataCodewordsCount & 0x7) != 0) + { + throw new ArgumentException($"{nameof(dataCodewords)} is not byte sized."); + } + else if (dataCodewordsCount >> 3 != vcStruct.VersionDetail.NumDataBytes) + { + throw new ArgumentException($"{nameof(dataCodewords)} num of bytes not equal to {nameof(vcStruct.VersionDetail.NumDataBytes)} for current version"); + } - var encStruct = new EncodationStruct(vcStruct, dataCodewords); - return encStruct; - } + var encStruct = new EncodationStruct(vcStruct, dataCodewords); + return encStruct; + } - private static EncoderBase CreateEncoder(string encodingName) - { - return new EightBitByteEncoder(encodingName); - } -} + private static EncoderBase CreateEncoder(string encodingName) + { + return new EightBitByteEncoder(encodingName); + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/ECISet.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/ECISet.cs index 9afdc5d..1709b4c 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/ECISet.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/ECISet.cs @@ -1,255 +1,252 @@ -using System; -using System.Collections.Generic; - namespace Gma.QrCodeNet.Encoding.DataEncodation; public sealed class ECISet { - /// - /// ISO/IEC 18004:2006 Chapter 6.4.2 Mode indicator = 0111 Page 23 - /// - private const int ECIMode = 7; + /// + /// ISO/IEC 18004:2006 Chapter 6.4.2 Mode indicator = 0111 Page 23 + /// + private const int ECIMode = 7; - private const int ECIIndicatorNumBits = 4; + private const int ECIIndicatorNumBits = 4; - private Dictionary? _nameToValue; - private Dictionary? _valueToName; + private Dictionary? _nameToValue; + private Dictionary? _valueToName; - /// - /// Initialize ECI Set. - /// - /// AppendOption is enum under ECISet - /// Use NameToValue during Encode. ValueToName during Decode - internal ECISet(AppendOption option) - { - Initialize(option); - } + /// + /// Initialize ECI Set. + /// + /// AppendOption is enum under ECISet + /// Use NameToValue during Encode. ValueToName during Decode + internal ECISet(AppendOption option) + { + Initialize(option); + } - public enum AppendOption - { - NameToValue, - ValueToName, - Both - } + public enum AppendOption + { + NameToValue, + ValueToName, + Both + } - /// - /// Length indicator for number of ECI codewords - /// - /// ISO/IEC 18004:2006 Chapter 6.4.2 Page 24. - /// 1 codeword length = 0. Any additional codeword add 1 to front. Eg: 3 = 110 - /// Bits required for each one is: - /// one = 1, two = 2, three = 3 - private enum ECICodewordsLength - { - One = 0, - Two = 2, - Three = 6 - } + /// + /// Length indicator for number of ECI codewords + /// + /// ISO/IEC 18004:2006 Chapter 6.4.2 Page 24. + /// 1 codeword length = 0. Any additional codeword add 1 to front. Eg: 3 = 110 + /// Bits required for each one is: + /// one = 1, two = 2, three = 3 + private enum ECICodewordsLength + { + One = 0, + Two = 2, + Three = 6 + } - /// ISO/IEC 18004:2006E ECI Designator Page 24 - /// Range: 0 ~ 999999 - /// Number of Codewords(Byte) for ECI Assignment Value - private static int NumOfCodewords(int eCIValue) - { - if (eCIValue is >= 0 and <= 127) - { - return 1; - } - else if (eCIValue is > 127 and <= 16383) - { - return 2; - } - else if (eCIValue is > 16383 and <= 999999) - { - return 3; - } - else - { - throw new ArgumentOutOfRangeException($"{nameof(eCIValue)} should be in range: 0 to 999999."); - } - } + /// ISO/IEC 18004:2006E ECI Designator Page 24 + /// Range: 0 ~ 999999 + /// Number of Codewords(Byte) for ECI Assignment Value + private static int NumOfCodewords(int eCIValue) + { + if (eCIValue is >= 0 and <= 127) + { + return 1; + } + else if (eCIValue is > 127 and <= 16383) + { + return 2; + } + else if (eCIValue is > 16383 and <= 999999) + { + return 3; + } + else + { + throw new ArgumentOutOfRangeException($"{nameof(eCIValue)} should be in range: 0 to 999999."); + } + } - /// ISO/IEC 18004:2006E ECI Designator Page 24 - /// Range: 0 ~ 999999 - /// Number of bits for ECI Assignment Value - private static int NumOfAssignmentBits(int eCIValue) => NumOfCodewords(eCIValue) * 8; + /// ISO/IEC 18004:2006E ECI Designator Page 24 + /// Range: 0 ~ 999999 + /// Number of bits for ECI Assignment Value + private static int NumOfAssignmentBits(int eCIValue) => NumOfCodewords(eCIValue) * 8; - private void AppendECI(string name, int value, AppendOption option) - { - switch (option) - { - case AppendOption.NameToValue: - _nameToValue?.Add(name, value); - break; + private void AppendECI(string name, int value, AppendOption option) + { + switch (option) + { + case AppendOption.NameToValue: + _nameToValue?.Add(name, value); + break; - case AppendOption.ValueToName: - _valueToName?.Add(value, name); - break; + case AppendOption.ValueToName: + _valueToName?.Add(value, name); + break; - case AppendOption.Both: - _nameToValue?.Add(name, value); - _valueToName?.Add(value, name); - break; + case AppendOption.Both: + _nameToValue?.Add(name, value); + _valueToName?.Add(value, name); + break; - default: - throw new InvalidOperationException($"There is no such {nameof(AppendOption)}."); - } - } + default: + throw new InvalidOperationException($"There is no such {nameof(AppendOption)}."); + } + } - private void Initialize(AppendOption option) - { - switch (option) - { - case AppendOption.NameToValue: - _nameToValue = new Dictionary(); - break; + private void Initialize(AppendOption option) + { + switch (option) + { + case AppendOption.NameToValue: + _nameToValue = new Dictionary(); + break; - case AppendOption.ValueToName: - _valueToName = new Dictionary(); - break; + case AppendOption.ValueToName: + _valueToName = new Dictionary(); + break; - case AppendOption.Both: - _nameToValue = new Dictionary(); - _valueToName = new Dictionary(); - break; + case AppendOption.Both: + _nameToValue = new Dictionary(); + _valueToName = new Dictionary(); + break; - default: - throw new InvalidOperationException($"There is no such {nameof(AppendOption)}."); - } + default: + throw new InvalidOperationException($"There is no such {nameof(AppendOption)}."); + } - // ECI table. Source 01 URL: http://strokescribe.com/en/ECI.html - // ECI table. Source 02 URL: http://lab.must.or.kr/Extended-Channel-Interpretations-ECI-Encoding.ashx - // ToDo. Fill up remaining missing table. - AppendECI("iso-8859-1", 1, option); - AppendECI("IBM437", 2, option); + // ECI table. Source 01 URL: http://strokescribe.com/en/ECI.html + // ECI table. Source 02 URL: http://lab.must.or.kr/Extended-Channel-Interpretations-ECI-Encoding.ashx + // ToDo. Fill up remaining missing table. + AppendECI("iso-8859-1", 1, option); + AppendECI("IBM437", 2, option); - // AppendECI("iso-8859-1", 3, option); //ECI value 1 is default encoding. - AppendECI("iso-8859-2", 4, option); - AppendECI("iso-8859-3", 5, option); - AppendECI("iso-8859-4", 6, option); - AppendECI("iso-8859-5", 7, option); - AppendECI("iso-8859-6", 8, option); - AppendECI("iso-8859-7", 9, option); - AppendECI("iso-8859-8", 10, option); - AppendECI("iso-8859-9", 11, option); - AppendECI("windows-874", 13, option); - AppendECI("iso-8859-13", 15, option); - AppendECI("iso-8859-15", 17, option); - AppendECI("shift_jis", 20, option); - AppendECI("utf-8", 26, option); - } + // AppendECI("iso-8859-1", 3, option); //ECI value 1 is default encoding. + AppendECI("iso-8859-2", 4, option); + AppendECI("iso-8859-3", 5, option); + AppendECI("iso-8859-4", 6, option); + AppendECI("iso-8859-5", 7, option); + AppendECI("iso-8859-6", 8, option); + AppendECI("iso-8859-7", 9, option); + AppendECI("iso-8859-8", 10, option); + AppendECI("iso-8859-9", 11, option); + AppendECI("windows-874", 13, option); + AppendECI("iso-8859-13", 15, option); + AppendECI("iso-8859-15", 17, option); + AppendECI("shift_jis", 20, option); + AppendECI("utf-8", 26, option); + } - /// ISO/IEC 18004:2006E ECI Designator Page 24 - /// Range: 0 ~ 999999 - /// Number of bits for ECI Header - internal static int NumOfECIHeaderBits(int eCIValue) => NumOfAssignmentBits(eCIValue) + 4; + /// ISO/IEC 18004:2006E ECI Designator Page 24 + /// Range: 0 ~ 999999 + /// Number of bits for ECI Header + internal static int NumOfECIHeaderBits(int eCIValue) => NumOfAssignmentBits(eCIValue) + 4; - internal int GetECIValueByName(string encodingName) - { - if (_nameToValue is null) - { - Initialize(AppendOption.NameToValue); - } + internal int GetECIValueByName(string encodingName) + { + if (_nameToValue is null) + { + Initialize(AppendOption.NameToValue); + } - if (_nameToValue!.TryGetValue(encodingName, out int eCIValue)) - { - return eCIValue; - } - else - { - throw new ArgumentOutOfRangeException($"ECI does not contain encoding: {encodingName}."); - } - } + if (_nameToValue!.TryGetValue(encodingName, out int eCIValue)) + { + return eCIValue; + } + else + { + throw new ArgumentOutOfRangeException($"ECI does not contain encoding: {encodingName}."); + } + } - internal string GetECINameByValue(int eCIValue) - { - if (_valueToName is null) - { - Initialize(AppendOption.ValueToName); - } + internal string GetECINameByValue(int eCIValue) + { + if (_valueToName is null) + { + Initialize(AppendOption.ValueToName); + } - if (_valueToName!.TryGetValue(eCIValue, out var eCIName)) - { - return eCIName; - } - else - { - throw new ArgumentOutOfRangeException($"ECI does not contain value: {eCIValue}."); - } - } + if (_valueToName!.TryGetValue(eCIValue, out var eCIName)) + { + return eCIName; + } + else + { + throw new ArgumentOutOfRangeException($"ECI does not contain value: {eCIValue}."); + } + } - /// ECI table in Dictionary collection - public Dictionary? GetECITable() - { - if (_nameToValue is null) - { - Initialize(AppendOption.NameToValue); - } + /// ECI table in Dictionary collection + public Dictionary? GetECITable() + { + if (_nameToValue is null) + { + Initialize(AppendOption.NameToValue); + } - return _nameToValue; - } + return _nameToValue; + } - public bool ContainsECIName(string encodingName) - { - if (_nameToValue is null) - { - Initialize(AppendOption.NameToValue); - } + public bool ContainsECIName(string encodingName) + { + if (_nameToValue is null) + { + Initialize(AppendOption.NameToValue); + } - return _nameToValue!.ContainsKey(encodingName); - } + return _nameToValue!.ContainsKey(encodingName); + } - public bool ContainsECIValue(int eciValue) - { - if (_valueToName is null) - { - Initialize(AppendOption.ValueToName); - } + public bool ContainsECIValue(int eciValue) + { + if (_valueToName is null) + { + Initialize(AppendOption.ValueToName); + } - return _valueToName!.ContainsKey(eciValue); - } + return _valueToName!.ContainsKey(eciValue); + } - /// ISO/IEC 18004:2006 Chapter 6.4.2 Page 24. - internal BitList GetECIHeader(string encodingName) - { - int eciValue = GetECIValueByName(encodingName); + /// ISO/IEC 18004:2006 Chapter 6.4.2 Page 24. + internal BitList GetECIHeader(string encodingName) + { + int eciValue = GetECIValueByName(encodingName); - BitList dataBits = new() - { - { ECIMode, ECIIndicatorNumBits } - }; + BitList dataBits = new() + { + { ECIMode, ECIIndicatorNumBits } + }; - int eciAssignmentByte = NumOfCodewords(eciValue); + int eciAssignmentByte = NumOfCodewords(eciValue); - // Number of bits = Num codewords indicator + codeword value = Number of codewords * 8 - // Chapter 6.4.2.1 ECI Designator ISOIEC 18004:2006 Page 24 - int eciAssignmentBits; - switch (eciAssignmentByte) - { - case 1: - // Indicator = 0. Page 24. Chapter 6.4.2.1 - dataBits.Add((int)ECICodewordsLength.One, 1); - eciAssignmentBits = (eciAssignmentByte * 8) - 1; - break; + // Number of bits = Num codewords indicator + codeword value = Number of codewords * 8 + // Chapter 6.4.2.1 ECI Designator ISOIEC 18004:2006 Page 24 + int eciAssignmentBits; + switch (eciAssignmentByte) + { + case 1: + // Indicator = 0. Page 24. Chapter 6.4.2.1 + dataBits.Add((int)ECICodewordsLength.One, 1); + eciAssignmentBits = (eciAssignmentByte * 8) - 1; + break; - case 2: - // Indicator = 10. Page 24. Chapter 6.4.2.1 - dataBits.Add((int)ECICodewordsLength.Two, 2); - eciAssignmentBits = (eciAssignmentByte * 8) - 2; - break; + case 2: + // Indicator = 10. Page 24. Chapter 6.4.2.1 + dataBits.Add((int)ECICodewordsLength.Two, 2); + eciAssignmentBits = (eciAssignmentByte * 8) - 2; + break; - case 3: - // Indicator = 110. Page 24. Chapter 6.4.2.1 - dataBits.Add((int)ECICodewordsLength.Three, 3); - eciAssignmentBits = (eciAssignmentByte * 8) - 3; - break; + case 3: + // Indicator = 110. Page 24. Chapter 6.4.2.1 + dataBits.Add((int)ECICodewordsLength.Three, 3); + eciAssignmentBits = (eciAssignmentByte * 8) - 3; + break; - default: - throw new InvalidOperationException("Assignment Codewords should be either 1, 2 or 3."); - } + default: + throw new InvalidOperationException("Assignment Codewords should be either 1, 2 or 3."); + } - dataBits.Add(eciValue, eciAssignmentBits); + dataBits.Add(eciValue, eciAssignmentBits); - return dataBits; - } -} + return dataBits; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/EightBitByteEncoder.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/EightBitByteEncoder.cs index e8ffe7b..866f94b 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/EightBitByteEncoder.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/EightBitByteEncoder.cs @@ -1,5 +1,3 @@ -using System; - namespace Gma.QrCodeNet.Encoding.DataEncodation; /// @@ -14,67 +12,67 @@ namespace Gma.QrCodeNet.Encoding.DataEncodation; /// ISO/IEC 18004:2000 Chapter 8.4.4 Page 22 internal class EightBitByteEncoder : EncoderBase { - private const string DefaultEncoding = QRCodeConstantVariable.DefaultEncoding; + private const string DefaultEncoding = QRCodeConstantVariable.DefaultEncoding; - /// - /// Bitcount, Chapter 8.4.4, P.24 - /// - private const int EightBitByteBitcount = 8; + /// + /// Bitcount, Chapter 8.4.4, P.24 + /// + private const int EightBitByteBitcount = 8; - /// - /// EightBitByte encoder's encoding will change according to different region - /// - /// Default encoding is "iso-8859-1" - internal EightBitByteEncoder(string encoding) : base() - { - Encoding = encoding ?? DefaultEncoding; - } + /// + /// EightBitByte encoder's encoding will change according to different region + /// + /// Default encoding is "iso-8859-1" + internal EightBitByteEncoder(string encoding) : base() + { + Encoding = encoding ?? DefaultEncoding; + } - internal EightBitByteEncoder() : base() - { - Encoding = DefaultEncoding; - } + internal EightBitByteEncoder() : base() + { + Encoding = DefaultEncoding; + } - internal string Encoding { get; private set; } + internal string Encoding { get; private set; } - protected byte[] EncodeContent(string content, string encoding) => System.Text.Encoding.GetEncoding(encoding).GetBytes(content); + protected byte[] EncodeContent(string content, string encoding) => System.Text.Encoding.GetEncoding(encoding).GetBytes(content); - internal override BitList GetDataBits(string content) - { - var eciSet = new ECISet(ECISet.AppendOption.NameToValue); - if (!eciSet.ContainsECIName(Encoding)) - { - throw new ArgumentOutOfRangeException( - nameof(Encoding), - $"Current ECI table does not support this encoding. Please check {nameof(ECISet)} class for more info."); - } + internal override BitList GetDataBits(string content) + { + var eciSet = new ECISet(ECISet.AppendOption.NameToValue); + if (!eciSet.ContainsECIName(Encoding)) + { + throw new ArgumentOutOfRangeException( + nameof(Encoding), + $"Current ECI table does not support this encoding. Please check {nameof(ECISet)} class for more info."); + } - byte[] contentBytes = EncodeContent(content, Encoding); + byte[] contentBytes = EncodeContent(content, Encoding); - return GetDataBitsByByteArray(contentBytes, Encoding); - } + return GetDataBitsByByteArray(contentBytes, Encoding); + } - internal BitList GetDataBitsByByteArray(byte[] encodeContent, string encodingName) - { - var dataBits = new BitList(); + internal BitList GetDataBitsByByteArray(byte[] encodeContent, string encodingName) + { + var dataBits = new BitList(); - // Current plan for UTF8 support is put Byte order Mark in front of content byte. - // Also include ECI header before encoding header. Which will be add with encoding header. - if (encodingName == "utf-8") - { - byte[] utf8BOM = QRCodeConstantVariable.UTF8ByteOrderMark; - for (int index = 0; index < utf8BOM.Length; index++) - { - dataBits.Add(utf8BOM[index], EightBitByteBitcount); - } - } + // Current plan for UTF8 support is put Byte order Mark in front of content byte. + // Also include ECI header before encoding header. Which will be add with encoding header. + if (encodingName == "utf-8") + { + byte[] utf8BOM = QRCodeConstantVariable.UTF8ByteOrderMark; + for (int index = 0; index < utf8BOM.Length; index++) + { + dataBits.Add(utf8BOM[index], EightBitByteBitcount); + } + } - for (int index = 0; index < encodeContent.Length; index++) - { - dataBits.Add(encodeContent[index], EightBitByteBitcount); - } - return dataBits; - } + for (int index = 0; index < encodeContent.Length; index++) + { + dataBits.Add(encodeContent[index], EightBitByteBitcount); + } + return dataBits; + } - protected override int GetBitCountInCharCountIndicator(int version) => CharCountIndicatorTable.GetBitCountInCharCountIndicator(version); -} + protected override int GetBitCountInCharCountIndicator(int version) => CharCountIndicatorTable.GetBitCountInCharCountIndicator(version); +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/EncodationStruct.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/EncodationStruct.cs index f9a854f..ec31c7a 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/EncodationStruct.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/EncodationStruct.cs @@ -4,12 +4,12 @@ namespace Gma.QrCodeNet.Encoding.DataEncodation; internal struct EncodationStruct { - internal EncodationStruct(VersionControlStruct vcStruct, BitList dataCodewords) - { - VersionDetail = vcStruct.VersionDetail; - DataCodewords = dataCodewords; - } + internal EncodationStruct(VersionControlStruct vcStruct, BitList dataCodewords) + { + VersionDetail = vcStruct.VersionDetail; + DataCodewords = dataCodewords; + } - internal VersionDetail VersionDetail { get; set; } - internal BitList DataCodewords { get; set; } -} + internal VersionDetail VersionDetail { get; set; } + internal BitList DataCodewords { get; set; } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/EncoderBase.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/EncoderBase.cs index b04dc73..454fab6 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/EncoderBase.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/EncoderBase.cs @@ -2,45 +2,45 @@ namespace Gma.QrCodeNet.Encoding.DataEncodation; public abstract class EncoderBase { - internal EncoderBase() - { - } + internal EncoderBase() + { + } - protected virtual int GetDataLength(string content) => content.Length; + protected virtual int GetDataLength(string content) => content.Length; - /// - /// Returns the bit representation of input data. - /// - internal abstract BitList GetDataBits(string content); + /// + /// Returns the bit representation of input data. + /// + internal abstract BitList GetDataBits(string content); - /// - /// Returns bit representation of Modevalue. - /// - /// See Chapter 8.4 Data encodation, Table 2 — Mode indicators - internal BitList GetModeIndicator() - { - BitList modeIndicatorBits = new() - { - { 0001 << 2, 4 } - }; - return modeIndicatorBits; - } + /// + /// Returns bit representation of Modevalue. + /// + /// See Chapter 8.4 Data encodation, Table 2 — Mode indicators + internal BitList GetModeIndicator() + { + BitList modeIndicatorBits = new() + { + { 0001 << 2, 4 } + }; + return modeIndicatorBits; + } - internal BitList GetCharCountIndicator(int characterCount, int version) - { - BitList characterCountBits = new(); - int bitCount = GetBitCountInCharCountIndicator(version); - characterCountBits.Add(characterCount, bitCount); - return characterCountBits; - } + internal BitList GetCharCountIndicator(int characterCount, int version) + { + BitList characterCountBits = new(); + int bitCount = GetBitCountInCharCountIndicator(version); + characterCountBits.Add(characterCount, bitCount); + return characterCountBits; + } - /// - /// Defines the length of the Character Count Indicator, - /// which varies according to the mode and the symbol version in use - /// - /// Number of bits in Character Count Indicator. - /// - /// See Chapter 8.4 Data encodation, Table 3 — Number of bits in Character Count Indicator. - /// - protected abstract int GetBitCountInCharCountIndicator(int version); -} + /// + /// Defines the length of the Character Count Indicator, + /// which varies according to the mode and the symbol version in use + /// + /// Number of bits in Character Count Indicator. + /// + /// See Chapter 8.4 Data encodation, Table 3 — Number of bits in Character Count Indicator. + /// + protected abstract int GetBitCountInCharCountIndicator(int version); +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/InputRecognition/InputRecognise.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/InputRecognition/InputRecognise.cs index b08a97e..b751a85 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/InputRecognition/InputRecognise.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/InputRecognition/InputRecognise.cs @@ -1,57 +1,54 @@ -using System; -using System.Collections.Generic; - namespace Gma.QrCodeNet.Encoding.DataEncodation.InputRecognition; public static class InputRecognise { - public static RecognitionStruct Recognise(string content) - { - string encodingName = EightBitByteRecognision(content, 0, content.Length); - return new RecognitionStruct(encodingName); - } + public static RecognitionStruct Recognise(string content) + { + string encodingName = EightBitByteRecognision(content, 0, content.Length); + return new RecognitionStruct(encodingName); + } - private static string EightBitByteRecognision(string content, int startPos, int contentLength) - { - if(string.IsNullOrEmpty(content)) + private static string EightBitByteRecognision(string content, int startPos, int contentLength) + { + if (string.IsNullOrEmpty(content)) throw new ArgumentNullException(nameof(content)); - var eciSets = new ECISet(ECISet.AppendOption.NameToValue); + var eciSets = new ECISet(ECISet.AppendOption.NameToValue); - Dictionary? eciSet = eciSets.GetECITable(); + Dictionary? eciSet = eciSets.GetECITable(); - if(eciSet == null) + if (eciSet == null) return string.Empty; - // we will not check for utf8 encoding. - eciSet.Remove(QRCodeConstantVariable.UTF8Encoding); - eciSet.Remove(QRCodeConstantVariable.DefaultEncoding); + // we will not check for utf8 encoding. + eciSet.Remove(QRCodeConstantVariable.UTF8Encoding); + eciSet.Remove(QRCodeConstantVariable.DefaultEncoding); - int scanPos = startPos; + int scanPos = startPos; - // default encoding as priority - scanPos = ModeEncodeCheck.TryEncodeEightBitByte(content, QRCodeConstantVariable.DefaultEncoding, scanPos, contentLength); - if (scanPos == -1) - { - return QRCodeConstantVariable.DefaultEncoding; - } + // default encoding as priority + scanPos = ModeEncodeCheck.TryEncodeEightBitByte(content, QRCodeConstantVariable.DefaultEncoding, scanPos, contentLength); + if (scanPos == -1) + { + return QRCodeConstantVariable.DefaultEncoding; + } - foreach (KeyValuePair kvp in eciSet) - { - scanPos = ModeEncodeCheck.TryEncodeEightBitByte(content, kvp.Key, scanPos, contentLength); - if (scanPos == -1) - { - return kvp.Key; - } - } + foreach (KeyValuePair kvp in eciSet) + { + scanPos = ModeEncodeCheck.TryEncodeEightBitByte(content, kvp.Key, scanPos, contentLength); + if (scanPos == -1) + { + return kvp.Key; + } + } - if (scanPos == -1) - { - throw new ArgumentException("foreach Loop check give wrong result."); - } - else - { - return QRCodeConstantVariable.UTF8Encoding; - } - } -} + if (scanPos == -1) + { + throw new ArgumentException("foreach Loop check give wrong result."); + } + else + { + return QRCodeConstantVariable.UTF8Encoding; + } + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/InputRecognition/ModeEncodeCheck.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/InputRecognition/ModeEncodeCheck.cs index ac9d215..f480a49 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/InputRecognition/ModeEncodeCheck.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/InputRecognition/ModeEncodeCheck.cs @@ -1,72 +1,70 @@ -using System; - namespace Gma.QrCodeNet.Encoding.DataEncodation.InputRecognition; public static class ModeEncodeCheck { - /// - /// Encoding.GetEncoding.GetBytes will transform char to 0x3F if that char not belong to current encoding table. - /// 0x3F is '?' - /// - private const int QuestionMarkChar = 0x3F; + /// + /// Encoding.GetEncoding.GetBytes will transform char to 0x3F if that char not belong to current encoding table. + /// 0x3F is '?' + /// + private const int QuestionMarkChar = 0x3F; - /// - /// Use given encoding to check input string from starting position. If encoding table is suitable solution. - /// it will return -1. Else it will return failed encoding position. - /// - /// Input string - /// Encoding name. Check ECI table - /// Returns -1 if from starting position to end encoding success. Else returns fail position - internal static int TryEncodeEightBitByte(string content, string encodingName, int startingPosition, int contentLength) - { - if (string.IsNullOrEmpty(content)) - { - throw new IndexOutOfRangeException("Input cannot be null or empty."); - } + /// + /// Use given encoding to check input string from starting position. If encoding table is suitable solution. + /// it will return -1. Else it will return failed encoding position. + /// + /// Input string + /// Encoding name. Check ECI table + /// Returns -1 if from starting position to end encoding success. Else returns fail position + internal static int TryEncodeEightBitByte(string content, string encodingName, int startingPosition, int contentLength) + { + if (string.IsNullOrEmpty(content)) + { + throw new IndexOutOfRangeException("Input cannot be null or empty."); + } - System.Text.Encoding encoding; - try - { - encoding = System.Text.Encoding.GetEncoding(encodingName); - } - catch (ArgumentException) - { - return startingPosition; - } + System.Text.Encoding encoding; + try + { + encoding = System.Text.Encoding.GetEncoding(encodingName); + } + catch (ArgumentException) + { + return startingPosition; + } - char[] currentChar = new char[1]; - byte[] bytes; + char[] currentChar = new char[1]; + byte[] bytes; - for (int index = startingPosition; index < contentLength; index++) - { - currentChar[0] = content[index]; - bytes = encoding.GetBytes(currentChar); - int length = bytes.Length; - if (currentChar[0] != '?' && length == 1 && bytes[0] == QuestionMarkChar) - { - return index; - } - else if (length > 1) - { - return index; - } - } + for (int index = startingPosition; index < contentLength; index++) + { + currentChar[0] = content[index]; + bytes = encoding.GetBytes(currentChar); + int length = bytes.Length; + if (currentChar[0] != '?' && length == 1 && bytes[0] == QuestionMarkChar) + { + return index; + } + else if (length > 1) + { + return index; + } + } - for (int index = 0; index < startingPosition; index++) - { - currentChar[0] = content[index]; - bytes = encoding.GetBytes(currentChar); - int length = bytes.Length; - if (currentChar[0] != '?' && length == 1 && bytes[0] == QuestionMarkChar) - { - return index; - } - else if (length > 1) - { - return index; - } - } + for (int index = 0; index < startingPosition; index++) + { + currentChar[0] = content[index]; + bytes = encoding.GetBytes(currentChar); + int length = bytes.Length; + if (currentChar[0] != '?' && length == 1 && bytes[0] == QuestionMarkChar) + { + return index; + } + else if (length > 1) + { + return index; + } + } - return -1; - } -} + return -1; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/InputRecognition/RecognitionStruct.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/InputRecognition/RecognitionStruct.cs index 256b3cf..986c923 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/InputRecognition/RecognitionStruct.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/InputRecognition/RecognitionStruct.cs @@ -2,11 +2,11 @@ namespace Gma.QrCodeNet.Encoding.DataEncodation.InputRecognition; public struct RecognitionStruct { - public RecognitionStruct(string encodingName) - : this() - { - EncodingName = encodingName; - } + public RecognitionStruct(string encodingName) + : this() + { + EncodingName = encodingName; + } - public string EncodingName { get; private set; } -} + public string EncodingName { get; private set; } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/EncodingRegion/BCHCalculator.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/EncodingRegion/BCHCalculator.cs index a9fec96..a4434f7 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/EncodingRegion/BCHCalculator.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/EncodingRegion/BCHCalculator.cs @@ -2,59 +2,59 @@ namespace Gma.QrCodeNet.Encoding.EncodingRegion; internal static class BCHCalculator { - /// - /// Calculate int length by search for Most significant bit - /// - /// Input Number - /// Most significant bit - internal static int PosMSB(int num) => num == 0 ? 0 : BinarySearchPos(num, 0, 32) + 1; + /// + /// Calculate int length by search for Most significant bit + /// + /// Input Number + /// Most significant bit + internal static int PosMSB(int num) => num == 0 ? 0 : BinarySearchPos(num, 0, 32) + 1; - /// - /// Search for right side bit of Most significant bit - /// - /// Input number - /// Lower boundary. At start should be 0 - /// Higher boundary. At start should be 32 - /// Most significant bit - 1 - private static int BinarySearchPos(int num, int lowBoundary, int highBoundary) - { - int mid = (lowBoundary + highBoundary) / 2; - int shiftResult = num >> mid; - if (shiftResult == 1) - { - return mid; - } - else if (shiftResult < 1) - { - return BinarySearchPos(num, lowBoundary, mid); - } - else - { - return BinarySearchPos(num, mid, highBoundary); - } - } + /// + /// Search for right side bit of Most significant bit + /// + /// Input number + /// Lower boundary. At start should be 0 + /// Higher boundary. At start should be 32 + /// Most significant bit - 1 + private static int BinarySearchPos(int num, int lowBoundary, int highBoundary) + { + int mid = (lowBoundary + highBoundary) / 2; + int shiftResult = num >> mid; + if (shiftResult == 1) + { + return mid; + } + else if (shiftResult < 1) + { + return BinarySearchPos(num, lowBoundary, mid); + } + else + { + return BinarySearchPos(num, mid, highBoundary); + } + } - /// - /// With input number and polynomial number. Method will calculate BCH value and return - /// - /// Input number - /// Polynomial number - /// BCH value - internal static int CalculateBCH(int num, int poly) - { - int polyMSB = PosMSB(poly); + /// + /// With input number and polynomial number. Method will calculate BCH value and return + /// + /// Input number + /// Polynomial number + /// BCH value + internal static int CalculateBCH(int num, int poly) + { + int polyMSB = PosMSB(poly); - // num's length will be old length + new length - 1. - // Once divide poly number. BCH number will be one length short than Poly number's length. - num <<= (polyMSB - 1); - int numMSB = PosMSB(num); - while (PosMSB(num) >= polyMSB) - { - // left shift Poly number to same level as num. Then xor. - // Remove most significant bits of num. - num ^= poly << (numMSB - polyMSB); - numMSB = PosMSB(num); - } - return num; - } -} + // num's length will be old length + new length - 1. + // Once divide poly number. BCH number will be one length short than Poly number's length. + num <<= (polyMSB - 1); + int numMSB = PosMSB(num); + while (PosMSB(num) >= polyMSB) + { + // left shift Poly number to same level as num. Then xor. + // Remove most significant bits of num. + num ^= poly << (numMSB - polyMSB); + numMSB = PosMSB(num); + } + return num; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/EncodingRegion/Codeword.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/EncodingRegion/Codeword.cs index d6b1c0a..5afeb9e 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/EncodingRegion/Codeword.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/EncodingRegion/Codeword.cs @@ -1,72 +1,70 @@ -using System; - namespace Gma.QrCodeNet.Encoding.EncodingRegion; /// ISO/IEC 18004:2000 Chapter 8.7.3 Page 46 internal static class Codeword { - internal static void TryEmbedCodewords(this TriStateMatrix tsMatrix, BitList codewords) - { - int sWidth = tsMatrix.Width; - int codewordsSize = codewords.Count; + internal static void TryEmbedCodewords(this TriStateMatrix tsMatrix, BitList codewords) + { + int sWidth = tsMatrix.Width; + int codewordsSize = codewords.Count; - int bitIndex = 0; - int directionUp = -1; + int bitIndex = 0; + int directionUp = -1; - int x = sWidth - 1; - int y = sWidth - 1; + int x = sWidth - 1; + int y = sWidth - 1; - while (x > 0) - { - // Skip vertical timing pattern - if (x == 6) - { - x -= 1; - } + while (x > 0) + { + // Skip vertical timing pattern + if (x == 6) + { + x -= 1; + } - while (y >= 0 && y < sWidth) - { - for (int xOffset = 0; xOffset < 2; xOffset++) - { - int xPos = x - xOffset; - if (tsMatrix.MStatus(xPos, y) == MatrixStatus.None) - { - bool bit; - if (bitIndex < codewordsSize) - { - bit = codewords[bitIndex]; - bitIndex++; - } - else - { - bit = false; - } + while (y >= 0 && y < sWidth) + { + for (int xOffset = 0; xOffset < 2; xOffset++) + { + int xPos = x - xOffset; + if (tsMatrix.MStatus(xPos, y) == MatrixStatus.None) + { + bool bit; + if (bitIndex < codewordsSize) + { + bit = codewords[bitIndex]; + bitIndex++; + } + else + { + bit = false; + } - tsMatrix[xPos, y, MatrixStatus.Data] = bit; - } - } + tsMatrix[xPos, y, MatrixStatus.Data] = bit; + } + } - y = NextY(y, directionUp); - } + y = NextY(y, directionUp); + } - directionUp = ChangeDirection(directionUp); - y = NextY(y, directionUp); - x -= 2; - } + directionUp = ChangeDirection(directionUp); + y = NextY(y, directionUp); + x -= 2; + } - if (bitIndex != codewordsSize) - { - throw new Exception($"Not all bits from {nameof(codewords)} consumed by matrix: {bitIndex} / {codewordsSize}."); - } - } + if (bitIndex != codewordsSize) + { + throw new Exception($"Not all bits from {nameof(codewords)} consumed by matrix: {bitIndex} / {codewordsSize}."); + } + } - internal static int NextY(int y, int directionUp) - { - return y + directionUp; - } + internal static int NextY(int y, int directionUp) + { + return y + directionUp; + } - internal static int ChangeDirection(int directionUp) - { - return -directionUp; - } -} + internal static int ChangeDirection(int directionUp) + { + return -directionUp; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/EncodingRegion/FormatInformation.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/EncodingRegion/FormatInformation.cs index eaaf733..3edc1c5 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/EncodingRegion/FormatInformation.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/EncodingRegion/FormatInformation.cs @@ -1,4 +1,3 @@ -using System; using Gma.QrCodeNet.Encoding.Masking; namespace Gma.QrCodeNet.Encoding.EncodingRegion; @@ -10,105 +9,105 @@ namespace Gma.QrCodeNet.Encoding.EncodingRegion; /// ISO/IEC 18004:2000 Chapter 8.9 Page 53 internal static class FormatInformation { - /// - /// From Appendix C in JISX0510:2004 (p.65). - /// - private const int FormatInfoPoly = 0x537; + /// + /// From Appendix C in JISX0510:2004 (p.65). + /// + private const int FormatInfoPoly = 0x537; - /// - /// From Appendix C in JISX0510:2004 (p.65). - /// - private const int FormatInfoMaskPattern = 0x5412; + /// + /// From Appendix C in JISX0510:2004 (p.65). + /// + private const int FormatInfoMaskPattern = 0x5412; - /// - /// Embed format information to tristatematrix. - /// Process combination of create info bits, BCH error correction bits calculation, embed towards matrix. - /// - /// ISO/IEC 18004:2000 Chapter 8.9 Page 53 - internal static void EmbedFormatInformation(this TriStateMatrix triMatrix, ErrorCorrectionLevel errorLevel, Pattern pattern) - { - BitList formatInfo = GetFormatInfoBits(errorLevel, pattern); - int width = triMatrix.Width; - for (int index = 0; index < 15; index++) - { - MatrixPoint point = PointForInfo1(index); - bool bit = formatInfo[index]; - triMatrix[point.X, point.Y, MatrixStatus.NoMask] = bit; + /// + /// Embed format information to tristatematrix. + /// Process combination of create info bits, BCH error correction bits calculation, embed towards matrix. + /// + /// ISO/IEC 18004:2000 Chapter 8.9 Page 53 + internal static void EmbedFormatInformation(this TriStateMatrix triMatrix, ErrorCorrectionLevel errorLevel, Pattern pattern) + { + BitList formatInfo = GetFormatInfoBits(errorLevel, pattern); + int width = triMatrix.Width; + for (int index = 0; index < 15; index++) + { + MatrixPoint point = PointForInfo1(index); + bool bit = formatInfo[index]; + triMatrix[point.X, point.Y, MatrixStatus.NoMask] = bit; - if (index < 7) - { - triMatrix[8, width - 1 - index, MatrixStatus.NoMask] = bit; - } - else - { - triMatrix[width - 8 + (index - 7), 8, MatrixStatus.NoMask] = bit; - } - } - } + if (index < 7) + { + triMatrix[8, width - 1 - index, MatrixStatus.NoMask] = bit; + } + else + { + triMatrix[width - 8 + (index - 7), 8, MatrixStatus.NoMask] = bit; + } + } + } - private static MatrixPoint PointForInfo1(int bitsIndex) - { - if (bitsIndex <= 7) - { - return bitsIndex >= 6 - ? new MatrixPoint(bitsIndex + 1, 8) - : new MatrixPoint(bitsIndex, 8); - } - else - { - return bitsIndex == 8 - ? new MatrixPoint(8, 8 - (bitsIndex - 7)) - : new MatrixPoint(8, 8 - (bitsIndex - 7) - 1); - } - } + private static MatrixPoint PointForInfo1(int bitsIndex) + { + if (bitsIndex <= 7) + { + return bitsIndex >= 6 + ? new MatrixPoint(bitsIndex + 1, 8) + : new MatrixPoint(bitsIndex, 8); + } + else + { + return bitsIndex == 8 + ? new MatrixPoint(8, 8 - (bitsIndex - 7)) + : new MatrixPoint(8, 8 - (bitsIndex - 7) - 1); + } + } - private static BitList GetFormatInfoBits(ErrorCorrectionLevel errorLevel, Pattern pattern) - { - int formatInfo = (int)pattern.MaskPatternType; + private static BitList GetFormatInfoBits(ErrorCorrectionLevel errorLevel, Pattern pattern) + { + int formatInfo = (int)pattern.MaskPatternType; - // Pattern bits length = 3 - formatInfo |= GetErrorCorrectionIndicatorBits(errorLevel) << 3; + // Pattern bits length = 3 + formatInfo |= GetErrorCorrectionIndicatorBits(errorLevel) << 3; - int bchCode = BCHCalculator.CalculateBCH(formatInfo, FormatInfoPoly); + int bchCode = BCHCalculator.CalculateBCH(formatInfo, FormatInfoPoly); - // bchCode length = 10 - formatInfo = (formatInfo << 10) | bchCode; + // bchCode length = 10 + formatInfo = (formatInfo << 10) | bchCode; - // xor maskPattern - formatInfo ^= FormatInfoMaskPattern; + // xor maskPattern + formatInfo ^= FormatInfoMaskPattern; - BitList resultBits = new() - { - { formatInfo, 15 } - }; + BitList resultBits = new() + { + { formatInfo, 15 } + }; - if (resultBits.Count != 15) - { - throw new Exception("FormatInfoBits length is not 15"); - } - else - { - return resultBits; - } - } + if (resultBits.Count != 15) + { + throw new Exception("FormatInfoBits length is not 15"); + } + else + { + return resultBits; + } + } - /// - /// According Table 25 — Error correction level indicators - /// Using these bits as enum values would destroy their order which currently corresponds to error correction strength. - /// - internal static int GetErrorCorrectionIndicatorBits(ErrorCorrectionLevel errorLevel) - { - // L 01 - // M 00 - // Q 11 - // H 10 - return errorLevel switch - { - ErrorCorrectionLevel.H => 0x02, - ErrorCorrectionLevel.L => 0x01, - ErrorCorrectionLevel.M => 0x00, - ErrorCorrectionLevel.Q => 0x03, - _ => throw new ArgumentException($"Unsupported error correction level [{errorLevel}]", nameof(errorLevel)) - }; - } -} + /// + /// According Table 25 — Error correction level indicators + /// Using these bits as enum values would destroy their order which currently corresponds to error correction strength. + /// + internal static int GetErrorCorrectionIndicatorBits(ErrorCorrectionLevel errorLevel) + { + // L 01 + // M 00 + // Q 11 + // H 10 + return errorLevel switch + { + ErrorCorrectionLevel.H => 0x02, + ErrorCorrectionLevel.L => 0x01, + ErrorCorrectionLevel.M => 0x00, + ErrorCorrectionLevel.Q => 0x03, + _ => throw new ArgumentException($"Unsupported error correction level [{errorLevel}]", nameof(errorLevel)) + }; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/EncodingRegion/VersionInformation.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/EncodingRegion/VersionInformation.cs index 11b9718..acf3577 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/EncodingRegion/VersionInformation.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/EncodingRegion/VersionInformation.cs @@ -1,5 +1,3 @@ -using System; - namespace Gma.QrCodeNet.Encoding.EncodingRegion; /// @@ -8,63 +6,63 @@ namespace Gma.QrCodeNet.Encoding.EncodingRegion; /// ISO/IEC 18004:2000 Chapter 8.10 Page 54 internal static class VersionInformation { - private const int VIRectangleHeight = 3; - private const int VIRectangleWidth = 6; + private const int VIRectangleHeight = 3; + private const int VIRectangleWidth = 6; - private const int LengthDataBits = 6; - private const int LengthECBits = 12; - private const int VersionBCHPoly = 0x1f25; + private const int LengthDataBits = 6; + private const int LengthECBits = 12; + private const int VersionBCHPoly = 0x1f25; - /// - /// Embed version information to Matrix - /// Only for version greater than or equal to 7 - /// - internal static void EmbedVersionInformation(this TriStateMatrix tsMatrix, int version) - { - if (version < 7) - { - return; - } + /// + /// Embed version information to Matrix + /// Only for version greater than or equal to 7 + /// + internal static void EmbedVersionInformation(this TriStateMatrix tsMatrix, int version) + { + if (version < 7) + { + return; + } - BitList versionInfo = VersionInfoBitList(version); + BitList versionInfo = VersionInfoBitList(version); - int matrixWidth = tsMatrix.Width; + int matrixWidth = tsMatrix.Width; - // 1 cell between version info and position stencil - int shiftLength = QRCodeConstantVariable.PositionStencilWidth + VIRectangleHeight + 1; + // 1 cell between version info and position stencil + int shiftLength = QRCodeConstantVariable.PositionStencilWidth + VIRectangleHeight + 1; - // Reverse order input - int viIndex = LengthDataBits + LengthECBits - 1; + // Reverse order input + int viIndex = LengthDataBits + LengthECBits - 1; - for (int viWidth = 0; viWidth < VIRectangleWidth; viWidth++) - { - for (int viHeight = 0; viHeight < VIRectangleHeight; viHeight++) - { - bool bit = versionInfo[viIndex]; - viIndex--; + for (int viWidth = 0; viWidth < VIRectangleWidth; viWidth++) + { + for (int viHeight = 0; viHeight < VIRectangleHeight; viHeight++) + { + bool bit = versionInfo[viIndex]; + viIndex--; - // Bottom left - tsMatrix[viWidth, (matrixWidth - shiftLength + viHeight), MatrixStatus.NoMask] = bit; + // Bottom left + tsMatrix[viWidth, (matrixWidth - shiftLength + viHeight), MatrixStatus.NoMask] = bit; - // Top right - tsMatrix[(matrixWidth - shiftLength + viHeight), viWidth, MatrixStatus.NoMask] = bit; - } - } - } + // Top right + tsMatrix[(matrixWidth - shiftLength + viHeight), viWidth, MatrixStatus.NoMask] = bit; + } + } + } - private static BitList VersionInfoBitList(int version) - { - BitList result = new() - { - { version, LengthDataBits }, - { BCHCalculator.CalculateBCH(version, VersionBCHPoly), LengthECBits } - }; + private static BitList VersionInfoBitList(int version) + { + BitList result = new() + { + { version, LengthDataBits }, + { BCHCalculator.CalculateBCH(version, VersionBCHPoly), LengthECBits } + }; - if (result.Count != (LengthECBits + LengthDataBits)) - { - throw new Exception("Version Info creation error. Result is not 18 bits"); - } + if (result.Count != (LengthECBits + LengthDataBits)) + { + throw new Exception("Version Info creation error. Result is not 18 bits"); + } - return result; - } -} + return result; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ErrorCorrection/ECGenerator.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ErrorCorrection/ECGenerator.cs index b9a3dff..b1c6b10 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ErrorCorrection/ECGenerator.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ErrorCorrection/ECGenerator.cs @@ -1,84 +1,82 @@ using Gma.QrCodeNet.Encoding.ReedSolomon; -using System; -using System.Collections.Generic; namespace Gma.QrCodeNet.Encoding.ErrorCorrection; internal static class ECGenerator { - internal static BitList FillECCodewords(BitList dataCodewords, VersionDetail vd) - { - List dataCodewordsByte = dataCodewords.List; + internal static BitList FillECCodewords(BitList dataCodewords, VersionDetail vd) + { + List dataCodewordsByte = dataCodewords.List; - int ecBlockGroup1 = vd.ECBlockGroup1; - int numDataBytesGroup1 = vd.NumDataBytesGroup1; - int numDataBytesGroup2 = vd.NumDataBytesGroup2; + int ecBlockGroup1 = vd.ECBlockGroup1; + int numDataBytesGroup1 = vd.NumDataBytesGroup1; + int numDataBytesGroup2 = vd.NumDataBytesGroup2; - int ecBytesPerBlock = vd.NumECBytesPerBlock; + int ecBytesPerBlock = vd.NumECBytesPerBlock; - int dataBytesOffset = 0; - byte[][] dByteJArray = new byte[vd.NumECBlocks][]; - byte[][] ecByteJArray = new byte[vd.NumECBlocks][]; + int dataBytesOffset = 0; + byte[][] dByteJArray = new byte[vd.NumECBlocks][]; + byte[][] ecByteJArray = new byte[vd.NumECBlocks][]; - GaloisField256 gf256 = GaloisField256.QRCodeGaloisField; - GeneratorPolynomial generator = new(gf256); + GaloisField256 gf256 = GaloisField256.QRCodeGaloisField; + GeneratorPolynomial generator = new(gf256); - for (int blockId = 0; blockId < vd.NumECBlocks; blockId++) - { - if (blockId < ecBlockGroup1) - { - dByteJArray[blockId] = new byte[numDataBytesGroup1]; - for (int index = 0; index < numDataBytesGroup1; index++) - { - dByteJArray[blockId][index] = dataCodewordsByte[dataBytesOffset + index]; - } - dataBytesOffset += numDataBytesGroup1; - } - else - { - dByteJArray[blockId] = new byte[numDataBytesGroup2]; - for (int index = 0; index < numDataBytesGroup2; index++) - { - dByteJArray[blockId][index] = dataCodewordsByte[dataBytesOffset + index]; - } - dataBytesOffset += numDataBytesGroup2; - } + for (int blockId = 0; blockId < vd.NumECBlocks; blockId++) + { + if (blockId < ecBlockGroup1) + { + dByteJArray[blockId] = new byte[numDataBytesGroup1]; + for (int index = 0; index < numDataBytesGroup1; index++) + { + dByteJArray[blockId][index] = dataCodewordsByte[dataBytesOffset + index]; + } + dataBytesOffset += numDataBytesGroup1; + } + else + { + dByteJArray[blockId] = new byte[numDataBytesGroup2]; + for (int index = 0; index < numDataBytesGroup2; index++) + { + dByteJArray[blockId][index] = dataCodewordsByte[dataBytesOffset + index]; + } + dataBytesOffset += numDataBytesGroup2; + } - ecByteJArray[blockId] = ReedSolomonEncoder.Encode(dByteJArray[blockId], ecBytesPerBlock, generator); - } - if (vd.NumDataBytes != dataBytesOffset) - { - throw new ArgumentException("Data bytes do not match offset"); - } + ecByteJArray[blockId] = ReedSolomonEncoder.Encode(dByteJArray[blockId], ecBytesPerBlock, generator); + } + if (vd.NumDataBytes != dataBytesOffset) + { + throw new ArgumentException("Data bytes do not match offset"); + } - BitList codewords = new(); + BitList codewords = new(); - int maxDataLength = ecBlockGroup1 == vd.NumECBlocks ? numDataBytesGroup1 : numDataBytesGroup2; + int maxDataLength = ecBlockGroup1 == vd.NumECBlocks ? numDataBytesGroup1 : numDataBytesGroup2; - for (int dataId = 0; dataId < maxDataLength; dataId++) - { - for (int blockId = 0; blockId < vd.NumECBlocks; blockId++) - { - if (!(dataId == numDataBytesGroup1 && blockId < ecBlockGroup1)) - { - codewords.Add(dByteJArray[blockId][dataId], 8); - } - } - } + for (int dataId = 0; dataId < maxDataLength; dataId++) + { + for (int blockId = 0; blockId < vd.NumECBlocks; blockId++) + { + if (!(dataId == numDataBytesGroup1 && blockId < ecBlockGroup1)) + { + codewords.Add(dByteJArray[blockId][dataId], 8); + } + } + } - for (int ecId = 0; ecId < ecBytesPerBlock; ecId++) - { - for (int blockId = 0; blockId < vd.NumECBlocks; blockId++) - { - codewords.Add(ecByteJArray[blockId][ecId], 8); - } - } + for (int ecId = 0; ecId < ecBytesPerBlock; ecId++) + { + for (int blockId = 0; blockId < vd.NumECBlocks; blockId++) + { + codewords.Add(ecByteJArray[blockId][ecId], 8); + } + } - if (vd.NumTotalBytes != codewords.Count >> 3) - { - throw new ArgumentException($"Total bytes: {vd.NumTotalBytes}. Actual bits: {codewords.Count}"); - } + if (vd.NumTotalBytes != codewords.Count >> 3) + { + throw new ArgumentException($"Total bytes: {vd.NumTotalBytes}. Actual bits: {codewords.Count}"); + } - return codewords; - } -} + return codewords; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ErrorCorrectionLevel.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ErrorCorrectionLevel.cs index 8fb6fb2..37e12cc 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ErrorCorrectionLevel.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ErrorCorrectionLevel.cs @@ -2,8 +2,8 @@ namespace Gma.QrCodeNet.Encoding; public enum ErrorCorrectionLevel { - L, - M, - Q, - H -} + L, + M, + Q, + H +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/InputOutOfBoundaryException.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/InputOutOfBoundaryException.cs index ffd2890..cfc7982 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/InputOutOfBoundaryException.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/InputOutOfBoundaryException.cs @@ -1,5 +1,3 @@ -using System; - namespace Gma.QrCodeNet.Encoding; /// @@ -7,11 +5,11 @@ namespace Gma.QrCodeNet.Encoding; /// public class InputOutOfBoundaryException : Exception { - public InputOutOfBoundaryException() : base() - { - } + public InputOutOfBoundaryException() : base() + { + } - public InputOutOfBoundaryException(string message) : base(message) - { - } -} + public InputOutOfBoundaryException(string message) : base(message) + { + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/MaskPatternType.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/MaskPatternType.cs index f95f6ab..b98fef9 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/MaskPatternType.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/MaskPatternType.cs @@ -2,12 +2,12 @@ namespace Gma.QrCodeNet.Encoding.Masking; public enum MaskPatternType { - Type0 = 0, - Type1 = 1, - Type2 = 2, - Type3 = 3, - Type4 = 4, - Type5 = 5, - Type6 = 6, - Type7 = 7 -} + Type0 = 0, + Type1 = 1, + Type2 = 2, + Type3 = 3, + Type4 = 4, + Type5 = 5, + Type6 = 6, + Type7 = 7 +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/MatrixExtensions.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/MatrixExtensions.cs index fa8c1eb..350a37a 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/MatrixExtensions.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/MatrixExtensions.cs @@ -1,44 +1,43 @@ -using System; using Gma.QrCodeNet.Encoding.EncodingRegion; namespace Gma.QrCodeNet.Encoding.Masking; public static class MatrixExtensions { - public static TriStateMatrix Xor(this TriStateMatrix first, Pattern second, ErrorCorrectionLevel errorLevel) - { - TriStateMatrix result = XorMatrix(first, second); - result.EmbedFormatInformation(errorLevel, second); - return result; - } + public static TriStateMatrix Xor(this TriStateMatrix first, Pattern second, ErrorCorrectionLevel errorLevel) + { + TriStateMatrix result = XorMatrix(first, second); + result.EmbedFormatInformation(errorLevel, second); + return result; + } - private static TriStateMatrix XorMatrix(TriStateMatrix first, BitMatrix second) - { - int width = first.Width; - TriStateMatrix maskedMatrix = new(width); - for (int x = 0; x < width; x++) - { - for (int y = 0; y < width; y++) - { - MatrixStatus states = first.MStatus(x, y); - switch (states) - { - case MatrixStatus.NoMask: - maskedMatrix[x, y, MatrixStatus.NoMask] = first[x, y]; - break; + private static TriStateMatrix XorMatrix(TriStateMatrix first, BitMatrix second) + { + int width = first.Width; + TriStateMatrix maskedMatrix = new(width); + for (int x = 0; x < width; x++) + { + for (int y = 0; y < width; y++) + { + MatrixStatus states = first.MStatus(x, y); + switch (states) + { + case MatrixStatus.NoMask: + maskedMatrix[x, y, MatrixStatus.NoMask] = first[x, y]; + break; - case MatrixStatus.Data: - maskedMatrix[x, y, MatrixStatus.Data] = first[x, y] ^ second[x, y]; - break; + case MatrixStatus.Data: + maskedMatrix[x, y, MatrixStatus.Data] = first[x, y] ^ second[x, y]; + break; - default: - throw new ArgumentException($"{nameof(TriStateMatrix)} has None value cell.", nameof(first)); - } - } - } + default: + throw new ArgumentException($"{nameof(TriStateMatrix)} has None value cell.", nameof(first)); + } + } + } - return maskedMatrix; - } + return maskedMatrix; + } - public static TriStateMatrix Apply(this TriStateMatrix matrix, Pattern pattern, ErrorCorrectionLevel errorLevel) => matrix.Xor(pattern, errorLevel); -} + public static TriStateMatrix Apply(this TriStateMatrix matrix, Pattern pattern, ErrorCorrectionLevel errorLevel) => matrix.Xor(pattern, errorLevel); +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern.cs index ee5a3e3..6303438 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern.cs @@ -1,13 +1,11 @@ -using System; - namespace Gma.QrCodeNet.Encoding.Masking; public abstract class Pattern : BitMatrix { - public override int Width => throw new NotSupportedException(); - public override int Height => throw new NotSupportedException(); + public override int Width => throw new NotSupportedException(); + public override int Height => throw new NotSupportedException(); - public override bool[,] InternalArray => throw new NotImplementedException(); + public override bool[,] InternalArray => throw new NotImplementedException(); - public abstract MaskPatternType MaskPatternType { get; } -} + public abstract MaskPatternType MaskPatternType { get; } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern0.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern0.cs index 7134912..1e5d594 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern0.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern0.cs @@ -1,14 +1,12 @@ -using System; - namespace Gma.QrCodeNet.Encoding.Masking; internal class Pattern0 : Pattern { - public override MaskPatternType MaskPatternType => MaskPatternType.Type0; + public override MaskPatternType MaskPatternType => MaskPatternType.Type0; - public override bool this[int i, int j] - { - get => (j + i) % 2 == 0; - set => throw new NotSupportedException(); - } -} + public override bool this[int i, int j] + { + get => (j + i) % 2 == 0; + set => throw new NotSupportedException(); + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern1.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern1.cs index 63903ac..3f94b65 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern1.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern1.cs @@ -1,14 +1,12 @@ -using System; - namespace Gma.QrCodeNet.Encoding.Masking; internal class Pattern1 : Pattern { - public override MaskPatternType MaskPatternType => MaskPatternType.Type1; + public override MaskPatternType MaskPatternType => MaskPatternType.Type1; - public override bool this[int i, int j] - { - get => j % 2 == 0; - set => throw new NotSupportedException(); - } -} + public override bool this[int i, int j] + { + get => j % 2 == 0; + set => throw new NotSupportedException(); + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern2.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern2.cs index 0bf6e3b..a170fb5 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern2.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern2.cs @@ -1,14 +1,12 @@ -using System; - namespace Gma.QrCodeNet.Encoding.Masking; internal class Pattern2 : Pattern { - public override MaskPatternType MaskPatternType => MaskPatternType.Type2; + public override MaskPatternType MaskPatternType => MaskPatternType.Type2; - public override bool this[int i, int j] - { - get => i % 3 == 0; - set => throw new NotSupportedException(); - } -} + public override bool this[int i, int j] + { + get => i % 3 == 0; + set => throw new NotSupportedException(); + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern3.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern3.cs index ce9d7f3..7a9cf83 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern3.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern3.cs @@ -1,14 +1,12 @@ -using System; - namespace Gma.QrCodeNet.Encoding.Masking; internal class Pattern3 : Pattern { - public override MaskPatternType MaskPatternType => MaskPatternType.Type3; + public override MaskPatternType MaskPatternType => MaskPatternType.Type3; - public override bool this[int i, int j] - { - get => (j + i) % 3 == 0; - set => throw new NotSupportedException(); - } -} + public override bool this[int i, int j] + { + get => (j + i) % 3 == 0; + set => throw new NotSupportedException(); + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern4.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern4.cs index 6da839c..1ce0521 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern4.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern4.cs @@ -1,14 +1,12 @@ -using System; - namespace Gma.QrCodeNet.Encoding.Masking; internal class Pattern4 : Pattern { - public override MaskPatternType MaskPatternType => MaskPatternType.Type4; + public override MaskPatternType MaskPatternType => MaskPatternType.Type4; - public override bool this[int i, int j] - { - get => ((j / 2) + (i / 3)) % 2 == 0; - set => throw new NotSupportedException(); - } -} + public override bool this[int i, int j] + { + get => ((j / 2) + (i / 3)) % 2 == 0; + set => throw new NotSupportedException(); + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern5.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern5.cs index 632aba3..ab62f07 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern5.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern5.cs @@ -1,14 +1,12 @@ -using System; - namespace Gma.QrCodeNet.Encoding.Masking; internal class Pattern5 : Pattern { - public override MaskPatternType MaskPatternType => MaskPatternType.Type5; + public override MaskPatternType MaskPatternType => MaskPatternType.Type5; - public override bool this[int i, int j] - { - get => (((i * j) % 2) + ((i * j) % 3)) == 0; - set => throw new NotSupportedException(); - } -} + public override bool this[int i, int j] + { + get => (((i * j) % 2) + ((i * j) % 3)) == 0; + set => throw new NotSupportedException(); + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern6.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern6.cs index 7ad6eeb..69cde6f 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern6.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern6.cs @@ -1,13 +1,12 @@ -using System; namespace Gma.QrCodeNet.Encoding.Masking; internal class Pattern6 : Pattern { - public override MaskPatternType MaskPatternType => MaskPatternType.Type6; + public override MaskPatternType MaskPatternType => MaskPatternType.Type6; - public override bool this[int i, int j] - { - get => ((((i * j) % 2) + ((i * j) % 3)) % 2) == 0; - set => throw new NotSupportedException(); - } -} + public override bool this[int i, int j] + { + get => ((((i * j) % 2) + ((i * j) % 3)) % 2) == 0; + set => throw new NotSupportedException(); + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern7.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern7.cs index d70b05c..4e1f436 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern7.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Pattern7.cs @@ -1,14 +1,12 @@ -using System; - namespace Gma.QrCodeNet.Encoding.Masking; internal class Pattern7 : Pattern { - public override MaskPatternType MaskPatternType => MaskPatternType.Type7; + public override MaskPatternType MaskPatternType => MaskPatternType.Type7; - public override bool this[int i, int j] - { - get => (((i * j) % 3) + (((i + j) % 2) % 2)) == 0; - set => throw new NotSupportedException(); - } -} + public override bool this[int i, int j] + { + get => (((i * j) % 3) + (((i + j) % 2) % 2)) == 0; + set => throw new NotSupportedException(); + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/PatternFactory.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/PatternFactory.cs index 986c541..4a5de40 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/PatternFactory.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/PatternFactory.cs @@ -1,31 +1,28 @@ -using System; -using System.Collections.Generic; - namespace Gma.QrCodeNet.Encoding.Masking; internal class PatternFactory { - internal Pattern CreateByType(MaskPatternType maskPatternType) - { - return maskPatternType switch - { - MaskPatternType.Type0 => new Pattern0(), - MaskPatternType.Type1 => new Pattern1(), - MaskPatternType.Type2 => new Pattern2(), - MaskPatternType.Type3 => new Pattern3(), - MaskPatternType.Type4 => new Pattern4(), - MaskPatternType.Type5 => new Pattern5(), - MaskPatternType.Type6 => new Pattern6(), - MaskPatternType.Type7 => new Pattern7(), - _ => throw new NotSupportedException("This should never happen.") - }; - } + internal Pattern CreateByType(MaskPatternType maskPatternType) + { + return maskPatternType switch + { + MaskPatternType.Type0 => new Pattern0(), + MaskPatternType.Type1 => new Pattern1(), + MaskPatternType.Type2 => new Pattern2(), + MaskPatternType.Type3 => new Pattern3(), + MaskPatternType.Type4 => new Pattern4(), + MaskPatternType.Type5 => new Pattern5(), + MaskPatternType.Type6 => new Pattern6(), + MaskPatternType.Type7 => new Pattern7(), + _ => throw new NotSupportedException("This should never happen.") + }; + } - internal IEnumerable AllPatterns() - { - foreach (MaskPatternType patternType in Enum.GetValues(typeof(MaskPatternType))) - { - yield return CreateByType(patternType); - } - } -} + internal IEnumerable AllPatterns() + { + foreach (MaskPatternType patternType in Enum.GetValues(typeof(MaskPatternType))) + { + yield return CreateByType(patternType); + } + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/MatrixScoreCalculator.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/MatrixScoreCalculator.cs index 1105279..2b5fd5a 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/MatrixScoreCalculator.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/MatrixScoreCalculator.cs @@ -1,36 +1,34 @@ -using System.Linq; - namespace Gma.QrCodeNet.Encoding.Masking.Scoring; internal static class MatrixScoreCalculator { - internal static BitMatrix GetLowestPenaltyMatrix(this TriStateMatrix matrix, ErrorCorrectionLevel errorLevel) - { - PatternFactory patternFactory = new(); - int score = int.MaxValue; - int tempScore; - TriStateMatrix result = new(matrix.Width); - TriStateMatrix triMatrix; - foreach (Pattern pattern in patternFactory.AllPatterns()) - { - triMatrix = matrix.Apply(pattern, errorLevel); - tempScore = triMatrix.PenaltyScore(); - if (tempScore < score) - { - score = tempScore; - result = triMatrix; - } - } + internal static BitMatrix GetLowestPenaltyMatrix(this TriStateMatrix matrix, ErrorCorrectionLevel errorLevel) + { + PatternFactory patternFactory = new(); + int score = int.MaxValue; + int tempScore; + TriStateMatrix result = new(matrix.Width); + TriStateMatrix triMatrix; + foreach (Pattern pattern in patternFactory.AllPatterns()) + { + triMatrix = matrix.Apply(pattern, errorLevel); + tempScore = triMatrix.PenaltyScore(); + if (tempScore < score) + { + score = tempScore; + result = triMatrix; + } + } - return result; - } + return result; + } - internal static int PenaltyScore(this BitMatrix matrix) - { - PenaltyFactory penaltyFactory = new(); - return - penaltyFactory - .AllRules() - .Sum(penalty => penalty.PenaltyCalculate(matrix)); - } -} + internal static int PenaltyScore(this BitMatrix matrix) + { + PenaltyFactory penaltyFactory = new(); + return + penaltyFactory + .AllRules() + .Sum(penalty => penalty.PenaltyCalculate(matrix)); + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty.cs index 761e5a4..39b000f 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty.cs @@ -2,5 +2,5 @@ namespace Gma.QrCodeNet.Encoding.Masking.Scoring; public abstract class Penalty { - internal abstract int PenaltyCalculate(BitMatrix matrix); -} + internal abstract int PenaltyCalculate(BitMatrix matrix); +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty1.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty1.cs index ff80b32..c8be43e 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty1.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty1.cs @@ -5,81 +5,81 @@ namespace Gma.QrCodeNet.Encoding.Masking.Scoring; /// internal class Penalty1 : Penalty { - /// - /// Calculate penalty value for first rule. - /// - internal override int PenaltyCalculate(BitMatrix matrix) - { - int penaltyValue = PenaltyCalculation(matrix, true) + PenaltyCalculation(matrix, false); - return penaltyValue; - } + /// + /// Calculate penalty value for first rule. + /// + internal override int PenaltyCalculate(BitMatrix matrix) + { + int penaltyValue = PenaltyCalculation(matrix, true) + PenaltyCalculation(matrix, false); + return penaltyValue; + } - private int PenaltyCalculation(BitMatrix matrix, bool isHorizontal) - { - int penalty = 0; - int width = matrix.Width; + private int PenaltyCalculation(BitMatrix matrix, bool isHorizontal) + { + int penalty = 0; + int width = matrix.Width; - int i = 0; - int j = 0; + int i = 0; + int j = 0; - while (i < width) - { - while (j < width - 4) - { - bool preBit = isHorizontal - ? matrix[j + 4, i] - : matrix[i, j + 4]; - int numSameBitCell = 1; + while (i < width) + { + while (j < width - 4) + { + bool preBit = isHorizontal + ? matrix[j + 4, i] + : matrix[i, j + 4]; + int numSameBitCell = 1; - for (int x = 1; x <= 4; x++) - { - bool bit = isHorizontal - ? matrix[j + 4 - x, i] - : matrix[i, j + 4 - x]; - if (bit == preBit) - { - numSameBitCell++; - } - else - { - break; - } - } + for (int x = 1; x <= 4; x++) + { + bool bit = isHorizontal + ? matrix[j + 4 - x, i] + : matrix[i, j + 4 - x]; + if (bit == preBit) + { + numSameBitCell++; + } + else + { + break; + } + } - if (numSameBitCell == 1) - { - j += 4; - } - else - { - int x = 5; - while ((j + x) < width) - { - bool bit = isHorizontal - ? matrix[j + x, i] - : matrix[i, j + x]; - if (bit == preBit) - { - numSameBitCell++; - } - else - { - break; - } - x++; - } - if (numSameBitCell >= 5) - { - penalty += (3 + (numSameBitCell - 5)); - } + if (numSameBitCell == 1) + { + j += 4; + } + else + { + int x = 5; + while ((j + x) < width) + { + bool bit = isHorizontal + ? matrix[j + x, i] + : matrix[i, j + x]; + if (bit == preBit) + { + numSameBitCell++; + } + else + { + break; + } + x++; + } + if (numSameBitCell >= 5) + { + penalty += (3 + (numSameBitCell - 5)); + } - j += x; - } - } - j = 0; - i++; - } + j += x; + } + } + j = 0; + i++; + } - return penalty; - } -} + return penalty; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty2.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty2.cs index f99e6d8..283da24 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty2.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty2.cs @@ -5,47 +5,47 @@ namespace Gma.QrCodeNet.Encoding.Masking.Scoring; /// internal class Penalty2 : Penalty { - internal override int PenaltyCalculate(BitMatrix matrix) - { - int width = matrix.Width; - int x = 0; - int y = 0; - int penalty = 0; + internal override int PenaltyCalculate(BitMatrix matrix) + { + int width = matrix.Width; + int x = 0; + int y = 0; + int penalty = 0; - while (y < (width - 1)) - { - while (x < (width - 1)) - { - bool topR = matrix[x + 1, y]; + while (y < (width - 1)) + { + while (x < (width - 1)) + { + bool topR = matrix[x + 1, y]; - if (topR == matrix[x + 1, y + 1]) // Bottom Right - { - if (topR == matrix[x, y + 1]) // Bottom Left - { - if (topR == matrix[x, y]) // Top Left - { - penalty += 3; - x += 1; - } - else - { - x += 1; - } - } - else - { - x += 1; - } - } - else - { - x += 2; - } - } + if (topR == matrix[x + 1, y + 1]) // Bottom Right + { + if (topR == matrix[x, y + 1]) // Bottom Left + { + if (topR == matrix[x, y]) // Top Left + { + penalty += 3; + x += 1; + } + else + { + x += 1; + } + } + else + { + x += 1; + } + } + else + { + x += 2; + } + } - x = 0; - y++; - } - return penalty; - } -} + x = 0; + y++; + } + return penalty; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty3.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty3.cs index 4b0ce1b..7bc285f 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty3.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty3.cs @@ -5,137 +5,137 @@ namespace Gma.QrCodeNet.Encoding.Masking.Scoring; /// internal class Penalty3 : Penalty { - /// - /// Calculate penalty value for Third rule. - /// - internal override int PenaltyCalculate(BitMatrix matrix) => PenaltyCalculation(matrix, true) + PenaltyCalculation(matrix, false); + /// + /// Calculate penalty value for Third rule. + /// + internal override int PenaltyCalculate(BitMatrix matrix) => PenaltyCalculation(matrix, true) + PenaltyCalculation(matrix, false); - private int PenaltyCalculation(BitMatrix matrix, bool isHorizontal) - { - int i = 0; - int j = 1; - int penalty = 0; - int width = matrix.Width; - bool bit; - while (i < width) - { - while (j < width - 5) - { - bit = isHorizontal - ? matrix[j + 4, i] - : matrix[i, j + 4]; - if (!bit) - { - bit = isHorizontal - ? matrix[j, i] - : matrix[i, j]; - if (!bit) - { - penalty += PatternCheck(matrix, i, j, isHorizontal); - j += 4; - } - else - { - j += 4; - } - } - else - { - for (int num = 4; num > 0; num--) - { - bit = isHorizontal - ? matrix[j + num, i] - : matrix[i, j + num]; - if (!bit) - { - j += num; - break; - } - if (num == 1) - { - j += 5; - } - } - } - } - j = 0; - i++; - } - return penalty; - } + private int PenaltyCalculation(BitMatrix matrix, bool isHorizontal) + { + int i = 0; + int j = 1; + int penalty = 0; + int width = matrix.Width; + bool bit; + while (i < width) + { + while (j < width - 5) + { + bit = isHorizontal + ? matrix[j + 4, i] + : matrix[i, j + 4]; + if (!bit) + { + bit = isHorizontal + ? matrix[j, i] + : matrix[i, j]; + if (!bit) + { + penalty += PatternCheck(matrix, i, j, isHorizontal); + j += 4; + } + else + { + j += 4; + } + } + else + { + for (int num = 4; num > 0; num--) + { + bit = isHorizontal + ? matrix[j + num, i] + : matrix[i, j + num]; + if (!bit) + { + j += num; + break; + } + if (num == 1) + { + j += 5; + } + } + } + } + j = 0; + i++; + } + return penalty; + } - private int PatternCheck(BitMatrix matrix, int i, int j, bool isHorizontal) - { - bool bit; - for (int num = 3; num >= 1; num--) - { - bit = isHorizontal - ? matrix[j + num, i] - : matrix[i, j + num]; - if (!bit) - { - return 0; - } - } + private int PatternCheck(BitMatrix matrix, int i, int j, bool isHorizontal) + { + bool bit; + for (int num = 3; num >= 1; num--) + { + bit = isHorizontal + ? matrix[j + num, i] + : matrix[i, j + num]; + if (!bit) + { + return 0; + } + } - // Check for left side and right side x ( xoxxxox ). - if ((j - 1) < 0 || (j + 1) >= matrix.Width) - { - return 0; - } + // Check for left side and right side x ( xoxxxox ). + if ((j - 1) < 0 || (j + 1) >= matrix.Width) + { + return 0; + } - bit = isHorizontal - ? matrix[j + 5, i] - : matrix[i, j + 5]; - if (!bit) - { - return 0; - } + bit = isHorizontal + ? matrix[j + 5, i] + : matrix[i, j + 5]; + if (!bit) + { + return 0; + } - bit = isHorizontal - ? matrix[j - 1, i] - : matrix[i, j - 1]; - if (!bit) - { - return 0; - } + bit = isHorizontal + ? matrix[j - 1, i] + : matrix[i, j - 1]; + if (!bit) + { + return 0; + } - if ((j - 5) >= 0) - { - for (int num = -2; num >= -5; num--) - { - bit = isHorizontal - ? matrix[j + num, i] - : matrix[i, j + num]; - if (bit) - { - break; - } + if ((j - 5) >= 0) + { + for (int num = -2; num >= -5; num--) + { + bit = isHorizontal + ? matrix[j + num, i] + : matrix[i, j + num]; + if (bit) + { + break; + } - if (num == -5) - { - return 40; - } - } - } + if (num == -5) + { + return 40; + } + } + } - if ((j + 9) < matrix.Width) - { - for (int num = 6; num <= 9; num++) - { - bit = isHorizontal - ? matrix[j + num, i] - : matrix[i, j + num]; - if (bit) - { - return 0; - } - } - return 40; - } - else - { - return 0; - } - } -} + if ((j + 9) < matrix.Width) + { + for (int num = 6; num <= 9; num++) + { + bit = isHorizontal + ? matrix[j + num, i] + : matrix[i, j + num]; + if (bit) + { + return 0; + } + } + return 40; + } + else + { + return 0; + } + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty4.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty4.cs index 88382f5..0eeab71 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty4.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/Penalty4.cs @@ -1,5 +1,3 @@ -using System; - namespace Gma.QrCodeNet.Encoding.Masking.Scoring; /// @@ -7,30 +5,30 @@ namespace Gma.QrCodeNet.Encoding.Masking.Scoring; /// internal class Penalty4 : Penalty { - /// - /// Calculate penalty value for Fourth rule. - /// Perform O(n) search for available x modules - /// - internal override int PenaltyCalculate(BitMatrix matrix) - { - int width = matrix.Width; - int darkBitCount = 0; + /// + /// Calculate penalty value for Fourth rule. + /// Perform O(n) search for available x modules + /// + internal override int PenaltyCalculate(BitMatrix matrix) + { + int width = matrix.Width; + int darkBitCount = 0; - for (int j = 0; j < width; j++) - { - for (int i = 0; i < width; i++) - { - if (matrix[i, j]) - { - darkBitCount++; - } - } - } + for (int j = 0; j < width; j++) + { + for (int i = 0; i < width; i++) + { + if (matrix[i, j]) + { + darkBitCount++; + } + } + } - int matrixCount = width * width; + int matrixCount = width * width; - double ratio = (double)darkBitCount / matrixCount; + double ratio = (double)darkBitCount / matrixCount; - return Math.Abs((int)((ratio * 100) - 50)) / 5 * 10; - } -} + return Math.Abs((int)((ratio * 100) - 50)) / 5 * 10; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/PenaltyFactory.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/PenaltyFactory.cs index 36d1797..691933a 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/PenaltyFactory.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/PenaltyFactory.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; - namespace Gma.QrCodeNet.Encoding.Masking.Scoring; /// @@ -8,23 +5,23 @@ namespace Gma.QrCodeNet.Encoding.Masking.Scoring; /// internal class PenaltyFactory { - internal Penalty CreateByRule(PenaltyRules penaltyRule) - { - return penaltyRule switch - { - PenaltyRules.Rule01 => new Penalty1(), - PenaltyRules.Rule02 => new Penalty2(), - PenaltyRules.Rule03 => new Penalty3(), - PenaltyRules.Rule04 => new Penalty4(), - _ => throw new ArgumentException($"Unsupport penalty rule: {penaltyRule}", nameof(penaltyRule)) - }; - } + internal Penalty CreateByRule(PenaltyRules penaltyRule) + { + return penaltyRule switch + { + PenaltyRules.Rule01 => new Penalty1(), + PenaltyRules.Rule02 => new Penalty2(), + PenaltyRules.Rule03 => new Penalty3(), + PenaltyRules.Rule04 => new Penalty4(), + _ => throw new ArgumentException($"Unsupport penalty rule: {penaltyRule}", nameof(penaltyRule)) + }; + } - internal IEnumerable AllRules() - { - foreach (PenaltyRules penaltyRule in Enum.GetValues(typeof(PenaltyRules))) - { - yield return CreateByRule(penaltyRule); - } - } -} + internal IEnumerable AllRules() + { + foreach (PenaltyRules penaltyRule in Enum.GetValues(typeof(PenaltyRules))) + { + yield return CreateByRule(penaltyRule); + } + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/PenaltyRules.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/PenaltyRules.cs index 3cac9ed..c58ae1e 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/PenaltyRules.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Masking/Scoring/PenaltyRules.cs @@ -2,8 +2,8 @@ namespace Gma.QrCodeNet.Encoding.Masking.Scoring; public enum PenaltyRules { - Rule01 = 1, - Rule02 = 2, - Rule03 = 3, - Rule04 = 4 -} + Rule01 = 1, + Rule02 = 2, + Rule03 = 3, + Rule04 = 4 +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/MatrixPoint.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/MatrixPoint.cs index 66f73de..9f5cac9 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/MatrixPoint.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/MatrixPoint.cs @@ -2,19 +2,19 @@ namespace Gma.QrCodeNet.Encoding; public struct MatrixPoint { - internal MatrixPoint(int x, int y) - : this() - { - X = x; - Y = y; - } + internal MatrixPoint(int x, int y) + : this() + { + X = x; + Y = y; + } - public int X { get; private set; } - public int Y { get; private set; } + public int X { get; private set; } + public int Y { get; private set; } - public MatrixPoint Offset(MatrixPoint offset) => new(offset.X + X, offset.Y + Y); + public MatrixPoint Offset(MatrixPoint offset) => new(offset.X + X, offset.Y + Y); - internal MatrixPoint Offset(int offsetX, int offsetY) => Offset(new MatrixPoint(offsetX, offsetY)); + internal MatrixPoint Offset(int offsetX, int offsetY) => Offset(new MatrixPoint(offsetX, offsetY)); - public override string ToString() => $"Point({X};{Y})"; -} + public override string ToString() => $"Point({X};{Y})"; +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/MatrixRectangle.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/MatrixRectangle.cs index 2a9b202..b3e62cf 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/MatrixRectangle.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/MatrixRectangle.cs @@ -1,32 +1,31 @@ using System.Collections; -using System.Collections.Generic; namespace Gma.QrCodeNet.Encoding; internal struct MatrixRectangle : IEnumerable { - internal MatrixRectangle(MatrixPoint location, MatrixSize size) : - this() - { - Location = location; - Size = size; - } + internal MatrixRectangle(MatrixPoint location, MatrixSize size) : + this() + { + Location = location; + Size = size; + } - public MatrixPoint Location { get; private set; } - public MatrixSize Size { get; private set; } + public MatrixPoint Location { get; private set; } + public MatrixSize Size { get; private set; } - public IEnumerator GetEnumerator() - { - for (int j = Location.Y; j < Location.Y + Size.Height; j++) - { - for (int i = Location.X; i < Location.X + Size.Width; i++) - { - yield return new MatrixPoint(i, j); - } - } - } + public IEnumerator GetEnumerator() + { + for (int j = Location.Y; j < Location.Y + Size.Height; j++) + { + for (int i = Location.X; i < Location.X + Size.Width; i++) + { + yield return new MatrixPoint(i, j); + } + } + } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public override string ToString() => $"Rectangle({Location.X};{Location.Y}):({Size.Width} x {Size.Height})"; -} + public override string ToString() => $"Rectangle({Location.X};{Location.Y}):({Size.Width} x {Size.Height})"; +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/MatrixSize.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/MatrixSize.cs index cc6b2b3..a5b2705 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/MatrixSize.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/MatrixSize.cs @@ -2,18 +2,18 @@ namespace Gma.QrCodeNet.Encoding; public struct MatrixSize { - internal MatrixSize(int width, int height) - : this() - { - Width = width; - Height = height; - } + internal MatrixSize(int width, int height) + : this() + { + Width = width; + Height = height; + } - public int Width { get; private set; } - public int Height { get; private set; } + public int Width { get; private set; } + public int Height { get; private set; } - public override string ToString() - { - return $"Size({Width};{Height})"; - } -} + public override string ToString() + { + return $"Size({Width};{Height})"; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/MatrixStatus.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/MatrixStatus.cs index 4cd3647..d996b40 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/MatrixStatus.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/MatrixStatus.cs @@ -2,7 +2,7 @@ namespace Gma.QrCodeNet.Encoding; public enum MatrixStatus { - None, - NoMask, - Data -} + None, + NoMask, + Data +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/PositioninngPatternBuilder.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/PositioninngPatternBuilder.cs index 3d695cb..d33bb0b 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/PositioninngPatternBuilder.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/PositioninngPatternBuilder.cs @@ -4,11 +4,11 @@ namespace Gma.QrCodeNet.Encoding.Positioning; internal static class PositioningPatternBuilder { - internal static void EmbedBasicPatterns(int version, TriStateMatrix matrix) - { - new PositionDetectionPattern(version).ApplyTo(matrix); - new DarkDotAtLeftBottom(version).ApplyTo(matrix); - new AlignmentPattern(version).ApplyTo(matrix); - new TimingPattern(version).ApplyTo(matrix); - } -} + internal static void EmbedBasicPatterns(int version, TriStateMatrix matrix) + { + new PositionDetectionPattern(version).ApplyTo(matrix); + new DarkDotAtLeftBottom(version).ApplyTo(matrix); + new AlignmentPattern(version).ApplyTo(matrix); + new TimingPattern(version).ApplyTo(matrix); + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/AlignmentPattern.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/AlignmentPattern.cs index 17c8b7e..ed186b1 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/AlignmentPattern.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/AlignmentPattern.cs @@ -1,105 +1,101 @@ -using System; -using System.Collections.Generic; -using System.Linq; - namespace Gma.QrCodeNet.Encoding.Positioning.Stencils; internal class AlignmentPattern : PatternStencilBase { - public AlignmentPattern(int version) - : base(version) - { - } + public AlignmentPattern(int version) + : base(version) + { + } - private static bool[,] AlignmentPatternArray { get; } = - new[,] - { - { X, X, X, X, X }, - { X, O, O, O, X }, - { X, O, X, O, X }, - { X, O, O, O, X }, - { X, X, X, X, X } - }; + private static bool[,] AlignmentPatternArray { get; } = + new[,] + { + { X, X, X, X, X }, + { X, O, O, O, X }, + { X, O, X, O, X }, + { X, O, O, O, X }, + { X, X, X, X, X } + }; - public override bool[,] Stencil => AlignmentPatternArray; + public override bool[,] Stencil => AlignmentPatternArray; - // Table E.1 — Row/column coordinates of center module of Alignment Patterns - private static byte[][] AlignmentPatternCoordinatesByVersion { get; } = - new[] - { - Array.Empty(), - Array.Empty(), - new byte[] { 6, 18 }, - new byte[] { 6, 22 }, - new byte[] { 6, 26 }, - new byte[] { 6, 30 }, - new byte[] { 6, 34 }, - new byte[] { 6, 22, 38 }, - new byte[] { 6, 24, 42 }, - new byte[] { 6, 26, 46 }, - new byte[] { 6, 28, 50 }, - new byte[] { 6, 30, 54 }, - new byte[] { 6, 32, 58 }, - new byte[] { 6, 34, 62 }, - new byte[] { 6, 26, 46, 66 }, - new byte[] { 6, 26, 48, 70 }, - new byte[] { 6, 26, 50, 74 }, - new byte[] { 6, 30, 54, 78 }, - new byte[] { 6, 30, 56, 82 }, - new byte[] { 6, 30, 58, 86 }, - new byte[] { 6, 34, 62, 90 }, - new byte[] { 6, 28, 50, 72, 94 }, - new byte[] { 6, 26, 50, 74, 98 }, - new byte[] { 6, 30, 54, 78, 102 }, - new byte[] { 6, 28, 54, 80, 106 }, - new byte[] { 6, 32, 58, 84, 110 }, - new byte[] { 6, 30, 58, 86, 114 }, - new byte[] { 6, 34, 62, 90, 118 }, - new byte[] { 6, 26, 50, 74, 98, 122 }, - new byte[] { 6, 30, 54, 78, 102, 126 }, - new byte[] { 6, 26, 52, 78, 104, 130 }, - new byte[] { 6, 30, 56, 82, 108, 134 }, - new byte[] { 6, 34, 60, 86, 112, 138 }, - new byte[] { 6, 30, 58, 86, 114, 142 }, - new byte[] { 6, 34, 62, 90, 118, 146 }, - new byte[] { 6, 30, 54, 78, 102, 126, 150 }, - new byte[] { 6, 24, 50, 76, 102, 128, 154 }, - new byte[] { 6, 28, 54, 80, 106, 132, 158 }, - new byte[] { 6, 32, 58, 84, 110, 136, 162 }, - new byte[] { 6, 26, 54, 82, 110, 138, 166 }, - new byte[] { 6, 30, 58, 86, 114, 142, 170 } - }; + // Table E.1 — Row/column coordinates of center module of Alignment Patterns + private static byte[][] AlignmentPatternCoordinatesByVersion { get; } = + new[] + { + Array.Empty(), + Array.Empty(), + new byte[] { 6, 18 }, + new byte[] { 6, 22 }, + new byte[] { 6, 26 }, + new byte[] { 6, 30 }, + new byte[] { 6, 34 }, + new byte[] { 6, 22, 38 }, + new byte[] { 6, 24, 42 }, + new byte[] { 6, 26, 46 }, + new byte[] { 6, 28, 50 }, + new byte[] { 6, 30, 54 }, + new byte[] { 6, 32, 58 }, + new byte[] { 6, 34, 62 }, + new byte[] { 6, 26, 46, 66 }, + new byte[] { 6, 26, 48, 70 }, + new byte[] { 6, 26, 50, 74 }, + new byte[] { 6, 30, 54, 78 }, + new byte[] { 6, 30, 56, 82 }, + new byte[] { 6, 30, 58, 86 }, + new byte[] { 6, 34, 62, 90 }, + new byte[] { 6, 28, 50, 72, 94 }, + new byte[] { 6, 26, 50, 74, 98 }, + new byte[] { 6, 30, 54, 78, 102 }, + new byte[] { 6, 28, 54, 80, 106 }, + new byte[] { 6, 32, 58, 84, 110 }, + new byte[] { 6, 30, 58, 86, 114 }, + new byte[] { 6, 34, 62, 90, 118 }, + new byte[] { 6, 26, 50, 74, 98, 122 }, + new byte[] { 6, 30, 54, 78, 102, 126 }, + new byte[] { 6, 26, 52, 78, 104, 130 }, + new byte[] { 6, 30, 56, 82, 108, 134 }, + new byte[] { 6, 34, 60, 86, 112, 138 }, + new byte[] { 6, 30, 58, 86, 114, 142 }, + new byte[] { 6, 34, 62, 90, 118, 146 }, + new byte[] { 6, 30, 54, 78, 102, 126, 150 }, + new byte[] { 6, 24, 50, 76, 102, 128, 154 }, + new byte[] { 6, 28, 54, 80, 106, 132, 158 }, + new byte[] { 6, 32, 58, 84, 110, 136, 162 }, + new byte[] { 6, 26, 54, 82, 110, 138, 166 }, + new byte[] { 6, 30, 58, 86, 114, 142, 170 } + }; - public override void ApplyTo(TriStateMatrix matrix) - { - foreach (MatrixPoint coordinatePair in GetNonColidingCoordinatePairs(matrix)) - { - CopyTo(matrix, coordinatePair, MatrixStatus.NoMask); - } - } + public override void ApplyTo(TriStateMatrix matrix) + { + foreach (MatrixPoint coordinatePair in GetNonColidingCoordinatePairs(matrix)) + { + CopyTo(matrix, coordinatePair, MatrixStatus.NoMask); + } + } - public IEnumerable GetNonColidingCoordinatePairs(TriStateMatrix matrix) - { - return - GetAllCoordinatePairs() - .Where(point => matrix.MStatus(point.Offset(2, 2)) == MatrixStatus.None); - } + public IEnumerable GetNonColidingCoordinatePairs(TriStateMatrix matrix) + { + return + GetAllCoordinatePairs() + .Where(point => matrix.MStatus(point.Offset(2, 2)) == MatrixStatus.None); + } - private IEnumerable GetAllCoordinatePairs() - { - IEnumerable coordinates = GetPatternCoordinatesByVersion(Version); - foreach (byte centerX in coordinates) - { - foreach (byte centerY in coordinates) - { - MatrixPoint location = new(centerX - 2, centerY - 2); - yield return location; - } - } - } + private IEnumerable GetAllCoordinatePairs() + { + IEnumerable coordinates = GetPatternCoordinatesByVersion(Version); + foreach (byte centerX in coordinates) + { + foreach (byte centerY in coordinates) + { + MatrixPoint location = new(centerX - 2, centerY - 2); + yield return location; + } + } + } - private static IEnumerable GetPatternCoordinatesByVersion(int version) - { - return AlignmentPatternCoordinatesByVersion[version]; - } -} + private static IEnumerable GetPatternCoordinatesByVersion(int version) + { + return AlignmentPatternCoordinatesByVersion[version]; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/DarkDotAtLeftBottom.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/DarkDotAtLeftBottom.cs index 829e3bc..f4747fd 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/DarkDotAtLeftBottom.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/DarkDotAtLeftBottom.cs @@ -1,17 +1,15 @@ -using System; - namespace Gma.QrCodeNet.Encoding.Positioning.Stencils; internal class DarkDotAtLeftBottom : PatternStencilBase { - public DarkDotAtLeftBottom(int version) : base(version) - { - } + public DarkDotAtLeftBottom(int version) : base(version) + { + } - public override bool[,] Stencil => throw new NotImplementedException(); + public override bool[,] Stencil => throw new NotImplementedException(); - public override void ApplyTo(TriStateMatrix matrix) - { - matrix[8, matrix.Width - 8, MatrixStatus.NoMask] = true; - } -} + public override void ApplyTo(TriStateMatrix matrix) + { + matrix[8, matrix.Width - 8, MatrixStatus.NoMask] = true; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/PatternStencilBase.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/PatternStencilBase.cs index 874042e..a69c6c9 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/PatternStencilBase.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/PatternStencilBase.cs @@ -1,32 +1,30 @@ -using System; - namespace Gma.QrCodeNet.Encoding.Positioning.Stencils; internal abstract class PatternStencilBase : BitMatrix { - protected const bool O = false; - protected const bool X = true; + protected const bool O = false; + protected const bool X = true; - internal PatternStencilBase(int version) - { - Version = version; - } + internal PatternStencilBase(int version) + { + Version = version; + } - public int Version { get; private set; } + public int Version { get; private set; } - public abstract bool[,] Stencil { get; } + public abstract bool[,] Stencil { get; } - public override int Width => Stencil.GetLength(0); + public override int Width => Stencil.GetLength(0); - public override int Height => Stencil.GetLength(1); + public override int Height => Stencil.GetLength(1); - public override bool[,] InternalArray => throw new NotImplementedException(); + public override bool[,] InternalArray => throw new NotImplementedException(); - public override bool this[int i, int j] - { - get => Stencil[i, j]; - set => throw new NotSupportedException(); - } + public override bool this[int i, int j] + { + get => Stencil[i, j]; + set => throw new NotSupportedException(); + } - public abstract void ApplyTo(TriStateMatrix matrix); -} + public abstract void ApplyTo(TriStateMatrix matrix); +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/PositionDetectionPattern.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/PositionDetectionPattern.cs index 0ebb501..75fc45b 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/PositionDetectionPattern.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/PositionDetectionPattern.cs @@ -2,40 +2,40 @@ namespace Gma.QrCodeNet.Encoding.Positioning.Stencils; internal class PositionDetectionPattern : PatternStencilBase { - public PositionDetectionPattern(int version) - : base(version) - { - } + public PositionDetectionPattern(int version) + : base(version) + { + } - private static bool[,] PositionDetection { get; } = - new[,] - { - { O, O, O, O, O, O, O, O, O }, - { O, X, X, X, X, X, X, X, O }, - { O, X, O, O, O, O, O, X, O }, - { O, X, O, X, X, X, O, X, O }, - { O, X, O, X, X, X, O, X, O }, - { O, X, O, X, X, X, O, X, O }, - { O, X, O, O, O, O, O, X, O }, - { O, X, X, X, X, X, X, X, O }, - { O, O, O, O, O, O, O, O, O } - }; + private static bool[,] PositionDetection { get; } = + new[,] + { + { O, O, O, O, O, O, O, O, O }, + { O, X, X, X, X, X, X, X, O }, + { O, X, O, O, O, O, O, X, O }, + { O, X, O, X, X, X, O, X, O }, + { O, X, O, X, X, X, O, X, O }, + { O, X, O, X, X, X, O, X, O }, + { O, X, O, O, O, O, O, X, O }, + { O, X, X, X, X, X, X, X, O }, + { O, O, O, O, O, O, O, O, O } + }; - public override bool[,] Stencil => PositionDetection; + public override bool[,] Stencil => PositionDetection; - public override void ApplyTo(TriStateMatrix matrix) - { - MatrixSize size = GetSizeOfSquareWithSeparators(); + public override void ApplyTo(TriStateMatrix matrix) + { + MatrixSize size = GetSizeOfSquareWithSeparators(); - MatrixPoint leftTopCorner = new(0, 0); - CopyTo(matrix, new MatrixRectangle(new MatrixPoint(1, 1), size), leftTopCorner, MatrixStatus.NoMask); + MatrixPoint leftTopCorner = new(0, 0); + CopyTo(matrix, new MatrixRectangle(new MatrixPoint(1, 1), size), leftTopCorner, MatrixStatus.NoMask); - MatrixPoint rightTopCorner = new(matrix.Width - Width + 1, 0); - CopyTo(matrix, new MatrixRectangle(new MatrixPoint(0, 1), size), rightTopCorner, MatrixStatus.NoMask); + MatrixPoint rightTopCorner = new(matrix.Width - Width + 1, 0); + CopyTo(matrix, new MatrixRectangle(new MatrixPoint(0, 1), size), rightTopCorner, MatrixStatus.NoMask); - MatrixPoint leftBottomCorner = new(0, matrix.Width - Width + 1); - CopyTo(matrix, new MatrixRectangle(new MatrixPoint(1, 0), size), leftBottomCorner, MatrixStatus.NoMask); - } + MatrixPoint leftBottomCorner = new(0, matrix.Width - Width + 1); + CopyTo(matrix, new MatrixRectangle(new MatrixPoint(1, 0), size), leftBottomCorner, MatrixStatus.NoMask); + } - private MatrixSize GetSizeOfSquareWithSeparators() => new(Width - 1, Height - 1); -} + private MatrixSize GetSizeOfSquareWithSeparators() => new(Width - 1, Height - 1); +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/TimingPattern.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/TimingPattern.cs index 1cb2dd0..1847fe6 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/TimingPattern.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Positioning/Stencils/TimingPattern.cs @@ -1,35 +1,33 @@ -using System; - namespace Gma.QrCodeNet.Encoding.Positioning.Stencils; internal class TimingPattern : PatternStencilBase { - public TimingPattern(int version) - : base(version) - { - } + public TimingPattern(int version) + : base(version) + { + } - public override bool[,] Stencil => throw new NotImplementedException(); + public override bool[,] Stencil => throw new NotImplementedException(); - public override void ApplyTo(TriStateMatrix matrix) - { - // -8 is for skipping position detection patterns (size 7), and two horizontal/vertical - // separation patterns (size 1). Thus, 8 = 7 + 1. - for (int i = 8; i < matrix.Width - 8; ++i) - { - bool value = (sbyte)((i + 1) % 2) == 1; + public override void ApplyTo(TriStateMatrix matrix) + { + // -8 is for skipping position detection patterns (size 7), and two horizontal/vertical + // separation patterns (size 1). Thus, 8 = 7 + 1. + for (int i = 8; i < matrix.Width - 8; ++i) + { + bool value = (sbyte)((i + 1) % 2) == 1; - // Horizontal line. - if (matrix.MStatus(6, i) == MatrixStatus.None) - { - matrix[6, i, MatrixStatus.NoMask] = value; - } + // Horizontal line. + if (matrix.MStatus(6, i) == MatrixStatus.None) + { + matrix[6, i, MatrixStatus.NoMask] = value; + } - // Vertical line. - if (matrix.MStatus(i, 6) == MatrixStatus.None) - { - matrix[i, 6, MatrixStatus.NoMask] = value; - } - } - } -} + // Vertical line. + if (matrix.MStatus(i, 6) == MatrixStatus.None) + { + matrix[i, 6, MatrixStatus.NoMask] = value; + } + } + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/QRCodeConstantVariable.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/QRCodeConstantVariable.cs index ca1518b..d2932cd 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/QRCodeConstantVariable.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/QRCodeConstantVariable.cs @@ -5,48 +5,48 @@ namespace Gma.QrCodeNet.Encoding; /// public static class QRCodeConstantVariable { - public const int MinVersion = 1; - public const int MaxVersion = 40; + public const int MinVersion = 1; + public const int MaxVersion = 40; - public const string DefaultEncoding = "iso-8859-1"; - public const string UTF8Encoding = "utf-8"; + public const string DefaultEncoding = "iso-8859-1"; + public const string UTF8Encoding = "utf-8"; - /// - /// ISO/IEC 18004:2006(E) Page 45 Chapter Generating the error correction codewords - /// Primative Polynomial = Bin 100011101 = Dec 285 - /// - public const int QRCodePrimitive = 285; + /// + /// ISO/IEC 18004:2006(E) Page 45 Chapter Generating the error correction codewords + /// Primative Polynomial = Bin 100011101 = Dec 285 + /// + public const int QRCodePrimitive = 285; - internal const int TerminatorNPaddingBit = 0; + internal const int TerminatorNPaddingBit = 0; - internal const int TerminatorLength = 4; + internal const int TerminatorLength = 4; - /// - /// 0xEC - /// - internal const int PadeCodewordsOdd = 0xec; + /// + /// 0xEC + /// + internal const int PadeCodewordsOdd = 0xec; - /// - /// 0x11 - /// - internal const int PadeCodewordsEven = 0x11; + /// + /// 0x11 + /// + internal const int PadeCodewordsEven = 0x11; - internal const int PositionStencilWidth = 7; + internal const int PositionStencilWidth = 7; - internal static bool[] PadeOdd = new bool[] - { - true, true, true, false, - true, true, false, false - }; + internal static bool[] PadeOdd = new bool[] + { + true, true, true, false, + true, true, false, false + }; - internal static bool[] PadeEven = new bool[] - { - false, false, false, true, - false, false, false, true - }; + internal static bool[] PadeEven = new bool[] + { + false, false, false, true, + false, false, false, true + }; - /// - /// URL:http://en.wikipedia.org/wiki/Byte-order_mark - /// - public static byte[] UTF8ByteOrderMark => new byte[] { 0xEF, 0xBB, 0xBF }; -} + /// + /// URL:http://en.wikipedia.org/wiki/Byte-order_mark + /// + public static byte[] UTF8ByteOrderMark => new byte[] { 0xEF, 0xBB, 0xBF }; +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/QRCodeEncode.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/QRCodeEncode.cs index 9ad782c..b353867 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/QRCodeEncode.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/QRCodeEncode.cs @@ -9,24 +9,24 @@ namespace Gma.QrCodeNet.Encoding; internal static class QRCodeEncode { - internal static BitMatrix Encode(string content, ErrorCorrectionLevel errorLevel) - { - EncodationStruct encodeStruct = DataEncode.Encode(content, errorLevel); + internal static BitMatrix Encode(string content, ErrorCorrectionLevel errorLevel) + { + EncodationStruct encodeStruct = DataEncode.Encode(content, errorLevel); - return ProcessEncodationResult(encodeStruct, errorLevel); - } + return ProcessEncodationResult(encodeStruct, errorLevel); + } - private static BitMatrix ProcessEncodationResult(EncodationStruct encodeStruct, ErrorCorrectionLevel errorLevel) - { - BitList codewords = ECGenerator.FillECCodewords(encodeStruct.DataCodewords, encodeStruct.VersionDetail); + private static BitMatrix ProcessEncodationResult(EncodationStruct encodeStruct, ErrorCorrectionLevel errorLevel) + { + BitList codewords = ECGenerator.FillECCodewords(encodeStruct.DataCodewords, encodeStruct.VersionDetail); - TriStateMatrix triMatrix = new(encodeStruct.VersionDetail.MatrixWidth); - PositioningPatternBuilder.EmbedBasicPatterns(encodeStruct.VersionDetail.Version, triMatrix); + TriStateMatrix triMatrix = new(encodeStruct.VersionDetail.MatrixWidth); + PositioningPatternBuilder.EmbedBasicPatterns(encodeStruct.VersionDetail.Version, triMatrix); - triMatrix.EmbedVersionInformation(encodeStruct.VersionDetail.Version); - triMatrix.EmbedFormatInformation(errorLevel, new Pattern0()); - triMatrix.TryEmbedCodewords(codewords); + triMatrix.EmbedVersionInformation(encodeStruct.VersionDetail.Version); + triMatrix.EmbedFormatInformation(errorLevel, new Pattern0()); + triMatrix.TryEmbedCodewords(codewords); - return triMatrix.GetLowestPenaltyMatrix(errorLevel); - } -} + return triMatrix.GetLowestPenaltyMatrix(errorLevel); + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/QrCode.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/QrCode.cs index de932c6..bcf4e26 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/QrCode.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/QrCode.cs @@ -8,21 +8,21 @@ namespace Gma.QrCodeNet.Encoding; /// public class QrCode { - internal QrCode(BitMatrix matrix) - { - Matrix = matrix; - IsContainMatrix = true; - } + internal QrCode(BitMatrix matrix) + { + Matrix = matrix; + IsContainMatrix = true; + } - public bool IsContainMatrix - { - get; - private set; - } + public bool IsContainMatrix + { + get; + private set; + } - public BitMatrix Matrix - { - get; - private set; - } -} + public BitMatrix Matrix + { + get; + private set; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/QrEncoder.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/QrEncoder.cs index c40d951..f78c155 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/QrEncoder.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/QrEncoder.cs @@ -2,38 +2,38 @@ namespace Gma.QrCodeNet.Encoding; public class QrEncoder { - /// - /// Default QrEncoder will set ErrorCorrectionLevel as M - /// - public QrEncoder() - : this(ErrorCorrectionLevel.M) - { - } + /// + /// Default QrEncoder will set ErrorCorrectionLevel as M + /// + public QrEncoder() + : this(ErrorCorrectionLevel.M) + { + } - /// - /// QrEncoder with parameter ErrorCorrectionLevel. - /// - public QrEncoder(ErrorCorrectionLevel errorCorrectionLevel) - { - ErrorCorrectionLevel = errorCorrectionLevel; - } + /// + /// QrEncoder with parameter ErrorCorrectionLevel. + /// + public QrEncoder(ErrorCorrectionLevel errorCorrectionLevel) + { + ErrorCorrectionLevel = errorCorrectionLevel; + } - public ErrorCorrectionLevel ErrorCorrectionLevel { get; set; } + public ErrorCorrectionLevel ErrorCorrectionLevel { get; set; } - /// - /// Encode string content to QrCode matrix - /// - /// - /// This exception for string content is null, empty or too large - public QrCode Encode(string content) - { - if (string.IsNullOrEmpty(content)) - { - throw new InputOutOfBoundaryException("Input cannot be null or empty."); - } - else - { - return new QrCode(QRCodeEncode.Encode(content, ErrorCorrectionLevel)); - } - } -} + /// + /// Encode string content to QrCode matrix + /// + /// + /// This exception for string content is null, empty or too large + public QrCode Encode(string content) + { + if (string.IsNullOrEmpty(content)) + { + throw new InputOutOfBoundaryException("Input cannot be null or empty."); + } + else + { + return new QrCode(QRCodeEncode.Encode(content, ErrorCorrectionLevel)); + } + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/GaloisField256.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/GaloisField256.cs index 7ab528f..52324a9 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/GaloisField256.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/GaloisField256.cs @@ -1,5 +1,3 @@ -using System; - namespace Gma.QrCodeNet.Encoding.ReedSolomon; /// @@ -7,116 +5,116 @@ namespace Gma.QrCodeNet.Encoding.ReedSolomon; /// internal sealed class GaloisField256 { - internal GaloisField256(int primitive) - { - AntiLogTable = new int[256]; - LogTable = new int[256]; + internal GaloisField256(int primitive) + { + AntiLogTable = new int[256]; + LogTable = new int[256]; - Primitive = primitive; + Primitive = primitive; - int gfx = 1; + int gfx = 1; - // Power cycle is from 0 to 254. 2^255 = 1 = 2^0 - // Value cycle is from 1 to 255. Thus there should not have Log(0). - for (int powers = 0; powers < 256; powers++) - { - AntiLogTable[powers] = gfx; - if (powers != 255) - { - LogTable[gfx] = powers; - } + // Power cycle is from 0 to 254. 2^255 = 1 = 2^0 + // Value cycle is from 1 to 255. Thus there should not have Log(0). + for (int powers = 0; powers < 256; powers++) + { + AntiLogTable[powers] = gfx; + if (powers != 255) + { + LogTable[gfx] = powers; + } - gfx <<= 1; // gfx = gfx * 2 where alpha is 2. + gfx <<= 1; // gfx = gfx * 2 where alpha is 2. - if (gfx > 255) - { - gfx ^= primitive; - } - } - } + if (gfx > 255) + { + gfx ^= primitive; + } + } + } - private int[] AntiLogTable { get; } - private int[] LogTable { get; } + private int[] AntiLogTable { get; } + private int[] LogTable { get; } - internal int Primitive { get; } + internal int Primitive { get; } - internal static GaloisField256 QRCodeGaloisField => new(QRCodeConstantVariable.QRCodePrimitive); + internal static GaloisField256 QRCodeGaloisField => new(QRCodeConstantVariable.QRCodePrimitive); - /// - /// Powers of a in GF table. Where a = 2 - /// - internal int Exponent(int powersOfa) => AntiLogTable[powersOfa]; + /// + /// Powers of a in GF table. Where a = 2 + /// + internal int Exponent(int powersOfa) => AntiLogTable[powersOfa]; - /// - /// Log (power of a) in GF table. Where a = 2 - /// - internal int Log(int gfValue) - { - if (gfValue == 0) - { - throw new ArgumentException("GaloisField value will not be equal to 0, Log method."); - } + /// + /// Log (power of a) in GF table. Where a = 2 + /// + internal int Log(int gfValue) + { + if (gfValue == 0) + { + throw new ArgumentException("GaloisField value will not be equal to 0, Log method."); + } - return LogTable[gfValue]; - } + return LogTable[gfValue]; + } - internal int Inverse(int gfValue) - { - if (gfValue == 0) - { - throw new ArgumentException("GaloisField value will not be equal to 0, Inverse method."); - } + internal int Inverse(int gfValue) + { + if (gfValue == 0) + { + throw new ArgumentException("GaloisField value will not be equal to 0, Inverse method."); + } - return Exponent(255 - Log(gfValue)); - } + return Exponent(255 - Log(gfValue)); + } - internal int Addition(int gfValueA, int gfValueB) => gfValueA ^ gfValueB; + internal int Addition(int gfValueA, int gfValueB) => gfValueA ^ gfValueB; - internal int Subtraction(int gfValueA, int gfValueB) => Addition(gfValueA, gfValueB); // Subtraction is same as addition. + internal int Subtraction(int gfValueA, int gfValueB) => Addition(gfValueA, gfValueB); // Subtraction is same as addition. - /// - /// Product of two values. - /// In other words. a multiply b - /// - internal int Product(int gfValueA, int gfValueB) - { - if (gfValueA == 0 || gfValueB == 0) - { - return 0; - } - if (gfValueA == 1) - { - return gfValueB; - } - if (gfValueB == 1) - { - return gfValueA; - } + /// + /// Product of two values. + /// In other words. a multiply b + /// + internal int Product(int gfValueA, int gfValueB) + { + if (gfValueA == 0 || gfValueB == 0) + { + return 0; + } + if (gfValueA == 1) + { + return gfValueB; + } + if (gfValueB == 1) + { + return gfValueA; + } - return Exponent((Log(gfValueA) + Log(gfValueB)) % 255); - } + return Exponent((Log(gfValueA) + Log(gfValueB)) % 255); + } - /// - /// Quotient of two values. - /// In other words. a divided b - /// - internal int Quotient(int gfValueA, int gfValueB) - { - if (gfValueA == 0) - { - return 0; - } + /// + /// Quotient of two values. + /// In other words. a divided b + /// + internal int Quotient(int gfValueA, int gfValueB) + { + if (gfValueA == 0) + { + return 0; + } - if (gfValueB == 0) - { - throw new ArgumentException($"{nameof(gfValueB)} cannot be zero."); - } + if (gfValueB == 0) + { + throw new ArgumentException($"{nameof(gfValueB)} cannot be zero."); + } - if (gfValueB == 1) - { - return gfValueA; - } + if (gfValueB == 1) + { + return gfValueA; + } - return Exponent(Math.Abs(Log(gfValueA) - Log(gfValueB)) % 255); - } -} + return Exponent(Math.Abs(Log(gfValueA) - Log(gfValueB)) % 255); + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/GeneratorPolynomial.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/GeneratorPolynomial.cs index 94619a8..61036e9 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/GeneratorPolynomial.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/GeneratorPolynomial.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace Gma.QrCodeNet.Encoding.ReedSolomon; /// @@ -7,56 +5,56 @@ namespace Gma.QrCodeNet.Encoding.ReedSolomon; /// internal sealed class GeneratorPolynomial { - /// - /// After create GeneratorPolynomial. Keep it as long as possible. - /// Unless QRCode encode is done or no more QRCode need to generate. - /// - internal GeneratorPolynomial(GaloisField256 gfield) - { - Gfield = gfield; - CacheGenerator = new List(10) - { - new Polynomial(Gfield, new int[] { 1 }) - }; - } + /// + /// After create GeneratorPolynomial. Keep it as long as possible. + /// Unless QRCode encode is done or no more QRCode need to generate. + /// + internal GeneratorPolynomial(GaloisField256 gfield) + { + Gfield = gfield; + CacheGenerator = new List(10) + { + new Polynomial(Gfield, new int[] { 1 }) + }; + } - private GaloisField256 Gfield { get; } + private GaloisField256 Gfield { get; } - private List CacheGenerator { get; } + private List CacheGenerator { get; } - /// - /// Get generator by degree. (Largest degree for that generator) - /// - /// Generator - internal Polynomial GetGenerator(int degree) - { - if (degree >= CacheGenerator.Count) - { - BuildGenerator(degree); - } + /// + /// Get generator by degree. (Largest degree for that generator) + /// + /// Generator + internal Polynomial GetGenerator(int degree) + { + if (degree >= CacheGenerator.Count) + { + BuildGenerator(degree); + } - return CacheGenerator[degree]; - } + return CacheGenerator[degree]; + } - /// - /// Build Generator if we cannot find specific degree of generator from cache - /// - private void BuildGenerator(int degree) - { - lock (CacheGenerator) - { - int currentCacheLength = CacheGenerator.Count; - if (degree >= currentCacheLength) - { - Polynomial lastGenerator = CacheGenerator[currentCacheLength - 1]; + /// + /// Build Generator if we cannot find specific degree of generator from cache + /// + private void BuildGenerator(int degree) + { + lock (CacheGenerator) + { + int currentCacheLength = CacheGenerator.Count; + if (degree >= currentCacheLength) + { + Polynomial lastGenerator = CacheGenerator[currentCacheLength - 1]; - for (int d = currentCacheLength; d <= degree; d++) - { - Polynomial nextGenerator = lastGenerator.Multiply(new Polynomial(Gfield, new int[] { 1, Gfield.Exponent(d - 1) })); - CacheGenerator.Add(nextGenerator); - lastGenerator = nextGenerator; - } - } - } - } -} + for (int d = currentCacheLength; d <= degree; d++) + { + Polynomial nextGenerator = lastGenerator.Multiply(new Polynomial(Gfield, new int[] { 1, Gfield.Exponent(d - 1) })); + CacheGenerator.Add(nextGenerator); + lastGenerator = nextGenerator; + } + } + } + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/PolyDivideStruct.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/PolyDivideStruct.cs index 2619030..7b20bbd 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/PolyDivideStruct.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/PolyDivideStruct.cs @@ -2,14 +2,14 @@ namespace Gma.QrCodeNet.Encoding.ReedSolomon; internal struct PolyDivideStruct { - internal PolyDivideStruct(Polynomial quotient, Polynomial remainder) - : this() - { - Quotient = quotient; - Remainder = remainder; - } + internal PolyDivideStruct(Polynomial quotient, Polynomial remainder) + : this() + { + Quotient = quotient; + Remainder = remainder; + } - internal Polynomial Quotient { get; private set; } + internal Polynomial Quotient { get; private set; } - internal Polynomial Remainder { get; private set; } -} + internal Polynomial Remainder { get; private set; } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/Polynomial.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/Polynomial.cs index c0daaa5..ad70fad 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/Polynomial.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/Polynomial.cs @@ -1,242 +1,240 @@ -using System; - namespace Gma.QrCodeNet.Encoding.ReedSolomon; internal sealed class Polynomial { - internal Polynomial(GaloisField256 gfield, int[] coefficients) - { - int coefficientsLength = coefficients.Length; + internal Polynomial(GaloisField256 gfield, int[] coefficients) + { + int coefficientsLength = coefficients.Length; - if (coefficientsLength == 0 || coefficients is null) - { - throw new ArithmeticException($"Cannot create empty {nameof(Polynomial)}."); - } + if (coefficientsLength == 0 || coefficients is null) + { + throw new ArithmeticException($"Cannot create empty {nameof(Polynomial)}."); + } - GField = gfield; + GField = gfield; - Primitive = gfield.Primitive; + Primitive = gfield.Primitive; - if (coefficientsLength > 1 && coefficients[0] == 0) - { - int firstNonZeroIndex = 1; - while (firstNonZeroIndex < coefficientsLength && coefficients[firstNonZeroIndex] == 0) - { - firstNonZeroIndex++; - } + if (coefficientsLength > 1 && coefficients[0] == 0) + { + int firstNonZeroIndex = 1; + while (firstNonZeroIndex < coefficientsLength && coefficients[firstNonZeroIndex] == 0) + { + firstNonZeroIndex++; + } - if (firstNonZeroIndex == coefficientsLength) - { - Coefficients = new int[] { 0 }; - } - else - { - int newLength = coefficientsLength - firstNonZeroIndex; - Coefficients = new int[newLength]; - Array.Copy(coefficients, firstNonZeroIndex, Coefficients, 0, newLength); - } - } - else - { - Coefficients = new int[coefficientsLength]; - Array.Copy(coefficients, Coefficients, coefficientsLength); - } - } + if (firstNonZeroIndex == coefficientsLength) + { + Coefficients = new int[] { 0 }; + } + else + { + int newLength = coefficientsLength - firstNonZeroIndex; + Coefficients = new int[newLength]; + Array.Copy(coefficients, firstNonZeroIndex, Coefficients, 0, newLength); + } + } + else + { + Coefficients = new int[coefficientsLength]; + Array.Copy(coefficients, Coefficients, coefficientsLength); + } + } - internal int[] Coefficients { get; } + internal int[] Coefficients { get; } - internal GaloisField256 GField { get; } + internal GaloisField256 GField { get; } - internal int Degree => Coefficients.Length - 1; + internal int Degree => Coefficients.Length - 1; - internal int Primitive { get; } + internal int Primitive { get; } - internal bool IsMonomialZero => Coefficients[0] == 0; + internal bool IsMonomialZero => Coefficients[0] == 0; - /// - /// Coefficient position. where (coefficient)x^degree - /// - internal int GetCoefficient(int degree) - { - // Eg: x^2 + x + 1. degree 1, reverse position = degree + 1 = 2. - // Pos = 3 - 2 = 1 - return Coefficients[^(degree + 1)]; - } + /// + /// Coefficient position. where (coefficient)x^degree + /// + internal int GetCoefficient(int degree) + { + // Eg: x^2 + x + 1. degree 1, reverse position = degree + 1 = 2. + // Pos = 3 - 2 = 1 + return Coefficients[^(degree + 1)]; + } - /// - /// Add another Polynomial to current one - /// - /// The polynomial need to add or subtract to current one - /// Result polynomial after add or subtract - internal Polynomial AddOrSubtract(Polynomial other) - { - if (Primitive != other.Primitive) - { - throw new ArgumentException($"{nameof(Polynomial)} cannot perform {nameof(AddOrSubtract)} as they do not have the same {nameof(Primitive)}" + - $" for {nameof(GaloisField256)}."); - } - if (IsMonomialZero) - { - return other; - } - else if (other.IsMonomialZero) - { - return this; - } + /// + /// Add another Polynomial to current one + /// + /// The polynomial need to add or subtract to current one + /// Result polynomial after add or subtract + internal Polynomial AddOrSubtract(Polynomial other) + { + if (Primitive != other.Primitive) + { + throw new ArgumentException($"{nameof(Polynomial)} cannot perform {nameof(AddOrSubtract)} as they do not have the same {nameof(Primitive)}" + + $" for {nameof(GaloisField256)}."); + } + if (IsMonomialZero) + { + return other; + } + else if (other.IsMonomialZero) + { + return this; + } - int otherLength = other.Coefficients.Length; - int thisLength = Coefficients.Length; + int otherLength = other.Coefficients.Length; + int thisLength = Coefficients.Length; - if (otherLength > thisLength) - { - return CoefficientXor(Coefficients, other.Coefficients); - } - else - { - return CoefficientXor(other.Coefficients, Coefficients); - } - } + if (otherLength > thisLength) + { + return CoefficientXor(Coefficients, other.Coefficients); + } + else + { + return CoefficientXor(other.Coefficients, Coefficients); + } + } - internal Polynomial CoefficientXor(int[] smallerCoefficients, int[] largerCoefficients) - { - if (smallerCoefficients.Length > largerCoefficients.Length) - { - throw new ArgumentException($"Cannot perform {nameof(CoefficientXor)} method as smaller {nameof(Coefficients)} length is greater than the larger one."); - } + internal Polynomial CoefficientXor(int[] smallerCoefficients, int[] largerCoefficients) + { + if (smallerCoefficients.Length > largerCoefficients.Length) + { + throw new ArgumentException($"Cannot perform {nameof(CoefficientXor)} method as smaller {nameof(Coefficients)} length is greater than the larger one."); + } - int targetLength = largerCoefficients.Length; - int[] xorCoefficient = new int[targetLength]; - int lengthDiff = largerCoefficients.Length - smallerCoefficients.Length; + int targetLength = largerCoefficients.Length; + int[] xorCoefficient = new int[targetLength]; + int lengthDiff = largerCoefficients.Length - smallerCoefficients.Length; - Array.Copy(largerCoefficients, 0, xorCoefficient, 0, lengthDiff); + Array.Copy(largerCoefficients, 0, xorCoefficient, 0, lengthDiff); - for (int index = lengthDiff; index < targetLength; index++) - { - xorCoefficient[index] = GField.Addition(largerCoefficients[index], smallerCoefficients[index - lengthDiff]); - } + for (int index = lengthDiff; index < targetLength; index++) + { + xorCoefficient[index] = GField.Addition(largerCoefficients[index], smallerCoefficients[index - lengthDiff]); + } - return new Polynomial(GField, xorCoefficient); - } + return new Polynomial(GField, xorCoefficient); + } - /// - /// Multiply current Polynomial to another one. - /// - /// Result polynomial after multiply - internal Polynomial Multiply(Polynomial other) - { - if (Primitive != other.Primitive) - { - throw new ArgumentException($"{nameof(Polynomial)} cannot perform {nameof(Multiply)} as they do not have the same {nameof(Primitive)}" + - $" for {nameof(GaloisField256)}."); - } - if (IsMonomialZero || other.IsMonomialZero) - { - return new Polynomial(GField, new int[] { 0 }); - } + /// + /// Multiply current Polynomial to another one. + /// + /// Result polynomial after multiply + internal Polynomial Multiply(Polynomial other) + { + if (Primitive != other.Primitive) + { + throw new ArgumentException($"{nameof(Polynomial)} cannot perform {nameof(Multiply)} as they do not have the same {nameof(Primitive)}" + + $" for {nameof(GaloisField256)}."); + } + if (IsMonomialZero || other.IsMonomialZero) + { + return new Polynomial(GField, new int[] { 0 }); + } - int[] aCoefficients = Coefficients; - int aLength = aCoefficients.Length; - int[] bCoefficient = other.Coefficients; - int bLength = bCoefficient.Length; - int[] rCoefficients = new int[aLength + bLength - 1]; + int[] aCoefficients = Coefficients; + int aLength = aCoefficients.Length; + int[] bCoefficient = other.Coefficients; + int bLength = bCoefficient.Length; + int[] rCoefficients = new int[aLength + bLength - 1]; - for (int aIndex = 0; aIndex < aLength; aIndex++) - { - int aCoeff = aCoefficients[aIndex]; - for (int bIndex = 0; bIndex < bLength; bIndex++) - { - rCoefficients[aIndex + bIndex] = - GField.Addition(rCoefficients[aIndex + bIndex], GField.Product(aCoeff, bCoefficient[bIndex])); - } - } - return new Polynomial(GField, rCoefficients); - } + for (int aIndex = 0; aIndex < aLength; aIndex++) + { + int aCoeff = aCoefficients[aIndex]; + for (int bIndex = 0; bIndex < bLength; bIndex++) + { + rCoefficients[aIndex + bIndex] = + GField.Addition(rCoefficients[aIndex + bIndex], GField.Product(aCoeff, bCoefficient[bIndex])); + } + } + return new Polynomial(GField, rCoefficients); + } - /// - /// Multiplay scalar to current polynomial - /// - /// Result of polynomial after multiply scalar - internal Polynomial MultiplyScalar(int scalar) - { - if (scalar == 0) - { - return new Polynomial(GField, new int[] { 0 }); - } - else if (scalar == 1) - { - return this; - } + /// + /// Multiplay scalar to current polynomial + /// + /// Result of polynomial after multiply scalar + internal Polynomial MultiplyScalar(int scalar) + { + if (scalar == 0) + { + return new Polynomial(GField, new int[] { 0 }); + } + else if (scalar == 1) + { + return this; + } - int length = Coefficients.Length; - int[] rCoefficient = new int[length]; + int length = Coefficients.Length; + int[] rCoefficient = new int[length]; - for (int index = 0; index < length; index++) - { - rCoefficient[index] = GField.Product(Coefficients[index], scalar); - } + for (int index = 0; index < length; index++) + { + rCoefficient[index] = GField.Product(Coefficients[index], scalar); + } - return new Polynomial(GField, rCoefficient); - } + return new Polynomial(GField, rCoefficient); + } - /// - /// Divide current polynomial by "other" - /// - /// Result polynomial after divide - internal PolyDivideStruct Divide(Polynomial other) - { - if (Primitive != other.Primitive) - { - throw new ArgumentException($"{nameof(Polynomial)} cannot perform {nameof(Divide)} as they do not have the same {nameof(Primitive)}" + - $" for {nameof(GaloisField256)}."); - } - if (other.IsMonomialZero) - { - throw new ArgumentException($"Cannot divide by {nameof(Polynomial)} Zero."); - } + /// + /// Divide current polynomial by "other" + /// + /// Result polynomial after divide + internal PolyDivideStruct Divide(Polynomial other) + { + if (Primitive != other.Primitive) + { + throw new ArgumentException($"{nameof(Polynomial)} cannot perform {nameof(Divide)} as they do not have the same {nameof(Primitive)}" + + $" for {nameof(GaloisField256)}."); + } + if (other.IsMonomialZero) + { + throw new ArgumentException($"Cannot divide by {nameof(Polynomial)} Zero."); + } - // This divide by other = a divide by b - int aLength = Coefficients.Length; + // This divide by other = a divide by b + int aLength = Coefficients.Length; - // We will make change to aCoefficient. It will return as remainder - int[] aCoefficients = new int[aLength]; - Array.Copy(Coefficients, 0, aCoefficients, 0, aLength); + // We will make change to aCoefficient. It will return as remainder + int[] aCoefficients = new int[aLength]; + Array.Copy(Coefficients, 0, aCoefficients, 0, aLength); - int bLength = other.Coefficients.Length; + int bLength = other.Coefficients.Length; - if (aLength < bLength) - { - return new PolyDivideStruct(new Polynomial(GField, new int[] { 0 }), this); - } - else - { - // Quotient coefficients - // qLastIndex = alength - blength qlength = qLastIndex + 1 - int[] qCoefficients = new int[(aLength - bLength) + 1]; + if (aLength < bLength) + { + return new PolyDivideStruct(new Polynomial(GField, new int[] { 0 }), this); + } + else + { + // Quotient coefficients + // qLastIndex = alength - blength qlength = qLastIndex + 1 + int[] qCoefficients = new int[(aLength - bLength) + 1]; - // Denominator - int otherLeadingTerm = other.GetCoefficient(other.Degree); - int inverseOtherLeadingTerm = GField.Inverse(otherLeadingTerm); + // Denominator + int otherLeadingTerm = other.GetCoefficient(other.Degree); + int inverseOtherLeadingTerm = GField.Inverse(otherLeadingTerm); - for (int aIndex = 0; aIndex <= aLength - bLength; aIndex++) - { - if (aCoefficients[aIndex] != 0) - { - int aScalar = GField.Product(inverseOtherLeadingTerm, aCoefficients[aIndex]); - Polynomial term = other.MultiplyScalar(aScalar); - qCoefficients[aIndex] = aScalar; + for (int aIndex = 0; aIndex <= aLength - bLength; aIndex++) + { + if (aCoefficients[aIndex] != 0) + { + int aScalar = GField.Product(inverseOtherLeadingTerm, aCoefficients[aIndex]); + Polynomial term = other.MultiplyScalar(aScalar); + qCoefficients[aIndex] = aScalar; - int[] bCoefficient = term.Coefficients; - if (bCoefficient[0] != 0) - { - for (int bIndex = 0; bIndex < bLength; bIndex++) - { - aCoefficients[aIndex + bIndex] = GField.Subtraction(aCoefficients[aIndex + bIndex], bCoefficient[bIndex]); - } - } - } - } + int[] bCoefficient = term.Coefficients; + if (bCoefficient[0] != 0) + { + for (int bIndex = 0; bIndex < bLength; bIndex++) + { + aCoefficients[aIndex + bIndex] = GField.Subtraction(aCoefficients[aIndex + bIndex], bCoefficient[bIndex]); + } + } + } + } - return new PolyDivideStruct(new Polynomial(GField, qCoefficients), new Polynomial(GField, aCoefficients)); - } - } -} + return new PolyDivideStruct(new Polynomial(GField, qCoefficients), new Polynomial(GField, aCoefficients)); + } + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/ReedSolomonEncoder.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/ReedSolomonEncoder.cs index 6bc47de..3aa8136 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/ReedSolomonEncoder.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/ReedSolomon/ReedSolomonEncoder.cs @@ -1,91 +1,89 @@ -using System; - namespace Gma.QrCodeNet.Encoding.ReedSolomon; internal sealed class ReedSolomonEncoder { - /// - /// Encode an array of data codeword with GaloisField 256. - /// - /// Array of data codewords for a single block. - /// Number of error correction codewords for data codewords - /// Cached or newly create GeneratorPolynomial - /// Return error correction codewords array - internal static byte[] Encode(byte[] dataBytes, int numECBytes, GeneratorPolynomial generatorPoly) - { - int dataLength = dataBytes.Length; + /// + /// Encode an array of data codeword with GaloisField 256. + /// + /// Array of data codewords for a single block. + /// Number of error correction codewords for data codewords + /// Cached or newly create GeneratorPolynomial + /// Return error correction codewords array + internal static byte[] Encode(byte[] dataBytes, int numECBytes, GeneratorPolynomial generatorPoly) + { + int dataLength = dataBytes.Length; if (generatorPoly == null) throw new ArgumentNullException(nameof(generatorPoly)); if (dataLength == 0) - { - throw new ArgumentException("There is no data bytes to encode."); - } + { + throw new ArgumentException("There is no data bytes to encode."); + } - if (numECBytes <= 0) - { - throw new ArgumentException("No Error Correction bytes."); - } + if (numECBytes <= 0) + { + throw new ArgumentException("No Error Correction bytes."); + } - int[] toEncode = ConvertToIntArray(dataBytes, dataLength, numECBytes); + int[] toEncode = ConvertToIntArray(dataBytes, dataLength, numECBytes); - Polynomial generator = generatorPoly.GetGenerator(numECBytes); + Polynomial generator = generatorPoly.GetGenerator(numECBytes); - Polynomial dataPoly = new(generator.GField, toEncode); + Polynomial dataPoly = new(generator.GField, toEncode); - PolyDivideStruct divideResult = dataPoly.Divide(generator); + PolyDivideStruct divideResult = dataPoly.Divide(generator); - int[] remainderCoeffs = divideResult.Remainder.Coefficients; + int[] remainderCoeffs = divideResult.Remainder.Coefficients; - return ConvertTosByteArray(remainderCoeffs, numECBytes); - } + return ConvertTosByteArray(remainderCoeffs, numECBytes); + } - /// - /// Convert data codewords to int array. And add error correction space at end of that array - /// - /// Data codewords array - /// Data codewords length - /// Num of error correction bytes - /// Int array for data codewords array follow by error correction space - private static int[] ConvertToIntArray(byte[] dataBytes, int dataLength, int numECBytes) - { - int[] resultArray = new int[dataLength + numECBytes]; + /// + /// Convert data codewords to int array. And add error correction space at end of that array + /// + /// Data codewords array + /// Data codewords length + /// Num of error correction bytes + /// Int array for data codewords array follow by error correction space + private static int[] ConvertToIntArray(byte[] dataBytes, int dataLength, int numECBytes) + { + int[] resultArray = new int[dataLength + numECBytes]; - for (int index = 0; index < dataLength; index++) - { - resultArray[index] = dataBytes[index] & 0xff; - } + for (int index = 0; index < dataLength; index++) + { + resultArray[index] = dataBytes[index] & 0xff; + } - return resultArray; - } + return resultArray; + } - /// - /// Reassembly error correction codewords. As Polynomial class will eliminate zero monomial at front. - /// - /// Remainder byte array after divide. - /// Error correction codewords length - /// Error correction codewords - private static byte[] ConvertTosByteArray(int[] remainder, int numECBytes) - { - int remainderLength = remainder.Length; - if (remainderLength > numECBytes) - { - throw new ArgumentException($"Num of {nameof(remainder)} bytes cannot be larger than {nameof(numECBytes)}."); - } + /// + /// Reassembly error correction codewords. As Polynomial class will eliminate zero monomial at front. + /// + /// Remainder byte array after divide. + /// Error correction codewords length + /// Error correction codewords + private static byte[] ConvertTosByteArray(int[] remainder, int numECBytes) + { + int remainderLength = remainder.Length; + if (remainderLength > numECBytes) + { + throw new ArgumentException($"Num of {nameof(remainder)} bytes cannot be larger than {nameof(numECBytes)}."); + } - int numZeroCoeffs = numECBytes - remainderLength; + int numZeroCoeffs = numECBytes - remainderLength; - byte[] resultArray = new byte[numECBytes]; - for (int index = 0; index < numZeroCoeffs; index++) - { - resultArray[index] = 0; - } + byte[] resultArray = new byte[numECBytes]; + for (int index = 0; index < numZeroCoeffs; index++) + { + resultArray[index] = 0; + } - for (int rIndex = 0; rIndex < remainderLength; rIndex++) - { - resultArray[numZeroCoeffs + rIndex] = (byte)remainder[rIndex]; - } + for (int rIndex = 0; rIndex < remainderLength; rIndex++) + { + resultArray[numZeroCoeffs + rIndex] = (byte)remainder[rIndex]; + } - return resultArray; - } -} + return resultArray; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/StateMatrix.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/StateMatrix.cs index 617e771..6378620 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/StateMatrix.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/StateMatrix.cs @@ -2,27 +2,27 @@ namespace Gma.QrCodeNet.Encoding; public sealed class StateMatrix { - public StateMatrix(int width) - { - Width = width; - MatrixStatus = new MatrixStatus[width, width]; - } + public StateMatrix(int width) + { + Width = width; + MatrixStatus = new MatrixStatus[width, width]; + } - private MatrixStatus[,] MatrixStatus { get; } + private MatrixStatus[,] MatrixStatus { get; } - public MatrixStatus this[int x, int y] - { - get => MatrixStatus[x, y]; - set => MatrixStatus[x, y] = value; - } + public MatrixStatus this[int x, int y] + { + get => MatrixStatus[x, y]; + set => MatrixStatus[x, y] = value; + } - internal MatrixStatus this[MatrixPoint point] - { - get => this[point.X, point.Y]; - set => this[point.X, point.Y] = value; - } + internal MatrixStatus this[MatrixPoint point] + { + get => this[point.X, point.Y]; + set => this[point.X, point.Y] = value; + } - public int Width { get; } + public int Width { get; } - public int Height => Width; -} + public int Height => Width; +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Terminate/Terminator.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Terminate/Terminator.cs index 2e9b9aa..586c8e8 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Terminate/Terminator.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Terminate/Terminator.cs @@ -1,73 +1,71 @@ -using System; - namespace Gma.QrCodeNet.Encoding.Terminate; internal static class Terminator { - private const int NumBitsForByte = 8; + private const int NumBitsForByte = 8; - /// - /// This method will create BitList that contains - /// terminator, padding and pad codewords for given datacodewords. - /// Use it to full fill the data codewords capacity. Thus avoid massive empty bits. - /// - /// ISO/IEC 18004:2006 P. 32 33. - /// Terminator / Bit stream to codeword conversion - /// Method will add terminator bits (Terminator, padding and padcodewords) at end of baseList - /// Num of bits for datacodewords without terminator - /// Total number of datacodewords for specific version. - /// Receive it under Version/VersionTable - internal static void TerminateBites(this BitList baseList, int dataCount, int numTotalDataCodewords) - { - int numTotalDataBits = numTotalDataCodewords << 3; - int numDataBits = dataCount; + /// + /// This method will create BitList that contains + /// terminator, padding and pad codewords for given datacodewords. + /// Use it to full fill the data codewords capacity. Thus avoid massive empty bits. + /// + /// ISO/IEC 18004:2006 P. 32 33. + /// Terminator / Bit stream to codeword conversion + /// Method will add terminator bits (Terminator, padding and padcodewords) at end of baseList + /// Num of bits for datacodewords without terminator + /// Total number of datacodewords for specific version. + /// Receive it under Version/VersionTable + internal static void TerminateBites(this BitList baseList, int dataCount, int numTotalDataCodewords) + { + int numTotalDataBits = numTotalDataCodewords << 3; + int numDataBits = dataCount; - int numFillerBits = numTotalDataBits - numDataBits; - int numBitsNeedForLastByte = numFillerBits & 0x7; - int numFillerBytes = numFillerBits >> 3; + int numFillerBits = numTotalDataBits - numDataBits; + int numBitsNeedForLastByte = numFillerBits & 0x7; + int numFillerBytes = numFillerBits >> 3; - // BitList result = new BitList(); - if (numBitsNeedForLastByte >= QRCodeConstantVariable.TerminatorLength) - { - baseList.TerminatorPadding(numBitsNeedForLastByte); - baseList.PadeCodewords(numFillerBytes); - } - else if (numFillerBytes == 0) - { - baseList.TerminatorPadding(numBitsNeedForLastByte); - } - else if (numFillerBytes > 0) - { - baseList.TerminatorPadding(numBitsNeedForLastByte + NumBitsForByte); - baseList.PadeCodewords(numFillerBytes - 1); - } + // BitList result = new BitList(); + if (numBitsNeedForLastByte >= QRCodeConstantVariable.TerminatorLength) + { + baseList.TerminatorPadding(numBitsNeedForLastByte); + baseList.PadeCodewords(numFillerBytes); + } + else if (numFillerBytes == 0) + { + baseList.TerminatorPadding(numBitsNeedForLastByte); + } + else if (numFillerBytes > 0) + { + baseList.TerminatorPadding(numBitsNeedForLastByte + NumBitsForByte); + baseList.PadeCodewords(numFillerBytes - 1); + } - if (baseList.Count != numTotalDataBits) - { - throw new ArgumentException( - $"Generate terminator and Padding fail. Num of bits need: {numFillerBytes}. Actual length: {baseList.Count - numDataBits}"); - } - } + if (baseList.Count != numTotalDataBits) + { + throw new ArgumentException( + $"Generate terminator and Padding fail. Num of bits need: {numFillerBytes}. Actual length: {baseList.Count - numDataBits}"); + } + } - private static void PadeCodewords(this BitList mainList, int numOfPadeCodewords) - { - if (numOfPadeCodewords < 0) - { - throw new ArgumentException("Num of pade codewords is less than Zero"); - } + private static void PadeCodewords(this BitList mainList, int numOfPadeCodewords) + { + if (numOfPadeCodewords < 0) + { + throw new ArgumentException("Num of pade codewords is less than Zero"); + } - for (int numOfP = 1; numOfP <= numOfPadeCodewords; numOfP++) - { - if (numOfP % 2 == 1) - { - mainList.Add(QRCodeConstantVariable.PadeCodewordsOdd, NumBitsForByte); - } - else - { - mainList.Add(QRCodeConstantVariable.PadeCodewordsEven, NumBitsForByte); - } - } - } + for (int numOfP = 1; numOfP <= numOfPadeCodewords; numOfP++) + { + if (numOfP % 2 == 1) + { + mainList.Add(QRCodeConstantVariable.PadeCodewordsOdd, NumBitsForByte); + } + else + { + mainList.Add(QRCodeConstantVariable.PadeCodewordsEven, NumBitsForByte); + } + } + } - private static void TerminatorPadding(this BitList mainList, int numBits) => mainList.Add(QRCodeConstantVariable.TerminatorNPaddingBit, numBits); -} + private static void TerminatorPadding(this BitList mainList, int numBits) => mainList.Add(QRCodeConstantVariable.TerminatorNPaddingBit, numBits); +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/TriStateMatrix.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/TriStateMatrix.cs index 584eb1e..d852c57 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/TriStateMatrix.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/TriStateMatrix.cs @@ -1,48 +1,46 @@ -using System; - namespace Gma.QrCodeNet.Encoding; public class TriStateMatrix : BitMatrixBase { - public TriStateMatrix(int width) : base(width, new bool[width, width]) - { - StateMatrix = new StateMatrix(width); - } + public TriStateMatrix(int width) : base(width, new bool[width, width]) + { + StateMatrix = new StateMatrix(width); + } - internal TriStateMatrix(bool[,] internalArray) : base(internalArray) - { - StateMatrix = new StateMatrix(internalArray.GetLength(0)); - } + internal TriStateMatrix(bool[,] internalArray) : base(internalArray) + { + StateMatrix = new StateMatrix(internalArray.GetLength(0)); + } - private StateMatrix StateMatrix { get; } + private StateMatrix StateMatrix { get; } - public override bool this[int i, int j] - { - get => InternalArray[i, j]; - set - { - if (MStatus(i, j) is MatrixStatus.None or MatrixStatus.NoMask) - { - throw new InvalidOperationException($"The value of cell [{i}, {j}] is not set or is Stencil."); - } - InternalArray[i, j] = value; - } - } + public override bool this[int i, int j] + { + get => InternalArray[i, j]; + set + { + if (MStatus(i, j) is MatrixStatus.None or MatrixStatus.NoMask) + { + throw new InvalidOperationException($"The value of cell [{i}, {j}] is not set or is Stencil."); + } + InternalArray[i, j] = value; + } + } - public bool this[int i, int j, MatrixStatus mstatus] - { - set - { - StateMatrix[i, j] = mstatus; - InternalArray[i, j] = value; - } - } + public bool this[int i, int j, MatrixStatus mstatus] + { + set + { + StateMatrix[i, j] = mstatus; + InternalArray[i, j] = value; + } + } - public override int Height => Width; + public override int Height => Width; - public override int Width => base.Width; + public override int Width => base.Width; - internal MatrixStatus MStatus(int i, int j) => StateMatrix[i, j]; + internal MatrixStatus MStatus(int i, int j) => StateMatrix[i, j]; - internal MatrixStatus MStatus(MatrixPoint point) => MStatus(point.X, point.Y); -} + internal MatrixStatus MStatus(MatrixPoint point) => MStatus(point.X, point.Y); +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/VersionDetail.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/VersionDetail.cs index f7f66e4..efb108a 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/VersionDetail.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/VersionDetail.cs @@ -2,33 +2,33 @@ namespace Gma.QrCodeNet.Encoding; public struct VersionDetail { - internal VersionDetail(int version, int numTotalBytes, int numDataBytes, int numECBlocks) - : this() - { - Version = version; - NumTotalBytes = numTotalBytes; - NumDataBytes = numDataBytes; - NumECBlocks = numECBlocks; - } + internal VersionDetail(int version, int numTotalBytes, int numDataBytes, int numECBlocks) + : this() + { + Version = version; + NumTotalBytes = numTotalBytes; + NumDataBytes = numDataBytes; + NumECBlocks = numECBlocks; + } - internal int Version { get; private set; } - internal int NumTotalBytes { get; private set; } - internal int NumDataBytes { get; private set; } - internal int NumECBlocks { get; private set; } + internal int Version { get; private set; } + internal int NumTotalBytes { get; private set; } + internal int NumDataBytes { get; private set; } + internal int NumECBlocks { get; private set; } - internal int MatrixWidth => Width(Version); + internal int MatrixWidth => Width(Version); - internal int ECBlockGroup1 => NumECBlocks - ECBlockGroup2; + internal int ECBlockGroup1 => NumECBlocks - ECBlockGroup2; - internal int ECBlockGroup2 => NumTotalBytes % NumECBlocks; + internal int ECBlockGroup2 => NumTotalBytes % NumECBlocks; - internal int NumDataBytesGroup1 => NumDataBytes / NumECBlocks; + internal int NumDataBytesGroup1 => NumDataBytes / NumECBlocks; - internal int NumDataBytesGroup2 => NumDataBytesGroup1 + 1; + internal int NumDataBytesGroup2 => NumDataBytesGroup1 + 1; - internal int NumECBytesPerBlock => (NumTotalBytes - NumDataBytes) / NumECBlocks; + internal int NumECBytesPerBlock => (NumTotalBytes - NumDataBytes) / NumECBlocks; - internal static int Width(int version) => 17 + (4 * version); + internal static int Width(int version) => 17 + (4 * version); - public override string ToString() => $"{Version};{NumTotalBytes};{NumDataBytes};{NumECBlocks}"; -} + public override string ToString() => $"{Version};{NumTotalBytes};{NumDataBytes};{NumECBlocks}"; +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/ErrorCorrectionBlock.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/ErrorCorrectionBlock.cs index cb796ef..7d900a1 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/ErrorCorrectionBlock.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/ErrorCorrectionBlock.cs @@ -2,14 +2,14 @@ namespace Gma.QrCodeNet.Encoding.Versions; internal struct ErrorCorrectionBlock { - internal ErrorCorrectionBlock(int numErrorCorrectionBlock, int numDataCodewards) - : this() - { - NumErrorCorrectionBlock = numErrorCorrectionBlock; - NumDataCodewords = numDataCodewards; - } + internal ErrorCorrectionBlock(int numErrorCorrectionBlock, int numDataCodewards) + : this() + { + NumErrorCorrectionBlock = numErrorCorrectionBlock; + NumDataCodewords = numDataCodewards; + } - internal int NumErrorCorrectionBlock { get; private set; } + internal int NumErrorCorrectionBlock { get; private set; } - internal int NumDataCodewords { get; private set; } -} + internal int NumDataCodewords { get; private set; } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/ErrorCorrectionBlocks.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/ErrorCorrectionBlocks.cs index b1fc966..e15f844 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/ErrorCorrectionBlocks.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/ErrorCorrectionBlocks.cs @@ -1,55 +1,53 @@ -using System; - namespace Gma.QrCodeNet.Encoding.Versions; internal struct ErrorCorrectionBlocks { - internal ErrorCorrectionBlocks(int numErrorCorrectionCodewords, ErrorCorrectionBlock ecBlock) - : this() - { - NumErrorCorrectionCodewards = numErrorCorrectionCodewords; - ECBlock = new ErrorCorrectionBlock[] { ecBlock }; + internal ErrorCorrectionBlocks(int numErrorCorrectionCodewords, ErrorCorrectionBlock ecBlock) + : this() + { + NumErrorCorrectionCodewards = numErrorCorrectionCodewords; + ECBlock = new ErrorCorrectionBlock[] { ecBlock }; - Initialize(); - } + Initialize(); + } - internal ErrorCorrectionBlocks(int numErrorCorrectionCodewords, ErrorCorrectionBlock ecBlock1, ErrorCorrectionBlock ecBlock2) - : this() - { - NumErrorCorrectionCodewards = numErrorCorrectionCodewords; - ECBlock = new ErrorCorrectionBlock[] { ecBlock1, ecBlock2 }; + internal ErrorCorrectionBlocks(int numErrorCorrectionCodewords, ErrorCorrectionBlock ecBlock1, ErrorCorrectionBlock ecBlock2) + : this() + { + NumErrorCorrectionCodewards = numErrorCorrectionCodewords; + ECBlock = new ErrorCorrectionBlock[] { ecBlock1, ecBlock2 }; - Initialize(); - } + Initialize(); + } - internal int NumErrorCorrectionCodewards { get; private set; } + internal int NumErrorCorrectionCodewards { get; private set; } - internal int NumBlocks { get; private set; } + internal int NumBlocks { get; private set; } - internal int ErrorCorrectionCodewordsPerBlock { get; private set; } + internal int ErrorCorrectionCodewordsPerBlock { get; private set; } - private ErrorCorrectionBlock[] ECBlock { get; } + private ErrorCorrectionBlock[] ECBlock { get; } - /// - /// Get Error Correction Blocks - /// - internal ErrorCorrectionBlock[] GetECBlocks() => ECBlock; + /// + /// Get Error Correction Blocks + /// + internal ErrorCorrectionBlock[] GetECBlocks() => ECBlock; - /// - /// Initialize for NumBlocks and ErrorCorrectionCodewordsPerBlock - /// - private void Initialize() + /// + /// Initialize for NumBlocks and ErrorCorrectionCodewordsPerBlock + /// + private void Initialize() { if (ECBlock == null) throw new ArgumentNullException(nameof(ECBlock)); NumBlocks = 0; - int blockLength = ECBlock.Length; - for (int i = 0; i < blockLength; i++) - { - NumBlocks += ECBlock[i].NumErrorCorrectionBlock; - } + int blockLength = ECBlock.Length; + for (int i = 0; i < blockLength; i++) + { + NumBlocks += ECBlock[i].NumErrorCorrectionBlock; + } - ErrorCorrectionCodewordsPerBlock = NumErrorCorrectionCodewards / NumBlocks; - } -} + ErrorCorrectionCodewordsPerBlock = NumErrorCorrectionCodewards / NumBlocks; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/QRCodeVersion.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/QRCodeVersion.cs index cfa160e..30ad44b 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/QRCodeVersion.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/QRCodeVersion.cs @@ -1,35 +1,33 @@ -using System; - namespace Gma.QrCodeNet.Encoding.Versions; internal struct QRCodeVersion { - internal QRCodeVersion(int versionNum, int totalCodewords, ErrorCorrectionBlocks ecblocksL, ErrorCorrectionBlocks ecblocksM, ErrorCorrectionBlocks ecblocksQ, ErrorCorrectionBlocks ecblocksH) - : this() - { - VersionNum = versionNum; - TotalCodewords = totalCodewords; - ECBlocks = new ErrorCorrectionBlocks[] { ecblocksL, ecblocksM, ecblocksQ, ecblocksH }; - DimensionForVersion = 17 + (versionNum * 4); - } + internal QRCodeVersion(int versionNum, int totalCodewords, ErrorCorrectionBlocks ecblocksL, ErrorCorrectionBlocks ecblocksM, ErrorCorrectionBlocks ecblocksQ, ErrorCorrectionBlocks ecblocksH) + : this() + { + VersionNum = versionNum; + TotalCodewords = totalCodewords; + ECBlocks = new ErrorCorrectionBlocks[] { ecblocksL, ecblocksM, ecblocksQ, ecblocksH }; + DimensionForVersion = 17 + (versionNum * 4); + } - internal int VersionNum { get; private set; } + internal int VersionNum { get; private set; } - internal int TotalCodewords { get; private set; } + internal int TotalCodewords { get; private set; } - internal int DimensionForVersion { get; private set; } + internal int DimensionForVersion { get; private set; } - private ErrorCorrectionBlocks[] ECBlocks { get; } + private ErrorCorrectionBlocks[] ECBlocks { get; } - internal ErrorCorrectionBlocks GetECBlocksByLevel(ErrorCorrectionLevel eCLevel) - { - return eCLevel switch - { - ErrorCorrectionLevel.L => ECBlocks[0], - ErrorCorrectionLevel.M => ECBlocks[1], - ErrorCorrectionLevel.Q => ECBlocks[2], - ErrorCorrectionLevel.H => ECBlocks[3], - _ => throw new ArgumentOutOfRangeException(nameof(eCLevel)) - }; - } -} + internal ErrorCorrectionBlocks GetECBlocksByLevel(ErrorCorrectionLevel eCLevel) + { + return eCLevel switch + { + ErrorCorrectionLevel.L => ECBlocks[0], + ErrorCorrectionLevel.M => ECBlocks[1], + ErrorCorrectionLevel.Q => ECBlocks[2], + ErrorCorrectionLevel.H => ECBlocks[3], + _ => throw new ArgumentOutOfRangeException(nameof(eCLevel)) + }; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/VersionControl.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/VersionControl.cs index 0fdd767..30d5fea 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/VersionControl.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/VersionControl.cs @@ -1,144 +1,143 @@ -using System; using Gma.QrCodeNet.Encoding.DataEncodation; namespace Gma.QrCodeNet.Encoding.Versions; internal static class VersionControl { - private const int NumBitsModeIndicator = 4; - private const string DefaultEncoding = QRCodeConstantVariable.DefaultEncoding; + private const int NumBitsModeIndicator = 4; + private const string DefaultEncoding = QRCodeConstantVariable.DefaultEncoding; - private static readonly int[] VERSION_GROUP = new int[] { 9, 26, 40 }; + private static readonly int[] VERSION_GROUP = new int[] { 9, 26, 40 }; - /// - /// Determine which version to use - /// - /// Number of bits for encoded content - /// Encoding name for EightBitByte - /// VersionDetail and ECI - internal static VersionControlStruct InitialSetup(int dataBitsLength, ErrorCorrectionLevel level, string encodingName) - { - int totalDataBits = dataBitsLength; + /// + /// Determine which version to use + /// + /// Number of bits for encoded content + /// Encoding name for EightBitByte + /// VersionDetail and ECI + internal static VersionControlStruct InitialSetup(int dataBitsLength, ErrorCorrectionLevel level, string encodingName) + { + int totalDataBits = dataBitsLength; - bool containECI = false; + bool containECI = false; - BitList eciHeader = new(); + BitList eciHeader = new(); - if (encodingName is not DefaultEncoding and not QRCodeConstantVariable.UTF8Encoding) - { - ECISet eciSet = new(ECISet.AppendOption.NameToValue); - int eciValue = eciSet.GetECIValueByName(encodingName); + if (encodingName is not DefaultEncoding and not QRCodeConstantVariable.UTF8Encoding) + { + ECISet eciSet = new(ECISet.AppendOption.NameToValue); + int eciValue = eciSet.GetECIValueByName(encodingName); - totalDataBits += ECISet.NumOfECIHeaderBits(eciValue); - eciHeader = eciSet.GetECIHeader(encodingName); - containECI = true; - } + totalDataBits += ECISet.NumOfECIHeaderBits(eciValue); + eciHeader = eciSet.GetECIHeader(encodingName); + containECI = true; + } - // Determine which version group it belong to - int searchGroup = DynamicSearchIndicator(totalDataBits, level); + // Determine which version group it belong to + int searchGroup = DynamicSearchIndicator(totalDataBits, level); - int[] charCountIndicator = CharCountIndicatorTable.GetCharCountIndicatorSet(); + int[] charCountIndicator = CharCountIndicatorTable.GetCharCountIndicatorSet(); - totalDataBits += (NumBitsModeIndicator + charCountIndicator[searchGroup]); + totalDataBits += (NumBitsModeIndicator + charCountIndicator[searchGroup]); - int lowerSearchBoundary = searchGroup == 0 ? 1 : (VERSION_GROUP[searchGroup - 1] + 1); - int higherSearchBoundary = VERSION_GROUP[searchGroup]; + int lowerSearchBoundary = searchGroup == 0 ? 1 : (VERSION_GROUP[searchGroup - 1] + 1); + int higherSearchBoundary = VERSION_GROUP[searchGroup]; - // Binary search to find proper version - int versionNum = BinarySearch(totalDataBits, level, lowerSearchBoundary, higherSearchBoundary); + // Binary search to find proper version + int versionNum = BinarySearch(totalDataBits, level, lowerSearchBoundary, higherSearchBoundary); - VersionControlStruct vcStruct = FillVCStruct(versionNum, level); + VersionControlStruct vcStruct = FillVCStruct(versionNum, level); - vcStruct.IsContainECI = containECI; + vcStruct.IsContainECI = containECI; - vcStruct.ECIHeader = eciHeader; + vcStruct.ECIHeader = eciHeader; - return vcStruct; - } + return vcStruct; + } - private static VersionControlStruct FillVCStruct(int versionNum, ErrorCorrectionLevel level) - { - if (versionNum is < 1 or > 40) - { - throw new InvalidOperationException($"Unexpected version number: {versionNum}"); - } + private static VersionControlStruct FillVCStruct(int versionNum, ErrorCorrectionLevel level) + { + if (versionNum is < 1 or > 40) + { + throw new InvalidOperationException($"Unexpected version number: {versionNum}"); + } - VersionControlStruct vcStruct = new(); + VersionControlStruct vcStruct = new(); - int version = versionNum; + int version = versionNum; - QRCodeVersion versionData = VersionTable.GetVersionByNum(versionNum); + QRCodeVersion versionData = VersionTable.GetVersionByNum(versionNum); - int numTotalBytes = versionData.TotalCodewords; + int numTotalBytes = versionData.TotalCodewords; - ErrorCorrectionBlocks ecBlocks = versionData.GetECBlocksByLevel(level); - int numDataBytes = numTotalBytes - ecBlocks.NumErrorCorrectionCodewards; - int numECBlocks = ecBlocks.NumBlocks; + ErrorCorrectionBlocks ecBlocks = versionData.GetECBlocksByLevel(level); + int numDataBytes = numTotalBytes - ecBlocks.NumErrorCorrectionCodewards; + int numECBlocks = ecBlocks.NumBlocks; - VersionDetail vcDetail = new(version, numTotalBytes, numDataBytes, numECBlocks); + VersionDetail vcDetail = new(version, numTotalBytes, numDataBytes, numECBlocks); - vcStruct.VersionDetail = vcDetail; - return vcStruct; - } + vcStruct.VersionDetail = vcDetail; + return vcStruct; + } - /// - /// Decide which version group it belong to - /// - /// Number of bits for bitlist where it contain DataBits encode from input content and ECI header - /// Error correction level - /// Version group index for VERSION_GROUP - private static int DynamicSearchIndicator(int numBits, ErrorCorrectionLevel level) - { - int[] charCountIndicator = CharCountIndicatorTable.GetCharCountIndicatorSet(); - int loopLength = VERSION_GROUP.Length; - for (int i = 0; i < loopLength; i++) - { - int totalBits = numBits + NumBitsModeIndicator + charCountIndicator[i]; + /// + /// Decide which version group it belong to + /// + /// Number of bits for bitlist where it contain DataBits encode from input content and ECI header + /// Error correction level + /// Version group index for VERSION_GROUP + private static int DynamicSearchIndicator(int numBits, ErrorCorrectionLevel level) + { + int[] charCountIndicator = CharCountIndicatorTable.GetCharCountIndicatorSet(); + int loopLength = VERSION_GROUP.Length; + for (int i = 0; i < loopLength; i++) + { + int totalBits = numBits + NumBitsModeIndicator + charCountIndicator[i]; - QRCodeVersion version = VersionTable.GetVersionByNum(VERSION_GROUP[i]); - int numECCodewords = version.GetECBlocksByLevel(level).NumErrorCorrectionCodewards; + QRCodeVersion version = VersionTable.GetVersionByNum(VERSION_GROUP[i]); + int numECCodewords = version.GetECBlocksByLevel(level).NumErrorCorrectionCodewards; - int dataCodewords = version.TotalCodewords - numECCodewords; + int dataCodewords = version.TotalCodewords - numECCodewords; - if (totalBits <= dataCodewords * 8) - { - return i; - } - } + if (totalBits <= dataCodewords * 8) + { + return i; + } + } - throw new InputOutOfBoundaryException($"QRCode do not have enough space for {(numBits + NumBitsModeIndicator + charCountIndicator[2])} bits"); - } + throw new InputOutOfBoundaryException($"QRCode do not have enough space for {(numBits + NumBitsModeIndicator + charCountIndicator[2])} bits"); + } - /// - /// Use number of data bits(header + eci header + data bits from EncoderBase) to search for proper version to use - /// between min and max boundary. - /// Boundary define by DynamicSearchIndicator method. - /// - private static int BinarySearch(int numDataBits, ErrorCorrectionLevel level, int lowerVersionNum, int higherVersionNum) - { - int middleVersionNumber; + /// + /// Use number of data bits(header + eci header + data bits from EncoderBase) to search for proper version to use + /// between min and max boundary. + /// Boundary define by DynamicSearchIndicator method. + /// + private static int BinarySearch(int numDataBits, ErrorCorrectionLevel level, int lowerVersionNum, int higherVersionNum) + { + int middleVersionNumber; - while (lowerVersionNum <= higherVersionNum) - { - middleVersionNumber = (lowerVersionNum + higherVersionNum) / 2; - QRCodeVersion version = VersionTable.GetVersionByNum(middleVersionNumber); - int numECCodewords = version.GetECBlocksByLevel(level).NumErrorCorrectionCodewards; - int dataCodewords = version.TotalCodewords - numECCodewords; + while (lowerVersionNum <= higherVersionNum) + { + middleVersionNumber = (lowerVersionNum + higherVersionNum) / 2; + QRCodeVersion version = VersionTable.GetVersionByNum(middleVersionNumber); + int numECCodewords = version.GetECBlocksByLevel(level).NumErrorCorrectionCodewards; + int dataCodewords = version.TotalCodewords - numECCodewords; - if (dataCodewords << 3 == numDataBits) - { - return middleVersionNumber; - } + if (dataCodewords << 3 == numDataBits) + { + return middleVersionNumber; + } - if (dataCodewords << 3 > numDataBits) - { - higherVersionNum = middleVersionNumber - 1; - } - else - { - lowerVersionNum = middleVersionNumber + 1; - } - } - return lowerVersionNum; - } -} + if (dataCodewords << 3 > numDataBits) + { + higherVersionNum = middleVersionNumber - 1; + } + else + { + lowerVersionNum = middleVersionNumber + 1; + } + } + return lowerVersionNum; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/VersionControlStruct.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/VersionControlStruct.cs index 7979f18..682e478 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/VersionControlStruct.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/VersionControlStruct.cs @@ -2,7 +2,7 @@ namespace Gma.QrCodeNet.Encoding.Versions; internal struct VersionControlStruct { - internal VersionDetail VersionDetail { get; set; } - internal bool IsContainECI { get; set; } - internal BitList ECIHeader { get; set; } -} + internal VersionDetail VersionDetail { get; set; } + internal bool IsContainECI { get; set; } + internal BitList ECIHeader { get; set; } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/VersionTable.cs b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/VersionTable.cs index ef30b0b..79f6028 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/VersionTable.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/Versions/VersionTable.cs @@ -1,317 +1,315 @@ -using System; - namespace Gma.QrCodeNet.Encoding.Versions; public static class VersionTable { - private static readonly QRCodeVersion[] Version = Initialize(); + private static readonly QRCodeVersion[] Version = Initialize(); - internal static QRCodeVersion GetVersionByNum(int versionNum) - { - if (versionNum is < QRCodeConstantVariable.MinVersion or > QRCodeConstantVariable.MaxVersion) - { - throw new InvalidOperationException($"Unexpected version number: {versionNum}."); - } + internal static QRCodeVersion GetVersionByNum(int versionNum) + { + if (versionNum is < QRCodeConstantVariable.MinVersion or > QRCodeConstantVariable.MaxVersion) + { + throw new InvalidOperationException($"Unexpected version number: {versionNum}."); + } - return Version[versionNum - 1]; - } + return Version[versionNum - 1]; + } - internal static QRCodeVersion GetVersionByWidth(int matrixWidth) - { - if ((matrixWidth - 17) % 4 != 0) - { - throw new ArgumentException("Incorrect matrix width."); - } - else - { - return GetVersionByNum((matrixWidth - 17) / 4); - } - } + internal static QRCodeVersion GetVersionByWidth(int matrixWidth) + { + if ((matrixWidth - 17) % 4 != 0) + { + throw new ArgumentException("Incorrect matrix width."); + } + else + { + return GetVersionByNum((matrixWidth - 17) / 4); + } + } - private static QRCodeVersion[] Initialize() - { - return new QRCodeVersion[] - { - new QRCodeVersion( - 1, - 26, - new ErrorCorrectionBlocks(7, new ErrorCorrectionBlock(1, 19)), - new ErrorCorrectionBlocks(10, new ErrorCorrectionBlock(1, 16)), - new ErrorCorrectionBlocks(13, new ErrorCorrectionBlock(1, 13)), - new ErrorCorrectionBlocks(17, new ErrorCorrectionBlock(1, 9))), - new QRCodeVersion( - 2, - 44, - new ErrorCorrectionBlocks(10, new ErrorCorrectionBlock(1, 34)), - new ErrorCorrectionBlocks(16, new ErrorCorrectionBlock(1, 28)), - new ErrorCorrectionBlocks(22, new ErrorCorrectionBlock(1, 22)), - new ErrorCorrectionBlocks(28, new ErrorCorrectionBlock(1, 16))), - new QRCodeVersion( - 3, - 70, - new ErrorCorrectionBlocks(15, new ErrorCorrectionBlock(1, 55)), - new ErrorCorrectionBlocks(26, new ErrorCorrectionBlock(1, 44)), - new ErrorCorrectionBlocks(36, new ErrorCorrectionBlock(2, 17)), - new ErrorCorrectionBlocks(44, new ErrorCorrectionBlock(2, 13))), - new QRCodeVersion( - 4, - 100, - new ErrorCorrectionBlocks(20, new ErrorCorrectionBlock(1, 80)), - new ErrorCorrectionBlocks(36, new ErrorCorrectionBlock(2, 32)), - new ErrorCorrectionBlocks(52, new ErrorCorrectionBlock(2, 24)), - new ErrorCorrectionBlocks(64, new ErrorCorrectionBlock(4, 9))), - new QRCodeVersion( - 5, - 134, - new ErrorCorrectionBlocks(26, new ErrorCorrectionBlock(1, 108)), - new ErrorCorrectionBlocks(48, new ErrorCorrectionBlock(2, 43)), - new ErrorCorrectionBlocks(72, new ErrorCorrectionBlock(2, 15), new ErrorCorrectionBlock(2, 16)), - new ErrorCorrectionBlocks(88, new ErrorCorrectionBlock(2, 11), new ErrorCorrectionBlock(2, 12))), - new QRCodeVersion( - 6, - 172, - new ErrorCorrectionBlocks(36, new ErrorCorrectionBlock(2, 68)), - new ErrorCorrectionBlocks(64, new ErrorCorrectionBlock(4, 27)), - new ErrorCorrectionBlocks(96, new ErrorCorrectionBlock(4, 19)), - new ErrorCorrectionBlocks(112, new ErrorCorrectionBlock(4, 15))), - new QRCodeVersion( - 7, - 196, - new ErrorCorrectionBlocks(40, new ErrorCorrectionBlock(2, 78)), - new ErrorCorrectionBlocks(72, new ErrorCorrectionBlock(4, 31)), - new ErrorCorrectionBlocks(108, new ErrorCorrectionBlock(2, 14), new ErrorCorrectionBlock(4, 15)), - new ErrorCorrectionBlocks(130, new ErrorCorrectionBlock(4, 13), new ErrorCorrectionBlock(1, 14))), - new QRCodeVersion( - 8, - 242, - new ErrorCorrectionBlocks(48, new ErrorCorrectionBlock(2, 97)), - new ErrorCorrectionBlocks(88, new ErrorCorrectionBlock(2, 38), new ErrorCorrectionBlock(2, 39)), - new ErrorCorrectionBlocks(132, new ErrorCorrectionBlock(4, 18), new ErrorCorrectionBlock(2, 19)), - new ErrorCorrectionBlocks(156, new ErrorCorrectionBlock(4, 14), new ErrorCorrectionBlock(2, 15))), - new QRCodeVersion( - 9, - 292, - new ErrorCorrectionBlocks(60, new ErrorCorrectionBlock(2, 116)), - new ErrorCorrectionBlocks(110, new ErrorCorrectionBlock(3, 36), new ErrorCorrectionBlock(2, 37)), - new ErrorCorrectionBlocks(160, new ErrorCorrectionBlock(4, 16), new ErrorCorrectionBlock(4, 17)), - new ErrorCorrectionBlocks(192, new ErrorCorrectionBlock(4, 12), new ErrorCorrectionBlock(4, 13))), - new QRCodeVersion( - 10, - 346, - new ErrorCorrectionBlocks(72, new ErrorCorrectionBlock(2, 68), new ErrorCorrectionBlock(2, 69)), - new ErrorCorrectionBlocks(130, new ErrorCorrectionBlock(4, 43), new ErrorCorrectionBlock(1, 44)), - new ErrorCorrectionBlocks(192, new ErrorCorrectionBlock(6, 19), new ErrorCorrectionBlock(2, 20)), - new ErrorCorrectionBlocks(224, new ErrorCorrectionBlock(6, 15), new ErrorCorrectionBlock(2, 16))), - new QRCodeVersion( - 11, - 404, - new ErrorCorrectionBlocks(80, new ErrorCorrectionBlock(4, 81)), - new ErrorCorrectionBlocks(150, new ErrorCorrectionBlock(1, 50), new ErrorCorrectionBlock(4, 51)), - new ErrorCorrectionBlocks(224, new ErrorCorrectionBlock(4, 22), new ErrorCorrectionBlock(4, 23)), - new ErrorCorrectionBlocks(264, new ErrorCorrectionBlock(3, 12), new ErrorCorrectionBlock(8, 13))), - new QRCodeVersion( - 12, - 466, - new ErrorCorrectionBlocks(96, new ErrorCorrectionBlock(2, 92), new ErrorCorrectionBlock(2, 93)), - new ErrorCorrectionBlocks(176, new ErrorCorrectionBlock(6, 36), new ErrorCorrectionBlock(2, 37)), - new ErrorCorrectionBlocks(260, new ErrorCorrectionBlock(4, 20), new ErrorCorrectionBlock(6, 21)), - new ErrorCorrectionBlocks(308, new ErrorCorrectionBlock(7, 14), new ErrorCorrectionBlock(4, 15))), - new QRCodeVersion( - 13, - 532, - new ErrorCorrectionBlocks(104, new ErrorCorrectionBlock(4, 107)), - new ErrorCorrectionBlocks(198, new ErrorCorrectionBlock(8, 37), new ErrorCorrectionBlock(1, 38)), - new ErrorCorrectionBlocks(288, new ErrorCorrectionBlock(8, 20), new ErrorCorrectionBlock(4, 21)), - new ErrorCorrectionBlocks(352, new ErrorCorrectionBlock(12, 11), new ErrorCorrectionBlock(4, 12))), - new QRCodeVersion( - 14, - 581, - new ErrorCorrectionBlocks(120, new ErrorCorrectionBlock(3, 115), new ErrorCorrectionBlock(1, 116)), - new ErrorCorrectionBlocks(216, new ErrorCorrectionBlock(4, 40), new ErrorCorrectionBlock(5, 41)), - new ErrorCorrectionBlocks(320, new ErrorCorrectionBlock(11, 16), new ErrorCorrectionBlock(5, 17)), - new ErrorCorrectionBlocks(384, new ErrorCorrectionBlock(11, 12), new ErrorCorrectionBlock(5, 13))), - new QRCodeVersion( - 15, - 655, - new ErrorCorrectionBlocks(132, new ErrorCorrectionBlock(5, 87), new ErrorCorrectionBlock(1, 88)), - new ErrorCorrectionBlocks(240, new ErrorCorrectionBlock(5, 41), new ErrorCorrectionBlock(5, 42)), - new ErrorCorrectionBlocks(360, new ErrorCorrectionBlock(5, 24), new ErrorCorrectionBlock(7, 25)), - new ErrorCorrectionBlocks(432, new ErrorCorrectionBlock(11, 12), new ErrorCorrectionBlock(7, 13))), - new QRCodeVersion( - 16, - 733, - new ErrorCorrectionBlocks(144, new ErrorCorrectionBlock(5, 98), new ErrorCorrectionBlock(1, 99)), - new ErrorCorrectionBlocks(280, new ErrorCorrectionBlock(7, 45), new ErrorCorrectionBlock(3, 46)), - new ErrorCorrectionBlocks(408, new ErrorCorrectionBlock(15, 19), new ErrorCorrectionBlock(2, 20)), - new ErrorCorrectionBlocks(480, new ErrorCorrectionBlock(3, 15), new ErrorCorrectionBlock(13, 16))), - new QRCodeVersion( - 17, - 815, - new ErrorCorrectionBlocks(168, new ErrorCorrectionBlock(1, 107), new ErrorCorrectionBlock(5, 108)), - new ErrorCorrectionBlocks(308, new ErrorCorrectionBlock(10, 46), new ErrorCorrectionBlock(1, 47)), - new ErrorCorrectionBlocks(448, new ErrorCorrectionBlock(1, 22), new ErrorCorrectionBlock(15, 23)), - new ErrorCorrectionBlocks(532, new ErrorCorrectionBlock(2, 14), new ErrorCorrectionBlock(17, 15))), - new QRCodeVersion( - 18, - 901, - new ErrorCorrectionBlocks(180, new ErrorCorrectionBlock(5, 120), new ErrorCorrectionBlock(1, 121)), - new ErrorCorrectionBlocks(338, new ErrorCorrectionBlock(9, 43), new ErrorCorrectionBlock(4, 44)), - new ErrorCorrectionBlocks(504, new ErrorCorrectionBlock(17, 22), new ErrorCorrectionBlock(1, 23)), - new ErrorCorrectionBlocks(588, new ErrorCorrectionBlock(2, 14), new ErrorCorrectionBlock(19, 15))), - new QRCodeVersion( - 19, - 991, - new ErrorCorrectionBlocks(196, new ErrorCorrectionBlock(3, 113), new ErrorCorrectionBlock(4, 114)), - new ErrorCorrectionBlocks(364, new ErrorCorrectionBlock(3, 44), new ErrorCorrectionBlock(11, 45)), - new ErrorCorrectionBlocks(546, new ErrorCorrectionBlock(17, 21), new ErrorCorrectionBlock(4, 22)), - new ErrorCorrectionBlocks(650, new ErrorCorrectionBlock(9, 13), new ErrorCorrectionBlock(16, 14))), - new QRCodeVersion( - 20, - 1085, - new ErrorCorrectionBlocks(224, new ErrorCorrectionBlock(3, 107), new ErrorCorrectionBlock(5, 108)), - new ErrorCorrectionBlocks(416, new ErrorCorrectionBlock(3, 41), new ErrorCorrectionBlock(13, 42)), - new ErrorCorrectionBlocks(600, new ErrorCorrectionBlock(15, 24), new ErrorCorrectionBlock(5, 25)), - new ErrorCorrectionBlocks(700, new ErrorCorrectionBlock(15, 15), new ErrorCorrectionBlock(10, 16))), - new QRCodeVersion( - 21, - 1156, - new ErrorCorrectionBlocks(224, new ErrorCorrectionBlock(4, 116), new ErrorCorrectionBlock(4, 117)), - new ErrorCorrectionBlocks(442, new ErrorCorrectionBlock(17, 42)), - new ErrorCorrectionBlocks(644, new ErrorCorrectionBlock(17, 22), new ErrorCorrectionBlock(6, 23)), - new ErrorCorrectionBlocks(750, new ErrorCorrectionBlock(19, 16), new ErrorCorrectionBlock(6, 17))), - new QRCodeVersion( - 22, - 1258, - new ErrorCorrectionBlocks(252, new ErrorCorrectionBlock(2, 111), new ErrorCorrectionBlock(7, 112)), - new ErrorCorrectionBlocks(476, new ErrorCorrectionBlock(17, 46)), - new ErrorCorrectionBlocks(690, new ErrorCorrectionBlock(7, 24), new ErrorCorrectionBlock(16, 25)), - new ErrorCorrectionBlocks(816, new ErrorCorrectionBlock(34, 13))), - new QRCodeVersion( - 23, - 1364, - new ErrorCorrectionBlocks(270, new ErrorCorrectionBlock(4, 121), new ErrorCorrectionBlock(5, 122)), - new ErrorCorrectionBlocks(504, new ErrorCorrectionBlock(4, 47), new ErrorCorrectionBlock(14, 48)), - new ErrorCorrectionBlocks(750, new ErrorCorrectionBlock(11, 24), new ErrorCorrectionBlock(14, 25)), - new ErrorCorrectionBlocks(900, new ErrorCorrectionBlock(16, 15), new ErrorCorrectionBlock(14, 16))), - new QRCodeVersion( - 24, - 1474, - new ErrorCorrectionBlocks(300, new ErrorCorrectionBlock(6, 117), new ErrorCorrectionBlock(4, 118)), - new ErrorCorrectionBlocks(560, new ErrorCorrectionBlock(6, 45), new ErrorCorrectionBlock(14, 46)), - new ErrorCorrectionBlocks(810, new ErrorCorrectionBlock(11, 24), new ErrorCorrectionBlock(16, 25)), - new ErrorCorrectionBlocks(960, new ErrorCorrectionBlock(30, 16), new ErrorCorrectionBlock(2, 17))), - new QRCodeVersion( - 25, - 1588, - new ErrorCorrectionBlocks(312, new ErrorCorrectionBlock(8, 106), new ErrorCorrectionBlock(4, 107)), - new ErrorCorrectionBlocks(588, new ErrorCorrectionBlock(8, 47), new ErrorCorrectionBlock(13, 48)), - new ErrorCorrectionBlocks(870, new ErrorCorrectionBlock(7, 24), new ErrorCorrectionBlock(22, 25)), - new ErrorCorrectionBlocks(1050, new ErrorCorrectionBlock(22, 15), new ErrorCorrectionBlock(13, 16))), - new QRCodeVersion( - 26, - 1706, - new ErrorCorrectionBlocks(336, new ErrorCorrectionBlock(10, 114), new ErrorCorrectionBlock(2, 115)), - new ErrorCorrectionBlocks(644, new ErrorCorrectionBlock(19, 46), new ErrorCorrectionBlock(4, 47)), - new ErrorCorrectionBlocks(952, new ErrorCorrectionBlock(28, 22), new ErrorCorrectionBlock(6, 23)), - new ErrorCorrectionBlocks(1110, new ErrorCorrectionBlock(33, 16), new ErrorCorrectionBlock(4, 17))), - new QRCodeVersion( - 27, - 1828, - new ErrorCorrectionBlocks(360, new ErrorCorrectionBlock(8, 122), new ErrorCorrectionBlock(4, 123)), - new ErrorCorrectionBlocks(700, new ErrorCorrectionBlock(22, 45), new ErrorCorrectionBlock(3, 46)), - new ErrorCorrectionBlocks(1020, new ErrorCorrectionBlock(8, 23), new ErrorCorrectionBlock(26, 24)), - new ErrorCorrectionBlocks(1200, new ErrorCorrectionBlock(12, 15), new ErrorCorrectionBlock(28, 16))), - new QRCodeVersion( - 28, - 1921, - new ErrorCorrectionBlocks(390, new ErrorCorrectionBlock(3, 117), new ErrorCorrectionBlock(10, 118)), - new ErrorCorrectionBlocks(728, new ErrorCorrectionBlock(3, 45), new ErrorCorrectionBlock(23, 46)), - new ErrorCorrectionBlocks(1050, new ErrorCorrectionBlock(4, 24), new ErrorCorrectionBlock(31, 25)), - new ErrorCorrectionBlocks(1260, new ErrorCorrectionBlock(11, 15), new ErrorCorrectionBlock(31, 16))), - new QRCodeVersion( - 29, - 2051, - new ErrorCorrectionBlocks(420, new ErrorCorrectionBlock(7, 116), new ErrorCorrectionBlock(7, 117)), - new ErrorCorrectionBlocks(784, new ErrorCorrectionBlock(21, 45), new ErrorCorrectionBlock(7, 46)), - new ErrorCorrectionBlocks(1140, new ErrorCorrectionBlock(1, 23), new ErrorCorrectionBlock(37, 24)), - new ErrorCorrectionBlocks(1350, new ErrorCorrectionBlock(19, 15), new ErrorCorrectionBlock(26, 16))), - new QRCodeVersion( - 30, - 2185, - new ErrorCorrectionBlocks(450, new ErrorCorrectionBlock(5, 115), new ErrorCorrectionBlock(10, 116)), - new ErrorCorrectionBlocks(812, new ErrorCorrectionBlock(19, 47), new ErrorCorrectionBlock(10, 48)), - new ErrorCorrectionBlocks(1200, new ErrorCorrectionBlock(15, 24), new ErrorCorrectionBlock(25, 25)), - new ErrorCorrectionBlocks(1440, new ErrorCorrectionBlock(23, 15), new ErrorCorrectionBlock(25, 16))), - new QRCodeVersion( - 31, - 2323, - new ErrorCorrectionBlocks(480, new ErrorCorrectionBlock(13, 115), new ErrorCorrectionBlock(3, 116)), - new ErrorCorrectionBlocks(868, new ErrorCorrectionBlock(2, 46), new ErrorCorrectionBlock(29, 47)), - new ErrorCorrectionBlocks(1290, new ErrorCorrectionBlock(42, 24), new ErrorCorrectionBlock(1, 25)), - new ErrorCorrectionBlocks(1530, new ErrorCorrectionBlock(23, 15), new ErrorCorrectionBlock(28, 16))), - new QRCodeVersion( - 32, - 2465, - new ErrorCorrectionBlocks(510, new ErrorCorrectionBlock(17, 115)), - new ErrorCorrectionBlocks(924, new ErrorCorrectionBlock(10, 46), new ErrorCorrectionBlock(23, 47)), - new ErrorCorrectionBlocks(1350, new ErrorCorrectionBlock(10, 24), new ErrorCorrectionBlock(35, 25)), - new ErrorCorrectionBlocks(1620, new ErrorCorrectionBlock(19, 15), new ErrorCorrectionBlock(35, 16))), - new QRCodeVersion( - 33, - 2611, - new ErrorCorrectionBlocks(540, new ErrorCorrectionBlock(17, 115), new ErrorCorrectionBlock(1, 116)), - new ErrorCorrectionBlocks(980, new ErrorCorrectionBlock(14, 46), new ErrorCorrectionBlock(21, 47)), - new ErrorCorrectionBlocks(1440, new ErrorCorrectionBlock(29, 24), new ErrorCorrectionBlock(19, 25)), - new ErrorCorrectionBlocks(1710, new ErrorCorrectionBlock(11, 15), new ErrorCorrectionBlock(46, 16))), - new QRCodeVersion( - 34, - 2761, - new ErrorCorrectionBlocks(570, new ErrorCorrectionBlock(13, 115), new ErrorCorrectionBlock(6, 116)), - new ErrorCorrectionBlocks(1036, new ErrorCorrectionBlock(14, 46), new ErrorCorrectionBlock(23, 47)), - new ErrorCorrectionBlocks(1530, new ErrorCorrectionBlock(44, 24), new ErrorCorrectionBlock(7, 25)), - new ErrorCorrectionBlocks(1800, new ErrorCorrectionBlock(59, 16), new ErrorCorrectionBlock(1, 17))), - new QRCodeVersion( - 35, - 2876, - new ErrorCorrectionBlocks(570, new ErrorCorrectionBlock(12, 121), new ErrorCorrectionBlock(7, 122)), - new ErrorCorrectionBlocks(1064, new ErrorCorrectionBlock(12, 47), new ErrorCorrectionBlock(26, 48)), - new ErrorCorrectionBlocks(1590, new ErrorCorrectionBlock(39, 24), new ErrorCorrectionBlock(14, 25)), - new ErrorCorrectionBlocks(1890, new ErrorCorrectionBlock(22, 15), new ErrorCorrectionBlock(41, 16))), - new QRCodeVersion( - 36, - 3034, - new ErrorCorrectionBlocks(600, new ErrorCorrectionBlock(6, 121), new ErrorCorrectionBlock(14, 122)), - new ErrorCorrectionBlocks(1120, new ErrorCorrectionBlock(6, 47), new ErrorCorrectionBlock(34, 48)), - new ErrorCorrectionBlocks(1680, new ErrorCorrectionBlock(46, 24), new ErrorCorrectionBlock(10, 25)), - new ErrorCorrectionBlocks(1980, new ErrorCorrectionBlock(2, 15), new ErrorCorrectionBlock(64, 16))), - new QRCodeVersion( - 37, - 3196, - new ErrorCorrectionBlocks(630, new ErrorCorrectionBlock(17, 122), new ErrorCorrectionBlock(4, 123)), - new ErrorCorrectionBlocks(1204, new ErrorCorrectionBlock(29, 46), new ErrorCorrectionBlock(14, 47)), - new ErrorCorrectionBlocks(1770, new ErrorCorrectionBlock(49, 24), new ErrorCorrectionBlock(10, 25)), - new ErrorCorrectionBlocks(2100, new ErrorCorrectionBlock(24, 15), new ErrorCorrectionBlock(46, 16))), - new QRCodeVersion( - 38, - 3362, - new ErrorCorrectionBlocks(660, new ErrorCorrectionBlock(4, 122), new ErrorCorrectionBlock(18, 123)), - new ErrorCorrectionBlocks(1260, new ErrorCorrectionBlock(13, 46), new ErrorCorrectionBlock(32, 47)), - new ErrorCorrectionBlocks(1860, new ErrorCorrectionBlock(48, 24), new ErrorCorrectionBlock(14, 25)), - new ErrorCorrectionBlocks(2220, new ErrorCorrectionBlock(42, 15), new ErrorCorrectionBlock(32, 16))), - new QRCodeVersion( - 39, - 3532, - new ErrorCorrectionBlocks(720, new ErrorCorrectionBlock(20, 117), new ErrorCorrectionBlock(4, 118)), - new ErrorCorrectionBlocks(1316, new ErrorCorrectionBlock(40, 47), new ErrorCorrectionBlock(7, 48)), - new ErrorCorrectionBlocks(1950, new ErrorCorrectionBlock(43, 24), new ErrorCorrectionBlock(22, 25)), - new ErrorCorrectionBlocks(2310, new ErrorCorrectionBlock(10, 15), new ErrorCorrectionBlock(67, 16))), - new QRCodeVersion( - 40, - 3706, - new ErrorCorrectionBlocks(750, new ErrorCorrectionBlock(19, 118), new ErrorCorrectionBlock(6, 119)), - new ErrorCorrectionBlocks(1372, new ErrorCorrectionBlock(18, 47), new ErrorCorrectionBlock(31, 48)), - new ErrorCorrectionBlocks(2040, new ErrorCorrectionBlock(34, 24), new ErrorCorrectionBlock(34, 25)), - new ErrorCorrectionBlocks(2430, new ErrorCorrectionBlock(20, 15), new ErrorCorrectionBlock(61, 16))), - }; - } -} + private static QRCodeVersion[] Initialize() + { + return new QRCodeVersion[] + { + new QRCodeVersion( + 1, + 26, + new ErrorCorrectionBlocks(7, new ErrorCorrectionBlock(1, 19)), + new ErrorCorrectionBlocks(10, new ErrorCorrectionBlock(1, 16)), + new ErrorCorrectionBlocks(13, new ErrorCorrectionBlock(1, 13)), + new ErrorCorrectionBlocks(17, new ErrorCorrectionBlock(1, 9))), + new QRCodeVersion( + 2, + 44, + new ErrorCorrectionBlocks(10, new ErrorCorrectionBlock(1, 34)), + new ErrorCorrectionBlocks(16, new ErrorCorrectionBlock(1, 28)), + new ErrorCorrectionBlocks(22, new ErrorCorrectionBlock(1, 22)), + new ErrorCorrectionBlocks(28, new ErrorCorrectionBlock(1, 16))), + new QRCodeVersion( + 3, + 70, + new ErrorCorrectionBlocks(15, new ErrorCorrectionBlock(1, 55)), + new ErrorCorrectionBlocks(26, new ErrorCorrectionBlock(1, 44)), + new ErrorCorrectionBlocks(36, new ErrorCorrectionBlock(2, 17)), + new ErrorCorrectionBlocks(44, new ErrorCorrectionBlock(2, 13))), + new QRCodeVersion( + 4, + 100, + new ErrorCorrectionBlocks(20, new ErrorCorrectionBlock(1, 80)), + new ErrorCorrectionBlocks(36, new ErrorCorrectionBlock(2, 32)), + new ErrorCorrectionBlocks(52, new ErrorCorrectionBlock(2, 24)), + new ErrorCorrectionBlocks(64, new ErrorCorrectionBlock(4, 9))), + new QRCodeVersion( + 5, + 134, + new ErrorCorrectionBlocks(26, new ErrorCorrectionBlock(1, 108)), + new ErrorCorrectionBlocks(48, new ErrorCorrectionBlock(2, 43)), + new ErrorCorrectionBlocks(72, new ErrorCorrectionBlock(2, 15), new ErrorCorrectionBlock(2, 16)), + new ErrorCorrectionBlocks(88, new ErrorCorrectionBlock(2, 11), new ErrorCorrectionBlock(2, 12))), + new QRCodeVersion( + 6, + 172, + new ErrorCorrectionBlocks(36, new ErrorCorrectionBlock(2, 68)), + new ErrorCorrectionBlocks(64, new ErrorCorrectionBlock(4, 27)), + new ErrorCorrectionBlocks(96, new ErrorCorrectionBlock(4, 19)), + new ErrorCorrectionBlocks(112, new ErrorCorrectionBlock(4, 15))), + new QRCodeVersion( + 7, + 196, + new ErrorCorrectionBlocks(40, new ErrorCorrectionBlock(2, 78)), + new ErrorCorrectionBlocks(72, new ErrorCorrectionBlock(4, 31)), + new ErrorCorrectionBlocks(108, new ErrorCorrectionBlock(2, 14), new ErrorCorrectionBlock(4, 15)), + new ErrorCorrectionBlocks(130, new ErrorCorrectionBlock(4, 13), new ErrorCorrectionBlock(1, 14))), + new QRCodeVersion( + 8, + 242, + new ErrorCorrectionBlocks(48, new ErrorCorrectionBlock(2, 97)), + new ErrorCorrectionBlocks(88, new ErrorCorrectionBlock(2, 38), new ErrorCorrectionBlock(2, 39)), + new ErrorCorrectionBlocks(132, new ErrorCorrectionBlock(4, 18), new ErrorCorrectionBlock(2, 19)), + new ErrorCorrectionBlocks(156, new ErrorCorrectionBlock(4, 14), new ErrorCorrectionBlock(2, 15))), + new QRCodeVersion( + 9, + 292, + new ErrorCorrectionBlocks(60, new ErrorCorrectionBlock(2, 116)), + new ErrorCorrectionBlocks(110, new ErrorCorrectionBlock(3, 36), new ErrorCorrectionBlock(2, 37)), + new ErrorCorrectionBlocks(160, new ErrorCorrectionBlock(4, 16), new ErrorCorrectionBlock(4, 17)), + new ErrorCorrectionBlocks(192, new ErrorCorrectionBlock(4, 12), new ErrorCorrectionBlock(4, 13))), + new QRCodeVersion( + 10, + 346, + new ErrorCorrectionBlocks(72, new ErrorCorrectionBlock(2, 68), new ErrorCorrectionBlock(2, 69)), + new ErrorCorrectionBlocks(130, new ErrorCorrectionBlock(4, 43), new ErrorCorrectionBlock(1, 44)), + new ErrorCorrectionBlocks(192, new ErrorCorrectionBlock(6, 19), new ErrorCorrectionBlock(2, 20)), + new ErrorCorrectionBlocks(224, new ErrorCorrectionBlock(6, 15), new ErrorCorrectionBlock(2, 16))), + new QRCodeVersion( + 11, + 404, + new ErrorCorrectionBlocks(80, new ErrorCorrectionBlock(4, 81)), + new ErrorCorrectionBlocks(150, new ErrorCorrectionBlock(1, 50), new ErrorCorrectionBlock(4, 51)), + new ErrorCorrectionBlocks(224, new ErrorCorrectionBlock(4, 22), new ErrorCorrectionBlock(4, 23)), + new ErrorCorrectionBlocks(264, new ErrorCorrectionBlock(3, 12), new ErrorCorrectionBlock(8, 13))), + new QRCodeVersion( + 12, + 466, + new ErrorCorrectionBlocks(96, new ErrorCorrectionBlock(2, 92), new ErrorCorrectionBlock(2, 93)), + new ErrorCorrectionBlocks(176, new ErrorCorrectionBlock(6, 36), new ErrorCorrectionBlock(2, 37)), + new ErrorCorrectionBlocks(260, new ErrorCorrectionBlock(4, 20), new ErrorCorrectionBlock(6, 21)), + new ErrorCorrectionBlocks(308, new ErrorCorrectionBlock(7, 14), new ErrorCorrectionBlock(4, 15))), + new QRCodeVersion( + 13, + 532, + new ErrorCorrectionBlocks(104, new ErrorCorrectionBlock(4, 107)), + new ErrorCorrectionBlocks(198, new ErrorCorrectionBlock(8, 37), new ErrorCorrectionBlock(1, 38)), + new ErrorCorrectionBlocks(288, new ErrorCorrectionBlock(8, 20), new ErrorCorrectionBlock(4, 21)), + new ErrorCorrectionBlocks(352, new ErrorCorrectionBlock(12, 11), new ErrorCorrectionBlock(4, 12))), + new QRCodeVersion( + 14, + 581, + new ErrorCorrectionBlocks(120, new ErrorCorrectionBlock(3, 115), new ErrorCorrectionBlock(1, 116)), + new ErrorCorrectionBlocks(216, new ErrorCorrectionBlock(4, 40), new ErrorCorrectionBlock(5, 41)), + new ErrorCorrectionBlocks(320, new ErrorCorrectionBlock(11, 16), new ErrorCorrectionBlock(5, 17)), + new ErrorCorrectionBlocks(384, new ErrorCorrectionBlock(11, 12), new ErrorCorrectionBlock(5, 13))), + new QRCodeVersion( + 15, + 655, + new ErrorCorrectionBlocks(132, new ErrorCorrectionBlock(5, 87), new ErrorCorrectionBlock(1, 88)), + new ErrorCorrectionBlocks(240, new ErrorCorrectionBlock(5, 41), new ErrorCorrectionBlock(5, 42)), + new ErrorCorrectionBlocks(360, new ErrorCorrectionBlock(5, 24), new ErrorCorrectionBlock(7, 25)), + new ErrorCorrectionBlocks(432, new ErrorCorrectionBlock(11, 12), new ErrorCorrectionBlock(7, 13))), + new QRCodeVersion( + 16, + 733, + new ErrorCorrectionBlocks(144, new ErrorCorrectionBlock(5, 98), new ErrorCorrectionBlock(1, 99)), + new ErrorCorrectionBlocks(280, new ErrorCorrectionBlock(7, 45), new ErrorCorrectionBlock(3, 46)), + new ErrorCorrectionBlocks(408, new ErrorCorrectionBlock(15, 19), new ErrorCorrectionBlock(2, 20)), + new ErrorCorrectionBlocks(480, new ErrorCorrectionBlock(3, 15), new ErrorCorrectionBlock(13, 16))), + new QRCodeVersion( + 17, + 815, + new ErrorCorrectionBlocks(168, new ErrorCorrectionBlock(1, 107), new ErrorCorrectionBlock(5, 108)), + new ErrorCorrectionBlocks(308, new ErrorCorrectionBlock(10, 46), new ErrorCorrectionBlock(1, 47)), + new ErrorCorrectionBlocks(448, new ErrorCorrectionBlock(1, 22), new ErrorCorrectionBlock(15, 23)), + new ErrorCorrectionBlocks(532, new ErrorCorrectionBlock(2, 14), new ErrorCorrectionBlock(17, 15))), + new QRCodeVersion( + 18, + 901, + new ErrorCorrectionBlocks(180, new ErrorCorrectionBlock(5, 120), new ErrorCorrectionBlock(1, 121)), + new ErrorCorrectionBlocks(338, new ErrorCorrectionBlock(9, 43), new ErrorCorrectionBlock(4, 44)), + new ErrorCorrectionBlocks(504, new ErrorCorrectionBlock(17, 22), new ErrorCorrectionBlock(1, 23)), + new ErrorCorrectionBlocks(588, new ErrorCorrectionBlock(2, 14), new ErrorCorrectionBlock(19, 15))), + new QRCodeVersion( + 19, + 991, + new ErrorCorrectionBlocks(196, new ErrorCorrectionBlock(3, 113), new ErrorCorrectionBlock(4, 114)), + new ErrorCorrectionBlocks(364, new ErrorCorrectionBlock(3, 44), new ErrorCorrectionBlock(11, 45)), + new ErrorCorrectionBlocks(546, new ErrorCorrectionBlock(17, 21), new ErrorCorrectionBlock(4, 22)), + new ErrorCorrectionBlocks(650, new ErrorCorrectionBlock(9, 13), new ErrorCorrectionBlock(16, 14))), + new QRCodeVersion( + 20, + 1085, + new ErrorCorrectionBlocks(224, new ErrorCorrectionBlock(3, 107), new ErrorCorrectionBlock(5, 108)), + new ErrorCorrectionBlocks(416, new ErrorCorrectionBlock(3, 41), new ErrorCorrectionBlock(13, 42)), + new ErrorCorrectionBlocks(600, new ErrorCorrectionBlock(15, 24), new ErrorCorrectionBlock(5, 25)), + new ErrorCorrectionBlocks(700, new ErrorCorrectionBlock(15, 15), new ErrorCorrectionBlock(10, 16))), + new QRCodeVersion( + 21, + 1156, + new ErrorCorrectionBlocks(224, new ErrorCorrectionBlock(4, 116), new ErrorCorrectionBlock(4, 117)), + new ErrorCorrectionBlocks(442, new ErrorCorrectionBlock(17, 42)), + new ErrorCorrectionBlocks(644, new ErrorCorrectionBlock(17, 22), new ErrorCorrectionBlock(6, 23)), + new ErrorCorrectionBlocks(750, new ErrorCorrectionBlock(19, 16), new ErrorCorrectionBlock(6, 17))), + new QRCodeVersion( + 22, + 1258, + new ErrorCorrectionBlocks(252, new ErrorCorrectionBlock(2, 111), new ErrorCorrectionBlock(7, 112)), + new ErrorCorrectionBlocks(476, new ErrorCorrectionBlock(17, 46)), + new ErrorCorrectionBlocks(690, new ErrorCorrectionBlock(7, 24), new ErrorCorrectionBlock(16, 25)), + new ErrorCorrectionBlocks(816, new ErrorCorrectionBlock(34, 13))), + new QRCodeVersion( + 23, + 1364, + new ErrorCorrectionBlocks(270, new ErrorCorrectionBlock(4, 121), new ErrorCorrectionBlock(5, 122)), + new ErrorCorrectionBlocks(504, new ErrorCorrectionBlock(4, 47), new ErrorCorrectionBlock(14, 48)), + new ErrorCorrectionBlocks(750, new ErrorCorrectionBlock(11, 24), new ErrorCorrectionBlock(14, 25)), + new ErrorCorrectionBlocks(900, new ErrorCorrectionBlock(16, 15), new ErrorCorrectionBlock(14, 16))), + new QRCodeVersion( + 24, + 1474, + new ErrorCorrectionBlocks(300, new ErrorCorrectionBlock(6, 117), new ErrorCorrectionBlock(4, 118)), + new ErrorCorrectionBlocks(560, new ErrorCorrectionBlock(6, 45), new ErrorCorrectionBlock(14, 46)), + new ErrorCorrectionBlocks(810, new ErrorCorrectionBlock(11, 24), new ErrorCorrectionBlock(16, 25)), + new ErrorCorrectionBlocks(960, new ErrorCorrectionBlock(30, 16), new ErrorCorrectionBlock(2, 17))), + new QRCodeVersion( + 25, + 1588, + new ErrorCorrectionBlocks(312, new ErrorCorrectionBlock(8, 106), new ErrorCorrectionBlock(4, 107)), + new ErrorCorrectionBlocks(588, new ErrorCorrectionBlock(8, 47), new ErrorCorrectionBlock(13, 48)), + new ErrorCorrectionBlocks(870, new ErrorCorrectionBlock(7, 24), new ErrorCorrectionBlock(22, 25)), + new ErrorCorrectionBlocks(1050, new ErrorCorrectionBlock(22, 15), new ErrorCorrectionBlock(13, 16))), + new QRCodeVersion( + 26, + 1706, + new ErrorCorrectionBlocks(336, new ErrorCorrectionBlock(10, 114), new ErrorCorrectionBlock(2, 115)), + new ErrorCorrectionBlocks(644, new ErrorCorrectionBlock(19, 46), new ErrorCorrectionBlock(4, 47)), + new ErrorCorrectionBlocks(952, new ErrorCorrectionBlock(28, 22), new ErrorCorrectionBlock(6, 23)), + new ErrorCorrectionBlocks(1110, new ErrorCorrectionBlock(33, 16), new ErrorCorrectionBlock(4, 17))), + new QRCodeVersion( + 27, + 1828, + new ErrorCorrectionBlocks(360, new ErrorCorrectionBlock(8, 122), new ErrorCorrectionBlock(4, 123)), + new ErrorCorrectionBlocks(700, new ErrorCorrectionBlock(22, 45), new ErrorCorrectionBlock(3, 46)), + new ErrorCorrectionBlocks(1020, new ErrorCorrectionBlock(8, 23), new ErrorCorrectionBlock(26, 24)), + new ErrorCorrectionBlocks(1200, new ErrorCorrectionBlock(12, 15), new ErrorCorrectionBlock(28, 16))), + new QRCodeVersion( + 28, + 1921, + new ErrorCorrectionBlocks(390, new ErrorCorrectionBlock(3, 117), new ErrorCorrectionBlock(10, 118)), + new ErrorCorrectionBlocks(728, new ErrorCorrectionBlock(3, 45), new ErrorCorrectionBlock(23, 46)), + new ErrorCorrectionBlocks(1050, new ErrorCorrectionBlock(4, 24), new ErrorCorrectionBlock(31, 25)), + new ErrorCorrectionBlocks(1260, new ErrorCorrectionBlock(11, 15), new ErrorCorrectionBlock(31, 16))), + new QRCodeVersion( + 29, + 2051, + new ErrorCorrectionBlocks(420, new ErrorCorrectionBlock(7, 116), new ErrorCorrectionBlock(7, 117)), + new ErrorCorrectionBlocks(784, new ErrorCorrectionBlock(21, 45), new ErrorCorrectionBlock(7, 46)), + new ErrorCorrectionBlocks(1140, new ErrorCorrectionBlock(1, 23), new ErrorCorrectionBlock(37, 24)), + new ErrorCorrectionBlocks(1350, new ErrorCorrectionBlock(19, 15), new ErrorCorrectionBlock(26, 16))), + new QRCodeVersion( + 30, + 2185, + new ErrorCorrectionBlocks(450, new ErrorCorrectionBlock(5, 115), new ErrorCorrectionBlock(10, 116)), + new ErrorCorrectionBlocks(812, new ErrorCorrectionBlock(19, 47), new ErrorCorrectionBlock(10, 48)), + new ErrorCorrectionBlocks(1200, new ErrorCorrectionBlock(15, 24), new ErrorCorrectionBlock(25, 25)), + new ErrorCorrectionBlocks(1440, new ErrorCorrectionBlock(23, 15), new ErrorCorrectionBlock(25, 16))), + new QRCodeVersion( + 31, + 2323, + new ErrorCorrectionBlocks(480, new ErrorCorrectionBlock(13, 115), new ErrorCorrectionBlock(3, 116)), + new ErrorCorrectionBlocks(868, new ErrorCorrectionBlock(2, 46), new ErrorCorrectionBlock(29, 47)), + new ErrorCorrectionBlocks(1290, new ErrorCorrectionBlock(42, 24), new ErrorCorrectionBlock(1, 25)), + new ErrorCorrectionBlocks(1530, new ErrorCorrectionBlock(23, 15), new ErrorCorrectionBlock(28, 16))), + new QRCodeVersion( + 32, + 2465, + new ErrorCorrectionBlocks(510, new ErrorCorrectionBlock(17, 115)), + new ErrorCorrectionBlocks(924, new ErrorCorrectionBlock(10, 46), new ErrorCorrectionBlock(23, 47)), + new ErrorCorrectionBlocks(1350, new ErrorCorrectionBlock(10, 24), new ErrorCorrectionBlock(35, 25)), + new ErrorCorrectionBlocks(1620, new ErrorCorrectionBlock(19, 15), new ErrorCorrectionBlock(35, 16))), + new QRCodeVersion( + 33, + 2611, + new ErrorCorrectionBlocks(540, new ErrorCorrectionBlock(17, 115), new ErrorCorrectionBlock(1, 116)), + new ErrorCorrectionBlocks(980, new ErrorCorrectionBlock(14, 46), new ErrorCorrectionBlock(21, 47)), + new ErrorCorrectionBlocks(1440, new ErrorCorrectionBlock(29, 24), new ErrorCorrectionBlock(19, 25)), + new ErrorCorrectionBlocks(1710, new ErrorCorrectionBlock(11, 15), new ErrorCorrectionBlock(46, 16))), + new QRCodeVersion( + 34, + 2761, + new ErrorCorrectionBlocks(570, new ErrorCorrectionBlock(13, 115), new ErrorCorrectionBlock(6, 116)), + new ErrorCorrectionBlocks(1036, new ErrorCorrectionBlock(14, 46), new ErrorCorrectionBlock(23, 47)), + new ErrorCorrectionBlocks(1530, new ErrorCorrectionBlock(44, 24), new ErrorCorrectionBlock(7, 25)), + new ErrorCorrectionBlocks(1800, new ErrorCorrectionBlock(59, 16), new ErrorCorrectionBlock(1, 17))), + new QRCodeVersion( + 35, + 2876, + new ErrorCorrectionBlocks(570, new ErrorCorrectionBlock(12, 121), new ErrorCorrectionBlock(7, 122)), + new ErrorCorrectionBlocks(1064, new ErrorCorrectionBlock(12, 47), new ErrorCorrectionBlock(26, 48)), + new ErrorCorrectionBlocks(1590, new ErrorCorrectionBlock(39, 24), new ErrorCorrectionBlock(14, 25)), + new ErrorCorrectionBlocks(1890, new ErrorCorrectionBlock(22, 15), new ErrorCorrectionBlock(41, 16))), + new QRCodeVersion( + 36, + 3034, + new ErrorCorrectionBlocks(600, new ErrorCorrectionBlock(6, 121), new ErrorCorrectionBlock(14, 122)), + new ErrorCorrectionBlocks(1120, new ErrorCorrectionBlock(6, 47), new ErrorCorrectionBlock(34, 48)), + new ErrorCorrectionBlocks(1680, new ErrorCorrectionBlock(46, 24), new ErrorCorrectionBlock(10, 25)), + new ErrorCorrectionBlocks(1980, new ErrorCorrectionBlock(2, 15), new ErrorCorrectionBlock(64, 16))), + new QRCodeVersion( + 37, + 3196, + new ErrorCorrectionBlocks(630, new ErrorCorrectionBlock(17, 122), new ErrorCorrectionBlock(4, 123)), + new ErrorCorrectionBlocks(1204, new ErrorCorrectionBlock(29, 46), new ErrorCorrectionBlock(14, 47)), + new ErrorCorrectionBlocks(1770, new ErrorCorrectionBlock(49, 24), new ErrorCorrectionBlock(10, 25)), + new ErrorCorrectionBlocks(2100, new ErrorCorrectionBlock(24, 15), new ErrorCorrectionBlock(46, 16))), + new QRCodeVersion( + 38, + 3362, + new ErrorCorrectionBlocks(660, new ErrorCorrectionBlock(4, 122), new ErrorCorrectionBlock(18, 123)), + new ErrorCorrectionBlocks(1260, new ErrorCorrectionBlock(13, 46), new ErrorCorrectionBlock(32, 47)), + new ErrorCorrectionBlocks(1860, new ErrorCorrectionBlock(48, 24), new ErrorCorrectionBlock(14, 25)), + new ErrorCorrectionBlocks(2220, new ErrorCorrectionBlock(42, 15), new ErrorCorrectionBlock(32, 16))), + new QRCodeVersion( + 39, + 3532, + new ErrorCorrectionBlocks(720, new ErrorCorrectionBlock(20, 117), new ErrorCorrectionBlock(4, 118)), + new ErrorCorrectionBlocks(1316, new ErrorCorrectionBlock(40, 47), new ErrorCorrectionBlock(7, 48)), + new ErrorCorrectionBlocks(1950, new ErrorCorrectionBlock(43, 24), new ErrorCorrectionBlock(22, 25)), + new ErrorCorrectionBlocks(2310, new ErrorCorrectionBlock(10, 15), new ErrorCorrectionBlock(67, 16))), + new QRCodeVersion( + 40, + 3706, + new ErrorCorrectionBlocks(750, new ErrorCorrectionBlock(19, 118), new ErrorCorrectionBlock(6, 119)), + new ErrorCorrectionBlocks(1372, new ErrorCorrectionBlock(18, 47), new ErrorCorrectionBlock(31, 48)), + new ErrorCorrectionBlocks(2040, new ErrorCorrectionBlock(34, 24), new ErrorCorrectionBlock(34, 25)), + new ErrorCorrectionBlocks(2430, new ErrorCorrectionBlock(20, 15), new ErrorCorrectionBlock(61, 16))), + }; + } +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/QrCode/QrCode.cs b/Toolkit.UI.Controls.Avalonia/QrCode/QrCode.cs index 7c14ba5..ff5c6ea 100644 --- a/Toolkit.UI.Controls.Avalonia/QrCode/QrCode.cs +++ b/Toolkit.UI.Controls.Avalonia/QrCode/QrCode.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Threading.Tasks; using Avalonia; using Avalonia.Animation; using Avalonia.Controls; @@ -10,6 +6,7 @@ using Avalonia.Controls.Primitives; using Avalonia.Media; using Avalonia.Threading; using Gma.QrCodeNet.Encoding; +using System.Collections; namespace Toolkit.UI.Controls.Avalonia; @@ -20,41 +17,48 @@ namespace Toolkit.UI.Controls.Avalonia; public class QrCode : Control { #region Properties + /// /// Property for the Background brush (i.e. the area that has no data) /// public static readonly StyledProperty BackgroundProperty = Border.BackgroundProperty.AddOwner(); + /// /// Property for the Foreground brush (i.e. the actual data) /// public static readonly StyledProperty ForegroundProperty = TextElement.ForegroundProperty.AddOwner(); + /// /// Property indicating how rounded the corners will be /// public static readonly StyledProperty CornerRadiusProperty = Border.CornerRadiusProperty.AddOwner(); + /// /// Property indicating the Quiet Zone (distance between the edge of the control and where the data actually starts) - /// + /// /// Note: The Quiet Zone (aka Padding) is defined in the QC Code standard (ISO 18004) as the width of 4 modules on all /// sides, but is implemented separately in this control. Official support may wish to remove this property as adjusting /// it will technically make the generated QRCodes "non-standard". This implementation does not currently concern itself /// with this as the code itself it not meant for public consumption. /// public static readonly StyledProperty PaddingProperty = Decorator.PaddingProperty.AddOwner(); + /// /// Property indicating whether the Quiet Zone of 4 modules should be added to the QR Code as additional padding. Default: True /// /// Note: Disabling the Quiet Zone makes the generated QRCodes "non-standard" according to the ISO 18004 standard. /// The padding created by the Quiet Zone depends on the module size and therefore on the amount of data. This can be - /// disabled and a fixed can be set instead to have more control over the layout. + /// disabled and a fixed can be set instead to have more control over the layout. /// public static readonly StyledProperty IsQuietZoneEnabledProperty = AvaloniaProperty.Register(nameof(IsQuietZoneEnabled), true); + /// /// Property indicating the Error Correction Code of the generated data. Default: Medium /// /// Note: See for the specific definitions of each value. /// public static readonly StyledProperty ErrorCorrectionProperty = AvaloniaProperty.Register(nameof(ErrorCorrection), EccLevel.Medium); + /// /// Property for the data represented in the QRCode /// @@ -66,52 +70,61 @@ public class QrCode : Control get => GetValue(BackgroundProperty) ?? Brushes.White; set => SetValue(BackgroundProperty, value); } + /// public IBrush Foreground { get => GetValue(ForegroundProperty) ?? Brushes.Black; set => SetValue(ForegroundProperty, value); } + /// public CornerRadius CornerRadius { get => GetValue(CornerRadiusProperty); set => SetValue(CornerRadiusProperty, value); } + /// public Thickness Padding { get => GetValue(PaddingProperty); set => SetValue(PaddingProperty, value); } + /// public bool IsQuietZoneEnabled { get => GetValue(IsQuietZoneEnabledProperty); set => SetValue(IsQuietZoneEnabledProperty, value); } + /// public EccLevel ErrorCorrection { get => GetValue(ErrorCorrectionProperty); set => SetValue(ErrorCorrectionProperty, value); } + /// public string? Data { get => GetValue(DataProperty); set => SetValue(DataProperty, value); } - #endregion + + #endregion Properties /// - /// Engine to actually calculate the bit matrix of the QRCode. Currently a Nuget package, but official support may wish to implement and remove such dependency + /// Engine to actually calculate the bit matrix of the QRCode. Currently a Nuget package, but official support may wish to implement and remove such dependency /// private static readonly QrEncoder QrCodeGenerator = new(); + /// /// A cache of currently set bits in the bit matrix. This is used to potentially speed up processing. /// private readonly Hashtable _setBitsTable = new(); + /// /// A cache of the last encoded QRCode. This is used to reuse the last generated data whenever a style property like Width, Height or Padding was changed. /// @@ -119,6 +132,7 @@ public class QrCode : Control // QRCode specs mandate a standard 4-symbol-sized space on each side of the data. We support custom Padding and will ignore this zone when processing private int QuietZoneCount => IsQuietZoneEnabled ? 4 : 0; + private int QuietMargin => QuietZoneCount * 2; /// @@ -170,7 +184,7 @@ public class QrCode : Control { // Error Correction change requires the data to be reprocessed to recalculate the new bit matrix. This is unavoidable. case nameof(ErrorCorrection): - // A change in data obviously indicates the need to update the bit matrix + // A change in data obviously indicates the need to update the bit matrix case nameof(Data): _encodedQrCode = null; break; @@ -213,7 +227,6 @@ public class QrCode : Control _oldQrCodeGeometry = null; InvalidateVisual(); - }); } @@ -694,7 +707,6 @@ public class QrCode : Control // Render background over the foreground as the geometry has "cut outs" that allow the foreground to show through context.DrawGeometry(Background, null, newGeometry); } - } /// @@ -706,14 +718,17 @@ public class QrCode : Control /// The lowest level of error correction where up to ~7% of data can be be recovered if lost and uses the least amount of symbols to represent the data /// Lowest, + /// /// The standard level of error correction where up to ~15% of data can be be recovered if lost and represents a good compromise between a small size and reliability /// Medium, + /// /// A high readability level of error correction where up to ~25% of data can be be recovered if lost but requires a larger footprint to represent the data /// Quality, + /// /// The maximum level of error correction where up to ~30% of data can be be recovered if lost and represents the maximum achievable reliability /// @@ -722,7 +737,7 @@ public class QrCode : Control /// /// Converts from our EccLevel to the one used by whichever algorithm being used. - /// This exists as an abstraction layer for if/when the package or namespace of the actual QR Generator changes so that breaking changes are not introduced + /// This exists as an abstraction layer for if/when the package or namespace of the actual QR Generator changes so that breaking changes are not introduced /// /// The selected ECC Level to convert /// The appropriate ECC Level type used by the generator @@ -748,4 +763,4 @@ public class QrCode : Control BottomRight = 1 << 2, BottomLeft = 1 << 3 } -} +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/SettingsExpander/SettingsExpander.cs b/Toolkit.UI.Controls.Avalonia/SettingsExpander/SettingsExpander.cs index 90c8807..6fd231f 100644 --- a/Toolkit.UI.Controls.Avalonia/SettingsExpander/SettingsExpander.cs +++ b/Toolkit.UI.Controls.Avalonia/SettingsExpander/SettingsExpander.cs @@ -4,7 +4,7 @@ namespace Toolkit.UI.Controls.Avalonia; public class SettingsExpander : FluentAvalonia.UI.Controls.SettingsExpander { - protected override Type StyleKeyOverride => + protected override Type StyleKeyOverride => typeof(SettingsExpander); public new static readonly StyledProperty DescriptionProperty = @@ -30,4 +30,4 @@ public class SettingsExpanderItem : FluentAvalonia.UI.Controls.SettingsExpanderI get => GetValue(DescriptionProperty); set => SetValue(DescriptionProperty, value); } -} +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/SpacedGrid/ISpacingDefinition.cs b/Toolkit.UI.Controls.Avalonia/SpacedGrid/ISpacingDefinition.cs index bfb9320..1ea1e1e 100644 --- a/Toolkit.UI.Controls.Avalonia/SpacedGrid/ISpacingDefinition.cs +++ b/Toolkit.UI.Controls.Avalonia/SpacedGrid/ISpacingDefinition.cs @@ -3,4 +3,4 @@ public interface ISpacingDefinition { double Spacing { get; set; } -} +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/SpacedGrid/SpacedGrid.cs b/Toolkit.UI.Controls.Avalonia/SpacedGrid/SpacedGrid.cs index df4d52a..7536872 100644 --- a/Toolkit.UI.Controls.Avalonia/SpacedGrid/SpacedGrid.cs +++ b/Toolkit.UI.Controls.Avalonia/SpacedGrid/SpacedGrid.cs @@ -59,7 +59,7 @@ public class SpacedGrid : Grid } } - private void OnCollectionChanged(object? sender, + private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args) { if (args.Action == NotifyCollectionChangedAction.Add || args.Action == NotifyCollectionChangedAction.Replace) diff --git a/Toolkit.UI.Controls.Avalonia/SpacedGrid/SpacingColumnDefinition.cs b/Toolkit.UI.Controls.Avalonia/SpacedGrid/SpacingColumnDefinition.cs index 3b213f6..eff3fdc 100644 --- a/Toolkit.UI.Controls.Avalonia/SpacedGrid/SpacingColumnDefinition.cs +++ b/Toolkit.UI.Controls.Avalonia/SpacedGrid/SpacingColumnDefinition.cs @@ -11,4 +11,4 @@ public class SpacingColumnDefinition(double width) : get => Width.Value; set => Width = new GridLength(value, GridUnitType.Pixel); } -} +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/SpacedGrid/SpacingRowDefinition.cs b/Toolkit.UI.Controls.Avalonia/SpacedGrid/SpacingRowDefinition.cs index 6d6bc43..15909c8 100644 --- a/Toolkit.UI.Controls.Avalonia/SpacedGrid/SpacingRowDefinition.cs +++ b/Toolkit.UI.Controls.Avalonia/SpacedGrid/SpacingRowDefinition.cs @@ -11,4 +11,4 @@ public class SpacingRowDefinition(double height) : get => Height.Value; set => Height = new GridLength(value, GridUnitType.Pixel); } -} +} \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/Themes/ThemeResources.axaml.cs b/Toolkit.UI.Controls.Avalonia/Themes/ThemeResources.axaml.cs index 6989d3e..2df8fe9 100644 --- a/Toolkit.UI.Controls.Avalonia/Themes/ThemeResources.axaml.cs +++ b/Toolkit.UI.Controls.Avalonia/Themes/ThemeResources.axaml.cs @@ -10,4 +10,4 @@ public class ThemeResources : { AvaloniaXamlLoader.Load(provider, this); } -} +} \ No newline at end of file From 7e980dbfce5188631cfa1b8076f42db8f355e85c Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 28 Apr 2024 16:04:05 +0100 Subject: [PATCH 015/228] Make vault storages work --- Toolkit.Foundation/Changed.cs | 2 +- Toolkit.Foundation/Component.cs | 30 ++++++++ Toolkit.Foundation/ComponentFactory.cs | 57 +++++++++++++++ Toolkit.Foundation/DefaultHostBuilder.cs | 69 +++++++++++++++++++ Toolkit.Foundation/IComponentFactory.cs | 10 +++ ...ultBuilder.cs => IHostBuilderExtension.cs} | 65 +---------------- 6 files changed, 168 insertions(+), 65 deletions(-) create mode 100644 Toolkit.Foundation/Component.cs create mode 100644 Toolkit.Foundation/ComponentFactory.cs create mode 100644 Toolkit.Foundation/DefaultHostBuilder.cs create mode 100644 Toolkit.Foundation/IComponentFactory.cs rename Toolkit.Foundation/{DefaultBuilder.cs => IHostBuilderExtension.cs} (72%) diff --git a/Toolkit.Foundation/Changed.cs b/Toolkit.Foundation/Changed.cs index e8a009b..1750921 100644 --- a/Toolkit.Foundation/Changed.cs +++ b/Toolkit.Foundation/Changed.cs @@ -7,4 +7,4 @@ public record Changed public static Changed As(TValue value) => new(value); public static Changed As() where TValue : new() => new(new TValue()); -} \ No newline at end of file +} diff --git a/Toolkit.Foundation/Component.cs b/Toolkit.Foundation/Component.cs new file mode 100644 index 0000000..2ea0c05 --- /dev/null +++ b/Toolkit.Foundation/Component.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Toolkit.Foundation; + +public class Component : + IComponent +{ + private readonly IComponentBuilder builder; + + protected Component(IComponentBuilder builder) + { + this.builder = builder; + } + + public static TComponent? Create(IServiceProvider provider, + Action builderDelegate) + where TComponent : class, IComponent + { + if (provider.GetRequiredService() is IServiceFactory factory) + { + IComponentBuilder builder = ComponentBuilder.Create(); + builderDelegate(builder); + + return factory.Create(builder); + } + return default ; + } + + public virtual IComponentBuilder Create() => builder; +} \ No newline at end of file diff --git a/Toolkit.Foundation/ComponentFactory.cs b/Toolkit.Foundation/ComponentFactory.cs new file mode 100644 index 0000000..b23d95b --- /dev/null +++ b/Toolkit.Foundation/ComponentFactory.cs @@ -0,0 +1,57 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Toolkit.Foundation; + +public class ComponentFactory(IServiceProvider provider, + IProxyServiceCollection proxy, + IComponentScopeCollection scopes) : + IComponentFactory +{ + public IComponentHost? Create(string name, + ComponentConfiguration configuration, Action? servicesDelegate = null) + where TComponent : IComponent + { + if (provider.GetRequiredService() is TComponent component) + { + IComponentBuilder builder = component.Create(); + builder.AddServices(services => + { + services.AddTransient(_ => + provider.GetRequiredService>()); + + services.AddTransient(_ => + provider.GetRequiredService>()); + + services.AddScoped(_ => + provider.GetRequiredService()); + + services.AddScoped(_ => + provider.GetRequiredService()); + + services.AddScoped(_ => + provider.GetRequiredService()); + + services.AddTransient(_ => + provider.GetRequiredService()); + + services.AddRange(proxy.Services); + services.AddSingleton(new ComponentScope(name)); + + if (servicesDelegate is not null) + { + servicesDelegate(services); + } + }); + + builder.AddConfiguration(name, configuration); + IComponentHost host = builder.Build(); + + scopes.Add(new ComponentScopeDescriptor(name, + host.Services.GetRequiredService())); + + return host; + } + + return default; + } +} diff --git a/Toolkit.Foundation/DefaultHostBuilder.cs b/Toolkit.Foundation/DefaultHostBuilder.cs new file mode 100644 index 0000000..c238e97 --- /dev/null +++ b/Toolkit.Foundation/DefaultHostBuilder.cs @@ -0,0 +1,69 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Toolkit.Foundation; + +public class DefaultHostBuilder : + HostBuilder +{ + public static IHostBuilder Create() + { + return new HostBuilder() + .UseContentRoot("Local", true) + .ConfigureAppConfiguration((context, config) => + { + config.AddJsonFile("Settings.json", true, true); + }) + .ConfigureServices((context, services) => + { + services.AddScoped(provider => + new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!))); + + services.AddSingleton(); + + services.AddScoped(); + services.AddScoped(); + services.AddTransient(); + services.AddTransient(); + + services.AddScoped(); + + services.AddScoped>(provider => + new ProxyService(provider.GetRequiredService())); + + services.AddScoped>(provider => + new ProxyService(provider.GetRequiredService())); + + services.AddScoped>(provider => + new ProxyService(provider.GetRequiredService())); + + services.AddScoped(); + + services.AddTransient(); + + services.AddTransient(); + + services.AddScoped(); + services.AddTransient(); + + services.AddTransient(); + + services.AddSingleton(new ComponentScope("Root")); + services.AddScoped(provider => new ComponentScopeCollection + { + new ComponentScopeDescriptor("Root", provider.GetRequiredService()) + }); + + services.AddTransient(); + services.AddTransient(); + + services.AddHandler(); + services.AddHandler(); + + services.AddInitializer(); + services.AddHostedService(); + }); + } +} \ No newline at end of file diff --git a/Toolkit.Foundation/IComponentFactory.cs b/Toolkit.Foundation/IComponentFactory.cs new file mode 100644 index 0000000..6380136 --- /dev/null +++ b/Toolkit.Foundation/IComponentFactory.cs @@ -0,0 +1,10 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Toolkit.Foundation; + +public interface IComponentFactory +{ + IComponentHost? Create(string name, + ComponentConfiguration configuration, Action? servicesDelegate = null) + where TComponent : IComponent; +} \ No newline at end of file diff --git a/Toolkit.Foundation/DefaultBuilder.cs b/Toolkit.Foundation/IHostBuilderExtension.cs similarity index 72% rename from Toolkit.Foundation/DefaultBuilder.cs rename to Toolkit.Foundation/IHostBuilderExtension.cs index 2d0309f..944977b 100644 --- a/Toolkit.Foundation/DefaultBuilder.cs +++ b/Toolkit.Foundation/IHostBuilderExtension.cs @@ -8,7 +8,7 @@ using System.Text.Json; namespace Toolkit.Foundation; -public static class Test +public static class IHostBuilderExtension { public static IHostBuilder AddConfiguration(this IHostBuilder builder, string section) @@ -149,66 +149,3 @@ public static class Test return builder; } } - -public class DefaultBuilder : - HostBuilder -{ - public static IHostBuilder Create() - { - return new HostBuilder() - .UseContentRoot("Local", true) - .ConfigureAppConfiguration((context, config) => - { - config.AddJsonFile("Settings.json", true, true); - }) - .ConfigureServices((context, services) => - { - services.AddScoped(provider => - new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!))); - - services.AddSingleton(); - - services.AddScoped(); - services.AddScoped(); - services.AddTransient(); - services.AddTransient(); - - services.AddScoped(); - - services.AddScoped>(provider => - new ProxyService(provider.GetRequiredService())); - - services.AddScoped>(provider => - new ProxyService(provider.GetRequiredService())); - - services.AddScoped>(provider => - new ProxyService(provider.GetRequiredService())); - - services.AddScoped(); - - services.AddTransient(); - - services.AddTransient(); - - services.AddScoped(); - services.AddTransient(); - - services.AddTransient(); - - services.AddSingleton(new ComponentScope("Root")); - services.AddScoped(provider => new ComponentScopeCollection - { - new ComponentScopeDescriptor("Root", provider.GetRequiredService()) - }); - - services.AddTransient(); - - services.AddHandler(); - services.AddHandler(); - - services.AddInitializer(); - services.AddHostedService(); - }); - } -} \ No newline at end of file From b89f21b3ca491b24f826c22fa1e540572f6ffaa9 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Mon, 29 Apr 2024 21:42:04 +0100 Subject: [PATCH 016/228] encyption wip --- Toolkit.Foundation/ComponentConfiguration.cs | 4 +--- Toolkit.Foundation/ComponentFactory.cs | 8 +++++--- Toolkit.Foundation/IComponentFactory.cs | 7 ++++--- Toolkit.Foundation/Initialized.cs | 10 ---------- Toolkit.Foundation/Open.cs | 10 ++++++++++ 5 files changed, 20 insertions(+), 19 deletions(-) delete mode 100644 Toolkit.Foundation/Initialized.cs create mode 100644 Toolkit.Foundation/Open.cs diff --git a/Toolkit.Foundation/ComponentConfiguration.cs b/Toolkit.Foundation/ComponentConfiguration.cs index 1bf75b0..907c0a7 100644 --- a/Toolkit.Foundation/ComponentConfiguration.cs +++ b/Toolkit.Foundation/ComponentConfiguration.cs @@ -1,5 +1,3 @@ namespace Toolkit.Foundation; -public record ComponentConfiguration -{ -} \ No newline at end of file +public record ComponentConfiguration; \ No newline at end of file diff --git a/Toolkit.Foundation/ComponentFactory.cs b/Toolkit.Foundation/ComponentFactory.cs index b23d95b..d341915 100644 --- a/Toolkit.Foundation/ComponentFactory.cs +++ b/Toolkit.Foundation/ComponentFactory.cs @@ -7,9 +7,11 @@ public class ComponentFactory(IServiceProvider provider, IComponentScopeCollection scopes) : IComponentFactory { - public IComponentHost? Create(string name, - ComponentConfiguration configuration, Action? servicesDelegate = null) + public IComponentHost? Create(string name, + TConfiguration configuration, + Action? servicesDelegate = null) where TComponent : IComponent + where TConfiguration : ComponentConfiguration, new() { if (provider.GetRequiredService() is TComponent component) { @@ -43,7 +45,7 @@ public class ComponentFactory(IServiceProvider provider, } }); - builder.AddConfiguration(name, configuration); + builder.AddConfiguration(name, configuration); IComponentHost host = builder.Build(); scopes.Add(new ComponentScopeDescriptor(name, diff --git a/Toolkit.Foundation/IComponentFactory.cs b/Toolkit.Foundation/IComponentFactory.cs index 6380136..451f30f 100644 --- a/Toolkit.Foundation/IComponentFactory.cs +++ b/Toolkit.Foundation/IComponentFactory.cs @@ -4,7 +4,8 @@ namespace Toolkit.Foundation; public interface IComponentFactory { - IComponentHost? Create(string name, - ComponentConfiguration configuration, Action? servicesDelegate = null) - where TComponent : IComponent; + IComponentHost? Create(string name, + TConfiguration configuration, Action? servicesDelegate = null) + where TComponent : IComponent + where TConfiguration : ComponentConfiguration, new(); } \ No newline at end of file diff --git a/Toolkit.Foundation/Initialized.cs b/Toolkit.Foundation/Initialized.cs deleted file mode 100644 index 6302662..0000000 --- a/Toolkit.Foundation/Initialized.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Toolkit.Foundation; - -public record Initialized(TValue Value); - -public record Initialized -{ - public static Initialized As(TValue value) => new(value); - - public static Initialized As() where TValue : new() => new(new TValue()); -} \ No newline at end of file diff --git a/Toolkit.Foundation/Open.cs b/Toolkit.Foundation/Open.cs new file mode 100644 index 0000000..7e41b12 --- /dev/null +++ b/Toolkit.Foundation/Open.cs @@ -0,0 +1,10 @@ +namespace Toolkit.Foundation; + +public record Open(TValue Value); + +public record Open +{ + public static Open As(TValue value) => new(value); + + public static Open As() where TValue : new() => new(new TValue()); +} \ No newline at end of file From 7dfbb9176285ced37ce4e257491ea3f218b6bc04 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Tue, 30 Apr 2024 20:46:47 +0100 Subject: [PATCH 017/228] wip --- Toolkit.Foundation/ComponentBuilder.cs | 10 ++-------- Toolkit.Foundation/IHostBuilderExtension.cs | 3 ++- .../{IPipelineBehavior.cs => IPipelineBehaviour.cs} | 0 3 files changed, 4 insertions(+), 9 deletions(-) rename Toolkit.Foundation/{IPipelineBehavior.cs => IPipelineBehaviour.cs} (100%) diff --git a/Toolkit.Foundation/ComponentBuilder.cs b/Toolkit.Foundation/ComponentBuilder.cs index dde604e..2f90b1a 100644 --- a/Toolkit.Foundation/ComponentBuilder.cs +++ b/Toolkit.Foundation/ComponentBuilder.cs @@ -76,14 +76,8 @@ public class ComponentBuilder : configurationRegistered = true; - hostBuilder.ConfigureServices(services => - { - services.AddConfiguration(section: section, - defaultConfiguration: configuration); - - services.AddConfiguration(section: section, - defaultConfiguration: configuration); - }); + hostBuilder.AddConfiguration(section: section, + defaultConfiguration: configuration); return this; } diff --git a/Toolkit.Foundation/IHostBuilderExtension.cs b/Toolkit.Foundation/IHostBuilderExtension.cs index 944977b..de95c71 100644 --- a/Toolkit.Foundation/IHostBuilderExtension.cs +++ b/Toolkit.Foundation/IHostBuilderExtension.cs @@ -133,7 +133,8 @@ public static class IHostBuilderExtension services.AddTransient, ConfigurationInitializer>(provider => provider.GetRequiredService().Create>(section)); - services.AddTransient, WritableConfiguration>(); + services.TryAddKeyedTransient>(section, (provider, key) => + new WritableConfiguration(provider.GetRequiredKeyedService>(key))); services.TryAddKeyedTransient>(section, (provider, key) => new ConfigurationDescriptor(section, provider.GetRequiredKeyedService>(key))); diff --git a/Toolkit.Foundation/IPipelineBehavior.cs b/Toolkit.Foundation/IPipelineBehaviour.cs similarity index 100% rename from Toolkit.Foundation/IPipelineBehavior.cs rename to Toolkit.Foundation/IPipelineBehaviour.cs From 8d62f36931a8c59b69149077705c6eaa5f7d8904 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Thu, 2 May 2024 20:58:12 +0100 Subject: [PATCH 018/228] bunch of fixes --- Toolkit.Foundation/AesDecryptor.cs | 28 ++++ Toolkit.Foundation/AesEncryptor.cs | 34 +++++ Toolkit.Foundation/ConfigurationSource.cs | 10 +- Toolkit.Foundation/ConfigurationWriter.cs | 17 ++- Toolkit.Foundation/Container.cs | 9 ++ Toolkit.Foundation/IContainer.cs | 8 ++ Toolkit.Foundation/IDecryptor.cs | 6 + Toolkit.Foundation/IEncryptor.cs | 5 + Toolkit.Foundation/IHostBuilderExtension.cs | 10 +- Toolkit.Foundation/IInitializer.cs | 5 + Toolkit.Foundation/IKeyDeriver.cs | 5 + Toolkit.Foundation/IKeyGenerator.cs | 5 + Toolkit.Foundation/IPasswordHasher.cs | 5 + .../IServiceCollectionExtensions.cs | 120 ------------------ Toolkit.Foundation/KeyDeriver.cs | 13 ++ Toolkit.Foundation/KeyGenerator.cs | 15 +++ Toolkit.Foundation/PasswordHasher.cs | 19 +++ 17 files changed, 180 insertions(+), 134 deletions(-) create mode 100644 Toolkit.Foundation/AesDecryptor.cs create mode 100644 Toolkit.Foundation/AesEncryptor.cs create mode 100644 Toolkit.Foundation/Container.cs create mode 100644 Toolkit.Foundation/IContainer.cs create mode 100644 Toolkit.Foundation/IDecryptor.cs create mode 100644 Toolkit.Foundation/IEncryptor.cs create mode 100644 Toolkit.Foundation/IKeyDeriver.cs create mode 100644 Toolkit.Foundation/IKeyGenerator.cs create mode 100644 Toolkit.Foundation/IPasswordHasher.cs create mode 100644 Toolkit.Foundation/KeyDeriver.cs create mode 100644 Toolkit.Foundation/KeyGenerator.cs create mode 100644 Toolkit.Foundation/PasswordHasher.cs diff --git a/Toolkit.Foundation/AesDecryptor.cs b/Toolkit.Foundation/AesDecryptor.cs new file mode 100644 index 0000000..5ef8653 --- /dev/null +++ b/Toolkit.Foundation/AesDecryptor.cs @@ -0,0 +1,28 @@ +using System.Security.Cryptography; + +namespace Toolkit.Foundation; + +public class AesDecryptor : + IDecryptor +{ + private const int IvSize = 16; + + public byte[] Decrypt(byte[] cipher, byte[] key) + { + Span iv = cipher.AsSpan(0, IvSize); + ReadOnlySpan encryptedContent = cipher.AsSpan(IvSize); + + using Aes aes = Aes.Create(); + aes.Key = key; + aes.IV = iv.ToArray(); + + using MemoryStream memoryStream = new(encryptedContent.ToArray()); + using ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV); + using CryptoStream cryptoStream = new(memoryStream, decryptor, CryptoStreamMode.Read); + + using MemoryStream resultStream = new(); + cryptoStream.CopyTo(resultStream); + + return resultStream.ToArray(); + } +} diff --git a/Toolkit.Foundation/AesEncryptor.cs b/Toolkit.Foundation/AesEncryptor.cs new file mode 100644 index 0000000..9e844d3 --- /dev/null +++ b/Toolkit.Foundation/AesEncryptor.cs @@ -0,0 +1,34 @@ +using System.Security.Cryptography; + +namespace Toolkit.Foundation; + +public class AesEncryptor : + IEncryptor +{ + private const int IvSize = 16; + + public byte[] Encrypt(byte[] data, + byte[] key) + { + if (key.Length != 32) + { + throw new ArgumentException("Key must be 256 bits (32 bytes)."); + } + + using Aes aes = Aes.Create(); + aes.Key = key; + aes.GenerateIV(); + + using MemoryStream memoryStream = new(); + memoryStream.Write(aes.IV.AsSpan(0, IvSize)); + + using (ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV)) + using (CryptoStream cryptoStream = new(memoryStream, encryptor, CryptoStreamMode.Write)) + { + cryptoStream.Write(data, 0, data.Length); + cryptoStream.FlushFinalBlock(); + } + + return memoryStream.ToArray(); + } +} diff --git a/Toolkit.Foundation/ConfigurationSource.cs b/Toolkit.Foundation/ConfigurationSource.cs index 58155ee..1538b76 100644 --- a/Toolkit.Foundation/ConfigurationSource.cs +++ b/Toolkit.Foundation/ConfigurationSource.cs @@ -120,7 +120,6 @@ public class ConfigurationSource(IConfigurationFile(IConfigurationFile(currentNode[segments[lastIndex]], - serializerOptions ?? defaultSerializerOptions()); - return true; + if (currentNode[segments[lastIndex]] is JsonNode sectionNode) + { + value = JsonSerializer.Deserialize(sectionNode, + serializerOptions ?? defaultSerializerOptions()); + return true; + } } } diff --git a/Toolkit.Foundation/ConfigurationWriter.cs b/Toolkit.Foundation/ConfigurationWriter.cs index 41544be..3274b14 100644 --- a/Toolkit.Foundation/ConfigurationWriter.cs +++ b/Toolkit.Foundation/ConfigurationWriter.cs @@ -1,19 +1,22 @@ namespace Toolkit.Foundation; -public class ConfigurationWriter(IConfigurationSource source) : +public class ConfigurationWriter(IConfigurationSource source, + IConfigurationFactory factory) : IConfigurationWriter where TConfiguration : class { public void Write(Action updateDelegate) { - if (source.TryGet(out TConfiguration? value)) + if (!source.TryGet(out TConfiguration? value)) { - if (value is not null) - { - updateDelegate?.Invoke(value); - Write(value); - } + value = (TConfiguration)factory.Create(); + } + + if (value is not null) + { + updateDelegate?.Invoke(value); + Write(value); } } diff --git a/Toolkit.Foundation/Container.cs b/Toolkit.Foundation/Container.cs new file mode 100644 index 0000000..30516e0 --- /dev/null +++ b/Toolkit.Foundation/Container.cs @@ -0,0 +1,9 @@ +namespace Toolkit.Foundation; + +public class Container : + IContainer +{ + public T? Value { get; private set; } + + public void Set(T value) => Value = value; +} diff --git a/Toolkit.Foundation/IContainer.cs b/Toolkit.Foundation/IContainer.cs new file mode 100644 index 0000000..7c7dd2d --- /dev/null +++ b/Toolkit.Foundation/IContainer.cs @@ -0,0 +1,8 @@ +namespace Toolkit.Foundation; + +public interface IContainer +{ + T? Value { get; } + + void Set(T value); +} diff --git a/Toolkit.Foundation/IDecryptor.cs b/Toolkit.Foundation/IDecryptor.cs new file mode 100644 index 0000000..0a618f7 --- /dev/null +++ b/Toolkit.Foundation/IDecryptor.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; +public interface IDecryptor +{ + byte[] Decrypt(byte[] cipher, + byte[] key); +} \ No newline at end of file diff --git a/Toolkit.Foundation/IEncryptor.cs b/Toolkit.Foundation/IEncryptor.cs new file mode 100644 index 0000000..fc37690 --- /dev/null +++ b/Toolkit.Foundation/IEncryptor.cs @@ -0,0 +1,5 @@ +namespace Toolkit.Foundation; +public interface IEncryptor +{ + byte[] Encrypt(byte[] data, byte[] key); +} \ No newline at end of file diff --git a/Toolkit.Foundation/IHostBuilderExtension.cs b/Toolkit.Foundation/IHostBuilderExtension.cs index de95c71..dd9723e 100644 --- a/Toolkit.Foundation/IHostBuilderExtension.cs +++ b/Toolkit.Foundation/IHostBuilderExtension.cs @@ -100,7 +100,7 @@ public static class IHostBuilderExtension return new ConfigurationFile(fileInfo); }); - services.TryAddKeyedSingleton>(section, (provider, KeyAccelerator) => + services.TryAddKeyedTransient>(section, (provider, key) => { JsonSerializerOptions? defaultSerializer = null; if (serializerDelegate is not null) @@ -119,10 +119,11 @@ public static class IHostBuilderExtension provider.GetRequiredKeyedService>(key))); services.TryAddKeyedTransient>(section, (provider, key) => - new ConfigurationWriter(provider.GetRequiredKeyedService>(key))); + new ConfigurationWriter(provider.GetRequiredKeyedService>(key), + provider.GetRequiredKeyedService>(key))); services.TryAddKeyedTransient>(section, (provider, key) => - new ConfigurationFactory(() => defaultConfiguration ?? new TConfiguration())); + new ConfigurationFactory(() => defaultConfiguration ?? provider.GetRequiredKeyedService(key))); services.AddTransient>(provider => new ConfigurationInitializer(provider.GetRequiredKeyedService>(section), @@ -136,6 +137,9 @@ public static class IHostBuilderExtension services.TryAddKeyedTransient>(section, (provider, key) => new WritableConfiguration(provider.GetRequiredKeyedService>(key))); + services.TryAddTransient>(provider => + new WritableConfiguration(provider.GetRequiredKeyedService>(section))); + services.TryAddKeyedTransient>(section, (provider, key) => new ConfigurationDescriptor(section, provider.GetRequiredKeyedService>(key))); diff --git a/Toolkit.Foundation/IInitializer.cs b/Toolkit.Foundation/IInitializer.cs index 00a126a..1f803f6 100644 --- a/Toolkit.Foundation/IInitializer.cs +++ b/Toolkit.Foundation/IInitializer.cs @@ -3,4 +3,9 @@ public interface IInitializer { Task Initialize(); +} + +public interface IInitializer +{ + Task Initialize(); } \ No newline at end of file diff --git a/Toolkit.Foundation/IKeyDeriver.cs b/Toolkit.Foundation/IKeyDeriver.cs new file mode 100644 index 0000000..4a5a1b6 --- /dev/null +++ b/Toolkit.Foundation/IKeyDeriver.cs @@ -0,0 +1,5 @@ +namespace Toolkit.Foundation; +public interface IKeyDeriver +{ + byte[] DeriveKey(string password, byte[] salt, int keySize = 32, int iterations = 10000); +} diff --git a/Toolkit.Foundation/IKeyGenerator.cs b/Toolkit.Foundation/IKeyGenerator.cs new file mode 100644 index 0000000..aeaaa74 --- /dev/null +++ b/Toolkit.Foundation/IKeyGenerator.cs @@ -0,0 +1,5 @@ +namespace Toolkit.Foundation; +public interface IKeyGenerator +{ + byte[] Generate(int size); +} \ No newline at end of file diff --git a/Toolkit.Foundation/IPasswordHasher.cs b/Toolkit.Foundation/IPasswordHasher.cs new file mode 100644 index 0000000..d31d66e --- /dev/null +++ b/Toolkit.Foundation/IPasswordHasher.cs @@ -0,0 +1,5 @@ +namespace Toolkit.Foundation; +public interface IPasswordHasher +{ + string HashPassword(string password, int iterations = 10000); +} \ No newline at end of file diff --git a/Toolkit.Foundation/IServiceCollectionExtensions.cs b/Toolkit.Foundation/IServiceCollectionExtensions.cs index bee5f85..e53b5c1 100644 --- a/Toolkit.Foundation/IServiceCollectionExtensions.cs +++ b/Toolkit.Foundation/IServiceCollectionExtensions.cs @@ -35,126 +35,6 @@ public static class IServiceCollectionExtensions return services; } - public static IServiceCollection AddConfiguration(this IServiceCollection services, - Func> changed) - where TConfiguration : class - where TValue : class, new() - { - services.AddSingleton(new ConfigurationValue(changed)); - services.AddHandler>(); - - return services; - } - - public static IServiceCollection AddConfiguration(this IServiceCollection services, - string section) - where TConfiguration : class, new() => - services.AddConfiguration(section, "Settings.json", null); - - public static IServiceCollection AddConfiguration(this IServiceCollection services) - where TConfiguration : class, new() => - services.AddConfiguration(typeof(TConfiguration).Name, "Settings.json", null); - - public static IServiceCollection AddConfiguration(this IServiceCollection services, - Action configurationDelegate) - where TConfiguration : class, new() - { - TConfiguration configuration = new(); - configurationDelegate.Invoke(configuration); - - return services.AddConfiguration(typeof(TConfiguration).Name, "Settings.json", configuration); - } - - public static IServiceCollection AddConfiguration(this IServiceCollection services, - Action configurationDelegate, - string section) - where TConfiguration : class, new() - { - TConfiguration configuration = new(); - configurationDelegate.Invoke(configuration); - - return services.AddConfiguration(section, "Settings.json", configuration); - } - - public static IServiceCollection AddConfiguration(this IServiceCollection services, - TConfiguration configuration) - where TConfiguration : class, new() => - services.AddConfiguration(configuration.GetType().Name, "Settings.json", configuration); - - public static IServiceCollection AddConfiguration(this IServiceCollection services, - object configuration) - where TConfiguration : class, new() => - services.AddConfiguration(configuration.GetType().Name, - "Settings.json", (TConfiguration?)configuration); - - public static IServiceCollection AddConfiguration(this IServiceCollection services, - string section, - string path = "Settings.json", - TConfiguration? defaultConfiguration = null, - Action? serializerDelegate = null) - where TConfiguration : class, new() - { - services.TryAddSingleton>(provider => - { - IFileInfo? fileInfo = null; - if (provider.GetService() is IHostEnvironment hostEnvironment) - { - IFileProvider fileProvider = hostEnvironment.ContentRootFileProvider; - fileInfo = fileProvider.GetFileInfo(path); - } - - fileInfo ??= new PhysicalFileInfo(new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path))); - return new ConfigurationFile(fileInfo); - }); - - services.TryAddKeyedSingleton>(section, (provider, KeyAccelerator) => - { - JsonSerializerOptions? defaultSerializer = null; - if (serializerDelegate is not null) - { - defaultSerializer = new JsonSerializerOptions(); - serializerDelegate.Invoke(defaultSerializer); - } - - return new ConfigurationSource(provider.GetRequiredService>(), - section, defaultSerializer); - }); - - //services.AddHostedService>(); - services.TryAddKeyedTransient>(section, (provider, key) => - new ConfigurationReader(provider.GetRequiredKeyedService>(key), - provider.GetRequiredKeyedService>(key))); - - services.TryAddKeyedTransient>(section, (provider, key) => - new ConfigurationWriter(provider.GetRequiredKeyedService>(key))); - - services.TryAddKeyedTransient>(section, (provider, key) => - new ConfigurationFactory(() => defaultConfiguration ?? new TConfiguration())); - - services.AddTransient>(provider => - new ConfigurationInitializer(provider.GetRequiredKeyedService>(section), - provider.GetRequiredKeyedService>(section), - provider.GetRequiredKeyedService>(section), - provider.GetRequiredService())); - - services.AddTransient, ConfigurationInitializer>(provider => - provider.GetRequiredService().Create>(section)); - - services.AddTransient, WritableConfiguration>(); - - services.TryAddKeyedTransient>(section, (provider, key) => - new ConfigurationDescriptor(section, provider.GetRequiredKeyedService>(key))); - - services.AddTransient(provider => - provider.GetRequiredKeyedService>(section)); - - services.AddTransient(provider => - provider.GetRequiredKeyedService>(section).Value); - - return services; - } - public static IServiceCollection AddHandler(this IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Transient) where THandler : IHandler diff --git a/Toolkit.Foundation/KeyDeriver.cs b/Toolkit.Foundation/KeyDeriver.cs new file mode 100644 index 0000000..af3ea68 --- /dev/null +++ b/Toolkit.Foundation/KeyDeriver.cs @@ -0,0 +1,13 @@ +using System.Security.Cryptography; + +namespace Toolkit.Foundation; + +public class KeyDeriver : + IKeyDeriver +{ + public byte[] DeriveKey(string password, byte[] salt, int keySize = 32, int iterations = 100000) + { + using Rfc2898DeriveBytes pbkdf2 = new(password, salt, iterations, HashAlgorithmName.SHA256); + return pbkdf2.GetBytes(keySize); + } +} diff --git a/Toolkit.Foundation/KeyGenerator.cs b/Toolkit.Foundation/KeyGenerator.cs new file mode 100644 index 0000000..362e201 --- /dev/null +++ b/Toolkit.Foundation/KeyGenerator.cs @@ -0,0 +1,15 @@ +using System.Security.Cryptography; + +namespace Toolkit.Foundation; + +public class KeyGenerator : + IKeyGenerator +{ + public byte[] Generate(int size) + { + byte[] key = new byte[size]; + RandomNumberGenerator.Fill(key); + + return key; + } +} diff --git a/Toolkit.Foundation/PasswordHasher.cs b/Toolkit.Foundation/PasswordHasher.cs new file mode 100644 index 0000000..4ad4d48 --- /dev/null +++ b/Toolkit.Foundation/PasswordHasher.cs @@ -0,0 +1,19 @@ +using System.Security.Cryptography; + +namespace Toolkit.Foundation; + +public class PasswordHasher : + IPasswordHasher +{ + private const int SaltSize = 16; + + public string HashPassword(string password, int iterations = 10000) + { + using Rfc2898DeriveBytes pbkdf2 = new(password, SaltSize, iterations, HashAlgorithmName.SHA256); + + byte[] salt = pbkdf2.Salt; + byte[] hash = pbkdf2.GetBytes(32); + + return $"{Convert.ToBase64String(salt)}:{Convert.ToBase64String(hash)}"; + } +} From 7582f63a68739419271bbd9dc4f8ed8d79f88edf Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Thu, 2 May 2024 23:07:48 +0100 Subject: [PATCH 019/228] Add code for opening vault --- Toolkit.Foundation/IKeyDeriver.cs | 5 ++++- Toolkit.Foundation/KeyDeriver.cs | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Toolkit.Foundation/IKeyDeriver.cs b/Toolkit.Foundation/IKeyDeriver.cs index 4a5a1b6..02fae2c 100644 --- a/Toolkit.Foundation/IKeyDeriver.cs +++ b/Toolkit.Foundation/IKeyDeriver.cs @@ -1,5 +1,8 @@ namespace Toolkit.Foundation; public interface IKeyDeriver { - byte[] DeriveKey(string password, byte[] salt, int keySize = 32, int iterations = 10000); + byte[] DeriveKey(byte[] phrased, + byte[] salt, + int keySize = 32, + int iterations = 10000); } diff --git a/Toolkit.Foundation/KeyDeriver.cs b/Toolkit.Foundation/KeyDeriver.cs index af3ea68..44bda0c 100644 --- a/Toolkit.Foundation/KeyDeriver.cs +++ b/Toolkit.Foundation/KeyDeriver.cs @@ -5,9 +5,12 @@ namespace Toolkit.Foundation; public class KeyDeriver : IKeyDeriver { - public byte[] DeriveKey(string password, byte[] salt, int keySize = 32, int iterations = 100000) + public byte[] DeriveKey(byte[] phrase, + byte[] salt, + int keySize = 32, + int iterations = 100000) { - using Rfc2898DeriveBytes pbkdf2 = new(password, salt, iterations, HashAlgorithmName.SHA256); + using Rfc2898DeriveBytes pbkdf2 = new(phrase, salt, iterations, HashAlgorithmName.SHA256); return pbkdf2.GetBytes(keySize); } } From abcff30da6a0eb8f759260f781a0825b4de06593 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Fri, 3 May 2024 19:33:32 +0100 Subject: [PATCH 020/228] vault unlocking WIP --- Toolkit.Avalonia/AvaloniaDispatcher.cs | 5 +- Toolkit.Avalonia/ContentDialogHandler.cs | 2 +- Toolkit.Foundation/AesDecryptor.cs | 46 ++++++++++++----- Toolkit.Foundation/AesEncryptor.cs | 49 ++++++++++++------- Toolkit.Foundation/IDecryptor.cs | 5 +- Toolkit.Foundation/IDispatcher.cs | 2 +- Toolkit.Foundation/IEncryptor.cs | 5 +- .../ObservableCollectionViewModel.cs | 11 +++++ Toolkit.Foundation/Publisher.cs | 10 ++-- 9 files changed, 92 insertions(+), 43 deletions(-) diff --git a/Toolkit.Avalonia/AvaloniaDispatcher.cs b/Toolkit.Avalonia/AvaloniaDispatcher.cs index 0870623..1f190e6 100644 --- a/Toolkit.Avalonia/AvaloniaDispatcher.cs +++ b/Toolkit.Avalonia/AvaloniaDispatcher.cs @@ -6,8 +6,5 @@ namespace Toolkit.Avalonia; public class AvaloniaDispatcher : IDispatcher { - public async Task InvokeAsync(Action action) - { - await Dispatcher.UIThread.InvokeAsync(action); - } + public async Task Invoke(Action action) => await Dispatcher.UIThread.InvokeAsync(action); } \ No newline at end of file diff --git a/Toolkit.Avalonia/ContentDialogHandler.cs b/Toolkit.Avalonia/ContentDialogHandler.cs index 18c20d8..1f340c5 100644 --- a/Toolkit.Avalonia/ContentDialogHandler.cs +++ b/Toolkit.Avalonia/ContentDialogHandler.cs @@ -79,7 +79,7 @@ public class ContentDialogHandler(IDispatcher dispatcher) : async void DeactivateHandler(object? sender, EventArgs args) { deactivatable.DeactivateHandler -= DeactivateHandler; - await dispatcher.InvokeAsync(contentDialog.Hide); + await dispatcher.Invoke(contentDialog.Hide); } deactivatable.DeactivateHandler += DeactivateHandler; diff --git a/Toolkit.Foundation/AesDecryptor.cs b/Toolkit.Foundation/AesDecryptor.cs index 5ef8653..c988943 100644 --- a/Toolkit.Foundation/AesDecryptor.cs +++ b/Toolkit.Foundation/AesDecryptor.cs @@ -7,22 +7,44 @@ public class AesDecryptor : { private const int IvSize = 16; - public byte[] Decrypt(byte[] cipher, byte[] key) + public bool TryDecrypt(byte[] cipher, + byte[] key, + out byte[]? decryptedData) { - Span iv = cipher.AsSpan(0, IvSize); - ReadOnlySpan encryptedContent = cipher.AsSpan(IvSize); + decryptedData = null; - using Aes aes = Aes.Create(); - aes.Key = key; - aes.IV = iv.ToArray(); + if (cipher is null || key is null || cipher.Length < IvSize) + { + return false; + } - using MemoryStream memoryStream = new(encryptedContent.ToArray()); - using ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV); - using CryptoStream cryptoStream = new(memoryStream, decryptor, CryptoStreamMode.Read); + try + { + Span iv = cipher.AsSpan(0, IvSize); + ReadOnlySpan encryptedContent = cipher.AsSpan(IvSize); - using MemoryStream resultStream = new(); - cryptoStream.CopyTo(resultStream); + using Aes aes = Aes.Create(); + aes.Key = key; + aes.IV = iv.ToArray(); - return resultStream.ToArray(); + using MemoryStream memoryStream = new(encryptedContent.ToArray()); + using ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV); + using CryptoStream cryptoStream = new(memoryStream, decryptor, CryptoStreamMode.Read); + + using MemoryStream resultStream = new(); + cryptoStream.CopyTo(resultStream); + + decryptedData = resultStream.ToArray(); + + return true; + } + catch (CryptographicException) + { + return false; + } + catch (Exception) + { + return false; + } } } diff --git a/Toolkit.Foundation/AesEncryptor.cs b/Toolkit.Foundation/AesEncryptor.cs index 9e844d3..08a3996 100644 --- a/Toolkit.Foundation/AesEncryptor.cs +++ b/Toolkit.Foundation/AesEncryptor.cs @@ -7,28 +7,43 @@ public class AesEncryptor : { private const int IvSize = 16; - public byte[] Encrypt(byte[] data, - byte[] key) + public bool TryEncrypt(byte[] data, + byte[] key, + out byte[]? encryptedData) { - if (key.Length != 32) + encryptedData = null; + + if (data is null || key is null || key.Length != 32) { - throw new ArgumentException("Key must be 256 bits (32 bytes)."); + return false; } - using Aes aes = Aes.Create(); - aes.Key = key; - aes.GenerateIV(); - - using MemoryStream memoryStream = new(); - memoryStream.Write(aes.IV.AsSpan(0, IvSize)); - - using (ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV)) - using (CryptoStream cryptoStream = new(memoryStream, encryptor, CryptoStreamMode.Write)) + try { - cryptoStream.Write(data, 0, data.Length); - cryptoStream.FlushFinalBlock(); - } + using Aes aes = Aes.Create(); + aes.Key = key; + aes.GenerateIV(); - return memoryStream.ToArray(); + using MemoryStream memoryStream = new(); + memoryStream.Write(aes.IV, 0, IvSize); + + using (ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV)) + using (CryptoStream cryptoStream = new(memoryStream, encryptor, CryptoStreamMode.Write)) + { + cryptoStream.Write(data, 0, data.Length); + cryptoStream.FlushFinalBlock(); + } + + encryptedData = memoryStream.ToArray(); + return true; + } + catch (CryptographicException) + { + return false; + } + catch (Exception) + { + return false; + } } } diff --git a/Toolkit.Foundation/IDecryptor.cs b/Toolkit.Foundation/IDecryptor.cs index 0a618f7..70c47c1 100644 --- a/Toolkit.Foundation/IDecryptor.cs +++ b/Toolkit.Foundation/IDecryptor.cs @@ -1,6 +1,7 @@ namespace Toolkit.Foundation; public interface IDecryptor { - byte[] Decrypt(byte[] cipher, - byte[] key); + bool TryDecrypt(byte[] cipher, + byte[] key, + out byte[]? decryptedData); } \ No newline at end of file diff --git a/Toolkit.Foundation/IDispatcher.cs b/Toolkit.Foundation/IDispatcher.cs index 7a4f6ce..86790e5 100644 --- a/Toolkit.Foundation/IDispatcher.cs +++ b/Toolkit.Foundation/IDispatcher.cs @@ -2,5 +2,5 @@ public interface IDispatcher { - Task InvokeAsync(Action action); + Task Invoke(Action action); } \ No newline at end of file diff --git a/Toolkit.Foundation/IEncryptor.cs b/Toolkit.Foundation/IEncryptor.cs index fc37690..755d3c7 100644 --- a/Toolkit.Foundation/IEncryptor.cs +++ b/Toolkit.Foundation/IEncryptor.cs @@ -1,5 +1,8 @@ namespace Toolkit.Foundation; + public interface IEncryptor { - byte[] Encrypt(byte[] data, byte[] key); + bool TryEncrypt(byte[] data, + byte[] key, + out byte[]? encryptedData); } \ No newline at end of file diff --git a/Toolkit.Foundation/ObservableCollectionViewModel.cs b/Toolkit.Foundation/ObservableCollectionViewModel.cs index 569f831..4ebc292 100644 --- a/Toolkit.Foundation/ObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/ObservableCollectionViewModel.cs @@ -412,6 +412,17 @@ public partial class ObservableCollectionViewModel : CollectionChanged?.Invoke(this, args); } +public partial class ObservableCollectionViewModel(IServiceProvider provider, + IServiceFactory factory, + IMediator mediator, + IPublisher publisher, + ISubscriber subscriber, IDisposer disposer) : ObservableCollectionViewModel(provider, factory, mediator, publisher, subscriber, disposer) + where TViewModel : notnull +{ + [ObservableProperty] + private TValue? value; +} + public class ObservableCollectionViewModel(IServiceProvider provider, IServiceFactory factory, IMediator mediator, diff --git a/Toolkit.Foundation/Publisher.cs b/Toolkit.Foundation/Publisher.cs index ac17c94..6f1ce5a 100644 --- a/Toolkit.Foundation/Publisher.cs +++ b/Toolkit.Foundation/Publisher.cs @@ -73,29 +73,29 @@ public class Publisher(ISubscriptionManager subscriptionManager, public Task PublishUI(object key, CancellationToken cancellationToken = default) where TMessage : new() => - Publish(new TMessage(), args => dispatcher.InvokeAsync(async () => await args()), + Publish(new TMessage(), args => dispatcher.Invoke(async () => await args()), key, cancellationToken); public Task PublishUI(TMessage message, CancellationToken cancellationToken = default) where TMessage : notnull => - Publish(message, args => dispatcher.InvokeAsync(async () => await args()), + Publish(message, args => dispatcher.Invoke(async () => await args()), null, cancellationToken); public Task PublishUI(TMessage message, object key, CancellationToken cancellationToken = default) where TMessage : notnull => - Publish(message, args => dispatcher.InvokeAsync(async () => await args()), + Publish(message, args => dispatcher.Invoke(async () => await args()), key, cancellationToken); public Task PublishUI(CancellationToken cancellationToken = default) where TMessage : new() => - Publish(new TMessage(), args => dispatcher.InvokeAsync(async () => await args()), + Publish(new TMessage(), args => dispatcher.Invoke(async () => await args()), null, cancellationToken); public Task PublishUI(object message, CancellationToken cancellationToken = default) => Publish(message, args => - dispatcher.InvokeAsync(async () => await args()), + dispatcher.Invoke(async () => await args()), null, cancellationToken); } \ No newline at end of file From fe07d9e99469c1b0cbaf1d2cb248ce0ef6de5dd9 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sat, 4 May 2024 15:13:46 +0100 Subject: [PATCH 021/228] WIP --- Toolkit.UI.Avalonia/NavigateAction.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Toolkit.UI.Avalonia/NavigateAction.cs b/Toolkit.UI.Avalonia/NavigateAction.cs index eb1ce4c..9cb4ce3 100644 --- a/Toolkit.UI.Avalonia/NavigateAction.cs +++ b/Toolkit.UI.Avalonia/NavigateAction.cs @@ -74,8 +74,7 @@ public class NavigateAction : ParameterBindings.Select(binding => new KeyValuePair(binding.Key, binding.Value)).ToArray() : Enumerable.Empty>()]; - observableViewModel.Publisher.Publish(new Navigate(Route, Context - ?? null, Scope ?? null, control.DataContext, Navigated, parameters)); + observableViewModel.Publisher.Publish(new Navigate(Route, Context == this ? control : Context, Scope ?? null, control.DataContext, Navigated, parameters)); } } From 53efbb13c5feb2c78f04f182a3574a36404e7412 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sat, 4 May 2024 22:35:12 +0100 Subject: [PATCH 022/228] UI --- Toolkit.Foundation/IProvider.cs | 5 ++ .../ObservableCollectionViewModel.cs | 2 +- ...tachedBehavior.cs => AttachedBehaviour.cs} | 4 +- ...avior.cs => KeyBindingTriggerBehaviour.cs} | 6 +- .../NavigationViewItemInvokedBehaviour.cs | 86 +++++++++++++++++++ .../SelectNavigationViewItemAction.cs | 48 +++++++++++ .../Toolkit.UI.Avalonia.csproj | 1 + 7 files changed, 146 insertions(+), 6 deletions(-) rename Toolkit.UI.Avalonia/{AttachedBehavior.cs => AttachedBehaviour.cs} (85%) rename Toolkit.UI.Avalonia/{KeyBindingTriggerBehavior.cs => KeyBindingTriggerBehaviour.cs} (83%) create mode 100644 Toolkit.UI.Avalonia/NavigationViewItemInvokedBehaviour.cs create mode 100644 Toolkit.UI.Avalonia/SelectNavigationViewItemAction.cs diff --git a/Toolkit.Foundation/IProvider.cs b/Toolkit.Foundation/IProvider.cs index ce8113b..82beb4d 100644 --- a/Toolkit.Foundation/IProvider.cs +++ b/Toolkit.Foundation/IProvider.cs @@ -8,4 +8,9 @@ public interface IProvider public interface IProvider { TService? Get(); +} + +public interface ISelectable +{ + bool Selected { get; set; } } \ No newline at end of file diff --git a/Toolkit.Foundation/ObservableCollectionViewModel.cs b/Toolkit.Foundation/ObservableCollectionViewModel.cs index 4ebc292..7aaa905 100644 --- a/Toolkit.Foundation/ObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/ObservableCollectionViewModel.cs @@ -97,7 +97,7 @@ public partial class ObservableCollectionViewModel : public TViewModel this[int index] { - get => collection[index]; + get => Count > 0 ? collection[index] : default; set => SetItem(index, value); } diff --git a/Toolkit.UI.Avalonia/AttachedBehavior.cs b/Toolkit.UI.Avalonia/AttachedBehaviour.cs similarity index 85% rename from Toolkit.UI.Avalonia/AttachedBehavior.cs rename to Toolkit.UI.Avalonia/AttachedBehaviour.cs index d9d61ae..b343d5c 100644 --- a/Toolkit.UI.Avalonia/AttachedBehavior.cs +++ b/Toolkit.UI.Avalonia/AttachedBehaviour.cs @@ -2,11 +2,11 @@ namespace Toolkit.UI.Avalonia; -public class AttachedBehavior : Trigger +public class AttachedBehaviour : Trigger { protected override void OnAttachedToVisualTree() { Interaction.ExecuteActions(AssociatedObject, Actions, null); base.OnAttachedToVisualTree(); } -} \ No newline at end of file +} diff --git a/Toolkit.UI.Avalonia/KeyBindingTriggerBehavior.cs b/Toolkit.UI.Avalonia/KeyBindingTriggerBehaviour.cs similarity index 83% rename from Toolkit.UI.Avalonia/KeyBindingTriggerBehavior.cs rename to Toolkit.UI.Avalonia/KeyBindingTriggerBehaviour.cs index 50c0775..8946c24 100644 --- a/Toolkit.UI.Avalonia/KeyBindingTriggerBehavior.cs +++ b/Toolkit.UI.Avalonia/KeyBindingTriggerBehaviour.cs @@ -5,12 +5,12 @@ using System.Windows.Input; namespace Toolkit.UI.Avalonia; -public class KeyBindingTriggerBehavior : +public class KeyBindingTriggerBehaviour : Trigger, ICommand { public static readonly StyledProperty GestureProperty = - AvaloniaProperty.Register(nameof(Gesture)); + AvaloniaProperty.Register(nameof(Gesture)); public KeyGesture Gesture { @@ -24,7 +24,7 @@ public class KeyBindingTriggerBehavior : { if (Gesture is not null) { - KeyBinding keyBinding = new KeyBinding + KeyBinding keyBinding = new() { Gesture = Gesture, Command = this diff --git a/Toolkit.UI.Avalonia/NavigationViewItemInvokedBehaviour.cs b/Toolkit.UI.Avalonia/NavigationViewItemInvokedBehaviour.cs new file mode 100644 index 0000000..55e6304 --- /dev/null +++ b/Toolkit.UI.Avalonia/NavigationViewItemInvokedBehaviour.cs @@ -0,0 +1,86 @@ +using Avalonia; +using Avalonia.VisualTree; +using Avalonia.Xaml.Interactivity; +using FluentAvalonia.Core; +using System.Collections; +using Toolkit.UI.Controls.Avalonia; +using ISelectable = Toolkit.Foundation.ISelectable; + +namespace Toolkit.UI.Avalonia; + +public class NavigationViewItemInvokedBehaviour : Trigger +{ + public static readonly StyledProperty SelectsChildOnInvokedProperty = + AvaloniaProperty.Register(nameof(SelectsChildOnInvoked)); + + private NavigationView? navigationView; + + public event TypedEventHandler? Invoked; + + public bool SelectsChildOnInvoked + { + get => GetValue(SelectsChildOnInvokedProperty); + set => SetValue(SelectsChildOnInvokedProperty, value); + } + + protected override void OnAttached() + { + if (AssociatedObject is NavigationViewItem navigationViewItem) + { + navigationViewItem.AttachedToVisualTree += OnAttachedToVisualTree; + } + + base.OnAttached(); + } + + protected override void OnDetachedFromVisualTree() + { + if (navigationView is not null) + { + navigationView.ItemInvoked -= OnItemInvoked; + } + + base.OnDetachedFromVisualTree(); + } + + private void OnAttachedToVisualTree(object? sender, + VisualTreeAttachmentEventArgs args) + { + if (AssociatedObject is NavigationViewItem navigationViewItem) + { + navigationViewItem.AttachedToVisualTree -= OnAttachedToVisualTree; + SetupNavigationView(); + } + } + + private void OnItemInvoked(object? sender, + FluentAvalonia.UI.Controls.NavigationViewItemInvokedEventArgs args) + { + if (AssociatedObject is NavigationViewItem navigationViewItem) + { + if (args.InvokedItemContainer == AssociatedObject) + { + Interaction.ExecuteActions(AssociatedObject, Actions, null); + if (!navigationViewItem.IsChildSelected && navigationViewItem.MenuItemsSource is IList collection) + { + if (collection is { Count: > 0 } && collection[0] is ISelectable selectable) + { + selectable.Selected = true; + } + } + } + } + } + + private void SetupNavigationView() + { + if (AssociatedObject is NavigationViewItem navigationViewItem) + { + if (navigationViewItem.GetVisualAncestors().OfType().FirstOrDefault() is NavigationView navigationView) + { + this.navigationView = navigationView; + navigationView.ItemInvoked += OnItemInvoked; + } + } + } +} diff --git a/Toolkit.UI.Avalonia/SelectNavigationViewItemAction.cs b/Toolkit.UI.Avalonia/SelectNavigationViewItemAction.cs new file mode 100644 index 0000000..50c9fc1 --- /dev/null +++ b/Toolkit.UI.Avalonia/SelectNavigationViewItemAction.cs @@ -0,0 +1,48 @@ +using Avalonia; +using Avalonia.Threading; +using Avalonia.Xaml.Interactivity; +using System.Collections; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using Toolkit.Foundation; +using Toolkit.UI.Controls.Avalonia; + +namespace Toolkit.UI.Avalonia; + +public class SelectNavigationViewItemAction : + AvaloniaObject, + IAction +{ + public object? Execute(object? sender, object? parameter) + { + if (sender is NavigationViewItem navigationViewItem) + { + Dispatcher.UIThread.Post(() => + { + if (navigationViewItem.MenuItemsSource is IList collection) + { + if (collection is { Count: > 0 } && collection[0] is ISelectable selectable) + { + selectable.Selected = true; + } + } + }, DispatcherPriority.ContextIdle); + } + + if (sender is NavigationView navigationView) + { + Dispatcher.UIThread.Post(() => + { + if (navigationView.MenuItemsSource is IList collection) + { + if (collection is { Count: > 0 } && collection[0] is ISelectable selectable) + { + selectable.Selected = true; + } + } + }, DispatcherPriority.ContextIdle); + } + + return true; + } +} diff --git a/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj b/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj index e84927b..53a1c8c 100644 --- a/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj +++ b/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj @@ -10,5 +10,6 @@ + \ No newline at end of file From 287aa3131bcf520b5bea62b7da39e7ad5994f10c Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 5 May 2024 09:42:21 +0100 Subject: [PATCH 023/228] WIP --- .../AttachedEventTriggerBehaviour.cs | 34 ++++++++ Toolkit.UI.Avalonia/ItemInvokedEventArgs.cs | 9 ++ .../NavigationViewItemExtension.cs | 68 +++++++++++++++ .../NavigationViewItemInvokedBehaviour.cs | 86 ------------------- 4 files changed, 111 insertions(+), 86 deletions(-) create mode 100644 Toolkit.UI.Avalonia/AttachedEventTriggerBehaviour.cs create mode 100644 Toolkit.UI.Avalonia/ItemInvokedEventArgs.cs create mode 100644 Toolkit.UI.Avalonia/NavigationViewItemExtension.cs delete mode 100644 Toolkit.UI.Avalonia/NavigationViewItemInvokedBehaviour.cs diff --git a/Toolkit.UI.Avalonia/AttachedEventTriggerBehaviour.cs b/Toolkit.UI.Avalonia/AttachedEventTriggerBehaviour.cs new file mode 100644 index 0000000..8c47319 --- /dev/null +++ b/Toolkit.UI.Avalonia/AttachedEventTriggerBehaviour.cs @@ -0,0 +1,34 @@ +using Avalonia; +using Avalonia.Interactivity; +using Avalonia.Xaml.Interactivity; + +namespace Toolkit.UI.Avalonia; + +public class AttachedEventTriggerBehaviour : Trigger +{ + public static readonly StyledProperty RoutedEventProperty = + AvaloniaProperty.Register(nameof(RoutedEvent)); + + public RoutedEvent RoutedEvent + { + get => GetValue(RoutedEventProperty); + set => SetValue(RoutedEventProperty, value); + } + + protected override void OnAttached() + { + if (RoutedEvent is not null) + { + if (AssociatedObject is Interactive interactive) + { + interactive.AddHandler(RoutedEvent, (object sender, RoutedEventArgs args) => { + + Interaction.ExecuteActions(AssociatedObject, Actions, null); + + }); + } + } + + base.OnAttached(); + } +} diff --git a/Toolkit.UI.Avalonia/ItemInvokedEventArgs.cs b/Toolkit.UI.Avalonia/ItemInvokedEventArgs.cs new file mode 100644 index 0000000..d15588c --- /dev/null +++ b/Toolkit.UI.Avalonia/ItemInvokedEventArgs.cs @@ -0,0 +1,9 @@ +using Avalonia.Interactivity; + +namespace Toolkit.UI.Avalonia; + +public class ItemInvokedEventArgs : + RoutedEventArgs +{ + +} diff --git a/Toolkit.UI.Avalonia/NavigationViewItemExtension.cs b/Toolkit.UI.Avalonia/NavigationViewItemExtension.cs new file mode 100644 index 0000000..125b4f6 --- /dev/null +++ b/Toolkit.UI.Avalonia/NavigationViewItemExtension.cs @@ -0,0 +1,68 @@ +using Avalonia; +using Avalonia.Interactivity; +using Avalonia.LogicalTree; +using Toolkit.UI.Controls.Avalonia; + +namespace Toolkit.UI.Avalonia; + +public class NavigationViewItemExtension +{ + public static readonly AttachedProperty IsItemClickEnabledProperty = + AvaloniaProperty.RegisterAttached("IsItemClickEnabled", + typeof(NavigationViewItemExtension), false); + + public static readonly RoutedEvent ItemClickEvent = + RoutedEvent.Register("ItemClick", + RoutingStrategies.Bubble, typeof(NavigationViewItemExtension)); + + static NavigationViewItemExtension() + { + IsItemClickEnabledProperty.Changed.AddClassHandler(OnIsItemClickEnabledPropertyChanged); + } + + private static void OnIsItemClickEnabledPropertyChanged(NavigationViewItem sender, + AvaloniaPropertyChangedEventArgs args) + { + bool TrySetupNavigationView() + { + if (sender.GetLogicalAncestors().OfType().FirstOrDefault() is NavigationView navigationView) + { + void OnItemInvoked(object? _, FluentAvalonia.UI.Controls.NavigationViewItemInvokedEventArgs args) + { + if (args.InvokedItemContainer == sender) + { + sender.RaiseEvent(new ItemInvokedEventArgs { RoutedEvent = ItemClickEvent }); + } + } + + navigationView.ItemInvoked += OnItemInvoked; + return true; + } + + return false; + } + + if (!TrySetupNavigationView()) + { + void OnAttachedToVisualTree(object? _, VisualTreeAttachmentEventArgs __) + { + sender.AttachedToVisualTree -= OnAttachedToVisualTree; + TrySetupNavigationView(); + } + + sender.AttachedToVisualTree += OnAttachedToVisualTree; + } + } + + public static bool GetIsItemClickEnabled(NavigationViewItem element) => + element.GetValue(IsItemClickEnabledProperty); + + public static void SetIsItemClickEnabled(NavigationViewItem element, bool value) => + element.SetValue(IsItemClickEnabledProperty, value); + + public static void AddItemClickHandler(NavigationViewItem element, EventHandler handler) => + element.AddHandler(ItemClickEvent, handler); + + public static void RemoveItemClickHandler(NavigationViewItem element, EventHandler handler) => + element.RemoveHandler(ItemClickEvent, handler); +} diff --git a/Toolkit.UI.Avalonia/NavigationViewItemInvokedBehaviour.cs b/Toolkit.UI.Avalonia/NavigationViewItemInvokedBehaviour.cs deleted file mode 100644 index 55e6304..0000000 --- a/Toolkit.UI.Avalonia/NavigationViewItemInvokedBehaviour.cs +++ /dev/null @@ -1,86 +0,0 @@ -using Avalonia; -using Avalonia.VisualTree; -using Avalonia.Xaml.Interactivity; -using FluentAvalonia.Core; -using System.Collections; -using Toolkit.UI.Controls.Avalonia; -using ISelectable = Toolkit.Foundation.ISelectable; - -namespace Toolkit.UI.Avalonia; - -public class NavigationViewItemInvokedBehaviour : Trigger -{ - public static readonly StyledProperty SelectsChildOnInvokedProperty = - AvaloniaProperty.Register(nameof(SelectsChildOnInvoked)); - - private NavigationView? navigationView; - - public event TypedEventHandler? Invoked; - - public bool SelectsChildOnInvoked - { - get => GetValue(SelectsChildOnInvokedProperty); - set => SetValue(SelectsChildOnInvokedProperty, value); - } - - protected override void OnAttached() - { - if (AssociatedObject is NavigationViewItem navigationViewItem) - { - navigationViewItem.AttachedToVisualTree += OnAttachedToVisualTree; - } - - base.OnAttached(); - } - - protected override void OnDetachedFromVisualTree() - { - if (navigationView is not null) - { - navigationView.ItemInvoked -= OnItemInvoked; - } - - base.OnDetachedFromVisualTree(); - } - - private void OnAttachedToVisualTree(object? sender, - VisualTreeAttachmentEventArgs args) - { - if (AssociatedObject is NavigationViewItem navigationViewItem) - { - navigationViewItem.AttachedToVisualTree -= OnAttachedToVisualTree; - SetupNavigationView(); - } - } - - private void OnItemInvoked(object? sender, - FluentAvalonia.UI.Controls.NavigationViewItemInvokedEventArgs args) - { - if (AssociatedObject is NavigationViewItem navigationViewItem) - { - if (args.InvokedItemContainer == AssociatedObject) - { - Interaction.ExecuteActions(AssociatedObject, Actions, null); - if (!navigationViewItem.IsChildSelected && navigationViewItem.MenuItemsSource is IList collection) - { - if (collection is { Count: > 0 } && collection[0] is ISelectable selectable) - { - selectable.Selected = true; - } - } - } - } - } - - private void SetupNavigationView() - { - if (AssociatedObject is NavigationViewItem navigationViewItem) - { - if (navigationViewItem.GetVisualAncestors().OfType().FirstOrDefault() is NavigationView navigationView) - { - this.navigationView = navigationView; - navigationView.ItemInvoked += OnItemInvoked; - } - } - } -} From daf746aa86a561ec7a8fc41772c3e8f50a1dd620 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 5 May 2024 17:41:38 +0100 Subject: [PATCH 024/228] Improve vault navifaiton --- Toolkit.Avalonia/FrameHandler.cs | 3 ++- Toolkit.Foundation/Activated.cs | 3 +++ Toolkit.Foundation/Deactivated.cs | 3 +++ Toolkit.Foundation/IActivated.cs | 2 +- Toolkit.Foundation/Selected.cs | 12 ++++++++++++ ...emAction.cs => InvokeNavigationViewItemAction.cs} | 4 +--- 6 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 Toolkit.Foundation/Activated.cs create mode 100644 Toolkit.Foundation/Deactivated.cs create mode 100644 Toolkit.Foundation/Selected.cs rename Toolkit.UI.Avalonia/{SelectNavigationViewItemAction.cs => InvokeNavigationViewItemAction.cs} (91%) diff --git a/Toolkit.Avalonia/FrameHandler.cs b/Toolkit.Avalonia/FrameHandler.cs index 68b0bd8..0a38126 100644 --- a/Toolkit.Avalonia/FrameHandler.cs +++ b/Toolkit.Avalonia/FrameHandler.cs @@ -1,5 +1,6 @@ using Avalonia.Controls; using Avalonia.Controls.Primitives; +using FluentAvalonia.UI.Media.Animation; using FluentAvalonia.UI.Navigation; using System.Reflection; using Toolkit.Foundation; @@ -183,7 +184,7 @@ public class FrameHandler(INavigationContext navigationContext) : navigationContext.Set(control); NavigatedTo(args.Sender, control); - frame.NavigateFromObject(control); + frame.NavigateFromObject(control, new FrameNavigationOptions { TransitionInfoOverride = new SuppressNavigationTransitionInfo() }); } } diff --git a/Toolkit.Foundation/Activated.cs b/Toolkit.Foundation/Activated.cs new file mode 100644 index 0000000..ca3d61c --- /dev/null +++ b/Toolkit.Foundation/Activated.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record Activated; \ No newline at end of file diff --git a/Toolkit.Foundation/Deactivated.cs b/Toolkit.Foundation/Deactivated.cs new file mode 100644 index 0000000..5dc4ca6 --- /dev/null +++ b/Toolkit.Foundation/Deactivated.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record Deactivated; \ No newline at end of file diff --git a/Toolkit.Foundation/IActivated.cs b/Toolkit.Foundation/IActivated.cs index 224ec70..079d435 100644 --- a/Toolkit.Foundation/IActivated.cs +++ b/Toolkit.Foundation/IActivated.cs @@ -8,4 +8,4 @@ public interface IActivated public interface IActivated { Task Activated(TResult result); -} \ No newline at end of file +} diff --git a/Toolkit.Foundation/Selected.cs b/Toolkit.Foundation/Selected.cs new file mode 100644 index 0000000..da3d5e7 --- /dev/null +++ b/Toolkit.Foundation/Selected.cs @@ -0,0 +1,12 @@ +namespace Toolkit.Foundation; + +public record Selected(TValue Value); + +public record Selected +{ + public static Selected As(TValue value) => + new(value); + + public static Selected As() where TValue : new() => + new(new TValue()); +} \ No newline at end of file diff --git a/Toolkit.UI.Avalonia/SelectNavigationViewItemAction.cs b/Toolkit.UI.Avalonia/InvokeNavigationViewItemAction.cs similarity index 91% rename from Toolkit.UI.Avalonia/SelectNavigationViewItemAction.cs rename to Toolkit.UI.Avalonia/InvokeNavigationViewItemAction.cs index 50c9fc1..db8f831 100644 --- a/Toolkit.UI.Avalonia/SelectNavigationViewItemAction.cs +++ b/Toolkit.UI.Avalonia/InvokeNavigationViewItemAction.cs @@ -2,14 +2,12 @@ using Avalonia.Threading; using Avalonia.Xaml.Interactivity; using System.Collections; -using System.Collections.ObjectModel; -using System.Collections.Specialized; using Toolkit.Foundation; using Toolkit.UI.Controls.Avalonia; namespace Toolkit.UI.Avalonia; -public class SelectNavigationViewItemAction : +public class InvokeNavigationViewItemAction : AvaloniaObject, IAction { From 40845bb5b3246d87f367a5237a9f5dd44cc8abba Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 5 May 2024 20:43:17 +0100 Subject: [PATCH 025/228] Make it possible to feed in options to the Enumerate handler --- Toolkit.Foundation/Enumerate.cs | 22 ++++++++++++++++++- .../ObservableCollectionViewModel.cs | 17 ++++++++++---- Toolkit.Foundation/Selected.cs | 2 +- Toolkit.Foundation/SubscriptionManager.cs | 1 + 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/Toolkit.Foundation/Enumerate.cs b/Toolkit.Foundation/Enumerate.cs index 63aebe8..1f46636 100644 --- a/Toolkit.Foundation/Enumerate.cs +++ b/Toolkit.Foundation/Enumerate.cs @@ -1,3 +1,23 @@ namespace Toolkit.Foundation; -public record Enumerate(object? Key = null); \ No newline at end of file +public record Enumerate : IEnumerate +{ + public object? Key { get; init; } + + public static Enumerate With(TOptions options) where TOptions : class + { + return new Enumerate(options); + } +} + +public interface IEnumerate +{ + object? Key { get; init; } +} + + +public record Enumerate(TOptions? Options = null) : IEnumerate + where TOptions : class +{ + public object? Key { get; init; } +} \ No newline at end of file diff --git a/Toolkit.Foundation/ObservableCollectionViewModel.cs b/Toolkit.Foundation/ObservableCollectionViewModel.cs index 7aaa905..286da70 100644 --- a/Toolkit.Foundation/ObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/ObservableCollectionViewModel.cs @@ -97,7 +97,7 @@ public partial class ObservableCollectionViewModel : public TViewModel this[int index] { - get => Count > 0 ? collection[index] : default; + get => collection[index]; set => SetItem(index, value); } @@ -299,21 +299,30 @@ public partial class ObservableCollectionViewModel : public async Task Initialize() { - if (isInitialized) + if (IsInitialized) { return; } - isInitialized = true; + IsInitialized = true; + await Enumerate(); + } + + public async Task Enumerate() + { + Clear(); object? key = this.GetAttribute() is NotificationAttribute attribute ? this.GetPropertyValue(() => attribute.Key) is { } value ? value : attribute.Key : null; - await Publisher.PublishUI(new Enumerate(key)); + await Publisher.PublishUI(CreateEnumeration(key)); } + protected virtual IEnumerate CreateEnumeration(object? key) => + new Enumerate() with { Key = key }; + public void Insert(int index, TViewModel item) => InsertItem(index, item); diff --git a/Toolkit.Foundation/Selected.cs b/Toolkit.Foundation/Selected.cs index da3d5e7..40a7e22 100644 --- a/Toolkit.Foundation/Selected.cs +++ b/Toolkit.Foundation/Selected.cs @@ -1,6 +1,6 @@ namespace Toolkit.Foundation; -public record Selected(TValue Value); +public record Selected(TValue? Value); public record Selected { diff --git a/Toolkit.Foundation/SubscriptionManager.cs b/Toolkit.Foundation/SubscriptionManager.cs index d6f92a5..a8aea60 100644 --- a/Toolkit.Foundation/SubscriptionManager.cs +++ b/Toolkit.Foundation/SubscriptionManager.cs @@ -5,6 +5,7 @@ public class SubscriptionManager(SubscriptionCollection subscriptions) : { public IEnumerable GetHandlers(Type notificationType, object key) { + var d = subscriptions; if (subscriptions.TryGetValue($"{(key is not null ? $"{key}:" : "")}{notificationType}", out List? subscribers)) { From ff8e97f03055701ca91b59918dca156a57baaefc Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Mon, 6 May 2024 21:17:20 +0100 Subject: [PATCH 026/228] wip --- Toolkit.Avalonia/Toolkit.Avalonia.csproj | 2 +- Toolkit.Foundation/Enumerate.cs | 16 ++-- Toolkit.Foundation/EnumerateMode.cs | 7 ++ Toolkit.Foundation/IEnumerate.cs | 8 ++ Toolkit.Foundation/NotificationAttribute.cs | 8 +- .../ObservableCollectionViewModel.cs | 18 +++-- Toolkit.Foundation/ObservableViewModel.cs | 12 +++ Toolkit.Foundation/SubscriptionManager.cs | 12 ++- .../NavigationViewItemExtension.cs | 76 +++++++++++++++++++ .../Toolkit.UI.Avalonia.csproj | 4 +- .../Themes/ThemeResources.axaml | 1 - .../Themes/ThemeResources.axaml.cs | 5 +- .../Toolkit.UI.Controls.Avalonia.csproj | 2 +- 13 files changed, 145 insertions(+), 26 deletions(-) create mode 100644 Toolkit.Foundation/EnumerateMode.cs create mode 100644 Toolkit.Foundation/IEnumerate.cs diff --git a/Toolkit.Avalonia/Toolkit.Avalonia.csproj b/Toolkit.Avalonia/Toolkit.Avalonia.csproj index 2670c0f..a972a43 100644 --- a/Toolkit.Avalonia/Toolkit.Avalonia.csproj +++ b/Toolkit.Avalonia/Toolkit.Avalonia.csproj @@ -5,7 +5,7 @@ enable - + diff --git a/Toolkit.Foundation/Enumerate.cs b/Toolkit.Foundation/Enumerate.cs index 1f46636..8c54650 100644 --- a/Toolkit.Foundation/Enumerate.cs +++ b/Toolkit.Foundation/Enumerate.cs @@ -1,23 +1,23 @@ namespace Toolkit.Foundation; -public record Enumerate : IEnumerate +public record Enumerate : + IEnumerate { public object? Key { get; init; } + public EnumerateMode Mode { get; init; } + public static Enumerate With(TOptions options) where TOptions : class { return new Enumerate(options); } } -public interface IEnumerate -{ - object? Key { get; init; } -} - - -public record Enumerate(TOptions? Options = null) : IEnumerate +public record Enumerate(TOptions? Options = null) : + IEnumerate where TOptions : class { public object? Key { get; init; } + + public EnumerateMode Mode { get; init; } } \ No newline at end of file diff --git a/Toolkit.Foundation/EnumerateMode.cs b/Toolkit.Foundation/EnumerateMode.cs new file mode 100644 index 0000000..1827b0a --- /dev/null +++ b/Toolkit.Foundation/EnumerateMode.cs @@ -0,0 +1,7 @@ +namespace Toolkit.Foundation; + +public enum EnumerateMode +{ + Append, + Reset +} diff --git a/Toolkit.Foundation/IEnumerate.cs b/Toolkit.Foundation/IEnumerate.cs new file mode 100644 index 0000000..88782ca --- /dev/null +++ b/Toolkit.Foundation/IEnumerate.cs @@ -0,0 +1,8 @@ +namespace Toolkit.Foundation; + +public interface IEnumerate +{ + object? Key { get; init; } + + EnumerateMode Mode { get; init; } +} diff --git a/Toolkit.Foundation/NotificationAttribute.cs b/Toolkit.Foundation/NotificationAttribute.cs index d178ddd..cf48a5f 100644 --- a/Toolkit.Foundation/NotificationAttribute.cs +++ b/Toolkit.Foundation/NotificationAttribute.cs @@ -1,7 +1,13 @@ namespace Toolkit.Foundation; -[AttributeUsage(AttributeTargets.Class, Inherited = false)] +[AttributeUsage(AttributeTargets.Class, Inherited = true)] public class NotificationAttribute(object key) : Attribute { public object Key => key; +} + +public class EnumerateAttribute(object key, + EnumerateMode mode = EnumerateMode.Reset) : NotificationAttribute(key) +{ + public EnumerateMode Mode => mode; } \ No newline at end of file diff --git a/Toolkit.Foundation/ObservableCollectionViewModel.cs b/Toolkit.Foundation/ObservableCollectionViewModel.cs index 286da70..ff1748d 100644 --- a/Toolkit.Foundation/ObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/ObservableCollectionViewModel.cs @@ -310,17 +310,19 @@ public partial class ObservableCollectionViewModel : public async Task Enumerate() { - Clear(); + if (this.GetAttribute() is EnumerateAttribute attribute) + { + if (attribute.Mode == EnumerateMode.Reset) + { + Clear(); + } - object? key = this.GetAttribute() - is NotificationAttribute attribute - ? this.GetPropertyValue(() => attribute.Key) is { } value ? value : attribute.Key - : null; - - await Publisher.PublishUI(CreateEnumeration(key)); + object? key = this.GetPropertyValue(() => attribute.Key) is { } value ? value : attribute.Key; + await Publisher.PublishUI(PrepareEnumeration(key)); + } } - protected virtual IEnumerate CreateEnumeration(object? key) => + protected virtual IEnumerate PrepareEnumeration(object? key) => new Enumerate() with { Key = key }; public void Insert(int index, TViewModel item) => diff --git a/Toolkit.Foundation/ObservableViewModel.cs b/Toolkit.Foundation/ObservableViewModel.cs index 60b3585..47a45b0 100644 --- a/Toolkit.Foundation/ObservableViewModel.cs +++ b/Toolkit.Foundation/ObservableViewModel.cs @@ -73,4 +73,16 @@ public partial class ObservableViewModel : IsInitialized = true; return Task.CompletedTask; } +} + +public partial class ObservableViewModel(IServiceProvider provider, + IServiceFactory factory, + IMediator mediator, + IPublisher publisher, + ISubscriber subscriber, + IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) + where TValue : notnull +{ + [ObservableProperty] + private TValue? value; } \ No newline at end of file diff --git a/Toolkit.Foundation/SubscriptionManager.cs b/Toolkit.Foundation/SubscriptionManager.cs index a8aea60..2310df9 100644 --- a/Toolkit.Foundation/SubscriptionManager.cs +++ b/Toolkit.Foundation/SubscriptionManager.cs @@ -54,7 +54,17 @@ public class SubscriptionManager(SubscriptionCollection subscriptions) : { if (interfaceType.GetGenericArguments().FirstOrDefault() is Type argumentType) { - subscriptions.AddOrUpdate($"{(key is not null ? $"{key}:" : "")}{argumentType}", _ => new List { new(subscriber) }, (_, collection) => + if (key is not null) + { + subscriptions.AddOrUpdate($"{key}:{argumentType}", _ => new List { new(subscriber) }, (_, collection) => + { + collection.Add(new WeakReference(subscriber)); + return collection; + }); + + } + + subscriptions.AddOrUpdate($"{argumentType}", _ => new List { new(subscriber) }, (_, collection) => { collection.Add(new WeakReference(subscriber)); return collection; diff --git a/Toolkit.UI.Avalonia/NavigationViewItemExtension.cs b/Toolkit.UI.Avalonia/NavigationViewItemExtension.cs index 125b4f6..140a041 100644 --- a/Toolkit.UI.Avalonia/NavigationViewItemExtension.cs +++ b/Toolkit.UI.Avalonia/NavigationViewItemExtension.cs @@ -1,10 +1,86 @@ using Avalonia; +using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.LogicalTree; using Toolkit.UI.Controls.Avalonia; namespace Toolkit.UI.Avalonia; +public class ListBoxItemExtension +{ + public static readonly AttachedProperty IsItemClickEnabledProperty = + AvaloniaProperty.RegisterAttached("IsItemClickEnabled", + typeof(ListBoxItemExtension), false); + + public static readonly RoutedEvent ItemClickEvent = + RoutedEvent.Register("ItemClick", + RoutingStrategies.Bubble, typeof(ListBoxItemExtension)); + + static ListBoxItemExtension() + { + IsItemClickEnabledProperty.Changed.AddClassHandler(OnIsItemClickEnabledPropertyChanged); + } + + private static void OnIsItemClickEnabledPropertyChanged(ListBoxItem sender, + AvaloniaPropertyChangedEventArgs args) + { + bool TrySetupListBox() + { + if (sender.GetLogicalAncestors().OfType().FirstOrDefault() is ListBox listBox) + { + void OnItemInvoked(object? _, FluentAvalonia.UI.Controls.NavigationViewItemInvokedEventArgs args) + { + if (args.InvokedItemContainer == sender) + { + sender.RaiseEvent(new ItemInvokedEventArgs { RoutedEvent = ItemClickEvent }); + } + } + + void OnSelectionChanged(object? sender, SelectionChangedEventArgs args) + { + foreach (object item in args.AddedItems) + { + if (listBox.ContainerFromItem(item) == sender) + { + + } + } + } + + + listBox.SelectionChanged += OnSelectionChanged; + return true; + } + + return false; + } + + if (!TrySetupListBox()) + { + void OnAttachedToVisualTree(object? _, VisualTreeAttachmentEventArgs __) + { + sender.AttachedToVisualTree -= OnAttachedToVisualTree; + TrySetupListBox(); + } + + sender.AttachedToVisualTree += OnAttachedToVisualTree; + } + } + + public static bool GetIsItemClickEnabled(ListBoxItem element) => + element.GetValue(IsItemClickEnabledProperty); + + public static void SetIsItemClickEnabled(ListBoxItem element, bool value) => + element.SetValue(IsItemClickEnabledProperty, value); + + public static void AddItemClickHandler(ListBoxItem element, EventHandler handler) => + element.AddHandler(ItemClickEvent, handler); + + public static void RemoveItemClickHandler(ListBoxItem element, EventHandler handler) => + element.RemoveHandler(ItemClickEvent, handler); +} + + public class NavigationViewItemExtension { public static readonly AttachedProperty IsItemClickEnabledProperty = diff --git a/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj b/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj index 53a1c8c..3519c81 100644 --- a/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj +++ b/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj @@ -5,8 +5,8 @@ enable - - + + diff --git a/Toolkit.UI.Controls.Avalonia/Themes/ThemeResources.axaml b/Toolkit.UI.Controls.Avalonia/Themes/ThemeResources.axaml index 4d4bdf6..d8cdb09 100644 --- a/Toolkit.UI.Controls.Avalonia/Themes/ThemeResources.axaml +++ b/Toolkit.UI.Controls.Avalonia/Themes/ThemeResources.axaml @@ -4,7 +4,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:fluent="using:FluentAvalonia.Styling" xmlns:labs="using:Avalonia.Labs.Controls"> - diff --git a/Toolkit.UI.Controls.Avalonia/Themes/ThemeResources.axaml.cs b/Toolkit.UI.Controls.Avalonia/Themes/ThemeResources.axaml.cs index 2df8fe9..632cee3 100644 --- a/Toolkit.UI.Controls.Avalonia/Themes/ThemeResources.axaml.cs +++ b/Toolkit.UI.Controls.Avalonia/Themes/ThemeResources.axaml.cs @@ -1,10 +1,9 @@ using Avalonia.Markup.Xaml; -using Avalonia.Styling; +using FluentAvalonia.Styling; namespace Toolkit.UI.Controls.Avalonia; -public class ThemeResources : - Styles +public class ThemeResources : FluentAvaloniaTheme { public ThemeResources(IServiceProvider? provider = null) { diff --git a/Toolkit.UI.Controls.Avalonia/Toolkit.UI.Controls.Avalonia.csproj b/Toolkit.UI.Controls.Avalonia/Toolkit.UI.Controls.Avalonia.csproj index 4671d61..b0fb715 100644 --- a/Toolkit.UI.Controls.Avalonia/Toolkit.UI.Controls.Avalonia.csproj +++ b/Toolkit.UI.Controls.Avalonia/Toolkit.UI.Controls.Avalonia.csproj @@ -6,7 +6,7 @@ true - + From 003976c0ab35e56b79551375bde4a4b77c200a34 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Mon, 6 May 2024 21:57:03 +0100 Subject: [PATCH 027/228] Add vault content navigation --- Toolkit.Avalonia/Toolkit.Avalonia.csproj | 2 +- .../NavigationViewItemExtension.cs | 76 ------------------- .../Toolkit.UI.Avalonia.csproj | 2 +- .../Toolkit.UI.Controls.Avalonia.csproj | 2 +- 4 files changed, 3 insertions(+), 79 deletions(-) diff --git a/Toolkit.Avalonia/Toolkit.Avalonia.csproj b/Toolkit.Avalonia/Toolkit.Avalonia.csproj index a972a43..795e2a3 100644 --- a/Toolkit.Avalonia/Toolkit.Avalonia.csproj +++ b/Toolkit.Avalonia/Toolkit.Avalonia.csproj @@ -5,7 +5,7 @@ enable - + diff --git a/Toolkit.UI.Avalonia/NavigationViewItemExtension.cs b/Toolkit.UI.Avalonia/NavigationViewItemExtension.cs index 140a041..125b4f6 100644 --- a/Toolkit.UI.Avalonia/NavigationViewItemExtension.cs +++ b/Toolkit.UI.Avalonia/NavigationViewItemExtension.cs @@ -1,86 +1,10 @@ using Avalonia; -using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.LogicalTree; using Toolkit.UI.Controls.Avalonia; namespace Toolkit.UI.Avalonia; -public class ListBoxItemExtension -{ - public static readonly AttachedProperty IsItemClickEnabledProperty = - AvaloniaProperty.RegisterAttached("IsItemClickEnabled", - typeof(ListBoxItemExtension), false); - - public static readonly RoutedEvent ItemClickEvent = - RoutedEvent.Register("ItemClick", - RoutingStrategies.Bubble, typeof(ListBoxItemExtension)); - - static ListBoxItemExtension() - { - IsItemClickEnabledProperty.Changed.AddClassHandler(OnIsItemClickEnabledPropertyChanged); - } - - private static void OnIsItemClickEnabledPropertyChanged(ListBoxItem sender, - AvaloniaPropertyChangedEventArgs args) - { - bool TrySetupListBox() - { - if (sender.GetLogicalAncestors().OfType().FirstOrDefault() is ListBox listBox) - { - void OnItemInvoked(object? _, FluentAvalonia.UI.Controls.NavigationViewItemInvokedEventArgs args) - { - if (args.InvokedItemContainer == sender) - { - sender.RaiseEvent(new ItemInvokedEventArgs { RoutedEvent = ItemClickEvent }); - } - } - - void OnSelectionChanged(object? sender, SelectionChangedEventArgs args) - { - foreach (object item in args.AddedItems) - { - if (listBox.ContainerFromItem(item) == sender) - { - - } - } - } - - - listBox.SelectionChanged += OnSelectionChanged; - return true; - } - - return false; - } - - if (!TrySetupListBox()) - { - void OnAttachedToVisualTree(object? _, VisualTreeAttachmentEventArgs __) - { - sender.AttachedToVisualTree -= OnAttachedToVisualTree; - TrySetupListBox(); - } - - sender.AttachedToVisualTree += OnAttachedToVisualTree; - } - } - - public static bool GetIsItemClickEnabled(ListBoxItem element) => - element.GetValue(IsItemClickEnabledProperty); - - public static void SetIsItemClickEnabled(ListBoxItem element, bool value) => - element.SetValue(IsItemClickEnabledProperty, value); - - public static void AddItemClickHandler(ListBoxItem element, EventHandler handler) => - element.AddHandler(ItemClickEvent, handler); - - public static void RemoveItemClickHandler(ListBoxItem element, EventHandler handler) => - element.RemoveHandler(ItemClickEvent, handler); -} - - public class NavigationViewItemExtension { public static readonly AttachedProperty IsItemClickEnabledProperty = diff --git a/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj b/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj index 3519c81..7753f8f 100644 --- a/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj +++ b/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj @@ -5,7 +5,7 @@ enable - + diff --git a/Toolkit.UI.Controls.Avalonia/Toolkit.UI.Controls.Avalonia.csproj b/Toolkit.UI.Controls.Avalonia/Toolkit.UI.Controls.Avalonia.csproj index b0fb715..1363073 100644 --- a/Toolkit.UI.Controls.Avalonia/Toolkit.UI.Controls.Avalonia.csproj +++ b/Toolkit.UI.Controls.Avalonia/Toolkit.UI.Controls.Avalonia.csproj @@ -6,7 +6,7 @@ true - + From 3007f14d978557f51b9798b12c27f18897563220 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Wed, 8 May 2024 22:01:33 +0100 Subject: [PATCH 028/228] wip --- .../ResponsiveGrid/ResponsiveGrid.cs | 330 ++++++++++++++++++ .../ResponsiveGrid/SizeThresholds.cs | 36 ++ .../SizeThresholdsTypeConverter.cs | 44 +++ 3 files changed, 410 insertions(+) create mode 100644 Toolkit.UI.Controls.Avalonia/ResponsiveGrid/ResponsiveGrid.cs create mode 100644 Toolkit.UI.Controls.Avalonia/ResponsiveGrid/SizeThresholds.cs create mode 100644 Toolkit.UI.Controls.Avalonia/ResponsiveGrid/SizeThresholdsTypeConverter.cs diff --git a/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/ResponsiveGrid.cs b/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/ResponsiveGrid.cs new file mode 100644 index 0000000..cbe92ea --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/ResponsiveGrid.cs @@ -0,0 +1,330 @@ +using Avalonia; +using Avalonia.Controls; + +namespace Toolkit.UI.Controls.Avalonia +{ + public class ResponsiveGrid : Grid + { + public static readonly AvaloniaProperty ActualColumnProperty = + AvaloniaProperty.RegisterAttached("ActualColumn", 0); + + public static readonly AvaloniaProperty ActualRowProperty = + AvaloniaProperty.RegisterAttached("ActualRow", 0); + + public static readonly StyledProperty BreakPointsProperty = + AvaloniaProperty.Register(nameof(Thresholds)); + + public static readonly AvaloniaProperty LargeOffsetProperty = + AvaloniaProperty.RegisterAttached("LargeOffset", 0); + + public static readonly AvaloniaProperty LargePullProperty = + AvaloniaProperty.RegisterAttached("LargePull", 0); + + public static readonly AvaloniaProperty LargePushProperty = + AvaloniaProperty.RegisterAttached("LargePush", 0); + + public static readonly AvaloniaProperty LargeProperty = + AvaloniaProperty.RegisterAttached("Large", 0); + + public static readonly StyledProperty MaxDivisionProperty = + AvaloniaProperty.Register(nameof(MaxDivision), 12); + + public static readonly AvaloniaProperty MediumOffsetProperty = + AvaloniaProperty.RegisterAttached("MediumOffset", 0); + + public static readonly AvaloniaProperty MediumPullProperty = + AvaloniaProperty.RegisterAttached("MediumPull", 0); + + public static readonly AvaloniaProperty MediumPushProperty = + AvaloniaProperty.RegisterAttached("MediumPush", 0); + + public static readonly AvaloniaProperty MediumProperty = + AvaloniaProperty.RegisterAttached("Medium", 0); + + public static readonly AvaloniaProperty SmallOffsetProperty = + AvaloniaProperty.RegisterAttached("SmallOffset", 0); + + public static readonly AvaloniaProperty SmallPullProperty = + AvaloniaProperty.RegisterAttached("SmallPull", 0); + + public static readonly AvaloniaProperty SmallPushProperty = + AvaloniaProperty.RegisterAttached("SmallPush", 0); + + public static readonly AvaloniaProperty SmallProperty = + AvaloniaProperty.RegisterAttached("Small", 0); + + public static readonly AvaloniaProperty ExtraSmallOffsetProperty = + AvaloniaProperty.RegisterAttached("ExtraSmallOffset", 0); + + public static readonly AvaloniaProperty ExtraSmallPullProperty = + AvaloniaProperty.RegisterAttached("ExtraSmallPull", 0); + + public static readonly AvaloniaProperty ExtraSmallPushProperty = + AvaloniaProperty.RegisterAttached("ExtraSmallPush", 0); + + public static readonly AvaloniaProperty ExtraSmallProperty = + AvaloniaProperty.RegisterAttached("ExtraSmall", 0); + + static ResponsiveGrid() + { + AffectsMeasure( + MaxDivisionProperty, + BreakPointsProperty, + LargeProperty, + MediumProperty, + SmallProperty, + ExtraSmallProperty, + LargeOffsetProperty, + LargePullProperty, + LargePushProperty, + MediumOffsetProperty, + MediumPullProperty, + MediumPushProperty, + SmallOffsetProperty, + SmallPullProperty, + SmallPushProperty, + ExtraSmallOffsetProperty, + ExtraSmallPullProperty, + ExtraSmallPushProperty + ); + } + + public ResponsiveGrid() + { + MaxDivision = 12; + Thresholds = new SizeThresholds(); + } + + public int MaxDivision + { + get => GetValue(MaxDivisionProperty); + set => SetValue(MaxDivisionProperty, value); + } + + public SizeThresholds Thresholds + { + get => GetValue(BreakPointsProperty); + set => SetValue(BreakPointsProperty, value); + } + + public static int GetActualColumn(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(ActualColumnProperty); + + public static int GetActualRow(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(ActualRowProperty); + + public static int GetLarge(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(LargeProperty); + + public static int GetLargeOffset(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(LargeOffsetProperty); + + public static int GetLargePull(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(LargePullProperty); + + public static int GetLargePush(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(LargePushProperty); + + public static int GetMedium(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(MediumProperty); + + public static int GetMediumOffset(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(MediumOffsetProperty); + + public static int GetMediumPull(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(MediumPullProperty); + + public static int GetMediumPush(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(MediumPushProperty); + + public static int GetSmall(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(SmallProperty); + + public static int GetSmallOffset(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(SmallOffsetProperty); + + public static int GetSmallPull(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(SmallPullProperty); + + public static int GetSmallPush(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(SmallPushProperty); + + public static int GetExtraSmall(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(ExtraSmallProperty); + + public static int GetExtraSmallOffset(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(ExtraSmallOffsetProperty); + + public static int GetExtraSmallPull(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(ExtraSmallPullProperty); + + public static int GetExtraSmallPush(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(ExtraSmallPushProperty); + + public static void SetLarge(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(LargeProperty, value); + + public static void SetLargeOffset(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(LargeOffsetProperty, value); + + public static void SetLargePull(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(LargePullProperty, value); + + public static void SetLargePush(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(LargePushProperty, value); + + public static void SetMedium(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(MediumProperty, value); + + public static void SetMediumOffset(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(MediumOffsetProperty, value); + + public static void SetMediumPull(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(MediumPullProperty, value); + + public static void SetMediumPush(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(MediumPushProperty, value); + + public static void SetSmall(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(SmallProperty, value); + + public static void SetSmallOffset(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(SmallOffsetProperty, value); + + public static void SetSmallPull(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(SmallPullProperty, value); + + public static void SetSmallPush(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(SmallPushProperty, value); + + public static void SetExtraSmall(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(ExtraSmallProperty, value); + + public static void SetExtraSmallOffset(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(ExtraSmallOffsetProperty, value); + + public static void SetExtraSmallPull(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(ExtraSmallPullProperty, value); + + public static void SetExtraSmallPush(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(ExtraSmallPushProperty, value); + + protected static void SetActualColumn(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(ActualColumnProperty, value); + + protected static void SetActualRow(AvaloniaObject avaloniaObject, int value) => + avaloniaObject.SetValue(ActualRowProperty, value); + + protected override Size ArrangeOverride(Size finalSize) + { + double columnWidth = finalSize.Width / MaxDivision; + + IEnumerable> groupedRows = Children.OfType().GroupBy(GetActualRow); + + double yOffset = 0; + foreach (IGrouping row in groupedRows) + { + double maxRowHeight = row.Max(control => control.DesiredSize.Height); + + foreach (Control element in row) + { + int column = GetActualColumn(element); + int span = GetSpan(element, finalSize.Width); + + Rect rect = new(column * columnWidth, yOffset, span * columnWidth, maxRowHeight); + element.Arrange(rect); + } + + yOffset += maxRowHeight; + } + + return finalSize; + } + + protected int GetOffset(Control control, double width) + { + int GetXS() => Math.Max(0, GetExtraSmallOffset(control)); + int GetSM() => Math.Max(GetXS(), GetSmallOffset(control)); + int GetMD() => Math.Max(GetSM(), GetMediumOffset(control)); + int GetLG() => Math.Max(GetMD(), GetLargeOffset(control)); + + int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG(); + return Math.Min(span, MaxDivision); + } + + protected int GetPull(Control control, double width) + { + int GetXS() => Math.Max(0, GetExtraSmallPull(control)); + int GetSM() => Math.Max(GetXS(), GetSmallPull(control)); + int GetMD() => Math.Max(GetSM(), GetMediumPull(control)); + int GetLG() => Math.Max(GetMD(), GetLargePull(control)); + + int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG(); + return Math.Min(span, MaxDivision); + } + + protected int GetPush(Control control, double width) + { + int GetXS() => Math.Max(0, GetExtraSmallPush(control)); + int GetSM() => Math.Max(GetXS(), GetSmallPush(control)); + int GetMD() => Math.Max(GetSM(), GetMediumPush(control)); + int GetLG() => Math.Max(GetMD(), GetLargePush(control)); + + int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG(); + return Math.Min(span, MaxDivision); + } + + protected int GetSpan(Control control, double width) + { + int GetXS() => Math.Max(0, GetExtraSmall(control)); + int GetSM() => Math.Max(GetXS(), GetSmall(control)); + int GetMD() => Math.Max(GetSM(), GetMedium(control)); + int GetLG() => Math.Max(GetMD(), GetLarge(control)); + + int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG(); + return Math.Min(span, MaxDivision); + } + + protected override Size MeasureOverride(Size availableSize) + { + int count = 0; + int currentRow = 0; + + double availableWidth = double.IsPositiveInfinity(availableSize.Width) + ? double.PositiveInfinity + : availableSize.Width / MaxDivision; + + foreach (Control control in Children.OfType()) + { + if (control.IsVisible) + { + int span = GetSpan(control, availableSize.Width); + int offset = GetOffset(control, availableSize.Width); + int push = GetPush(control, availableSize.Width); + int pull = GetPull(control, availableSize.Width); + + if (count + span + offset > MaxDivision) + { + currentRow++; + count = 0; + } + + SetActualColumn(control, count + offset + push - pull); + SetActualRow(control, currentRow); + + count += span + offset; + + Size size = new(availableWidth * span, double.PositiveInfinity); + control.Measure(size); + } + } + + IEnumerable> groupedRows = Children.OfType().GroupBy(GetActualRow); + + Size totalSize = new(groupedRows.Max(rows => rows.Sum(control => control.DesiredSize.Width)), + groupedRows.Sum(rows => rows.Max(control => control.DesiredSize.Height))); + + return totalSize; + } + } +} diff --git a/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/SizeThresholds.cs b/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/SizeThresholds.cs new file mode 100644 index 0000000..12219a6 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/SizeThresholds.cs @@ -0,0 +1,36 @@ +using Avalonia; +using System.ComponentModel; + +namespace Toolkit.UI.Controls.Avalonia +{ + [TypeConverter(typeof(SizeThresholdsTypeConverter))] + public class SizeThresholds : AvaloniaObject + { + public static readonly StyledProperty MediumToLargeProperty = + AvaloniaProperty.Register("MediumToLarge", 1200.0); + + public static readonly StyledProperty SmallToMediumProperty = + AvaloniaProperty.Register("SmallToMedium", 992.0); + + public static readonly StyledProperty ExtraSmallToSmallProperty = + AvaloniaProperty.Register("ExtraSmallToSmall", 768.0); + + public double MediumToLarge + { + get => (double)GetValue(MediumToLargeProperty); + set => SetValue(MediumToLargeProperty, value); + } + + public double SmallToMedium + { + get => (double)GetValue(SmallToMediumProperty); + set => SetValue(SmallToMediumProperty, value); + } + + public double ExtraSmallToSmall + { + get => (double)GetValue(ExtraSmallToSmallProperty); + set => SetValue(ExtraSmallToSmallProperty, value); + } + } +} diff --git a/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/SizeThresholdsTypeConverter.cs b/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/SizeThresholdsTypeConverter.cs new file mode 100644 index 0000000..99c87ec --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/SizeThresholdsTypeConverter.cs @@ -0,0 +1,44 @@ +using System.ComponentModel; +using System.Globalization; + +namespace Toolkit.UI.Controls.Avalonia +{ + public class SizeThresholdsTypeConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type? sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext? context, + CultureInfo? culture, object value) + { + if (value is not string text) + { + return new SizeThresholds(); + } + + List values = text.Split(',') + .Select(o => o.Trim()) + .Select(o => int.TryParse(o, out var result) ? result : 0) + .ToList(); + + if (values.Count != 3) + { + return new SizeThresholds + { + ExtraSmallToSmall = 768, + SmallToMedium = 992, + MediumToLarge = 1200 + }; + } + + return new SizeThresholds + { + ExtraSmallToSmall = values[0], + SmallToMedium = values[1], + MediumToLarge = values[2] + }; + } + } +} From 24d289ad304ca8691e9ea6ad63791aa658eea776 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Wed, 8 May 2024 22:03:53 +0100 Subject: [PATCH 029/228] qip --- .../ResponsiveGrid/ResponsiveGrid.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/ResponsiveGrid.cs b/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/ResponsiveGrid.cs index cbe92ea..556a8de 100644 --- a/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/ResponsiveGrid.cs +++ b/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/ResponsiveGrid.cs @@ -248,7 +248,8 @@ namespace Toolkit.UI.Controls.Avalonia int GetMD() => Math.Max(GetSM(), GetMediumOffset(control)); int GetLG() => Math.Max(GetMD(), GetLargeOffset(control)); - int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG(); + int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? + GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG(); return Math.Min(span, MaxDivision); } @@ -259,7 +260,8 @@ namespace Toolkit.UI.Controls.Avalonia int GetMD() => Math.Max(GetSM(), GetMediumPull(control)); int GetLG() => Math.Max(GetMD(), GetLargePull(control)); - int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG(); + int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? + GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG(); return Math.Min(span, MaxDivision); } @@ -270,7 +272,8 @@ namespace Toolkit.UI.Controls.Avalonia int GetMD() => Math.Max(GetSM(), GetMediumPush(control)); int GetLG() => Math.Max(GetMD(), GetLargePush(control)); - int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG(); + int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? + GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG(); return Math.Min(span, MaxDivision); } @@ -281,7 +284,8 @@ namespace Toolkit.UI.Controls.Avalonia int GetMD() => Math.Max(GetSM(), GetMedium(control)); int GetLG() => Math.Max(GetMD(), GetLarge(control)); - int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG(); + int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? + GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG(); return Math.Min(span, MaxDivision); } From ee7083d009a61ae8d684e3af7ae07d7002ec122b Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Wed, 8 May 2024 23:33:13 +0100 Subject: [PATCH 030/228] Added ResponsiveGrid --- .../ResponsiveGrid/ResponsiveGrid.cs | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/ResponsiveGrid.cs b/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/ResponsiveGrid.cs index 556a8de..e70abd1 100644 --- a/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/ResponsiveGrid.cs +++ b/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/ResponsiveGrid.cs @@ -243,50 +243,52 @@ namespace Toolkit.UI.Controls.Avalonia protected int GetOffset(Control control, double width) { - int GetXS() => Math.Max(0, GetExtraSmallOffset(control)); - int GetSM() => Math.Max(GetXS(), GetSmallOffset(control)); - int GetMD() => Math.Max(GetSM(), GetMediumOffset(control)); - int GetLG() => Math.Max(GetMD(), GetLargeOffset(control)); + int GetXS(Control control) => GetExtraSmallOffset(control) is 0 ? 0 : GetExtraSmallOffset(control); + int GetSM(Control control) => GetSmallOffset(control) is 0 ? GetXS(control) : GetSmallOffset(control); + int GetMD(Control control) => GetMediumOffset(control) is 0 ? GetSM(control) : GetMediumOffset(control); + int GetLG(Control control) => GetLargeOffset(control) is 0 ? GetMD(control) : GetLargeOffset(control); - int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? - GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG(); + int span = width < Thresholds.ExtraSmallToSmall ? GetXS(control) : width < Thresholds.SmallToMedium ? + GetSM(control) : width < Thresholds.MediumToLarge ? GetMD(control) : GetLG(control); return Math.Min(span, MaxDivision); } protected int GetPull(Control control, double width) { - int GetXS() => Math.Max(0, GetExtraSmallPull(control)); - int GetSM() => Math.Max(GetXS(), GetSmallPull(control)); - int GetMD() => Math.Max(GetSM(), GetMediumPull(control)); - int GetLG() => Math.Max(GetMD(), GetLargePull(control)); + int GetXS(Control control) => GetExtraSmallPull(control) is 0 ? 0 : GetExtraSmallPull(control); + int GetSM(Control control) => GetSmallPull(control) is 0 ? GetXS(control) : GetSmallPull(control); + int GetMD(Control control) => GetMediumPull(control) is 0 ? GetSM(control) : GetMediumPull(control); + int GetLG(Control control) => GetLargePull(control) is 0 ? GetMD(control) : GetLargePull(control); - int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? - GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG(); + int span = width < Thresholds.ExtraSmallToSmall ? GetXS(control) : width < Thresholds.SmallToMedium ? + GetSM(control) : width < Thresholds.MediumToLarge ? GetMD(control) : GetLG(control); return Math.Min(span, MaxDivision); } protected int GetPush(Control control, double width) { - int GetXS() => Math.Max(0, GetExtraSmallPush(control)); - int GetSM() => Math.Max(GetXS(), GetSmallPush(control)); - int GetMD() => Math.Max(GetSM(), GetMediumPush(control)); - int GetLG() => Math.Max(GetMD(), GetLargePush(control)); + int GetXS(Control control) => GetExtraSmallPush(control) is 0 ? 0 : GetExtraSmallPush(control); + int GetSM(Control control) => GetSmallPush(control) is 0 ? GetXS(control) : GetSmallPush(control); + int GetMD(Control control) => GetMediumPush(control) is 0 ? GetSM(control) : GetMediumPush(control); + int GetLG(Control control) => GetLargePush(control) is 0 ? GetMD(control) : GetLargePush(control); + + int span = width < Thresholds.ExtraSmallToSmall ? GetXS(control) : width < Thresholds.SmallToMedium ? + GetSM(control) : width < Thresholds.MediumToLarge ? GetMD(control) : GetLG(control); - int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? - GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG(); return Math.Min(span, MaxDivision); } protected int GetSpan(Control control, double width) { - int GetXS() => Math.Max(0, GetExtraSmall(control)); - int GetSM() => Math.Max(GetXS(), GetSmall(control)); - int GetMD() => Math.Max(GetSM(), GetMedium(control)); - int GetLG() => Math.Max(GetMD(), GetLarge(control)); + int GetXS(Control control) => GetExtraSmall(control) is 0 ? MaxDivision : GetExtraSmall(control); + int GetSM(Control control) => GetSmall(control) is 0 ? GetXS(control) : GetSmall(control); + int GetMD(Control control) => GetMedium(control) is 0 ? GetSM(control) : GetMedium(control); + int GetLG(Control control) => GetLarge(control) is 0 ? GetMD(control) : GetLarge(control); - int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? - GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG(); - return Math.Min(span, MaxDivision); + int span = width < Thresholds.ExtraSmallToSmall ? GetXS(control) : width < Thresholds.SmallToMedium ? + GetSM(control) : width < Thresholds.MediumToLarge ? GetMD(control) : GetLG(control); + + return Math.Min(Math.Max(0, span), MaxDivision); ; } protected override Size MeasureOverride(Size availableSize) From 711353c8e9f2ec39b395a299efec2fa5849e5a2c Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Thu, 9 May 2024 19:09:18 +0100 Subject: [PATCH 031/228] Added PersonPicture --- .../IconElement/BitmapIcon.cs | 6 + .../IconElement/BitmapIconSource.cs | 6 + .../IconElement/ContentIcon.cs | 71 +++ .../IconElement/ContentIconSource.cs | 26 ++ .../IconElement/FAIconElement.cs | 6 + .../IconElement/FAPathIcon.cs | 6 + .../IconElement/FontIcon.cs | 6 + .../IconElement/FontIconSource.cs | 8 + .../IconElement/IconHelper.cs | 35 ++ .../IconElement/ImageIcon.cs | 6 + .../IconElement/ImageIconSource.cs | 6 + .../IconElement/PathIconSource.cs | 12 + .../IconElement/SymbolIcon.cs | 6 + .../IconElement/SymbolIconSource.cs | 6 + .../PersonPicture/PersonPicture.axaml | 75 ++++ .../PersonPicture/PersonPicture.cs | 379 ++++++++++++++++ .../PersonPictureCharacterType.cs | 9 + .../PersonPictureInitialsGenerator.cs | 413 ++++++++++++++++++ .../PersonPictureTemplateSettings.cs | 26 ++ 19 files changed, 1108 insertions(+) create mode 100644 Toolkit.UI.Controls.Avalonia/IconElement/BitmapIcon.cs create mode 100644 Toolkit.UI.Controls.Avalonia/IconElement/BitmapIconSource.cs create mode 100644 Toolkit.UI.Controls.Avalonia/IconElement/ContentIcon.cs create mode 100644 Toolkit.UI.Controls.Avalonia/IconElement/ContentIconSource.cs create mode 100644 Toolkit.UI.Controls.Avalonia/IconElement/FAIconElement.cs create mode 100644 Toolkit.UI.Controls.Avalonia/IconElement/FAPathIcon.cs create mode 100644 Toolkit.UI.Controls.Avalonia/IconElement/FontIcon.cs create mode 100644 Toolkit.UI.Controls.Avalonia/IconElement/FontIconSource.cs create mode 100644 Toolkit.UI.Controls.Avalonia/IconElement/IconHelper.cs create mode 100644 Toolkit.UI.Controls.Avalonia/IconElement/ImageIcon.cs create mode 100644 Toolkit.UI.Controls.Avalonia/IconElement/ImageIconSource.cs create mode 100644 Toolkit.UI.Controls.Avalonia/IconElement/PathIconSource.cs create mode 100644 Toolkit.UI.Controls.Avalonia/IconElement/SymbolIcon.cs create mode 100644 Toolkit.UI.Controls.Avalonia/IconElement/SymbolIconSource.cs create mode 100644 Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.axaml create mode 100644 Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.cs create mode 100644 Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureCharacterType.cs create mode 100644 Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureInitialsGenerator.cs create mode 100644 Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureTemplateSettings.cs diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/BitmapIcon.cs b/Toolkit.UI.Controls.Avalonia/IconElement/BitmapIcon.cs new file mode 100644 index 0000000..d258a2e --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/BitmapIcon.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class BitmapIcon : FluentAvalonia.UI.Controls.BitmapIcon +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/BitmapIconSource.cs b/Toolkit.UI.Controls.Avalonia/IconElement/BitmapIconSource.cs new file mode 100644 index 0000000..d9213f2 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/BitmapIconSource.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class BitmapIconSource : FluentAvalonia.UI.Controls.BitmapIconSource +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/ContentIcon.cs b/Toolkit.UI.Controls.Avalonia/IconElement/ContentIcon.cs new file mode 100644 index 0000000..6873f11 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/ContentIcon.cs @@ -0,0 +1,71 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Templates; +using Avalonia.LogicalTree; +using Avalonia.Metadata; + +namespace Toolkit.UI.Controls.Avalonia; + +public class ContentIcon : FluentAvalonia.UI.Controls.FAIconElement +{ + public static readonly StyledProperty ContentProperty = + AvaloniaProperty.Register("Content"); + + public static readonly StyledProperty ContentTemplateProperty = + AvaloniaProperty.Register("ContentTemplate"); + + private ContentControl? content; + + [Content] + public object? Content + { + get => GetValue(ContentProperty); + set => SetValue(ContentProperty, value); + } + public IDataTemplate? IconTemplate + { + get => GetValue(ContentTemplateProperty); + set => SetValue(ContentTemplateProperty, value); + } + + protected override Size MeasureOverride(Size availableSize) + { + if (content == null) + { + CreateContent(); + } + + return base.MeasureOverride(availableSize); + + } + + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs args) + { + if (VisualChildren.Count > 0) + { + ((ILogical)VisualChildren[0]).NotifyAttachedToLogicalTree(args); + } + + base.OnAttachedToLogicalTree(args); + } + + protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs args) + { + if (VisualChildren.Count > 0) + { + ((ILogical)VisualChildren[0]).NotifyDetachedFromLogicalTree(args); + } + + base.OnDetachedFromLogicalTree(args); + } + private void CreateContent() + { + content = new ContentControl(); + + content.Bind(ContentControl.ContentProperty, this.GetBindingObservable(ContentProperty)); + content.Bind(ContentControl.ContentTemplateProperty, this.GetBindingObservable(ContentTemplateProperty)); + + LogicalChildren.Add(content); + VisualChildren.Add(content); + } +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/ContentIconSource.cs b/Toolkit.UI.Controls.Avalonia/IconElement/ContentIconSource.cs new file mode 100644 index 0000000..d92c967 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/ContentIconSource.cs @@ -0,0 +1,26 @@ +using Avalonia; +using Avalonia.Controls.Templates; +using Avalonia.Metadata; + +namespace Toolkit.UI.Controls.Avalonia; + +public class ContentIconSource : FluentAvalonia.UI.Controls.IconSource +{ + public static readonly StyledProperty ContentProperty = + AvaloniaProperty.Register("Content"); + + public static readonly StyledProperty ContentTemplateProperty = + AvaloniaProperty.Register("ContentTemplate"); + + [Content] + public object? Content + { + get => GetValue(ContentProperty); + set => SetValue(ContentProperty, value); + } + public IDataTemplate? IconTemplate + { + get => GetValue(ContentTemplateProperty); + set => SetValue(ContentTemplateProperty, value); + } +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/FAIconElement.cs b/Toolkit.UI.Controls.Avalonia/IconElement/FAIconElement.cs new file mode 100644 index 0000000..6964c15 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/FAIconElement.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class FAIconElement : FluentAvalonia.UI.Controls.FAIconElement +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/FAPathIcon.cs b/Toolkit.UI.Controls.Avalonia/IconElement/FAPathIcon.cs new file mode 100644 index 0000000..c50e979 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/FAPathIcon.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class FAPathIcon : FluentAvalonia.UI.Controls.FAPathIcon +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/FontIcon.cs b/Toolkit.UI.Controls.Avalonia/IconElement/FontIcon.cs new file mode 100644 index 0000000..de453cd --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/FontIcon.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class FontIcon : FluentAvalonia.UI.Controls.FontIcon +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/FontIconSource.cs b/Toolkit.UI.Controls.Avalonia/IconElement/FontIconSource.cs new file mode 100644 index 0000000..6fb4e19 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/FontIconSource.cs @@ -0,0 +1,8 @@ +namespace Kromek.UI.Avalonia.Controls +{ + + public class FontIconSource : FluentAvalonia.UI.Controls.FontIconSource + { + + } +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/IconHelper.cs b/Toolkit.UI.Controls.Avalonia/IconElement/IconHelper.cs new file mode 100644 index 0000000..017a26d --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/IconHelper.cs @@ -0,0 +1,35 @@ +using System.Reflection; + +namespace Toolkit.UI.Controls.Avalonia; + +public class IconHelper +{ + private static MethodInfo? invoker; + + public static FluentAvalonia.UI.Controls.FAIconElement? CreateIconElement(FluentAvalonia.UI.Controls.IconSource source) + { + if (source is ContentIconSource contentIconSource) + { + ContentIcon contentIcon = new() + { + [!ContentIcon.ContentProperty] = contentIconSource[!ContentIconSource.ContentProperty], + [!ContentIcon.ContentTemplateProperty] = contentIconSource[!ContentIconSource.ContentTemplateProperty], + }; + + return contentIcon; + } + else + { + if (invoker == null) + { + Type? iconHelpersType = Type.GetType("FluentAvalonia.UI.Controls.IconHelpers,FluentAvalonia"); + if (iconHelpersType?.GetMethod("CreateFromUnknown", BindingFlags.Public | BindingFlags.Static) is MethodInfo createFromUnknown) + { + invoker = createFromUnknown; + } + } + + return (FluentAvalonia.UI.Controls.FAIconElement?)invoker?.Invoke(null, new object[] { source }); + } + } +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/ImageIcon.cs b/Toolkit.UI.Controls.Avalonia/IconElement/ImageIcon.cs new file mode 100644 index 0000000..5f89aff --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/ImageIcon.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class ImageIcon : FluentAvalonia.UI.Controls.ImageIcon +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/ImageIconSource.cs b/Toolkit.UI.Controls.Avalonia/IconElement/ImageIconSource.cs new file mode 100644 index 0000000..4bb0ae4 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/ImageIconSource.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class ImageIconSource : FluentAvalonia.UI.Controls.ImageIconSource +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/PathIconSource.cs b/Toolkit.UI.Controls.Avalonia/IconElement/PathIconSource.cs new file mode 100644 index 0000000..12a7c54 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/PathIconSource.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls.Templates; +using Avalonia.Controls; +using Avalonia.LogicalTree; +using Avalonia.Metadata; +using Avalonia; + +namespace Toolkit.UI.Controls.Avalonia; + +public class PathIconSource : FluentAvalonia.UI.Controls.PathIconSource +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/SymbolIcon.cs b/Toolkit.UI.Controls.Avalonia/IconElement/SymbolIcon.cs new file mode 100644 index 0000000..319badc --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/SymbolIcon.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class SymbolIcon : FluentAvalonia.UI.Controls.SymbolIcon +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/IconElement/SymbolIconSource.cs b/Toolkit.UI.Controls.Avalonia/IconElement/SymbolIconSource.cs new file mode 100644 index 0000000..7f426cf --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/IconElement/SymbolIconSource.cs @@ -0,0 +1,6 @@ +namespace Toolkit.UI.Controls.Avalonia; + +public class SymbolIconSource : FluentAvalonia.UI.Controls.SymbolIconSource +{ + +} diff --git a/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.axaml b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.axaml new file mode 100644 index 0000000..e40798d --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.axaml @@ -0,0 +1,75 @@ + + 1 + 1 + 1 + 2 + 0,-4,-4,0 + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.cs b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.cs new file mode 100644 index 0000000..69f8eb5 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.cs @@ -0,0 +1,379 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Shapes; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using Kromek.UI.Avalonia.Controls; +using System; + +namespace Toolkit.UI.Controls.Avalonia; + +public class PersonPicture : TemplatedControl +{ + public static readonly StyledProperty BadgeGlyphProperty = + AvaloniaProperty.Register(nameof(BadgeGlyph)); + + public static readonly StyledProperty BadgeImageSourceProperty = + AvaloniaProperty.Register(nameof(BadgeImageSource)); + + public static readonly StyledProperty BadgeNumberProperty = + AvaloniaProperty.Register(nameof(BadgeNumber)); + + public static readonly StyledProperty BadgeTextProperty = + AvaloniaProperty.Register(nameof(BadgeText)); + + public static readonly StyledProperty DisplayNameProperty = + AvaloniaProperty.Register(nameof(DisplayName)); + + public static readonly StyledProperty InitialsProperty = + AvaloniaProperty.Register(nameof(Initials)); + + public static readonly StyledProperty IsGroupProperty = + AvaloniaProperty.Register(nameof(IsGroup)); + + public static readonly StyledProperty ProfilePictureProperty = + AvaloniaProperty.Register(nameof(ProfilePicture)); + + private static readonly StyledProperty TemplateSettingsProperty = + AvaloniaProperty.Register(nameof(TemplateSettings)); + + private FontIcon badgeGlyphIcon; + private ImageBrush badgeImageBrush; + private TextBlock badgeNumberTextBlock; + private Ellipse badgingBackgroundEllipse; + private Ellipse badgingEllipse; + private string displayNameInitials; + private TextBlock initialsTextBlock; + + public PersonPicture() + { + SetValue(TemplateSettingsProperty, new PersonPictureTemplateSettings()); + SizeChanged += OnSizeChanged; + } + + public string BadgeGlyph + { + get => GetValue(BadgeGlyphProperty); + set => SetValue(BadgeGlyphProperty, value); + } + + public IImage BadgeImageSource + { + get => GetValue(BadgeImageSourceProperty); + set => SetValue(BadgeImageSourceProperty, value); + } + + public int BadgeNumber + { + get => GetValue(BadgeNumberProperty); + set => SetValue(BadgeNumberProperty, value); + } + + public string BadgeText + { + get => GetValue(BadgeTextProperty); + set => SetValue(BadgeTextProperty, value); + } + + public string DisplayName + { + get => GetValue(DisplayNameProperty); + set => SetValue(DisplayNameProperty, value); + } + + public string Initials + { + get => GetValue(InitialsProperty); + set => SetValue(InitialsProperty, value); + } + + public bool IsGroup + { + get => GetValue(IsGroupProperty); + set => SetValue(IsGroupProperty, value); + } + + public IImage ProfilePicture + { + get => GetValue(ProfilePictureProperty); + set => SetValue(ProfilePictureProperty, value); + } + + public PersonPictureTemplateSettings TemplateSettings + { + get => GetValue(TemplateSettingsProperty); + set => SetValue(TemplateSettingsProperty, value); + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs args) + { + base.OnApplyTemplate(args); + + initialsTextBlock = args.NameScope.Get("InitialsTextBlock"); + + badgeNumberTextBlock = args.NameScope.Get("BadgeNumberTextBlock"); + badgeGlyphIcon = args.NameScope.Get("BadgeGlyphIcon"); + badgingEllipse = args.NameScope.Get("BadgingEllipse"); + badgingBackgroundEllipse = args.NameScope.Get("BadgingBackgroundEllipse"); + + UpdateBadge(); + UpdateIfReady(); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == BadgeGlyphProperty) + { + UpdateBadge(); + } + + if (change.Property == BadgeImageSourceProperty) + { + UpdateBadge(); + } + + if (change.Property == BadgeNumberProperty) + { + UpdateBadge(); + } + + if (change.Property == DisplayNameProperty) + { + UpdateDisplayName(); + } + + if (change.Property == InitialsProperty) + { + UpdateIfReady(); + } + + if (change.Property == IsGroupProperty) + { + UpdateIfReady(); + } + + if (change.Property == ProfilePictureProperty) + { + + } + } + + private IImage? GetImageSource() + { + if (ProfilePicture != null) + { + return ProfilePicture; + } + + return null; + } + + private string GetInitials() + { + if (!string.IsNullOrEmpty(Initials)) + { + return Initials; + } + else if (!string.IsNullOrEmpty(displayNameInitials)) + { + return displayNameInitials; + } + + return ""; + } + + private void OnSizeChanged(object? sender, SizeChangedEventArgs args) + { + { + bool widthChanged = args.NewSize.Width != args.PreviousSize.Width; + bool heightChanged = args.NewSize.Height != args.PreviousSize.Height; + double newSize; + + if (widthChanged && heightChanged) + { + newSize = args.NewSize.Width < args.NewSize.Height ? args.NewSize.Width : args.NewSize.Height; + } + else if (widthChanged) + { + newSize = args.NewSize.Width; + } + else if (heightChanged) + { + newSize = args.NewSize.Height; + } + else + { + return; + } + + Height = newSize; + Width = newSize; + } + + double fontSize = Math.Max(1.0, Width * .42); + + if (initialsTextBlock is not null) + { + initialsTextBlock.FontSize = fontSize; + } + + if (badgingEllipse is not null && badgingBackgroundEllipse is not null && badgeNumberTextBlock is not null && badgeGlyphIcon is not null) + { + double newSize = args.NewSize.Width < args.NewSize.Height ? args.NewSize.Width : args.NewSize.Height; + badgingEllipse.Height = newSize * 0.5; + badgingEllipse.Width = newSize * 0.5; + badgingBackgroundEllipse.Height = newSize * 0.5; + badgingBackgroundEllipse.Width = newSize * 0.5; + badgeNumberTextBlock.FontSize = Math.Max(1.0, badgingEllipse.Height * 0.6); + badgeGlyphIcon.FontSize = Math.Max(1.0, badgingEllipse.Height * 0.6); + } + } + + private void UpdateBadge() + { + if (BadgeImageSource != null) + { + UpdateBadgeImageSource(); + } + else if (BadgeNumber != 0) + { + UpdateBadgeNumber(); + } + else if (!string.IsNullOrEmpty(BadgeGlyph)) + { + UpdateBadgeGlyph(); + } + else + { + PseudoClasses.Set(":NoBadge", true); + if (badgeNumberTextBlock != null) + { + badgeNumberTextBlock.Text = ""; + } + + if (badgeGlyphIcon != null) + { + badgeGlyphIcon.Glyph = ""; + } + } + } + + private void UpdateBadgeGlyph() + { + if (badgingEllipse == null || badgeGlyphIcon == null) + { + return; + } + + if (string.IsNullOrEmpty(BadgeGlyph)) + { + PseudoClasses.Set(":NoBadge", true); + badgeGlyphIcon.Glyph = ""; + return; + } + + PseudoClasses.Set(":BadgeWithoutImageSource", true); + badgeGlyphIcon.Glyph = BadgeGlyph; + } + + private void UpdateBadgeImageSource() + { + if (badgingEllipse == null || badgeImageBrush == null) + { + return; + } + + badgeImageBrush.Source = (Bitmap?)BadgeImageSource; + + if (BadgeImageSource != null) + { + PseudoClasses.Set(":BadgeWithImageSource", true); + } + else + { + PseudoClasses.Set(":NoBadge", true); + } + } + private void UpdateBadgeNumber() + { + if (badgingEllipse == null || badgeNumberTextBlock == null) + { + return; + } + + if (BadgeNumber <= 0) + { + PseudoClasses.Set(":NoBadge", true); + badgeNumberTextBlock.Text = ""; + + return; + } + + PseudoClasses.Set(":BadgeWithoutImageSource", true); + if (BadgeNumber <= 99) + { + badgeNumberTextBlock.Text = BadgeNumber.ToString(); + } + else + { + badgeNumberTextBlock.Text = "99+"; + } + } + private void UpdateDisplayName() + { + displayNameInitials = PersonPictureInitialsGenerator.InitialsFromDisplayName(DisplayName); + UpdateIfReady(); + } + + private void UpdateIfReady() + { + string initials = GetInitials(); + IImage? imageSource = GetImageSource(); + + PersonPictureTemplateSettings templateSettings = TemplateSettings; + templateSettings.ActualInitials = initials; + + if (imageSource is not null) + { + ImageBrush? imageBrush = templateSettings.ActualImageBrush; + if (imageBrush == null) + { + imageBrush = new ImageBrush + { + Stretch = Stretch.UniformToFill + }; + + templateSettings.ActualImageBrush = imageBrush; + } + + imageBrush.Source = (Bitmap?)imageSource; + } + else + { + templateSettings.ActualImageBrush = null; + } + + if (IsGroup) + { + PseudoClasses.Set(":Group", true); + } + else + { + if (imageSource != null) + { + PseudoClasses.Set(":Photo", true); + } + else if (!string.IsNullOrEmpty(initials)) + { + PseudoClasses.Set(":Initials", true); + } + else + { + PseudoClasses.Set(":NoPhotoOrInitials", true); + } + } + } +} diff --git a/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureCharacterType.cs b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureCharacterType.cs new file mode 100644 index 0000000..9651afd --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureCharacterType.cs @@ -0,0 +1,9 @@ +namespace Toolkit.UI.Controls.Avalonia; + +internal enum PersonPictureCharacterType +{ + Other = 0, + Standard = 1, + Symbolic = 2, + Glyph = 3 +} diff --git a/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureInitialsGenerator.cs b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureInitialsGenerator.cs new file mode 100644 index 0000000..d0ba9ed --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureInitialsGenerator.cs @@ -0,0 +1,413 @@ +namespace Toolkit.UI.Controls.Avalonia; + +internal class PersonPictureInitialsGenerator +{ + public static PersonPictureCharacterType GetCharacterType(string str) + { + + PersonPictureCharacterType result = PersonPictureCharacterType.Other; + for (int i = 0; i < 3; i++) + { + if ((i >= str.Length) || (str[i] == '\0') || (str[i] == 0xFEFF)) + { + break; + } + + char character = str[i]; + PersonPictureCharacterType evaluationResult = GetCharacterType(character); + + switch (evaluationResult) + { + case PersonPictureCharacterType.Glyph: + result = PersonPictureCharacterType.Glyph; + break; + case PersonPictureCharacterType.Symbolic: + if (result != PersonPictureCharacterType.Glyph) + { + result = PersonPictureCharacterType.Symbolic; + } + break; + case PersonPictureCharacterType.Standard: + if ((result != PersonPictureCharacterType.Glyph) && (result != PersonPictureCharacterType.Symbolic)) + { + result = PersonPictureCharacterType.Standard; + } + break; + default: + break; + } + } + return result; + } + + public static PersonPictureCharacterType GetCharacterType(char character) + { + + // IPA Extensions + if ((character >= 0x0250) && (character <= 0x02AF)) + { + return PersonPictureCharacterType.Glyph; + } + // Arabic + if ((character >= 0x0600) && (character <= 0x06FF)) + { + return PersonPictureCharacterType.Glyph; + } + // Arabic Supplement + if ((character >= 0x0750) && (character <= 0x077F)) + { + return PersonPictureCharacterType.Glyph; + } + // Arabic Extended-A + if ((character >= 0x08A0) && (character <= 0x08FF)) + { + return PersonPictureCharacterType.Glyph; + } + // Arabic Presentation Forms-A + if ((character >= 0xFB50) && (character <= 0xFDFF)) + { + return PersonPictureCharacterType.Glyph; + } + // Arabic Presentation Forms-B + if ((character >= 0xFE70) && (character <= 0xFEFF)) + { + return PersonPictureCharacterType.Glyph; + } + // Devanagari + if ((character >= 0x0900) && (character <= 0x097F)) + { + return PersonPictureCharacterType.Glyph; + } + // Devanagari Extended + if ((character >= 0xA8E0) && (character <= 0xA8FF)) + { + return PersonPictureCharacterType.Glyph; + } + // Bengali + if ((character >= 0x0980) && (character <= 0x09FF)) + { + return PersonPictureCharacterType.Glyph; + } + // Gurmukhi + if ((character >= 0x0A00) && (character <= 0x0A7F)) + { + return PersonPictureCharacterType.Glyph; + } + // Gujarati + if ((character >= 0x0A80) && (character <= 0x0AFF)) + { + return PersonPictureCharacterType.Glyph; + } + // Oriya + if ((character >= 0x0B00) && (character <= 0x0B7F)) + { + return PersonPictureCharacterType.Glyph; + } + // Tamil + if ((character >= 0x0B80) && (character <= 0x0BFF)) + { + return PersonPictureCharacterType.Glyph; + } + // Telugu + if ((character >= 0x0C00) && (character <= 0x0C7F)) + { + return PersonPictureCharacterType.Glyph; + } + // Kannada + if ((character >= 0x0C80) && (character <= 0x0CFF)) + { + return PersonPictureCharacterType.Glyph; + } + // Malayalam + if ((character >= 0x0D00) && (character <= 0x0D7F)) + { + return PersonPictureCharacterType.Glyph; + } + // Sinhala + if ((character >= 0x0D80) && (character <= 0x0DFF)) + { + return PersonPictureCharacterType.Glyph; + } + // Thai + if ((character >= 0x0E00) && (character <= 0x0E7F)) + { + return PersonPictureCharacterType.Glyph; + } + // Lao + if ((character >= 0x0E80) && (character <= 0x0EFF)) + { + return PersonPictureCharacterType.Glyph; + } + // SYMBOLIC + // + // CJK Unified Ideographs + if ((character >= 0x4E00) && (character <= 0x9FFF)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Unified Ideographs Extension + if ((character >= 0x3400) && (character <= 0x4DBF)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Unified Ideographs Extension B + if ((character >= 0x20000) && (character <= 0x2A6DF)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Unified Ideographs Extension C + if ((character >= 0x2A700) && (character <= 0x2B73F)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Unified Ideographs Extension D + if ((character >= 0x2B740) && (character <= 0x2B81F)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Radicals Supplement + if ((character >= 0x2E80) && (character <= 0x2EFF)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Symbols and Punctuation + if ((character >= 0x3000) && (character <= 0x303F)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Strokes + if ((character >= 0x31C0) && (character <= 0x31EF)) + { + return PersonPictureCharacterType.Symbolic; + } + // Enclosed CJK Letters and Months + if ((character >= 0x3200) && (character <= 0x32FF)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Compatibility + if ((character >= 0x3300) && (character <= 0x33FF)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Compatibility Ideographs + if ((character >= 0xF900) && (character <= 0xFAFF)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Compatibility Forms + if ((character >= 0xFE30) && (character <= 0xFE4F)) + { + return PersonPictureCharacterType.Symbolic; + } + // CJK Compatibility Ideographs Supplement + if ((character >= 0x2F800) && (character <= 0x2FA1F)) + { + return PersonPictureCharacterType.Symbolic; + } + // Greek and Coptic + if ((character >= 0x0370) && (character <= 0x03FF)) + { + return PersonPictureCharacterType.Symbolic; + } + // Hebrew + if ((character >= 0x0590) && (character <= 0x05FF)) + { + return PersonPictureCharacterType.Symbolic; + } + // Armenian + if ((character >= 0x0530) && (character <= 0x058F)) + { + return PersonPictureCharacterType.Symbolic; + } + // LATIN + // + // Basic Latin + if ((character > 0x0000) && (character <= 0x007F)) + { + return PersonPictureCharacterType.Standard; + } + // Latin-1 Supplement + if ((character >= 0x0080) && (character <= 0x00FF)) + { + return PersonPictureCharacterType.Standard; + } + // Latin Extended-A + if ((character >= 0x0100) && (character <= 0x017F)) + { + return PersonPictureCharacterType.Standard; + } + // Latin Extended-B + if ((character >= 0x0180) && (character <= 0x024F)) + { + return PersonPictureCharacterType.Standard; + } + // Latin Extended-C + if ((character >= 0x2C60) && (character <= 0x2C7F)) + { + return PersonPictureCharacterType.Standard; + } + // Latin Extended-D + if ((character >= 0xA720) && (character <= 0xA7FF)) + { + return PersonPictureCharacterType.Standard; + } + // Latin Extended-E + if ((character >= 0xAB30) && (character <= 0xAB6F)) + { + return PersonPictureCharacterType.Standard; + } + // Latin Extended Additional + if ((character >= 0x1E00) && (character <= 0x1EFF)) + { + return PersonPictureCharacterType.Standard; + } + // Cyrillic + if ((character >= 0x0400) && (character <= 0x04FF)) + { + return PersonPictureCharacterType.Standard; + } + // Cyrillic Supplement + if ((character >= 0x0500) && (character <= 0x052F)) + { + return PersonPictureCharacterType.Standard; + } + // Combining Diacritical Marks + if ((character >= 0x0300) && (character <= 0x036F)) + { + return PersonPictureCharacterType.Standard; + } + return PersonPictureCharacterType.Other; + } + + public static string InitialsFromDisplayName(string contactDisplayName) + { + PersonPictureCharacterType type = GetCharacterType(contactDisplayName); + if (type == PersonPictureCharacterType.Standard) + { + string displayName = contactDisplayName; + StripTrailingBrackets(ref displayName); + string[] words = Split(displayName, ' '); + + if (words.Length == 1) + { + string firstWord = words.First(); + string result = GetFirstFullCharacter(firstWord); + + return result.ToUpper(); + } + else if (words.Length > 1) + { + string firstWord = words.First(); + string lastWord = words.Last(); + string result = GetFirstFullCharacter(firstWord); + result += GetFirstFullCharacter(lastWord); + + return result.ToUpper(); + } + else + { + return string.Empty; + } + } + else + { + return string.Empty; + } + } + private static string GetFirstFullCharacter(string str) + { + int start = 0; + while (start < str.Length) + { + char character = str[start]; + // Omit ! " # $ % & ' ( ) * + , - . / + if ((character >= 0x0021) && (character <= 0x002F)) + { + start++; + continue; + } + // Omit : ; < = > ? @ + if ((character >= 0x003A) && (character <= 0x0040)) + { + start++; + continue; + } + // Omit { | } ~ + if ((character >= 0x007B) && (character <= 0x007E)) + { + start++; + continue; + } + break; + } + + if (start >= str.Length) + { + start = 0; + } + + int index = start + 1; + while (index < str.Length) + { + char character = str[index]; + + if ((character < 0x0300) || (character > 0x036F)) + { + break; + } + + index++; + } + + int strLength = index - start; + return SafeSubstring(str, start, strLength); + } + + private static string SafeSubstring(string value, int startIndex, int length) + { + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (startIndex > value.Length) + { + return string.Empty; + } + + if (length > value.Length - startIndex) + { + length = value.Length - startIndex; + } + + return value.Substring(startIndex, length); + } + private static string[] Split(string source, char delim, int maxIterations = 25) + { + return source.Split(new[] { delim }, maxIterations); + } + + private static void StripTrailingBrackets(ref string source) + { + string[] delimiters = { "{}", "()", "[]" }; + if (source.Length == 0) + { + return; + } + foreach (var delimiter in delimiters) + { + if (source[source.Length - 1] != delimiter[1]) + { + continue; + } + var start = source.LastIndexOf(delimiter[0]); + if (start == -1) + { + continue; + } + source = source.Remove(start); + return; + } + } +} diff --git a/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureTemplateSettings.cs b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureTemplateSettings.cs new file mode 100644 index 0000000..d7cf56f --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureTemplateSettings.cs @@ -0,0 +1,26 @@ +using Avalonia; +using Avalonia.Media; + +namespace Toolkit.UI.Controls.Avalonia; + +public class PersonPictureTemplateSettings : AvaloniaObject +{ + private static readonly StyledProperty ActualImageBrushProperty = + AvaloniaProperty.Register(nameof(ActualImageBrush)); + + + private static readonly StyledProperty ActualInitialsProperty = + AvaloniaProperty.Register(nameof(ActualInitials)); + + public ImageBrush? ActualImageBrush + { + get => GetValue(ActualImageBrushProperty); + set => SetValue(ActualImageBrushProperty, value); + } + + public string ActualInitials + { + get => GetValue(ActualInitialsProperty); + set => SetValue(ActualInitialsProperty, value); + } +} From 54d2b5374d423429538bd0c49f05f474e5e599ad Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Thu, 9 May 2024 22:37:36 +0100 Subject: [PATCH 032/228] Improvement to navigation regions --- .../ClassicDesktopStyleApplicationHandler.cs | 4 +- Toolkit.Avalonia/ContentControlHandler.cs | 4 +- Toolkit.Avalonia/ContentTemplate.cs | 4 +- Toolkit.Avalonia/FrameHandler.cs | 57 +------------------ Toolkit.Avalonia/INavigationContext.cs | 8 --- .../IServiceCollectionExtensions.cs | 6 +- Toolkit.Avalonia/NavigationContext.cs | 36 ------------ Toolkit.Avalonia/NavigationRegion.cs | 28 +++++++++ .../SingleViewApplicationHandler.cs | 4 +- Toolkit.Foundation/ComponentBuilder.cs | 4 +- Toolkit.Foundation/ComponentFactory.cs | 8 +-- Toolkit.Foundation/ComponentInitializer.cs | 6 +- Toolkit.Foundation/ComponentScope.cs | 3 - Toolkit.Foundation/DefaultHostBuilder.cs | 10 ++-- Toolkit.Foundation/INavigationRegion.cs | 7 +++ ...tion.cs => INavigationRegionCollection.cs} | 2 +- ...ovider.cs => INavigationRegionProvider.cs} | 2 +- Toolkit.Foundation/NamedComponent.cs | 6 ++ Toolkit.Foundation/NavigateHandler.cs | 2 +- .../NavigationContextAttribute.cs | 12 ---- .../NavigationContextCollection.cs | 4 -- .../NavigationRegionCollection.cs | 4 ++ ...rovider.cs => NavigationRegionProvider.cs} | 4 +- Toolkit.Foundation/NavigationScope.cs | 2 +- Toolkit.UI.Avalonia/ConditionAction.cs | 3 +- Toolkit.UI.Avalonia/NavigateAction.cs | 3 +- Toolkit.UI.Avalonia/NavigateBackAction.cs | 2 +- Toolkit.UI.Avalonia/NavigateRegionAction.cs | 47 +++++++++++++++ .../PersonPicture/PersonPicture.axaml | 18 ++++++ .../PersonPictureInitialsGenerator.cs | 56 +++++++++--------- .../Themes/ControlResources.axaml | 1 + 31 files changed, 173 insertions(+), 184 deletions(-) delete mode 100644 Toolkit.Avalonia/INavigationContext.cs delete mode 100644 Toolkit.Avalonia/NavigationContext.cs create mode 100644 Toolkit.Avalonia/NavigationRegion.cs delete mode 100644 Toolkit.Foundation/ComponentScope.cs create mode 100644 Toolkit.Foundation/INavigationRegion.cs rename Toolkit.Foundation/{INavigationContextCollection.cs => INavigationRegionCollection.cs} (58%) rename Toolkit.Foundation/{INavigationContextProvider.cs => INavigationRegionProvider.cs} (73%) create mode 100644 Toolkit.Foundation/NamedComponent.cs delete mode 100644 Toolkit.Foundation/NavigationContextAttribute.cs delete mode 100644 Toolkit.Foundation/NavigationContextCollection.cs create mode 100644 Toolkit.Foundation/NavigationRegionCollection.cs rename Toolkit.Foundation/{NavigationContextProvider.cs => NavigationRegionProvider.cs} (80%) create mode 100644 Toolkit.UI.Avalonia/NavigateRegionAction.cs diff --git a/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs b/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs index 94d3087..f93024e 100644 --- a/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs +++ b/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs @@ -5,7 +5,7 @@ using Toolkit.Foundation; namespace Toolkit.Avalonia; -public class ClassicDesktopStyleApplicationHandler(INavigationContext navigationContext) : +public class ClassicDesktopStyleApplicationHandler : INavigateHandler { public Task Handle(Navigate args, @@ -18,8 +18,6 @@ public class ClassicDesktopStyleApplicationHandler(INavigationContext navigation { lifeTime.MainWindow = window; window.DataContext = args.Content; - - navigationContext.Set(window); } } diff --git a/Toolkit.Avalonia/ContentControlHandler.cs b/Toolkit.Avalonia/ContentControlHandler.cs index 3905680..cf958a7 100644 --- a/Toolkit.Avalonia/ContentControlHandler.cs +++ b/Toolkit.Avalonia/ContentControlHandler.cs @@ -4,7 +4,7 @@ using Toolkit.Foundation; namespace Toolkit.Avalonia; -public class ContentControlHandler(INavigationContext navigationContext) : +public class ContentControlHandler : INavigateHandler { public async Task Handle(Navigate args, @@ -51,8 +51,6 @@ public class ContentControlHandler(INavigationContext navigationContext) : control.DataContext = args.Content; contentControl.Content = control; - - navigationContext.Set(control); await taskCompletionSource.Task; } } diff --git a/Toolkit.Avalonia/ContentTemplate.cs b/Toolkit.Avalonia/ContentTemplate.cs index 5a02417..5ccc0c7 100644 --- a/Toolkit.Avalonia/ContentTemplate.cs +++ b/Toolkit.Avalonia/ContentTemplate.cs @@ -17,7 +17,7 @@ public class ContentTemplate : if (observableViewModel.Provider is IServiceProvider provider) { IContentTemplateDescriptorProvider? contentTemplateProvider = provider.GetService(); - INavigationContext? viewModelContentBinder = provider.GetService(); + INavigationRegion? viewModelContentBinder = provider.GetService(); if (contentTemplateProvider?.Get(item.GetType().Name) is IContentTemplateDescriptor descriptor) { @@ -55,7 +55,7 @@ public class ContentTemplate : control.Loaded += HandleLoaded; control.Unloaded += HandleUnloaded; - viewModelContentBinder?.Set(control); + //viewModelContentBinder?.Register(control); return control; } diff --git a/Toolkit.Avalonia/FrameHandler.cs b/Toolkit.Avalonia/FrameHandler.cs index 0a38126..22cd7e1 100644 --- a/Toolkit.Avalonia/FrameHandler.cs +++ b/Toolkit.Avalonia/FrameHandler.cs @@ -8,7 +8,7 @@ using Toolkit.UI.Controls.Avalonia; namespace Toolkit.Avalonia; -public class FrameHandler(INavigationContext navigationContext) : +public class FrameHandler : INavigateHandler, INavigateBackHandler { @@ -45,32 +45,6 @@ public class FrameHandler(INavigationContext navigationContext) : { await deactivating.Deactivating(); } - - Type contentType = content.GetType(); - if (contentType.GetInterfaces() is Type[] contracts) - { - foreach (Type contract in contracts) - { - if (contract.Name == typeof(IDeactivating<>).Name && - contract.GetGenericArguments() is { Length: 1 } arguments) - { - if (contentType.GetMethods().FirstOrDefault(x => - x.Name == "Deactivating" && x.ReturnType == typeof(Task<>) - .MakeGenericType(arguments[0])) - is MethodInfo methodInfo) - { - if (methodInfo.GetCustomAttribute() - is NavigationContextAttribute attribute) - { - if (await methodInfo.InvokeAsync(content) is object result) - { - results.Add(attribute.Name, result); - } - } - } - } - } - } } } } @@ -100,33 +74,6 @@ public class FrameHandler(INavigationContext navigationContext) : { await deactivated.Deactivated(); } - - Type contentType = content.GetType(); - if (contentType.GetInterfaces() is Type[] contracts) - { - foreach (Type contract in contracts) - { - if (contract.Name == typeof(IActivated<>).Name && - contract.GetGenericArguments() is { Length: 1 } arguments) - { - if (contentType.GetMethods().FirstOrDefault(x => - x.Name == "NavigatedToAsync" && - x.GetCustomAttribute() - is NavigationContextAttribute attribute && results.ContainsKey(attribute.Name)) - is MethodInfo methodInfo) - { - if (methodInfo.GetCustomAttribute() - is NavigationContextAttribute attribute) - { - if (results.TryGetValue(attribute.Name, out object? value)) - { - await methodInfo.InvokeAsync(content, value); - } - } - } - } - } - } } } @@ -181,8 +128,6 @@ public class FrameHandler(INavigationContext navigationContext) : } control.DataContext = args.Content; - navigationContext.Set(control); - NavigatedTo(args.Sender, control); frame.NavigateFromObject(control, new FrameNavigationOptions { TransitionInfoOverride = new SuppressNavigationTransitionInfo() }); } diff --git a/Toolkit.Avalonia/INavigationContext.cs b/Toolkit.Avalonia/INavigationContext.cs deleted file mode 100644 index 53d3792..0000000 --- a/Toolkit.Avalonia/INavigationContext.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Avalonia.Controls; - -namespace Toolkit.Avalonia; - -public interface INavigationContext -{ - void Set(Control control); -} \ No newline at end of file diff --git a/Toolkit.Avalonia/IServiceCollectionExtensions.cs b/Toolkit.Avalonia/IServiceCollectionExtensions.cs index 6f3ae03..737a798 100644 --- a/Toolkit.Avalonia/IServiceCollectionExtensions.cs +++ b/Toolkit.Avalonia/IServiceCollectionExtensions.cs @@ -126,7 +126,7 @@ public static class IServiceCollectionExtensions services.AddTransient(); services.AddTransient(); - services.AddTransient(); + services.AddTransient(); services.AddNavigateHandler(); services.AddNavigateHandler(); @@ -134,7 +134,7 @@ public static class IServiceCollectionExtensions services.AddNavigateHandler(); services.AddNavigateHandler(); - services.AddScoped(provider => new NavigationContextCollection + services.AddScoped(provider => new NavigationRegionCollection { { typeof(IClassicDesktopStyleApplicationLifetime), typeof(IClassicDesktopStyleApplicationLifetime) }, { typeof(ISingleViewApplicationLifetime), typeof(ISingleViewApplicationLifetime) } @@ -148,7 +148,7 @@ public static class IServiceCollectionExtensions services.AddTransient(); services.AddTransient(); - services.AddTransient(); + services.AddTransient(); services.AddNavigateHandler(); services.AddNavigateHandler(); diff --git a/Toolkit.Avalonia/NavigationContext.cs b/Toolkit.Avalonia/NavigationContext.cs deleted file mode 100644 index 4ea4635..0000000 --- a/Toolkit.Avalonia/NavigationContext.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Avalonia.Controls; -using Avalonia.Controls.Primitives; -using Avalonia.Interactivity; -using System.Reflection; -using Toolkit.Foundation; - -namespace Toolkit.Avalonia; - -public class NavigationContext(INavigationContextCollection contexts) : - INavigationContext -{ - public void Set(Control control) - { - if (control.GetType().GetCustomAttributes() - is IEnumerable attributes) - { - foreach (NavigationTargetAttribute attribute in attributes) - { - if (!contexts.ContainsKey(attribute.Name)) - { - if (control.Find(attribute.Name) is TemplatedControl content) - { - contexts.Add(attribute.Name, content); - void HandleUnloaded(object? sender, RoutedEventArgs args) - { - control.Unloaded -= HandleUnloaded; - contexts.Remove(attribute.Name); - } - - control.Unloaded += HandleUnloaded; - } - } - } - } - } -} \ No newline at end of file diff --git a/Toolkit.Avalonia/NavigationRegion.cs b/Toolkit.Avalonia/NavigationRegion.cs new file mode 100644 index 0000000..82d2897 --- /dev/null +++ b/Toolkit.Avalonia/NavigationRegion.cs @@ -0,0 +1,28 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Toolkit.Foundation; + +namespace Toolkit.Avalonia; + +public class NavigationRegion(INavigationRegionCollection collection) : + INavigationRegion +{ + public void Register(string name, + object target) + { + if (target is Control control) + { + if (!collection.ContainsKey(name)) + { + collection.Add(name, control); + void HandleUnloaded(object? sender, RoutedEventArgs args) + { + control.Unloaded -= HandleUnloaded; + collection.Remove(name); + } + + control.Unloaded += HandleUnloaded; + } + } + } +} \ No newline at end of file diff --git a/Toolkit.Avalonia/SingleViewApplicationHandler.cs b/Toolkit.Avalonia/SingleViewApplicationHandler.cs index 5dc4d4f..ff6ba63 100644 --- a/Toolkit.Avalonia/SingleViewApplicationHandler.cs +++ b/Toolkit.Avalonia/SingleViewApplicationHandler.cs @@ -5,7 +5,7 @@ using Toolkit.Foundation; namespace Toolkit.Avalonia; -public class SingleViewApplicationHandler(INavigationContext navigationContext) : +public class SingleViewApplicationHandler : INavigateHandler { public Task Handle(Navigate args, @@ -18,8 +18,6 @@ public class SingleViewApplicationHandler(INavigationContext navigationContext) { lifeTime.MainView = control; control.DataContext = args.Content; - - navigationContext.Set(control); } } diff --git a/Toolkit.Foundation/ComponentBuilder.cs b/Toolkit.Foundation/ComponentBuilder.cs index 2f90b1a..4bb92d1 100644 --- a/Toolkit.Foundation/ComponentBuilder.cs +++ b/Toolkit.Foundation/ComponentBuilder.cs @@ -39,8 +39,8 @@ public class ComponentBuilder : services.AddTransient(); services.AddTransient(); - services.AddScoped(); - services.AddTransient(); + services.AddScoped(); + services.AddTransient(); services.AddHandler(); services.AddHandler(); diff --git a/Toolkit.Foundation/ComponentFactory.cs b/Toolkit.Foundation/ComponentFactory.cs index d341915..68c32c0 100644 --- a/Toolkit.Foundation/ComponentFactory.cs +++ b/Toolkit.Foundation/ComponentFactory.cs @@ -25,10 +25,10 @@ public class ComponentFactory(IServiceProvider provider, provider.GetRequiredService>()); services.AddScoped(_ => - provider.GetRequiredService()); + provider.GetRequiredService()); services.AddScoped(_ => - provider.GetRequiredService()); + provider.GetRequiredService()); services.AddScoped(_ => provider.GetRequiredService()); @@ -37,7 +37,7 @@ public class ComponentFactory(IServiceProvider provider, provider.GetRequiredService()); services.AddRange(proxy.Services); - services.AddSingleton(new ComponentScope(name)); + services.AddSingleton(new NamedComponent(name)); if (servicesDelegate is not null) { @@ -45,7 +45,7 @@ public class ComponentFactory(IServiceProvider provider, } }); - builder.AddConfiguration(name, configuration); + builder.AddConfiguration(name, configuration); IComponentHost host = builder.Build(); scopes.Add(new ComponentScopeDescriptor(name, diff --git a/Toolkit.Foundation/ComponentInitializer.cs b/Toolkit.Foundation/ComponentInitializer.cs index 3221117..768e780 100644 --- a/Toolkit.Foundation/ComponentInitializer.cs +++ b/Toolkit.Foundation/ComponentInitializer.cs @@ -23,10 +23,10 @@ public class ComponentInitializer(IEnumerable components, provider.GetRequiredService>()); services.AddScoped(_ => - provider.GetRequiredService()); + provider.GetRequiredService()); services.AddScoped(_ => - provider.GetRequiredService()); + provider.GetRequiredService()); services.AddScoped(_ => provider.GetRequiredService()); @@ -36,7 +36,7 @@ public class ComponentInitializer(IEnumerable components, services.AddRange(typedServices.Services); - services.AddSingleton(new ComponentScope(component.GetType().Name)); + services.AddSingleton(new NamedComponent(component.GetType().Name)); }); IComponentHost host = builder.Build(); diff --git a/Toolkit.Foundation/ComponentScope.cs b/Toolkit.Foundation/ComponentScope.cs deleted file mode 100644 index 1c50570..0000000 --- a/Toolkit.Foundation/ComponentScope.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Toolkit.Foundation; - -public record ComponentScope(string Name); \ No newline at end of file diff --git a/Toolkit.Foundation/DefaultHostBuilder.cs b/Toolkit.Foundation/DefaultHostBuilder.cs index c238e97..e48a207 100644 --- a/Toolkit.Foundation/DefaultHostBuilder.cs +++ b/Toolkit.Foundation/DefaultHostBuilder.cs @@ -33,8 +33,8 @@ public class DefaultHostBuilder : services.AddScoped>(provider => new ProxyService(provider.GetRequiredService())); - services.AddScoped>(provider => - new ProxyService(provider.GetRequiredService())); + services.AddScoped>(provider => + new ProxyService(provider.GetRequiredService())); services.AddScoped>(provider => new ProxyService(provider.GetRequiredService())); @@ -45,12 +45,12 @@ public class DefaultHostBuilder : services.AddTransient(); - services.AddScoped(); - services.AddTransient(); + services.AddScoped(); + services.AddTransient(); services.AddTransient(); - services.AddSingleton(new ComponentScope("Root")); + services.AddSingleton(new NamedComponent("Root")); services.AddScoped(provider => new ComponentScopeCollection { new ComponentScopeDescriptor("Root", provider.GetRequiredService()) diff --git a/Toolkit.Foundation/INavigationRegion.cs b/Toolkit.Foundation/INavigationRegion.cs new file mode 100644 index 0000000..9fbf297 --- /dev/null +++ b/Toolkit.Foundation/INavigationRegion.cs @@ -0,0 +1,7 @@ +namespace Toolkit.Foundation; + +public interface INavigationRegion +{ + void Register(string name, + object target); +} diff --git a/Toolkit.Foundation/INavigationContextCollection.cs b/Toolkit.Foundation/INavigationRegionCollection.cs similarity index 58% rename from Toolkit.Foundation/INavigationContextCollection.cs rename to Toolkit.Foundation/INavigationRegionCollection.cs index 9bdae1f..8f242bd 100644 --- a/Toolkit.Foundation/INavigationContextCollection.cs +++ b/Toolkit.Foundation/INavigationRegionCollection.cs @@ -1,4 +1,4 @@ namespace Toolkit.Foundation; -public interface INavigationContextCollection : +public interface INavigationRegionCollection : IDictionary; \ No newline at end of file diff --git a/Toolkit.Foundation/INavigationContextProvider.cs b/Toolkit.Foundation/INavigationRegionProvider.cs similarity index 73% rename from Toolkit.Foundation/INavigationContextProvider.cs rename to Toolkit.Foundation/INavigationRegionProvider.cs index fb44955..1553415 100644 --- a/Toolkit.Foundation/INavigationContextProvider.cs +++ b/Toolkit.Foundation/INavigationRegionProvider.cs @@ -1,6 +1,6 @@ namespace Toolkit.Foundation; -public interface INavigationContextProvider +public interface INavigationRegionProvider { object? Get(object key); diff --git a/Toolkit.Foundation/NamedComponent.cs b/Toolkit.Foundation/NamedComponent.cs new file mode 100644 index 0000000..3c7b4d7 --- /dev/null +++ b/Toolkit.Foundation/NamedComponent.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; + +public record NamedComponent(string Name) +{ + public override string ToString() => Name; +} \ No newline at end of file diff --git a/Toolkit.Foundation/NavigateHandler.cs b/Toolkit.Foundation/NavigateHandler.cs index f1a4905..9ee3d04 100644 --- a/Toolkit.Foundation/NavigateHandler.cs +++ b/Toolkit.Foundation/NavigateHandler.cs @@ -2,7 +2,7 @@ namespace Toolkit.Foundation; -public class NavigateHandler(ComponentScope scope, +public class NavigateHandler(NamedComponent scope, IComponentScopeProvider provider) : INotificationHandler { diff --git a/Toolkit.Foundation/NavigationContextAttribute.cs b/Toolkit.Foundation/NavigationContextAttribute.cs deleted file mode 100644 index 62a889b..0000000 --- a/Toolkit.Foundation/NavigationContextAttribute.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Toolkit.Foundation; - -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] -public class NavigationContextAttribute : Attribute -{ - public NavigationContextAttribute(string name) - { - Name = name; - } - - public string Name { get; } -} \ No newline at end of file diff --git a/Toolkit.Foundation/NavigationContextCollection.cs b/Toolkit.Foundation/NavigationContextCollection.cs deleted file mode 100644 index 0cdf540..0000000 --- a/Toolkit.Foundation/NavigationContextCollection.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Toolkit.Foundation; - -public class NavigationContextCollection : Dictionary, - INavigationContextCollection; \ No newline at end of file diff --git a/Toolkit.Foundation/NavigationRegionCollection.cs b/Toolkit.Foundation/NavigationRegionCollection.cs new file mode 100644 index 0000000..b1c2f5a --- /dev/null +++ b/Toolkit.Foundation/NavigationRegionCollection.cs @@ -0,0 +1,4 @@ +namespace Toolkit.Foundation; + +public class NavigationRegionCollection : Dictionary, + INavigationRegionCollection; \ No newline at end of file diff --git a/Toolkit.Foundation/NavigationContextProvider.cs b/Toolkit.Foundation/NavigationRegionProvider.cs similarity index 80% rename from Toolkit.Foundation/NavigationContextProvider.cs rename to Toolkit.Foundation/NavigationRegionProvider.cs index a0173ef..9a5d7f0 100644 --- a/Toolkit.Foundation/NavigationContextProvider.cs +++ b/Toolkit.Foundation/NavigationRegionProvider.cs @@ -1,7 +1,7 @@ namespace Toolkit.Foundation; -public class NavigationContextProvider(INavigationContextCollection contexts) : - INavigationContextProvider +public class NavigationRegionProvider(INavigationRegionCollection contexts) : + INavigationRegionProvider { public object? Get(object key) => contexts.TryGetValue(key, out object? target) ? target : default; diff --git a/Toolkit.Foundation/NavigationScope.cs b/Toolkit.Foundation/NavigationScope.cs index bc8320e..c2cefd7 100644 --- a/Toolkit.Foundation/NavigationScope.cs +++ b/Toolkit.Foundation/NavigationScope.cs @@ -6,7 +6,7 @@ public class NavigationScope(IPublisher publisher, IServiceProvider provider, IServiceFactory factory, INavigationProvider navigationProvider, - INavigationContextProvider navigationContextProvider, + INavigationRegionProvider navigationContextProvider, IContentTemplateDescriptorProvider contentTemplateDescriptorProvider) : INavigationScope { diff --git a/Toolkit.UI.Avalonia/ConditionAction.cs b/Toolkit.UI.Avalonia/ConditionAction.cs index 23cf491..36519ed 100644 --- a/Toolkit.UI.Avalonia/ConditionAction.cs +++ b/Toolkit.UI.Avalonia/ConditionAction.cs @@ -9,8 +9,7 @@ public class ConditionAction : IAction { public static readonly DirectProperty ActionsProperty = - AvaloniaProperty.RegisterDirect(nameof(Actions), - x => x.Actions); + AvaloniaProperty.RegisterDirect(nameof(Actions), x => x.Actions); public static readonly StyledProperty ConditionProperty = AvaloniaProperty.Register(nameof(Condition)); diff --git a/Toolkit.UI.Avalonia/NavigateAction.cs b/Toolkit.UI.Avalonia/NavigateAction.cs index 9cb4ce3..93be81e 100644 --- a/Toolkit.UI.Avalonia/NavigateAction.cs +++ b/Toolkit.UI.Avalonia/NavigateAction.cs @@ -74,7 +74,8 @@ public class NavigateAction : ParameterBindings.Select(binding => new KeyValuePair(binding.Key, binding.Value)).ToArray() : Enumerable.Empty>()]; - observableViewModel.Publisher.Publish(new Navigate(Route, Context == this ? control : Context, Scope ?? null, control.DataContext, Navigated, parameters)); + observableViewModel.Publisher.Publish(new Navigate(Route, Context == this ? control : Context, Scope ?? null, + control.DataContext, Navigated, parameters)).ConfigureAwait(false); } } diff --git a/Toolkit.UI.Avalonia/NavigateBackAction.cs b/Toolkit.UI.Avalonia/NavigateBackAction.cs index 10bd27c..502c680 100644 --- a/Toolkit.UI.Avalonia/NavigateBackAction.cs +++ b/Toolkit.UI.Avalonia/NavigateBackAction.cs @@ -35,7 +35,7 @@ public class NavigateBackAction : if (control.DataContext is IObservableViewModel observableViewModel) { observableViewModel.Publisher.Publish(new NavigateBack(Context - ?? null, Scope ?? null)).GetAwaiter().GetResult(); + ?? null, Scope ?? null)).ConfigureAwait(false); } } diff --git a/Toolkit.UI.Avalonia/NavigateRegionAction.cs b/Toolkit.UI.Avalonia/NavigateRegionAction.cs new file mode 100644 index 0000000..cd103e2 --- /dev/null +++ b/Toolkit.UI.Avalonia/NavigateRegionAction.cs @@ -0,0 +1,47 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Metadata; +using Avalonia.Xaml.Interactivity; +using Microsoft.Extensions.DependencyInjection; +using Toolkit.Foundation; + +namespace Toolkit.UI.Avalonia; + +public class NavigateRegionAction : + AvaloniaObject, + IAction +{ + public static readonly DirectProperty ActionsProperty = + AvaloniaProperty.RegisterDirect(nameof(Actions), x => x.Actions); + + public static readonly StyledProperty NameProperty = + AvaloniaProperty.Register(nameof(Name)); + + private ActionCollection? actions; + + [Content] + public ActionCollection Actions => actions ??= []; + public string Name + { + get => GetValue(NameProperty); + set => SetValue(NameProperty, value); + } + + public object? Execute(object? sender, + object? parameter) + { + if (sender is Control control) + { + if (control.DataContext is IObservableViewModel observableViewModel) + { + if (observableViewModel.Provider.GetRequiredService() is INavigationRegion navigationRegion) + { + navigationRegion.Register(Name, sender); + Interaction.ExecuteActions(sender, Actions, parameter); + } + } + } + + return true; + } +} diff --git a/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.axaml b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.axaml index e40798d..a0741ce 100644 --- a/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.axaml +++ b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.axaml @@ -2,6 +2,24 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="using:Toolkit.UI.Controls.Avalonia"> + + + + + + + + + + + + + + + + + + 1 1 1 diff --git a/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureInitialsGenerator.cs b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureInitialsGenerator.cs index d0ba9ed..a3ed77e 100644 --- a/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureInitialsGenerator.cs +++ b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPictureInitialsGenerator.cs @@ -2,39 +2,41 @@ internal class PersonPictureInitialsGenerator { - public static PersonPictureCharacterType GetCharacterType(string str) + public static PersonPictureCharacterType GetCharacterType(string content) { - PersonPictureCharacterType result = PersonPictureCharacterType.Other; - for (int i = 0; i < 3; i++) + if (content is { Length: > 0 }) { - if ((i >= str.Length) || (str[i] == '\0') || (str[i] == 0xFEFF)) + for (int i = 0; i < 3; i++) { - break; - } + if ((i >= content.Length) || (content[i] == '\0') || (content[i] == 0xFEFF)) + { + break; + } - char character = str[i]; - PersonPictureCharacterType evaluationResult = GetCharacterType(character); + char character = content[i]; + PersonPictureCharacterType evaluationResult = GetCharacterType(character); - switch (evaluationResult) - { - case PersonPictureCharacterType.Glyph: - result = PersonPictureCharacterType.Glyph; - break; - case PersonPictureCharacterType.Symbolic: - if (result != PersonPictureCharacterType.Glyph) - { - result = PersonPictureCharacterType.Symbolic; - } - break; - case PersonPictureCharacterType.Standard: - if ((result != PersonPictureCharacterType.Glyph) && (result != PersonPictureCharacterType.Symbolic)) - { - result = PersonPictureCharacterType.Standard; - } - break; - default: - break; + switch (evaluationResult) + { + case PersonPictureCharacterType.Glyph: + result = PersonPictureCharacterType.Glyph; + break; + case PersonPictureCharacterType.Symbolic: + if (result != PersonPictureCharacterType.Glyph) + { + result = PersonPictureCharacterType.Symbolic; + } + break; + case PersonPictureCharacterType.Standard: + if ((result != PersonPictureCharacterType.Glyph) && (result != PersonPictureCharacterType.Symbolic)) + { + result = PersonPictureCharacterType.Standard; + } + break; + default: + break; + } } } return result; diff --git a/Toolkit.UI.Controls.Avalonia/Themes/ControlResources.axaml b/Toolkit.UI.Controls.Avalonia/Themes/ControlResources.axaml index 70fcccf..4955d66 100644 --- a/Toolkit.UI.Controls.Avalonia/Themes/ControlResources.axaml +++ b/Toolkit.UI.Controls.Avalonia/Themes/ControlResources.axaml @@ -6,6 +6,7 @@ + From 1dc4da48bbb9e3e6b7b758235bb68ba4aac91e27 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Fri, 10 May 2024 22:38:08 +0100 Subject: [PATCH 033/228] Prototyping --- Toolkit.Foundation/IValueInvoker.cs | 6 ++++++ Toolkit.Foundation/ObservableViewModel.cs | 15 +++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 Toolkit.Foundation/IValueInvoker.cs diff --git a/Toolkit.Foundation/IValueInvoker.cs b/Toolkit.Foundation/IValueInvoker.cs new file mode 100644 index 0000000..6bac696 --- /dev/null +++ b/Toolkit.Foundation/IValueInvoker.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; + +public interface IValueInvoker +{ + public void Invoke(TValue args); +} diff --git a/Toolkit.Foundation/ObservableViewModel.cs b/Toolkit.Foundation/ObservableViewModel.cs index 47a45b0..7bd5543 100644 --- a/Toolkit.Foundation/ObservableViewModel.cs +++ b/Toolkit.Foundation/ObservableViewModel.cs @@ -83,6 +83,21 @@ public partial class ObservableViewModel(IServiceProvider provider, IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) where TValue : notnull { + [ObservableProperty] + private TValue? value; +} + +public partial class ObservableViewModel(IServiceProvider provider, + IServiceFactory factory, + IMediator mediator, + IPublisher publisher, + ISubscriber subscriber, + IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) + where TValue : notnull +{ + [ObservableProperty] + private TKey? key; + [ObservableProperty] private TValue? value; } \ No newline at end of file From a44391f4d59c8da84462a29c9c9fe52578c7e3e5 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 12 May 2024 12:22:04 +0100 Subject: [PATCH 034/228] Add support for self scope --- Toolkit.Foundation/ComponentBuilder.cs | 3 +-- Toolkit.Foundation/DefaultHostBuilder.cs | 9 ++++--- Toolkit.Foundation/NavigateHandler.cs | 26 ++++++++++++------- .../ObservableCollectionViewModel.cs | 12 +++++++-- 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/Toolkit.Foundation/ComponentBuilder.cs b/Toolkit.Foundation/ComponentBuilder.cs index 4bb92d1..bc3ffdf 100644 --- a/Toolkit.Foundation/ComponentBuilder.cs +++ b/Toolkit.Foundation/ComponentBuilder.cs @@ -29,13 +29,12 @@ public class ComponentBuilder : services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddScoped(); - services.AddTransient(); services.AddTransient(); diff --git a/Toolkit.Foundation/DefaultHostBuilder.cs b/Toolkit.Foundation/DefaultHostBuilder.cs index e48a207..a65616e 100644 --- a/Toolkit.Foundation/DefaultHostBuilder.cs +++ b/Toolkit.Foundation/DefaultHostBuilder.cs @@ -24,11 +24,12 @@ public class DefaultHostBuilder : ComponentHostCollection>(); services.AddScoped(); - services.AddScoped(); + + services.AddTransient(); services.AddTransient(); - services.AddTransient(); - services.AddScoped(); + services.AddTransient(); + services.AddTransient(); services.AddScoped>(provider => new ProxyService(provider.GetRequiredService())); @@ -48,7 +49,7 @@ public class DefaultHostBuilder : services.AddScoped(); services.AddTransient(); - services.AddTransient(); + services.AddScoped(); services.AddSingleton(new NamedComponent("Root")); services.AddScoped(provider => new ComponentScopeCollection diff --git a/Toolkit.Foundation/NavigateHandler.cs b/Toolkit.Foundation/NavigateHandler.cs index 9ee3d04..cb8b6c2 100644 --- a/Toolkit.Foundation/NavigateHandler.cs +++ b/Toolkit.Foundation/NavigateHandler.cs @@ -3,20 +3,28 @@ namespace Toolkit.Foundation; public class NavigateHandler(NamedComponent scope, - IComponentScopeProvider provider) : + IComponentScopeProvider componentScopeProvider, + IServiceProvider provider) : INotificationHandler { public async Task Handle(Navigate args, - CancellationToken cancellationToken) + CancellationToken cancellationToken) { - if (provider.Get(args.Scope ?? scope.Name) - is ComponentScopeDescriptor descriptor) + INavigationScope? navigationScope; + if (args.Scope == "self") { - if (descriptor?.Services?.GetService() is INavigationScope navigationScope) - { - await navigationScope.NavigateAsync(args.Route, args.Sender, - args.Context, args.Navigated, args.Parameters, cancellationToken); - } + navigationScope = provider.GetRequiredService(); + } + else + { + ComponentScopeDescriptor? descriptor = componentScopeProvider.Get(args.Scope ?? scope.Name); + navigationScope = descriptor?.Services?.GetRequiredService(); + } + + if (navigationScope is not null) + { + await navigationScope.NavigateAsync(args.Route, args.Sender, + args.Context, args.Navigated, args.Parameters, cancellationToken); } } } \ No newline at end of file diff --git a/Toolkit.Foundation/ObservableCollectionViewModel.cs b/Toolkit.Foundation/ObservableCollectionViewModel.cs index ff1748d..ffc1b0c 100644 --- a/Toolkit.Foundation/ObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/ObservableCollectionViewModel.cs @@ -1,4 +1,5 @@ using CommunityToolkit.Mvvm.ComponentModel; +using Microsoft.Extensions.DependencyInjection; using System.Collections; using System.Collections.ObjectModel; using System.Collections.Specialized; @@ -140,11 +141,18 @@ public partial class ObservableCollectionViewModel : return item; } - public TViewModel Add() + public TViewModel Add(bool scope = false) where T : TViewModel { - T? item = Factory.Create(); + IServiceFactory? factory = null; + if (scope) + { + IServiceScope serviceScope = Provider.CreateScope(); + factory = serviceScope.ServiceProvider.GetRequiredService(); + } + + T? item = factory is not null ? factory.Create() : Factory.Create(); Add(item); return item; From 4171a31f8580e284f6850f7982bcbfb6aad910a6 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 12 May 2024 14:50:10 +0100 Subject: [PATCH 035/228] Get items stored and retrieved from the db --- Toolkit.Foundation/Confirm.cs | 12 ++++++++++++ Toolkit.Foundation/ObservableCollectionViewModel.cs | 1 + 2 files changed, 13 insertions(+) create mode 100644 Toolkit.Foundation/Confirm.cs diff --git a/Toolkit.Foundation/Confirm.cs b/Toolkit.Foundation/Confirm.cs new file mode 100644 index 0000000..d5687be --- /dev/null +++ b/Toolkit.Foundation/Confirm.cs @@ -0,0 +1,12 @@ +namespace Toolkit.Foundation; + +public record Confirm(TValue Value); + +public record Confirm +{ + public static Confirm As(TValue value) => + new(value); + + public static Confirm As() where TValue : new() => + new(new TValue()); +} \ No newline at end of file diff --git a/Toolkit.Foundation/ObservableCollectionViewModel.cs b/Toolkit.Foundation/ObservableCollectionViewModel.cs index ffc1b0c..8d5dd6a 100644 --- a/Toolkit.Foundation/ObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/ObservableCollectionViewModel.cs @@ -316,6 +316,7 @@ public partial class ObservableCollectionViewModel : await Enumerate(); } + public async Task Enumerate() { if (this.GetAttribute() is EnumerateAttribute attribute) From 5292896e13abe8c22e785c70d1b445d68985926e Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 12 May 2024 17:09:59 +0100 Subject: [PATCH 036/228] Ensure containers show up in the menu when they are created --- Toolkit.Foundation/Activated.cs | 9 ++++++++- Toolkit.Foundation/ConfigurationInitializer.cs | 2 +- .../{IContainer.cs => IValueStore.cs} | 2 +- Toolkit.Foundation/ObservableViewModel.cs | 7 ++++--- .../{Container.cs => ValueStore.cs} | 4 ++-- Toolkit.UI.Controls.Avalonia/Class1.cs | 17 +++++++++++++++++ 6 files changed, 33 insertions(+), 8 deletions(-) rename Toolkit.Foundation/{IContainer.cs => IValueStore.cs} (72%) rename Toolkit.Foundation/{Container.cs => ValueStore.cs} (72%) create mode 100644 Toolkit.UI.Controls.Avalonia/Class1.cs diff --git a/Toolkit.Foundation/Activated.cs b/Toolkit.Foundation/Activated.cs index ca3d61c..e75f9f8 100644 --- a/Toolkit.Foundation/Activated.cs +++ b/Toolkit.Foundation/Activated.cs @@ -1,3 +1,10 @@ namespace Toolkit.Foundation; -public record Activated; \ No newline at end of file +public record Activated(TValue? Value = default); + +public record Activated +{ + public static Activated As(TValue value) => new(value); + + public static Activated As() where TValue : new() => new(new TValue()); +} diff --git a/Toolkit.Foundation/ConfigurationInitializer.cs b/Toolkit.Foundation/ConfigurationInitializer.cs index b7240eb..57ea430 100644 --- a/Toolkit.Foundation/ConfigurationInitializer.cs +++ b/Toolkit.Foundation/ConfigurationInitializer.cs @@ -20,6 +20,6 @@ public class ConfigurationInitializer(IConfigurationReader(configuration)); + await publisher.PublishUI(new Activated(configuration)); } } \ No newline at end of file diff --git a/Toolkit.Foundation/IContainer.cs b/Toolkit.Foundation/IValueStore.cs similarity index 72% rename from Toolkit.Foundation/IContainer.cs rename to Toolkit.Foundation/IValueStore.cs index 7c7dd2d..ac6ca2a 100644 --- a/Toolkit.Foundation/IContainer.cs +++ b/Toolkit.Foundation/IValueStore.cs @@ -1,6 +1,6 @@ namespace Toolkit.Foundation; -public interface IContainer +public interface IValueStore { T? Value { get; } diff --git a/Toolkit.Foundation/ObservableViewModel.cs b/Toolkit.Foundation/ObservableViewModel.cs index 7bd5543..779394e 100644 --- a/Toolkit.Foundation/ObservableViewModel.cs +++ b/Toolkit.Foundation/ObservableViewModel.cs @@ -92,12 +92,13 @@ public partial class ObservableViewModel(IServiceProvider provider IMediator mediator, IPublisher publisher, ISubscriber subscriber, - IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) - where TValue : notnull + IDisposer disposer, + TValue? value = null) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) + where TValue : class { [ObservableProperty] private TKey? key; [ObservableProperty] - private TValue? value; + private TValue? value = value; } \ No newline at end of file diff --git a/Toolkit.Foundation/Container.cs b/Toolkit.Foundation/ValueStore.cs similarity index 72% rename from Toolkit.Foundation/Container.cs rename to Toolkit.Foundation/ValueStore.cs index 30516e0..0c1c6db 100644 --- a/Toolkit.Foundation/Container.cs +++ b/Toolkit.Foundation/ValueStore.cs @@ -1,7 +1,7 @@ namespace Toolkit.Foundation; -public class Container : - IContainer +public class ValueStore : + IValueStore { public T? Value { get; private set; } diff --git a/Toolkit.UI.Controls.Avalonia/Class1.cs b/Toolkit.UI.Controls.Avalonia/Class1.cs new file mode 100644 index 0000000..cef80b6 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/Class1.cs @@ -0,0 +1,17 @@ +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Toolkit.UI.Controls; + +public class AttachedFlyout : PopupFlyoutBase +{ + protected override Control CreatePresenter() + { + throw new NotImplementedException(); + } +} From 9eafcc55d001b650c9ce27ae2fb8789882882a77 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 12 May 2024 18:28:13 +0100 Subject: [PATCH 037/228] Enabled ability to order containers and the ability to insert new containers to their correct order --- Toolkit.Foundation/ComponentHost.cs | 7 +++++-- Toolkit.Foundation/IComponentHost.cs | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Toolkit.Foundation/ComponentHost.cs b/Toolkit.Foundation/ComponentHost.cs index 686b6ef..61d2461 100644 --- a/Toolkit.Foundation/ComponentHost.cs +++ b/Toolkit.Foundation/ComponentHost.cs @@ -10,13 +10,16 @@ public class ComponentHost(IServiceProvider services, { public IServiceProvider Services => services; - public ComponentConfiguration? Configuration => - Services.GetService(); public void Dispose() { } + public TConfiguration? GetConfiguration() where TConfiguration : ComponentConfiguration + { + return Services.GetService(); + } + public async Task StartAsync(CancellationToken cancellationToken = default) { foreach (IInitializer initializer in initializers) diff --git a/Toolkit.Foundation/IComponentHost.cs b/Toolkit.Foundation/IComponentHost.cs index 260a74c..fe46212 100644 --- a/Toolkit.Foundation/IComponentHost.cs +++ b/Toolkit.Foundation/IComponentHost.cs @@ -5,5 +5,5 @@ namespace Toolkit.Foundation; public interface IComponentHost : IHost { - ComponentConfiguration? Configuration { get; } + TConfiguration? GetConfiguration() where TConfiguration : ComponentConfiguration; } \ No newline at end of file From f0fd68c16e34ed4458d8a344853cdb577d201824 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 12 May 2024 19:55:20 +0100 Subject: [PATCH 038/228] Improved event naming --- .../ClassicDesktopStyleApplicationHandler.cs | 2 +- Toolkit.Avalonia/ContentControlHandler.cs | 6 ++-- Toolkit.Avalonia/ContentDialogHandler.cs | 4 +-- Toolkit.Avalonia/ContentTemplate.cs | 4 +-- Toolkit.Avalonia/FrameHandler.cs | 10 +++---- .../SingleViewApplicationHandler.cs | 2 +- Toolkit.Foundation/Activate.cs | 8 ++++++ Toolkit.Foundation/ActivateEventArgs.cs | 3 ++ Toolkit.Foundation/Activated.cs | 6 ++-- Toolkit.Foundation/ActivatedEventArgs.cs | 3 ++ Toolkit.Foundation/AppService.cs | 2 +- Toolkit.Foundation/Changed.cs | 6 ++-- Toolkit.Foundation/ChangedEventArgs.cs | 3 ++ .../ComponentConfigurationViewModel.cs | 20 ++++++------- .../ConfigurationChangedHandler.cs | 4 +-- .../ConfigurationInitializer.cs | 2 +- Toolkit.Foundation/ConfigurationMonitor.cs | 2 +- Toolkit.Foundation/Confirm.cs | 6 ++-- Toolkit.Foundation/ConfirmEventArgs.cs | 3 ++ Toolkit.Foundation/Create.cs | 6 ++-- Toolkit.Foundation/CreateEventArgs.cs | 3 ++ Toolkit.Foundation/Deactivated.cs | 7 ++++- Toolkit.Foundation/DeactivatedEventArgs.cs | 3 ++ Toolkit.Foundation/Enumerate.cs | 13 --------- Toolkit.Foundation/EnumerateEventArgs.cs | 14 ++++++++++ Toolkit.Foundation/IActivated.cs | 2 +- Toolkit.Foundation/IDeactivated.cs | 2 +- Toolkit.Foundation/IDeactivating.cs | 2 +- Toolkit.Foundation/INavigateBackHandler.cs | 2 +- Toolkit.Foundation/INavigateHandler.cs | 2 +- Toolkit.Foundation/Insert.cs | 6 ++-- Toolkit.Foundation/InsertEventArgs.cs | 3 ++ Toolkit.Foundation/Move.cs | 6 ++-- Toolkit.Foundation/MoveEventArgs.cs | 3 ++ Toolkit.Foundation/NavigateBack.cs | 5 ---- Toolkit.Foundation/NavigateBackEventArgs.cs | 5 ++++ Toolkit.Foundation/NavigateBackHandler.cs | 4 +-- Toolkit.Foundation/NavigateEventArgs.cs | 14 ++++++++++ Toolkit.Foundation/NavigateHandler.cs | 4 +-- Toolkit.Foundation/NavigationChanged.cs | 3 -- Toolkit.Foundation/NavigationScope.cs | 4 +-- .../ObservableCollectionViewModel.cs | 28 +++++++++---------- Toolkit.Foundation/ObservableViewModel.cs | 6 ++-- Toolkit.Foundation/Open.cs | 10 ------- Toolkit.Foundation/Remove.cs | 6 ++-- Toolkit.Foundation/RemoveEventArgs.cs | 3 ++ Toolkit.Foundation/Replace.cs | 6 ++-- Toolkit.Foundation/ReplaceEventArgs.cs | 3 ++ Toolkit.Foundation/Request.cs | 8 ++++-- Toolkit.Foundation/RequestEventArgs.cs | 3 ++ Toolkit.Foundation/Selected.cs | 6 ++-- Toolkit.Foundation/SelectedEventArgs.cs | 3 ++ .../{Started.cs => StartedEventArgs.cs} | 2 +- Toolkit.Foundation/Toolkit.Foundation.csproj | 3 ++ Toolkit.UI.Avalonia/NavigateAction.cs | 2 +- Toolkit.UI.Avalonia/NavigateBackAction.cs | 2 +- 56 files changed, 169 insertions(+), 131 deletions(-) create mode 100644 Toolkit.Foundation/Activate.cs create mode 100644 Toolkit.Foundation/ActivateEventArgs.cs create mode 100644 Toolkit.Foundation/ActivatedEventArgs.cs create mode 100644 Toolkit.Foundation/ChangedEventArgs.cs create mode 100644 Toolkit.Foundation/ConfirmEventArgs.cs create mode 100644 Toolkit.Foundation/CreateEventArgs.cs create mode 100644 Toolkit.Foundation/DeactivatedEventArgs.cs create mode 100644 Toolkit.Foundation/EnumerateEventArgs.cs create mode 100644 Toolkit.Foundation/InsertEventArgs.cs create mode 100644 Toolkit.Foundation/MoveEventArgs.cs delete mode 100644 Toolkit.Foundation/NavigateBack.cs create mode 100644 Toolkit.Foundation/NavigateBackEventArgs.cs create mode 100644 Toolkit.Foundation/NavigateEventArgs.cs delete mode 100644 Toolkit.Foundation/NavigationChanged.cs delete mode 100644 Toolkit.Foundation/Open.cs create mode 100644 Toolkit.Foundation/RemoveEventArgs.cs create mode 100644 Toolkit.Foundation/ReplaceEventArgs.cs create mode 100644 Toolkit.Foundation/RequestEventArgs.cs create mode 100644 Toolkit.Foundation/SelectedEventArgs.cs rename Toolkit.Foundation/{Started.cs => StartedEventArgs.cs} (52%) diff --git a/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs b/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs index f93024e..779766b 100644 --- a/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs +++ b/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs @@ -8,7 +8,7 @@ namespace Toolkit.Avalonia; public class ClassicDesktopStyleApplicationHandler : INavigateHandler { - public Task Handle(Navigate args, + public Task Handle(NavigateEventArgs args, CancellationToken cancellationToken = default) { if (Application.Current?.ApplicationLifetime is diff --git a/Toolkit.Avalonia/ContentControlHandler.cs b/Toolkit.Avalonia/ContentControlHandler.cs index cf958a7..5b1f0e5 100644 --- a/Toolkit.Avalonia/ContentControlHandler.cs +++ b/Toolkit.Avalonia/ContentControlHandler.cs @@ -7,7 +7,7 @@ namespace Toolkit.Avalonia; public class ContentControlHandler : INavigateHandler { - public async Task Handle(Navigate args, + public async Task Handle(NavigateEventArgs args, CancellationToken cancellationToken) { if (args.Context is ContentControl contentControl) @@ -27,7 +27,7 @@ public class ContentControlHandler : if (content is IActivated activated) { - await activated.Activated(); + await activated.OnActivated(); } } @@ -41,7 +41,7 @@ public class ContentControlHandler : { if (content is IDeactivated deactivated) { - await deactivated.Deactivated(); + await deactivated.OnDeactivated(); } } } diff --git a/Toolkit.Avalonia/ContentDialogHandler.cs b/Toolkit.Avalonia/ContentDialogHandler.cs index 1f340c5..d327a3c 100644 --- a/Toolkit.Avalonia/ContentDialogHandler.cs +++ b/Toolkit.Avalonia/ContentDialogHandler.cs @@ -6,7 +6,7 @@ namespace Toolkit.Avalonia; public class ContentDialogHandler(IDispatcher dispatcher) : INavigateHandler { - public async Task Handle(Navigate args, + public async Task Handle(NavigateEventArgs args, CancellationToken cancellationToken) { if (args.Context is ContentDialog contentDialog) @@ -94,7 +94,7 @@ public class ContentDialogHandler(IDispatcher dispatcher) : if (content is IActivated activated) { - await activated.Activated(); + await activated.OnActivated(); } } } diff --git a/Toolkit.Avalonia/ContentTemplate.cs b/Toolkit.Avalonia/ContentTemplate.cs index 5ccc0c7..aa4be42 100644 --- a/Toolkit.Avalonia/ContentTemplate.cs +++ b/Toolkit.Avalonia/ContentTemplate.cs @@ -35,7 +35,7 @@ public class ContentTemplate : if (content is IActivated activated) { - await activated.Activated(); + await activated.OnActivated(); } } } @@ -47,7 +47,7 @@ public class ContentTemplate : { if (content is IDeactivated deactivated) { - await deactivated.Deactivated(); + await deactivated.OnDeactivated(); } } } diff --git a/Toolkit.Avalonia/FrameHandler.cs b/Toolkit.Avalonia/FrameHandler.cs index 22cd7e1..44e423d 100644 --- a/Toolkit.Avalonia/FrameHandler.cs +++ b/Toolkit.Avalonia/FrameHandler.cs @@ -12,7 +12,7 @@ public class FrameHandler : INavigateHandler, INavigateBackHandler { - public Task Handle(Navigate args, + public Task Handle(NavigateEventArgs args, CancellationToken cancellationToken) { if (args.Context is Frame frame) @@ -43,7 +43,7 @@ public class FrameHandler : { if (content is IDeactivating deactivating) { - await deactivating.Deactivating(); + await deactivating.OnDeactivating(); } } } @@ -72,7 +72,7 @@ public class FrameHandler : { if (content is IDeactivated deactivated) { - await deactivated.Deactivated(); + await deactivated.OnDeactivated(); } } } @@ -119,7 +119,7 @@ public class FrameHandler : if (content is IActivated activated) { - await activated.Activated(); + await activated.OnActivated(); } } } @@ -136,7 +136,7 @@ public class FrameHandler : return Task.CompletedTask; } - public Task Handle(NavigateBack args, + public Task Handle(NavigateBackEventArgs args, CancellationToken cancellationToken = default) { if (args.Context is Frame frame) diff --git a/Toolkit.Avalonia/SingleViewApplicationHandler.cs b/Toolkit.Avalonia/SingleViewApplicationHandler.cs index ff6ba63..189bcc4 100644 --- a/Toolkit.Avalonia/SingleViewApplicationHandler.cs +++ b/Toolkit.Avalonia/SingleViewApplicationHandler.cs @@ -8,7 +8,7 @@ namespace Toolkit.Avalonia; public class SingleViewApplicationHandler : INavigateHandler { - public Task Handle(Navigate args, + public Task Handle(NavigateEventArgs args, CancellationToken cancellationToken = default) { if (Application.Current?.ApplicationLifetime is diff --git a/Toolkit.Foundation/Activate.cs b/Toolkit.Foundation/Activate.cs new file mode 100644 index 0000000..870a837 --- /dev/null +++ b/Toolkit.Foundation/Activate.cs @@ -0,0 +1,8 @@ +namespace Toolkit.Foundation; + +public record Activate +{ + public static ActivateEventArgs As(TValue value) => new(value); + + public static ActivateEventArgs As() where TValue : new() => new(new TValue()); +} diff --git a/Toolkit.Foundation/ActivateEventArgs.cs b/Toolkit.Foundation/ActivateEventArgs.cs new file mode 100644 index 0000000..59d933d --- /dev/null +++ b/Toolkit.Foundation/ActivateEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record ActivateEventArgs(TValue? Value = default); diff --git a/Toolkit.Foundation/Activated.cs b/Toolkit.Foundation/Activated.cs index e75f9f8..94360dc 100644 --- a/Toolkit.Foundation/Activated.cs +++ b/Toolkit.Foundation/Activated.cs @@ -1,10 +1,8 @@ namespace Toolkit.Foundation; -public record Activated(TValue? Value = default); - public record Activated { - public static Activated As(TValue value) => new(value); + public static ActivatedEventArgs As(TValue value) => new(value); - public static Activated As() where TValue : new() => new(new TValue()); + public static ActivatedEventArgs As() where TValue : new() => new(new TValue()); } diff --git a/Toolkit.Foundation/ActivatedEventArgs.cs b/Toolkit.Foundation/ActivatedEventArgs.cs new file mode 100644 index 0000000..d1e536b --- /dev/null +++ b/Toolkit.Foundation/ActivatedEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record ActivatedEventArgs(TValue? Value = default); diff --git a/Toolkit.Foundation/AppService.cs b/Toolkit.Foundation/AppService.cs index c318344..6d054d3 100644 --- a/Toolkit.Foundation/AppService.cs +++ b/Toolkit.Foundation/AppService.cs @@ -13,7 +13,7 @@ public class AppService(IEnumerable initializers, await initializer.Initialize(); } - await publisher.Publish(cancellationToken); + await publisher.Publish(cancellationToken); } public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; diff --git a/Toolkit.Foundation/Changed.cs b/Toolkit.Foundation/Changed.cs index 1750921..c32d286 100644 --- a/Toolkit.Foundation/Changed.cs +++ b/Toolkit.Foundation/Changed.cs @@ -1,10 +1,8 @@ namespace Toolkit.Foundation; -public record Changed(TValue? Value = default); - public record Changed { - public static Changed As(TValue value) => new(value); + public static ChangedEventArgs As(TValue value) => new(value); - public static Changed As() where TValue : new() => new(new TValue()); + public static ChangedEventArgs As() where TValue : new() => new(new TValue()); } diff --git a/Toolkit.Foundation/ChangedEventArgs.cs b/Toolkit.Foundation/ChangedEventArgs.cs new file mode 100644 index 0000000..20cd6e3 --- /dev/null +++ b/Toolkit.Foundation/ChangedEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record ChangedEventArgs(TValue? Value = default); diff --git a/Toolkit.Foundation/ComponentConfigurationViewModel.cs b/Toolkit.Foundation/ComponentConfigurationViewModel.cs index b74dd71..c326c36 100644 --- a/Toolkit.Foundation/ComponentConfigurationViewModel.cs +++ b/Toolkit.Foundation/ComponentConfigurationViewModel.cs @@ -5,7 +5,7 @@ namespace Toolkit.Foundation; public partial class ComponentConfigurationViewModel : ValueViewModel, IComponentConfigurationViewModel, - INotificationHandler> + INotificationHandler> where TConfiguration : class { public ComponentConfigurationViewModel(IServiceProvider provider, @@ -20,7 +20,7 @@ public partial class ComponentConfigurationViewModel args, + public Task Handle(ChangedEventArgs args, CancellationToken cancellationToken = default) { throw new NotImplementedException(); @@ -40,7 +40,7 @@ public partial class ComponentConfigurationViewModel(provider, factory, mediator, publisher, subscriber, disposer), IComponentConfigurationViewModel, - INotificationHandler> + INotificationHandler> where TConfiguration : class { [ObservableProperty] @@ -52,13 +52,13 @@ public partial class ComponentConfigurationViewModel args, + public Task Handle(ChangedEventArgs args, CancellationToken cancellationToken = default) { if (args.Value is TConfiguration configuration) @@ -83,7 +83,7 @@ public partial class ComponentConfigurationViewModel(provider, factory, mediator, publisher, subscriber, disposer), IComponentConfigurationViewModel, - INotificationHandler> + INotificationHandler> where TConfiguration : class { [ObservableProperty] @@ -95,13 +95,13 @@ public partial class ComponentConfigurationViewModel args, + public Task Handle(ChangedEventArgs args, CancellationToken cancellationToken = default) { if (args.Value is TConfiguration configuration) diff --git a/Toolkit.Foundation/ConfigurationChangedHandler.cs b/Toolkit.Foundation/ConfigurationChangedHandler.cs index e142bcc..6a1cf8a 100644 --- a/Toolkit.Foundation/ConfigurationChangedHandler.cs +++ b/Toolkit.Foundation/ConfigurationChangedHandler.cs @@ -1,11 +1,11 @@ namespace Toolkit.Foundation; public class ConfigurationChangedHandler(ConfigurationValue configurationValue) : - INotificationHandler> + INotificationHandler> where TValue : class, new() { - public Task Handle(Changed args, + public Task Handle(ChangedEventArgs args, CancellationToken cancellationToken = default) { if (args.Value is TConfiguration configuration) diff --git a/Toolkit.Foundation/ConfigurationInitializer.cs b/Toolkit.Foundation/ConfigurationInitializer.cs index 57ea430..aaf2185 100644 --- a/Toolkit.Foundation/ConfigurationInitializer.cs +++ b/Toolkit.Foundation/ConfigurationInitializer.cs @@ -20,6 +20,6 @@ public class ConfigurationInitializer(IConfigurationReader(configuration)); + await publisher.PublishUI(new ActivatedEventArgs(configuration)); } } \ No newline at end of file diff --git a/Toolkit.Foundation/ConfigurationMonitor.cs b/Toolkit.Foundation/ConfigurationMonitor.cs index df9b908..baa65c8 100644 --- a/Toolkit.Foundation/ConfigurationMonitor.cs +++ b/Toolkit.Foundation/ConfigurationMonitor.cs @@ -16,7 +16,7 @@ public class ConfigurationMonitor(IConfigurationFile(configuration)); + await publisher.PublishUI(new ChangedEventArgs(configuration)); } } diff --git a/Toolkit.Foundation/Confirm.cs b/Toolkit.Foundation/Confirm.cs index d5687be..2839cf4 100644 --- a/Toolkit.Foundation/Confirm.cs +++ b/Toolkit.Foundation/Confirm.cs @@ -1,12 +1,10 @@ namespace Toolkit.Foundation; -public record Confirm(TValue Value); - public record Confirm { - public static Confirm As(TValue value) => + public static ConfirmEventArgs As(TValue value) => new(value); - public static Confirm As() where TValue : new() => + public static ConfirmEventArgs As() where TValue : new() => new(new TValue()); } \ No newline at end of file diff --git a/Toolkit.Foundation/ConfirmEventArgs.cs b/Toolkit.Foundation/ConfirmEventArgs.cs new file mode 100644 index 0000000..d817934 --- /dev/null +++ b/Toolkit.Foundation/ConfirmEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record ConfirmEventArgs(TValue Value); diff --git a/Toolkit.Foundation/Create.cs b/Toolkit.Foundation/Create.cs index 67183de..25f55c0 100644 --- a/Toolkit.Foundation/Create.cs +++ b/Toolkit.Foundation/Create.cs @@ -1,12 +1,10 @@ namespace Toolkit.Foundation; -public record Create(TValue Value); - public record Create { - public static Create As(TValue value) => + public static CreateEventArgs As(TValue value) => new(value); - public static Create As() where TValue : new() => + public static CreateEventArgs As() where TValue : new() => new(new TValue()); } \ No newline at end of file diff --git a/Toolkit.Foundation/CreateEventArgs.cs b/Toolkit.Foundation/CreateEventArgs.cs new file mode 100644 index 0000000..6e9723d --- /dev/null +++ b/Toolkit.Foundation/CreateEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record CreateEventArgs(TValue Value); diff --git a/Toolkit.Foundation/Deactivated.cs b/Toolkit.Foundation/Deactivated.cs index 5dc4ca6..33ecdde 100644 --- a/Toolkit.Foundation/Deactivated.cs +++ b/Toolkit.Foundation/Deactivated.cs @@ -1,3 +1,8 @@ namespace Toolkit.Foundation; -public record Deactivated; \ No newline at end of file +public record Deactivated +{ + public static DeactivatedEventArgs As(TValue value) => new(value); + + public static DeactivatedEventArgs As() where TValue : new() => new(new TValue()); +} diff --git a/Toolkit.Foundation/DeactivatedEventArgs.cs b/Toolkit.Foundation/DeactivatedEventArgs.cs new file mode 100644 index 0000000..99aadc3 --- /dev/null +++ b/Toolkit.Foundation/DeactivatedEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record DeactivatedEventArgs(TValue? Value = default); diff --git a/Toolkit.Foundation/Enumerate.cs b/Toolkit.Foundation/Enumerate.cs index 8c54650..9a90559 100644 --- a/Toolkit.Foundation/Enumerate.cs +++ b/Toolkit.Foundation/Enumerate.cs @@ -1,18 +1,5 @@ namespace Toolkit.Foundation; -public record Enumerate : - IEnumerate -{ - public object? Key { get; init; } - - public EnumerateMode Mode { get; init; } - - public static Enumerate With(TOptions options) where TOptions : class - { - return new Enumerate(options); - } -} - public record Enumerate(TOptions? Options = null) : IEnumerate where TOptions : class diff --git a/Toolkit.Foundation/EnumerateEventArgs.cs b/Toolkit.Foundation/EnumerateEventArgs.cs new file mode 100644 index 0000000..1895294 --- /dev/null +++ b/Toolkit.Foundation/EnumerateEventArgs.cs @@ -0,0 +1,14 @@ +namespace Toolkit.Foundation; + +public record EnumerateEventArgs : + IEnumerate +{ + public object? Key { get; init; } + + public EnumerateMode Mode { get; init; } + + public static Enumerate With(TOptions options) where TOptions : class + { + return new Enumerate(options); + } +} \ No newline at end of file diff --git a/Toolkit.Foundation/IActivated.cs b/Toolkit.Foundation/IActivated.cs index 079d435..aded530 100644 --- a/Toolkit.Foundation/IActivated.cs +++ b/Toolkit.Foundation/IActivated.cs @@ -2,7 +2,7 @@ public interface IActivated { - Task Activated(); + Task OnActivated(); } public interface IActivated diff --git a/Toolkit.Foundation/IDeactivated.cs b/Toolkit.Foundation/IDeactivated.cs index f640164..84bbeb4 100644 --- a/Toolkit.Foundation/IDeactivated.cs +++ b/Toolkit.Foundation/IDeactivated.cs @@ -2,5 +2,5 @@ public interface IDeactivated { - Task Deactivated(); + Task OnDeactivated(); } \ No newline at end of file diff --git a/Toolkit.Foundation/IDeactivating.cs b/Toolkit.Foundation/IDeactivating.cs index 0c9a25a..78a0427 100644 --- a/Toolkit.Foundation/IDeactivating.cs +++ b/Toolkit.Foundation/IDeactivating.cs @@ -2,7 +2,7 @@ public interface IDeactivating { - Task Deactivating(); + Task OnDeactivating(); } public interface IDeactivating diff --git a/Toolkit.Foundation/INavigateBackHandler.cs b/Toolkit.Foundation/INavigateBackHandler.cs index 50805f1..db65a69 100644 --- a/Toolkit.Foundation/INavigateBackHandler.cs +++ b/Toolkit.Foundation/INavigateBackHandler.cs @@ -1,5 +1,5 @@ namespace Toolkit.Foundation; public interface INavigateBackHandler : - INotificationHandler>, + INotificationHandler>, INavigateHandler; \ No newline at end of file diff --git a/Toolkit.Foundation/INavigateHandler.cs b/Toolkit.Foundation/INavigateHandler.cs index 0cc5571..b6c5ee6 100644 --- a/Toolkit.Foundation/INavigateHandler.cs +++ b/Toolkit.Foundation/INavigateHandler.cs @@ -3,5 +3,5 @@ public interface INavigateHandler; public interface INavigateHandler : - INotificationHandler>, + INotificationHandler>, INavigateHandler; \ No newline at end of file diff --git a/Toolkit.Foundation/Insert.cs b/Toolkit.Foundation/Insert.cs index 08f8301..a9b29f1 100644 --- a/Toolkit.Foundation/Insert.cs +++ b/Toolkit.Foundation/Insert.cs @@ -1,12 +1,10 @@ namespace Toolkit.Foundation; -public record Insert(int Index, TValue Value); - public record Insert { - public static Insert As(int index, TValue value) => + public static InsertEventArgs As(int index, TValue value) => new(index, value); - public static Insert As(int index) where TValue : new() => + public static InsertEventArgs As(int index) where TValue : new() => new(index, new TValue()); } \ No newline at end of file diff --git a/Toolkit.Foundation/InsertEventArgs.cs b/Toolkit.Foundation/InsertEventArgs.cs new file mode 100644 index 0000000..a33df15 --- /dev/null +++ b/Toolkit.Foundation/InsertEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record InsertEventArgs(int Index, TValue Value); diff --git a/Toolkit.Foundation/Move.cs b/Toolkit.Foundation/Move.cs index 67e4d77..684a09e 100644 --- a/Toolkit.Foundation/Move.cs +++ b/Toolkit.Foundation/Move.cs @@ -1,12 +1,10 @@ namespace Toolkit.Foundation; -public record Move(int Index, TValue Value); - public record Move { - public static Move As(int index, TValue value) => + public static MoveEventArgs As(int index, TValue value) => new(index, value); - public static Insert As(int index) where TValue : new() => + public static InsertEventArgs As(int index) where TValue : new() => new(index, new TValue()); } \ No newline at end of file diff --git a/Toolkit.Foundation/MoveEventArgs.cs b/Toolkit.Foundation/MoveEventArgs.cs new file mode 100644 index 0000000..4bc8bde --- /dev/null +++ b/Toolkit.Foundation/MoveEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record MoveEventArgs(int Index, TValue Value); diff --git a/Toolkit.Foundation/NavigateBack.cs b/Toolkit.Foundation/NavigateBack.cs deleted file mode 100644 index 48feb09..0000000 --- a/Toolkit.Foundation/NavigateBack.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Toolkit.Foundation; - -public record NavigateBack(object? Context = null, string? Scope = null); - -public record NavigateBack(object? Context); \ No newline at end of file diff --git a/Toolkit.Foundation/NavigateBackEventArgs.cs b/Toolkit.Foundation/NavigateBackEventArgs.cs new file mode 100644 index 0000000..3636e47 --- /dev/null +++ b/Toolkit.Foundation/NavigateBackEventArgs.cs @@ -0,0 +1,5 @@ +namespace Toolkit.Foundation; + +public record NavigateBackEventArgs(object? Context = null, string? Scope = null); + +public record NavigateBackEventArgs(object? Context); \ No newline at end of file diff --git a/Toolkit.Foundation/NavigateBackHandler.cs b/Toolkit.Foundation/NavigateBackHandler.cs index fdd07a3..b8b740e 100644 --- a/Toolkit.Foundation/NavigateBackHandler.cs +++ b/Toolkit.Foundation/NavigateBackHandler.cs @@ -3,9 +3,9 @@ namespace Toolkit.Foundation; public class NavigateBackHandler(IComponentScopeProvider provider) : - INotificationHandler + INotificationHandler { - public async Task Handle(NavigateBack args, + public async Task Handle(NavigateBackEventArgs args, CancellationToken cancellationToken) { if (provider.Get(args.Scope ?? "Root") diff --git a/Toolkit.Foundation/NavigateEventArgs.cs b/Toolkit.Foundation/NavigateEventArgs.cs new file mode 100644 index 0000000..b6d2ff3 --- /dev/null +++ b/Toolkit.Foundation/NavigateEventArgs.cs @@ -0,0 +1,14 @@ +namespace Toolkit.Foundation; + +public record NavigateEventArgs(string Route, + object? Context = null, + string? Scope = null, + object? Sender = null, + EventHandler? Navigated = null, + object[]? Parameters = null); + +public record NavigateEventArgs(object Context, + object Template, + object Content, + object? Sender = null, + object[]? Parameters = null); \ No newline at end of file diff --git a/Toolkit.Foundation/NavigateHandler.cs b/Toolkit.Foundation/NavigateHandler.cs index cb8b6c2..ae1a266 100644 --- a/Toolkit.Foundation/NavigateHandler.cs +++ b/Toolkit.Foundation/NavigateHandler.cs @@ -5,9 +5,9 @@ namespace Toolkit.Foundation; public class NavigateHandler(NamedComponent scope, IComponentScopeProvider componentScopeProvider, IServiceProvider provider) : - INotificationHandler + INotificationHandler { - public async Task Handle(Navigate args, + public async Task Handle(NavigateEventArgs args, CancellationToken cancellationToken) { INavigationScope? navigationScope; diff --git a/Toolkit.Foundation/NavigationChanged.cs b/Toolkit.Foundation/NavigationChanged.cs deleted file mode 100644 index b6df381..0000000 --- a/Toolkit.Foundation/NavigationChanged.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Toolkit.Foundation; - -public record NavigationChanged(TValue? Value); \ No newline at end of file diff --git a/Toolkit.Foundation/NavigationScope.cs b/Toolkit.Foundation/NavigationScope.cs index c2cefd7..cadb894 100644 --- a/Toolkit.Foundation/NavigationScope.cs +++ b/Toolkit.Foundation/NavigationScope.cs @@ -62,7 +62,7 @@ public class NavigationScope(IPublisher publisher, if (navigationProvider.Get(context is Type type ? type : context.GetType()) is INavigation navigation) { - Type navigateType = typeof(Navigate<>).MakeGenericType(navigation.Type); + Type navigateType = typeof(NavigateEventArgs<>).MakeGenericType(navigation.Type); if (Activator.CreateInstance(navigateType, [context, view, viewModel, sender, parameters]) is object navigate) { await publisher.Publish(navigate, cancellationToken); @@ -92,7 +92,7 @@ public class NavigationScope(IPublisher publisher, if (navigationProvider.Get(context is Type type ? type : context.GetType()) is INavigation navigation) { - Type navigateType = typeof(NavigateBack<>).MakeGenericType(navigation.Type); + Type navigateType = typeof(NavigateBackEventArgs<>).MakeGenericType(navigation.Type); if (Activator.CreateInstance(navigateType, [context]) is object navigate) { await publisher.Publish(navigate, cancellationToken); diff --git a/Toolkit.Foundation/ObservableCollectionViewModel.cs b/Toolkit.Foundation/ObservableCollectionViewModel.cs index 8d5dd6a..3ce3f4d 100644 --- a/Toolkit.Foundation/ObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/ObservableCollectionViewModel.cs @@ -19,11 +19,11 @@ public partial class ObservableCollectionViewModel : IList, IReadOnlyList, INotifyCollectionChanged, - INotificationHandler>, - INotificationHandler>, - INotificationHandler>, - INotificationHandler>, - INotificationHandler> + INotificationHandler>, + INotificationHandler>, + INotificationHandler>, + INotificationHandler>, + INotificationHandler> where TViewModel : notnull { @@ -121,7 +121,7 @@ public partial class ObservableCollectionViewModel : } } - public virtual Task Activated() => + public virtual Task OnActivated() => Task.CompletedTask; public TViewModel Add() @@ -222,10 +222,10 @@ public partial class ObservableCollectionViewModel : return Task.CompletedTask; } - public virtual Task Deactivated() => + public virtual Task OnDeactivated() => Task.CompletedTask; - public virtual Task Deactivating() => + public virtual Task OnDeactivating() => Task.CompletedTask; public virtual void Dispose() @@ -240,7 +240,7 @@ public partial class ObservableCollectionViewModel : IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)collection).GetEnumerator(); - public Task Handle(Remove args, + public Task Handle(RemoveEventArgs args, CancellationToken cancellationToken) { foreach (TViewModel item in this.ToList()) @@ -254,7 +254,7 @@ public partial class ObservableCollectionViewModel : return Task.CompletedTask; } - public Task Handle(Create args, + public Task Handle(CreateEventArgs args, CancellationToken cancellationToken) { if (args.Value is TViewModel item) @@ -265,7 +265,7 @@ public partial class ObservableCollectionViewModel : return Task.CompletedTask; } - public Task Handle(Insert args, + public Task Handle(InsertEventArgs args, CancellationToken cancellationToken) { if (args.Value is TViewModel item) @@ -276,7 +276,7 @@ public partial class ObservableCollectionViewModel : return Task.CompletedTask; } - public Task Handle(Move args, + public Task Handle(MoveEventArgs args, CancellationToken cancellationToken) { if (args.Value is TViewModel item) @@ -287,7 +287,7 @@ public partial class ObservableCollectionViewModel : return Task.CompletedTask; } - public Task Handle(Replace args, + public Task Handle(ReplaceEventArgs args, CancellationToken cancellationToken) { if (args.Value is TViewModel item) @@ -332,7 +332,7 @@ public partial class ObservableCollectionViewModel : } protected virtual IEnumerate PrepareEnumeration(object? key) => - new Enumerate() with { Key = key }; + new EnumerateEventArgs() with { Key = key }; public void Insert(int index, TViewModel item) => InsertItem(index, item); diff --git a/Toolkit.Foundation/ObservableViewModel.cs b/Toolkit.Foundation/ObservableViewModel.cs index 779394e..9873f71 100644 --- a/Toolkit.Foundation/ObservableViewModel.cs +++ b/Toolkit.Foundation/ObservableViewModel.cs @@ -42,7 +42,7 @@ public partial class ObservableViewModel : public IPublisher Publisher { get; } - public virtual Task Activated() => + public virtual Task OnActivated() => Task.CompletedTask; public Task Deactivate() @@ -51,10 +51,10 @@ public partial class ObservableViewModel : return Task.CompletedTask; } - public virtual Task Deactivated() => + public virtual Task OnDeactivated() => Task.CompletedTask; - public virtual Task Deactivating() => + public virtual Task OnDeactivating() => Task.CompletedTask; public void Dispose() diff --git a/Toolkit.Foundation/Open.cs b/Toolkit.Foundation/Open.cs deleted file mode 100644 index 7e41b12..0000000 --- a/Toolkit.Foundation/Open.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Toolkit.Foundation; - -public record Open(TValue Value); - -public record Open -{ - public static Open As(TValue value) => new(value); - - public static Open As() where TValue : new() => new(new TValue()); -} \ No newline at end of file diff --git a/Toolkit.Foundation/Remove.cs b/Toolkit.Foundation/Remove.cs index 6cf37d8..75a5d71 100644 --- a/Toolkit.Foundation/Remove.cs +++ b/Toolkit.Foundation/Remove.cs @@ -1,12 +1,10 @@ namespace Toolkit.Foundation; -public record Remove(TValue Value); - public record Remove { - public static Remove As(TValue value) => + public static RemoveEventArgs As(TValue value) => new(value); - public static Remove As() where TValue : new() => + public static RemoveEventArgs As() where TValue : new() => new(new TValue()); } \ No newline at end of file diff --git a/Toolkit.Foundation/RemoveEventArgs.cs b/Toolkit.Foundation/RemoveEventArgs.cs new file mode 100644 index 0000000..e689d4c --- /dev/null +++ b/Toolkit.Foundation/RemoveEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record RemoveEventArgs(TValue Value); diff --git a/Toolkit.Foundation/Replace.cs b/Toolkit.Foundation/Replace.cs index b784976..f47c36f 100644 --- a/Toolkit.Foundation/Replace.cs +++ b/Toolkit.Foundation/Replace.cs @@ -1,12 +1,10 @@ namespace Toolkit.Foundation; -public record Replace(int Index, TValue Value); - public record Replace { - public static Replace As(int index, TValue value) => + public static ReplaceEventArgs As(int index, TValue value) => new(index, value); - public static Replace As(int index) where TValue : new() => + public static ReplaceEventArgs As(int index) where TValue : new() => new(index, new TValue()); } \ No newline at end of file diff --git a/Toolkit.Foundation/ReplaceEventArgs.cs b/Toolkit.Foundation/ReplaceEventArgs.cs new file mode 100644 index 0000000..e220f98 --- /dev/null +++ b/Toolkit.Foundation/ReplaceEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record ReplaceEventArgs(int Index, TValue Value); diff --git a/Toolkit.Foundation/Request.cs b/Toolkit.Foundation/Request.cs index c7f50b9..cbf71d6 100644 --- a/Toolkit.Foundation/Request.cs +++ b/Toolkit.Foundation/Request.cs @@ -1,8 +1,10 @@ namespace Toolkit.Foundation; -public record Request; - public class Request { - public static Request As() => new(); + public static RequestEventArgs As(TValue value) => + new(value); + + public static RequestEventArgs As() where TValue : new() => + new(new TValue()); } \ No newline at end of file diff --git a/Toolkit.Foundation/RequestEventArgs.cs b/Toolkit.Foundation/RequestEventArgs.cs new file mode 100644 index 0000000..109508e --- /dev/null +++ b/Toolkit.Foundation/RequestEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record RequestEventArgs(TValue Value); diff --git a/Toolkit.Foundation/Selected.cs b/Toolkit.Foundation/Selected.cs index 40a7e22..9b4182f 100644 --- a/Toolkit.Foundation/Selected.cs +++ b/Toolkit.Foundation/Selected.cs @@ -1,12 +1,10 @@ namespace Toolkit.Foundation; -public record Selected(TValue? Value); - public record Selected { - public static Selected As(TValue value) => + public static SelectedEventArgs As(TValue value) => new(value); - public static Selected As() where TValue : new() => + public static SelectedEventArgs As() where TValue : new() => new(new TValue()); } \ No newline at end of file diff --git a/Toolkit.Foundation/SelectedEventArgs.cs b/Toolkit.Foundation/SelectedEventArgs.cs new file mode 100644 index 0000000..8340338 --- /dev/null +++ b/Toolkit.Foundation/SelectedEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record SelectedEventArgs(TValue? Value); diff --git a/Toolkit.Foundation/Started.cs b/Toolkit.Foundation/StartedEventArgs.cs similarity index 52% rename from Toolkit.Foundation/Started.cs rename to Toolkit.Foundation/StartedEventArgs.cs index 3df6262..d04f3a0 100644 --- a/Toolkit.Foundation/Started.cs +++ b/Toolkit.Foundation/StartedEventArgs.cs @@ -1,3 +1,3 @@ namespace Toolkit.Foundation; -public record Started; \ No newline at end of file +public record StartedEventArgs; \ No newline at end of file diff --git a/Toolkit.Foundation/Toolkit.Foundation.csproj b/Toolkit.Foundation/Toolkit.Foundation.csproj index ef44a00..991be27 100644 --- a/Toolkit.Foundation/Toolkit.Foundation.csproj +++ b/Toolkit.Foundation/Toolkit.Foundation.csproj @@ -4,6 +4,9 @@ enable enable + + + diff --git a/Toolkit.UI.Avalonia/NavigateAction.cs b/Toolkit.UI.Avalonia/NavigateAction.cs index 93be81e..0e4c894 100644 --- a/Toolkit.UI.Avalonia/NavigateAction.cs +++ b/Toolkit.UI.Avalonia/NavigateAction.cs @@ -74,7 +74,7 @@ public class NavigateAction : ParameterBindings.Select(binding => new KeyValuePair(binding.Key, binding.Value)).ToArray() : Enumerable.Empty>()]; - observableViewModel.Publisher.Publish(new Navigate(Route, Context == this ? control : Context, Scope ?? null, + observableViewModel.Publisher.Publish(new NavigateEventArgs(Route, Context == this ? control : Context, Scope ?? null, control.DataContext, Navigated, parameters)).ConfigureAwait(false); } } diff --git a/Toolkit.UI.Avalonia/NavigateBackAction.cs b/Toolkit.UI.Avalonia/NavigateBackAction.cs index 502c680..13a92af 100644 --- a/Toolkit.UI.Avalonia/NavigateBackAction.cs +++ b/Toolkit.UI.Avalonia/NavigateBackAction.cs @@ -34,7 +34,7 @@ public class NavigateBackAction : { if (control.DataContext is IObservableViewModel observableViewModel) { - observableViewModel.Publisher.Publish(new NavigateBack(Context + observableViewModel.Publisher.Publish(new NavigateBackEventArgs(Context ?? null, Scope ?? null)).ConfigureAwait(false); } } From 1e9cb4b4b60dd68adec300234efd87cdad15eeed Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 12 May 2024 20:21:55 +0100 Subject: [PATCH 039/228] An attempt to send messages out of the scope --- .../PersonPicture/PersonPicture.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.cs b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.cs index 69f8eb5..5ee3cc2 100644 --- a/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.cs +++ b/Toolkit.UI.Controls.Avalonia/PersonPicture/PersonPicture.cs @@ -38,13 +38,13 @@ public class PersonPicture : TemplatedControl private static readonly StyledProperty TemplateSettingsProperty = AvaloniaProperty.Register(nameof(TemplateSettings)); - private FontIcon badgeGlyphIcon; - private ImageBrush badgeImageBrush; - private TextBlock badgeNumberTextBlock; - private Ellipse badgingBackgroundEllipse; - private Ellipse badgingEllipse; - private string displayNameInitials; - private TextBlock initialsTextBlock; + private FontIcon? badgeGlyphIcon; + private ImageBrush? badgeImageBrush; + private TextBlock? badgeNumberTextBlock; + private Ellipse? badgingBackgroundEllipse; + private Ellipse? badgingEllipse; + private string? displayNameInitials; + private TextBlock? initialsTextBlock; public PersonPicture() { From d6593aac3150680bff0d88512511f98d67d012a0 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 12 May 2024 22:02:51 +0100 Subject: [PATCH 040/228] Add suport for adding new items which are sorted without reloading the entrie list --- Toolkit.Foundation/Cache.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Toolkit.Foundation/Cache.cs b/Toolkit.Foundation/Cache.cs index f86b219..2b3ae64 100644 --- a/Toolkit.Foundation/Cache.cs +++ b/Toolkit.Foundation/Cache.cs @@ -4,10 +4,11 @@ using System.Reactive.Disposables; namespace Toolkit.Foundation; -public class Cache(IDisposer disposer) : +public class Cache(IDisposer disposer, + IComparer comparer) : ICache { - private readonly List cache = []; + private readonly SortedSet cache = new(comparer); public void Add(TValue value) { From 7efaf775ec45c14d5d94282175f576be64d27477 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 12 May 2024 23:21:05 +0100 Subject: [PATCH 041/228] Get disposing working for subscription manager --- Toolkit.Avalonia/ContentControlHandler.cs | 5 ++ Toolkit.Avalonia/ContentTemplate.cs | 8 +-- Toolkit.Foundation/SubscriptionManager.cs | 62 ++++++++++++++--------- 3 files changed, 47 insertions(+), 28 deletions(-) diff --git a/Toolkit.Avalonia/ContentControlHandler.cs b/Toolkit.Avalonia/ContentControlHandler.cs index 5b1f0e5..8343f78 100644 --- a/Toolkit.Avalonia/ContentControlHandler.cs +++ b/Toolkit.Avalonia/ContentControlHandler.cs @@ -43,6 +43,11 @@ public class ContentControlHandler : { await deactivated.OnDeactivated(); } + + if (content is IDisposable disposable) + { + disposable.Dispose(); + } } } diff --git a/Toolkit.Avalonia/ContentTemplate.cs b/Toolkit.Avalonia/ContentTemplate.cs index aa4be42..2471d1e 100644 --- a/Toolkit.Avalonia/ContentTemplate.cs +++ b/Toolkit.Avalonia/ContentTemplate.cs @@ -49,14 +49,16 @@ public class ContentTemplate : { await deactivated.OnDeactivated(); } + + if (content is IDisposable disposable) + { + disposable.Dispose(); + } } } control.Loaded += HandleLoaded; control.Unloaded += HandleUnloaded; - - //viewModelContentBinder?.Register(control); - return control; } } diff --git a/Toolkit.Foundation/SubscriptionManager.cs b/Toolkit.Foundation/SubscriptionManager.cs index 2310df9..f51ca3c 100644 --- a/Toolkit.Foundation/SubscriptionManager.cs +++ b/Toolkit.Foundation/SubscriptionManager.cs @@ -1,13 +1,15 @@ -namespace Toolkit.Foundation; +using System.Reactive.Disposables; -public class SubscriptionManager(SubscriptionCollection subscriptions) : +namespace Toolkit.Foundation; + +public class SubscriptionManager(SubscriptionCollection subscriptions, + IDisposer disposer) : ISubscriptionManager { public IEnumerable GetHandlers(Type notificationType, object key) { - var d = subscriptions; - if (subscriptions.TryGetValue($"{(key is not null ? $"{key}:" : "")}{notificationType}", - out List? subscribers)) + string subscriptionKey = $"{(key is not null ? $"{key}:" : "")}{notificationType}"; + if (subscriptions.TryGetValue(subscriptionKey, out List? subscribers)) { foreach (WeakReference weakRef in subscribers.ToArray()) { @@ -32,7 +34,8 @@ public class SubscriptionManager(SubscriptionCollection subscriptions) : { if (interfaceType.GetGenericArguments().FirstOrDefault() is Type argumentType) { - if (subscriptions.TryGetValue($"{(key is not null ? $"{key}:" : "")}{argumentType}", out List? subscribers)) + string subscriptionKey = $"{(key is not null ? $"{key}:" : "")}{argumentType}"; + if (subscriptions.TryGetValue(subscriptionKey, out List? subscribers)) { for (int i = subscribers.Count - 1; i >= 0; i--) { @@ -54,34 +57,43 @@ public class SubscriptionManager(SubscriptionCollection subscriptions) : { if (interfaceType.GetGenericArguments().FirstOrDefault() is Type argumentType) { - if (key is not null) - { - subscriptions.AddOrUpdate($"{key}:{argumentType}", _ => new List { new(subscriber) }, (_, collection) => - { - collection.Add(new WeakReference(subscriber)); - return collection; - }); - - } - - subscriptions.AddOrUpdate($"{argumentType}", _ => new List { new(subscriber) }, (_, collection) => + string subscriptionKey = $"{(key is not null ? $"{key}:" : "")}{argumentType}"; + subscriptions.AddOrUpdate(subscriptionKey, _ => new List { new(subscriber) }, (_, collection) => { collection.Add(new WeakReference(subscriber)); return collection; }); + + disposer.Add(subscriber, Disposable.Create(() => RemoveSubscriber(subscriber, argumentType))); } } } - private static object? GetKeyFromHandler(object handler) + private void RemoveSubscriber(object subscriber, Type argumentType) { - return handler.GetAttribute() - is NotificationAttribute attribute - ? handler.GetPropertyValue(() => attribute.Key) is { } value ? value : attribute.Key - : null; + string subscriptionKey = $"{argumentType}"; + if (subscriptions.TryGetValue(subscriptionKey, out List? subscribers)) + { + for (int i = subscribers.Count - 1; i >= 0; i--) + { + if (subscribers[i].Target == subscriber) + { + subscribers.RemoveAt(i); + } + } + + if (subscribers.Count == 0) + { + subscriptions.TryRemove(subscriptionKey, out _); + } + } } - private static IEnumerable GetHandlerInterfaces(Type handlerType) => + private object? GetKeyFromHandler(object handler) => + handler.GetAttribute() is NotificationAttribute attribute + ? handler.GetPropertyValue(() => attribute.Key) is { } value ? value : attribute.Key : null; + + private IEnumerable GetHandlerInterfaces(Type handlerType) => handlerType.GetInterfaces().Where(interfaceType => interfaceType.IsGenericType && - interfaceType.GetGenericTypeDefinition() == typeof(INotificationHandler<>)); -} \ No newline at end of file + interfaceType.GetGenericTypeDefinition() == typeof(INotificationHandler<>)); +} From c310b3080c08671a6373e83ece30efda028f54fc Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 12 May 2024 23:21:49 +0100 Subject: [PATCH 042/228] formatting --- Toolkit.Foundation/SubscriptionManager.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Toolkit.Foundation/SubscriptionManager.cs b/Toolkit.Foundation/SubscriptionManager.cs index f51ca3c..62b9216 100644 --- a/Toolkit.Foundation/SubscriptionManager.cs +++ b/Toolkit.Foundation/SubscriptionManager.cs @@ -6,7 +6,8 @@ public class SubscriptionManager(SubscriptionCollection subscriptions, IDisposer disposer) : ISubscriptionManager { - public IEnumerable GetHandlers(Type notificationType, object key) + public IEnumerable GetHandlers(Type notificationType, + object key) { string subscriptionKey = $"{(key is not null ? $"{key}:" : "")}{notificationType}"; if (subscriptions.TryGetValue(subscriptionKey, out List? subscribers)) @@ -69,7 +70,8 @@ public class SubscriptionManager(SubscriptionCollection subscriptions, } } - private void RemoveSubscriber(object subscriber, Type argumentType) + private void RemoveSubscriber(object subscriber, + Type argumentType) { string subscriptionKey = $"{argumentType}"; if (subscriptions.TryGetValue(subscriptionKey, out List? subscribers)) From 553a6fbaff87883f744bc1c20d4332ba03c3673f Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Tue, 14 May 2024 16:52:47 +0100 Subject: [PATCH 043/228] Fix some edge cases --- Toolkit.Avalonia/ContentTemplate.cs | 8 +- Toolkit.Foundation/ComponentBuilder.cs | 2 +- Toolkit.Foundation/DefaultHostBuilder.cs | 6 +- Toolkit.Foundation/Delete.cs | 10 +++ Toolkit.Foundation/DeleteEventArgs.cs | 3 + Toolkit.Foundation/Edit.cs | 10 +++ Toolkit.Foundation/EditEventArgs.cs | 3 + Toolkit.Foundation/IConfigurationSource.cs | 4 +- .../ObservableCollectionViewModel.cs | 77 +++++++++++-------- Toolkit.Foundation/ObservableViewModel.cs | 3 +- 10 files changed, 85 insertions(+), 41 deletions(-) create mode 100644 Toolkit.Foundation/Delete.cs create mode 100644 Toolkit.Foundation/DeleteEventArgs.cs create mode 100644 Toolkit.Foundation/Edit.cs create mode 100644 Toolkit.Foundation/EditEventArgs.cs diff --git a/Toolkit.Avalonia/ContentTemplate.cs b/Toolkit.Avalonia/ContentTemplate.cs index 2471d1e..499fc8c 100644 --- a/Toolkit.Avalonia/ContentTemplate.cs +++ b/Toolkit.Avalonia/ContentTemplate.cs @@ -50,10 +50,10 @@ public class ContentTemplate : await deactivated.OnDeactivated(); } - if (content is IDisposable disposable) - { - disposable.Dispose(); - } + //if (content is IDisposable disposable) + //{ + // disposable.Dispose(); + //} } } diff --git a/Toolkit.Foundation/ComponentBuilder.cs b/Toolkit.Foundation/ComponentBuilder.cs index bc3ffdf..425e3c9 100644 --- a/Toolkit.Foundation/ComponentBuilder.cs +++ b/Toolkit.Foundation/ComponentBuilder.cs @@ -29,7 +29,7 @@ public class ComponentBuilder : services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddSingleton(); services.AddTransient(); services.AddTransient(); diff --git a/Toolkit.Foundation/DefaultHostBuilder.cs b/Toolkit.Foundation/DefaultHostBuilder.cs index a65616e..5c731ac 100644 --- a/Toolkit.Foundation/DefaultHostBuilder.cs +++ b/Toolkit.Foundation/DefaultHostBuilder.cs @@ -23,9 +23,11 @@ public class DefaultHostBuilder : services.AddSingleton(); + services.AddSingleton(); + services.AddScoped(); - services.AddTransient(); + services.AddTransient(); services.AddTransient(); @@ -40,8 +42,6 @@ public class DefaultHostBuilder : services.AddScoped>(provider => new ProxyService(provider.GetRequiredService())); - services.AddScoped(); - services.AddTransient(); services.AddTransient(); diff --git a/Toolkit.Foundation/Delete.cs b/Toolkit.Foundation/Delete.cs new file mode 100644 index 0000000..a1a474c --- /dev/null +++ b/Toolkit.Foundation/Delete.cs @@ -0,0 +1,10 @@ +namespace Toolkit.Foundation; + +public record Delete +{ + public static DeleteEventArgs As(TValue value) => + new(value); + + public static DeleteEventArgs As() where TValue : new() => + new(new TValue()); +} \ No newline at end of file diff --git a/Toolkit.Foundation/DeleteEventArgs.cs b/Toolkit.Foundation/DeleteEventArgs.cs new file mode 100644 index 0000000..6901eff --- /dev/null +++ b/Toolkit.Foundation/DeleteEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record DeleteEventArgs(TValue Value); diff --git a/Toolkit.Foundation/Edit.cs b/Toolkit.Foundation/Edit.cs new file mode 100644 index 0000000..f39068d --- /dev/null +++ b/Toolkit.Foundation/Edit.cs @@ -0,0 +1,10 @@ +namespace Toolkit.Foundation; + +public record Edit +{ + public static EditEventArgs As(TValue value) => + new(value); + + public static EditEventArgs As() where TValue : new() => + new(new TValue()); +} diff --git a/Toolkit.Foundation/EditEventArgs.cs b/Toolkit.Foundation/EditEventArgs.cs new file mode 100644 index 0000000..9d1ff77 --- /dev/null +++ b/Toolkit.Foundation/EditEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record EditEventArgs(TValue Value); diff --git a/Toolkit.Foundation/IConfigurationSource.cs b/Toolkit.Foundation/IConfigurationSource.cs index 13d46f1..08da464 100644 --- a/Toolkit.Foundation/IConfigurationSource.cs +++ b/Toolkit.Foundation/IConfigurationSource.cs @@ -9,4 +9,6 @@ public interface IConfigurationSource void Set(TConfiguration value); void Set(object value); -} \ No newline at end of file +} + +public interface IRemovable : IDisposable; \ No newline at end of file diff --git a/Toolkit.Foundation/ObservableCollectionViewModel.cs b/Toolkit.Foundation/ObservableCollectionViewModel.cs index 3ce3f4d..3d5b971 100644 --- a/Toolkit.Foundation/ObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/ObservableCollectionViewModel.cs @@ -29,11 +29,18 @@ public partial class ObservableCollectionViewModel : { private readonly ObservableCollection collection = []; + private bool clearing; + [ObservableProperty] private bool isInitialized; + [ObservableProperty] + private int selectedIndex = 0; + + private bool selfDisposing; + public ObservableCollectionViewModel(IServiceProvider provider, - IServiceFactory factory, + IServiceFactory factory, IMediator mediator, IPublisher publisher, ISubscriber subscriber, @@ -121,9 +128,6 @@ public partial class ObservableCollectionViewModel : } } - public virtual Task OnActivated() => - Task.CompletedTask; - public TViewModel Add() { TViewModel? item = Factory.Create(); @@ -196,12 +200,16 @@ public partial class ObservableCollectionViewModel : public void Clear() { - foreach (TViewModel item in collection) + clearing = true; + + foreach (TViewModel item in this.ToList()) { Disposer.Dispose(item); } ClearItems(); + + clearing = false; } public bool Contains(TViewModel item) => @@ -222,18 +230,28 @@ public partial class ObservableCollectionViewModel : return Task.CompletedTask; } - public virtual Task OnDeactivated() => - Task.CompletedTask; - - public virtual Task OnDeactivating() => - Task.CompletedTask; - public virtual void Dispose() { + selfDisposing = true; + GC.SuppressFinalize(this); Disposer.Dispose(this); } + public async Task Enumerate() + { + if (this.GetAttribute() is EnumerateAttribute attribute) + { + if (attribute.Mode == EnumerateMode.Reset) + { + Clear(); + } + + object? key = this.GetPropertyValue(() => attribute.Key) is { } value ? value : attribute.Key; + await Publisher.PublishUI(PrepareEnumeration(key)); + } + } + public IEnumerator GetEnumerator() => collection.GetEnumerator(); @@ -316,24 +334,6 @@ public partial class ObservableCollectionViewModel : await Enumerate(); } - - public async Task Enumerate() - { - if (this.GetAttribute() is EnumerateAttribute attribute) - { - if (attribute.Mode == EnumerateMode.Reset) - { - Clear(); - } - - object? key = this.GetPropertyValue(() => attribute.Key) is { } value ? value : attribute.Key; - await Publisher.PublishUI(PrepareEnumeration(key)); - } - } - - protected virtual IEnumerate PrepareEnumeration(object? key) => - new EnumerateEventArgs() with { Key = key }; - public void Insert(int index, TViewModel item) => InsertItem(index, item); @@ -360,6 +360,14 @@ public partial class ObservableCollectionViewModel : return true; } + public virtual Task OnActivated() => + Task.CompletedTask; + + public virtual Task OnDeactivated() => + Task.CompletedTask; + + public virtual Task OnDeactivating() => + Task.CompletedTask; public bool Remove(TViewModel item) { int index = collection.IndexOf(item); @@ -402,23 +410,30 @@ public partial class ObservableCollectionViewModel : } protected virtual void ClearItems() => - collection.Clear(); + collection.Clear(); protected virtual void InsertItem(int index, TViewModel item) { Disposer.Add(this, item); - Disposer.Add(item, item, Disposable.Create(() => + Disposer.Add(item, Disposable.Create(() => { if (item is IList collection) { collection.Clear(); } + + if (item is IRemovable && !clearing) + { + Remove(item); + } })); collection.Insert(index, item); } + protected virtual IEnumerate PrepareEnumeration(object? key) => + new EnumerateEventArgs() with { Key = key }; protected virtual void RemoveItem(int index) => collection.RemoveAt(index); diff --git a/Toolkit.Foundation/ObservableViewModel.cs b/Toolkit.Foundation/ObservableViewModel.cs index 9873f71..22a1c16 100644 --- a/Toolkit.Foundation/ObservableViewModel.cs +++ b/Toolkit.Foundation/ObservableViewModel.cs @@ -9,7 +9,8 @@ public partial class ObservableViewModel : IActivated, IDeactivating, IDeactivated, - IDeactivatable + IDeactivatable, + IDisposable { [ObservableProperty] private bool isInitialized; From adb5ffe57afcd587015c16612d403a3d49f38598 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Tue, 14 May 2024 17:39:16 +0100 Subject: [PATCH 044/228] Fixed selection --- Toolkit.Foundation/ObservableCollectionViewModel.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Toolkit.Foundation/ObservableCollectionViewModel.cs b/Toolkit.Foundation/ObservableCollectionViewModel.cs index 3d5b971..5e29530 100644 --- a/Toolkit.Foundation/ObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/ObservableCollectionViewModel.cs @@ -32,10 +32,7 @@ public partial class ObservableCollectionViewModel : private bool clearing; [ObservableProperty] - private bool isInitialized; - - [ObservableProperty] - private int selectedIndex = 0; + private bool initialized; private bool selfDisposing; @@ -325,12 +322,12 @@ public partial class ObservableCollectionViewModel : public async Task Initialize() { - if (IsInitialized) + if (Initialized) { return; } - IsInitialized = true; + Initialized = true; await Enumerate(); } @@ -410,7 +407,7 @@ public partial class ObservableCollectionViewModel : } protected virtual void ClearItems() => - collection.Clear(); + collection.Clear(); protected virtual void InsertItem(int index, TViewModel item) From 4ba58a9863fac96ee017f2cd40498a37e2def051 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Tue, 14 May 2024 19:24:07 +0100 Subject: [PATCH 045/228] Improve Publisher --- .../ClassicDesktopStyleApplicationHandler.cs | 3 +- Toolkit.Avalonia/ContentControlHandler.cs | 3 +- Toolkit.Avalonia/ContentDialogHandler.cs | 5 +- Toolkit.Avalonia/FrameHandler.cs | 6 +- .../SingleViewApplicationHandler.cs | 3 +- Toolkit.Foundation/AppService.cs | 2 +- .../ComponentConfigurationViewModel.cs | 9 +-- .../ConfigurationChangedHandler.cs | 3 +- .../ConfigurationInitializer.cs | 5 +- Toolkit.Foundation/ConfigurationMonitor.cs | 4 +- Toolkit.Foundation/INavigationScope.cs | 6 +- Toolkit.Foundation/INotificationHandler.cs | 3 +- Toolkit.Foundation/IPublisher.cs | 36 ++++----- Toolkit.Foundation/NavigateBackHandler.cs | 7 +- Toolkit.Foundation/NavigateHandler.cs | 12 ++- Toolkit.Foundation/NavigationScope.cs | 11 ++- .../NotificationHandlerDelegate.cs | 3 +- .../NotificationHandlerWrapper.cs | 9 +-- .../ObservableCollectionViewModel.cs | 21 ++--- Toolkit.Foundation/Publisher.cs | 76 +++++++------------ Toolkit.UI.Avalonia/NavigateAction.cs | 4 +- Toolkit.UI.Avalonia/NavigateBackAction.cs | 2 +- 22 files changed, 93 insertions(+), 140 deletions(-) diff --git a/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs b/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs index 779766b..74068fe 100644 --- a/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs +++ b/Toolkit.Avalonia/ClassicDesktopStyleApplicationHandler.cs @@ -8,8 +8,7 @@ namespace Toolkit.Avalonia; public class ClassicDesktopStyleApplicationHandler : INavigateHandler { - public Task Handle(NavigateEventArgs args, - CancellationToken cancellationToken = default) + public Task Handle(NavigateEventArgs args) { if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifeTime) diff --git a/Toolkit.Avalonia/ContentControlHandler.cs b/Toolkit.Avalonia/ContentControlHandler.cs index 8343f78..0c69bc6 100644 --- a/Toolkit.Avalonia/ContentControlHandler.cs +++ b/Toolkit.Avalonia/ContentControlHandler.cs @@ -7,8 +7,7 @@ namespace Toolkit.Avalonia; public class ContentControlHandler : INavigateHandler { - public async Task Handle(NavigateEventArgs args, - CancellationToken cancellationToken) + public async Task Handle(NavigateEventArgs args) { if (args.Context is ContentControl contentControl) { diff --git a/Toolkit.Avalonia/ContentDialogHandler.cs b/Toolkit.Avalonia/ContentDialogHandler.cs index d327a3c..b7856e6 100644 --- a/Toolkit.Avalonia/ContentDialogHandler.cs +++ b/Toolkit.Avalonia/ContentDialogHandler.cs @@ -6,8 +6,7 @@ namespace Toolkit.Avalonia; public class ContentDialogHandler(IDispatcher dispatcher) : INavigateHandler { - public async Task Handle(NavigateEventArgs args, - CancellationToken cancellationToken) + public async Task Handle(NavigateEventArgs args) { if (args.Context is ContentDialog contentDialog) { @@ -86,7 +85,7 @@ public class ContentDialogHandler(IDispatcher dispatcher) : } // A hack to wait for the dialog to finish loading up to make it appear more responsive - await Task.Delay(250, cancellationToken); + await Task.Delay(250); if (content is IInitializer initializer) { await initializer.Initialize(); diff --git a/Toolkit.Avalonia/FrameHandler.cs b/Toolkit.Avalonia/FrameHandler.cs index 44e423d..04b5ab8 100644 --- a/Toolkit.Avalonia/FrameHandler.cs +++ b/Toolkit.Avalonia/FrameHandler.cs @@ -12,8 +12,7 @@ public class FrameHandler : INavigateHandler, INavigateBackHandler { - public Task Handle(NavigateEventArgs args, - CancellationToken cancellationToken) + public Task Handle(NavigateEventArgs args) { if (args.Context is Frame frame) { @@ -136,8 +135,7 @@ public class FrameHandler : return Task.CompletedTask; } - public Task Handle(NavigateBackEventArgs args, - CancellationToken cancellationToken = default) + public Task Handle(NavigateBackEventArgs args) { if (args.Context is Frame frame) { diff --git a/Toolkit.Avalonia/SingleViewApplicationHandler.cs b/Toolkit.Avalonia/SingleViewApplicationHandler.cs index 189bcc4..45825a7 100644 --- a/Toolkit.Avalonia/SingleViewApplicationHandler.cs +++ b/Toolkit.Avalonia/SingleViewApplicationHandler.cs @@ -8,8 +8,7 @@ namespace Toolkit.Avalonia; public class SingleViewApplicationHandler : INavigateHandler { - public Task Handle(NavigateEventArgs args, - CancellationToken cancellationToken = default) + public Task Handle(NavigateEventArgs args) { if (Application.Current?.ApplicationLifetime is ISingleViewApplicationLifetime lifeTime) diff --git a/Toolkit.Foundation/AppService.cs b/Toolkit.Foundation/AppService.cs index 6d054d3..a468121 100644 --- a/Toolkit.Foundation/AppService.cs +++ b/Toolkit.Foundation/AppService.cs @@ -13,7 +13,7 @@ public class AppService(IEnumerable initializers, await initializer.Initialize(); } - await publisher.Publish(cancellationToken); + publisher.Publish(cancellationToken); } public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; diff --git a/Toolkit.Foundation/ComponentConfigurationViewModel.cs b/Toolkit.Foundation/ComponentConfigurationViewModel.cs index c326c36..83dc531 100644 --- a/Toolkit.Foundation/ComponentConfigurationViewModel.cs +++ b/Toolkit.Foundation/ComponentConfigurationViewModel.cs @@ -20,8 +20,7 @@ public partial class ComponentConfigurationViewModel args, - CancellationToken cancellationToken = default) + public Task Handle(ChangedEventArgs args) { throw new NotImplementedException(); } @@ -58,8 +57,7 @@ public partial class ComponentConfigurationViewModel args, - CancellationToken cancellationToken = default) + public Task Handle(ChangedEventArgs args) { if (args.Value is TConfiguration configuration) { @@ -101,8 +99,7 @@ public partial class ComponentConfigurationViewModel args, - CancellationToken cancellationToken = default) + public Task Handle(ChangedEventArgs args) { if (args.Value is TConfiguration configuration) { diff --git a/Toolkit.Foundation/ConfigurationChangedHandler.cs b/Toolkit.Foundation/ConfigurationChangedHandler.cs index 6a1cf8a..1244aa6 100644 --- a/Toolkit.Foundation/ConfigurationChangedHandler.cs +++ b/Toolkit.Foundation/ConfigurationChangedHandler.cs @@ -5,8 +5,7 @@ public class ConfigurationChangedHandler(ConfigurationVa where TValue : class, new() { - public Task Handle(ChangedEventArgs args, - CancellationToken cancellationToken = default) + public Task Handle(ChangedEventArgs args) { if (args.Value is TConfiguration configuration) { diff --git a/Toolkit.Foundation/ConfigurationInitializer.cs b/Toolkit.Foundation/ConfigurationInitializer.cs index aaf2185..1d033b8 100644 --- a/Toolkit.Foundation/ConfigurationInitializer.cs +++ b/Toolkit.Foundation/ConfigurationInitializer.cs @@ -9,7 +9,7 @@ public class ConfigurationInitializer(IConfigurationReader(IConfigurationReader(configuration)); + publisher.PublishUI(new ActivatedEventArgs(configuration)); + return Task.CompletedTask; } } \ No newline at end of file diff --git a/Toolkit.Foundation/ConfigurationMonitor.cs b/Toolkit.Foundation/ConfigurationMonitor.cs index baa65c8..ca86ea6 100644 --- a/Toolkit.Foundation/ConfigurationMonitor.cs +++ b/Toolkit.Foundation/ConfigurationMonitor.cs @@ -11,12 +11,12 @@ public class ConfigurationMonitor(IConfigurationFile(configuration)); + publisher.PublishUI(new ChangedEventArgs(configuration)); } } diff --git a/Toolkit.Foundation/INavigationScope.cs b/Toolkit.Foundation/INavigationScope.cs index bdab8e8..b6add22 100644 --- a/Toolkit.Foundation/INavigationScope.cs +++ b/Toolkit.Foundation/INavigationScope.cs @@ -2,8 +2,8 @@ public interface INavigationScope { - Task NavigateAsync(string route, object? sender = null, object? context = null, - EventHandler? navigated = null, object[]? parameters = null, CancellationToken cancellationToken = default); + void Navigate(string route, object? sender = null, object? context = null, + EventHandler? navigated = null, object[]? parameters = null); - Task NavigateBackAsync(object? context, CancellationToken cancellationToken = default); + void Back(object? context); } \ No newline at end of file diff --git a/Toolkit.Foundation/INotificationHandler.cs b/Toolkit.Foundation/INotificationHandler.cs index 3da0f29..6d52db3 100644 --- a/Toolkit.Foundation/INotificationHandler.cs +++ b/Toolkit.Foundation/INotificationHandler.cs @@ -3,6 +3,5 @@ public interface INotificationHandler : IHandler { - Task Handle(TMessage args, - CancellationToken cancellationToken = default); + Task Handle(TMessage args); } \ No newline at end of file diff --git a/Toolkit.Foundation/IPublisher.cs b/Toolkit.Foundation/IPublisher.cs index 36d973a..c01d127 100644 --- a/Toolkit.Foundation/IPublisher.cs +++ b/Toolkit.Foundation/IPublisher.cs @@ -2,45 +2,37 @@ public interface IPublisher { - public Task Publish(object key, - CancellationToken cancellationToken = default) + void Publish(object key) where TMessage : new(); - public Task Publish(TMessage message, - CancellationToken cancellationToken = default) + void Publish(TMessage message) where TMessage : notnull; - public Task Publish(TMessage message, - object key, - CancellationToken cancellationToken = default) + void Publish(TMessage message, + object key) where TMessage : notnull; - Task PublishUI(TMessage message, - object key, - CancellationToken cancellationToken = default) + void PublishUI(TMessage message, + object key) where TMessage : notnull; - Task PublishUI(object key, - CancellationToken cancellationToken = default) + void PublishUI(object key) where TMessage : new(); - Task PublishUI(TMessage message, - CancellationToken cancellationToken = default) + void PublishUI(TMessage message) where TMessage : notnull; - Task PublishUI(object message, - CancellationToken cancellationToken = default); + void PublishUI(object message); - Task Publish(object message, + void Publish(object message, Func, Task> marshal, - object? key = null, - CancellationToken cancellationToken = default); + object? key = null); - Task PublishUI(CancellationToken cancellationToken = default) + void PublishUI() where TMessage : new(); - Task Publish(CancellationToken cancellationToken = default) + void Publish() where TMessage : new(); - public Task Publish(object message, CancellationToken cancellationToken = default); + void Publish(object message); } \ No newline at end of file diff --git a/Toolkit.Foundation/NavigateBackHandler.cs b/Toolkit.Foundation/NavigateBackHandler.cs index b8b740e..fa0785f 100644 --- a/Toolkit.Foundation/NavigateBackHandler.cs +++ b/Toolkit.Foundation/NavigateBackHandler.cs @@ -5,16 +5,17 @@ namespace Toolkit.Foundation; public class NavigateBackHandler(IComponentScopeProvider provider) : INotificationHandler { - public async Task Handle(NavigateBackEventArgs args, - CancellationToken cancellationToken) + public Task Handle(NavigateBackEventArgs args) { if (provider.Get(args.Scope ?? "Root") is ComponentScopeDescriptor descriptor) { if (descriptor?.Services?.GetService() is INavigationScope navigationScope) { - await navigationScope.NavigateBackAsync(args.Context, cancellationToken); + navigationScope.Back(args.Context); } } + + return Task.CompletedTask; } } \ No newline at end of file diff --git a/Toolkit.Foundation/NavigateHandler.cs b/Toolkit.Foundation/NavigateHandler.cs index ae1a266..7a06f42 100644 --- a/Toolkit.Foundation/NavigateHandler.cs +++ b/Toolkit.Foundation/NavigateHandler.cs @@ -7,8 +7,7 @@ public class NavigateHandler(NamedComponent scope, IServiceProvider provider) : INotificationHandler { - public async Task Handle(NavigateEventArgs args, - CancellationToken cancellationToken) + public Task Handle(NavigateEventArgs args) { INavigationScope? navigationScope; if (args.Scope == "self") @@ -21,10 +20,9 @@ public class NavigateHandler(NamedComponent scope, navigationScope = descriptor?.Services?.GetRequiredService(); } - if (navigationScope is not null) - { - await navigationScope.NavigateAsync(args.Route, args.Sender, - args.Context, args.Navigated, args.Parameters, cancellationToken); - } + navigationScope?.Navigate(args.Route, args.Sender, + args.Context, args.Navigated, args.Parameters); + + return Task.CompletedTask; } } \ No newline at end of file diff --git a/Toolkit.Foundation/NavigationScope.cs b/Toolkit.Foundation/NavigationScope.cs index cadb894..7a3430d 100644 --- a/Toolkit.Foundation/NavigationScope.cs +++ b/Toolkit.Foundation/NavigationScope.cs @@ -10,8 +10,8 @@ public class NavigationScope(IPublisher publisher, IContentTemplateDescriptorProvider contentTemplateDescriptorProvider) : INavigationScope { - public async Task NavigateAsync(string route, object? sender = null, object? context = null, - EventHandler? navigated = null, object[]? parameters = null, CancellationToken cancellationToken = default) + public void Navigate(string route, object? sender = null, object? context = null, + EventHandler? navigated = null, object[]? parameters = null) { string[] segments = route.Split('/'); int segmentCount = segments.Length; @@ -65,7 +65,7 @@ public class NavigationScope(IPublisher publisher, Type navigateType = typeof(NavigateEventArgs<>).MakeGenericType(navigation.Type); if (Activator.CreateInstance(navigateType, [context, view, viewModel, sender, parameters]) is object navigate) { - await publisher.Publish(navigate, cancellationToken); + publisher.Publish(navigate); if (currentSegmentIndex == segmentCount) { navigated?.Invoke(this, EventArgs.Empty); @@ -79,8 +79,7 @@ public class NavigationScope(IPublisher publisher, } } - public async Task NavigateBackAsync(object? context, - CancellationToken cancellationToken = default) + public void Back(object? context) { if (context is not null) { @@ -95,7 +94,7 @@ public class NavigationScope(IPublisher publisher, Type navigateType = typeof(NavigateBackEventArgs<>).MakeGenericType(navigation.Type); if (Activator.CreateInstance(navigateType, [context]) is object navigate) { - await publisher.Publish(navigate, cancellationToken); + publisher.Publish(navigate); } } } diff --git a/Toolkit.Foundation/NotificationHandlerDelegate.cs b/Toolkit.Foundation/NotificationHandlerDelegate.cs index 448ac77..f0e6031 100644 --- a/Toolkit.Foundation/NotificationHandlerDelegate.cs +++ b/Toolkit.Foundation/NotificationHandlerDelegate.cs @@ -1,4 +1,3 @@ namespace Toolkit.Foundation; -public delegate Task NotificationHandlerDelegate(TMessage message, - CancellationToken cancellationToken); \ No newline at end of file +public delegate Task NotificationHandlerDelegate(TMessage message); \ No newline at end of file diff --git a/Toolkit.Foundation/NotificationHandlerWrapper.cs b/Toolkit.Foundation/NotificationHandlerWrapper.cs index b6391bf..e012ab3 100644 --- a/Toolkit.Foundation/NotificationHandlerWrapper.cs +++ b/Toolkit.Foundation/NotificationHandlerWrapper.cs @@ -6,19 +6,18 @@ public class NotificationHandlerWrapper(INotificationHandler private readonly IEnumerable> pipelineBehaviours = pipelineBehaviours.Reverse(); - public async Task Handle(TMessage message, - CancellationToken cancellationToken) + public async Task Handle(TMessage message) { NotificationHandlerDelegate currentHandler = handler.Handle; foreach (IPipelineBehaviour behaviour in pipelineBehaviours) { NotificationHandlerDelegate previousHandler = currentHandler; - currentHandler = async (args, token) => + currentHandler = async (args) => { - await behaviour.Handle(args, previousHandler, token); + await behaviour.Handle(args, previousHandler); }; } - await currentHandler(message, cancellationToken); + await currentHandler(message); } } \ No newline at end of file diff --git a/Toolkit.Foundation/ObservableCollectionViewModel.cs b/Toolkit.Foundation/ObservableCollectionViewModel.cs index 5e29530..a4a1743 100644 --- a/Toolkit.Foundation/ObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/ObservableCollectionViewModel.cs @@ -235,7 +235,7 @@ public partial class ObservableCollectionViewModel : Disposer.Dispose(this); } - public async Task Enumerate() + public void Enumerate() { if (this.GetAttribute() is EnumerateAttribute attribute) { @@ -245,7 +245,7 @@ public partial class ObservableCollectionViewModel : } object? key = this.GetPropertyValue(() => attribute.Key) is { } value ? value : attribute.Key; - await Publisher.PublishUI(PrepareEnumeration(key)); + Publisher.PublishUI(PrepareEnumeration(key)); } } @@ -255,8 +255,7 @@ public partial class ObservableCollectionViewModel : IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)collection).GetEnumerator(); - public Task Handle(RemoveEventArgs args, - CancellationToken cancellationToken) + public Task Handle(RemoveEventArgs args) { foreach (TViewModel item in this.ToList()) { @@ -269,8 +268,7 @@ public partial class ObservableCollectionViewModel : return Task.CompletedTask; } - public Task Handle(CreateEventArgs args, - CancellationToken cancellationToken) + public Task Handle(CreateEventArgs args) { if (args.Value is TViewModel item) { @@ -280,8 +278,7 @@ public partial class ObservableCollectionViewModel : return Task.CompletedTask; } - public Task Handle(InsertEventArgs args, - CancellationToken cancellationToken) + public Task Handle(InsertEventArgs args) { if (args.Value is TViewModel item) { @@ -291,8 +288,7 @@ public partial class ObservableCollectionViewModel : return Task.CompletedTask; } - public Task Handle(MoveEventArgs args, - CancellationToken cancellationToken) + public Task Handle(MoveEventArgs args) { if (args.Value is TViewModel item) { @@ -302,8 +298,7 @@ public partial class ObservableCollectionViewModel : return Task.CompletedTask; } - public Task Handle(ReplaceEventArgs args, - CancellationToken cancellationToken) + public Task Handle(ReplaceEventArgs args) { if (args.Value is TViewModel item) { @@ -328,7 +323,7 @@ public partial class ObservableCollectionViewModel : } Initialized = true; - await Enumerate(); + Enumerate(); } public void Insert(int index, TViewModel item) => diff --git a/Toolkit.Foundation/Publisher.cs b/Toolkit.Foundation/Publisher.cs index 6f1ce5a..26ff1d5 100644 --- a/Toolkit.Foundation/Publisher.cs +++ b/Toolkit.Foundation/Publisher.cs @@ -8,29 +8,21 @@ public class Publisher(ISubscriptionManager subscriptionManager, IDispatcher dispatcher) : IPublisher { - public Task Publish(object key, - CancellationToken cancellationToken = default) + public void Publish(object key) where TMessage : new() => - Publish(new TMessage(), async args => await args(), - key, cancellationToken); + Publish(new TMessage(), async args => await args(), key); - public Task Publish(TMessage message, - CancellationToken cancellationToken = default) + public void Publish(TMessage message) where TMessage : notnull => - Publish(message, async args => await args(), - null, cancellationToken); + Publish(message, async args => await args(), null); - public Task Publish(TMessage message, - object key, - CancellationToken cancellationToken = default) - where TMessage : notnull => - Publish(message, async args => await args(), - key, cancellationToken); + public void Publish(TMessage message, object key) + where TMessage : notnull => + Publish(message, async args => await args(), key); - public async Task Publish(object message, + public void Publish(object message, Func, Task> marshal, - object? key = null, - CancellationToken cancellationToken = default) + object? key = null) { Type notificationType = message.GetType(); @@ -49,53 +41,41 @@ public class Publisher(ISubscriptionManager subscriptionManager, { Type? handlerType = handler.GetType(); MethodInfo? handleMethod = handlerType.GetMethod("Handle", - [notificationType, typeof(CancellationToken)]); + [notificationType]); if (handleMethod is not null) { - await marshal(() => (Task)handleMethod.Invoke(handler, new object[] - { message, cancellationToken })!); + marshal(() => (Task)handleMethod.Invoke(handler, new object[] + { message })!); } } } } - public Task Publish(object message, - CancellationToken cancellationToken = default) => Publish(message, - async args => await args(), - null, cancellationToken); + public void Publish(object message) => Publish(message, + async args => await args(), null); - public Task Publish(CancellationToken cancellationToken = default) + public void Publish() where TMessage : new() => - Publish(new TMessage(), async args => await args(), - null, cancellationToken); + Publish(new TMessage(), async args => await args(), null); - public Task PublishUI(object key, - CancellationToken cancellationToken = default) - where TMessage : new() => - Publish(new TMessage(), args => dispatcher.Invoke(async () => await args()), - key, cancellationToken); + public void PublishUI(object key) + where TMessage : new() => + Publish(new TMessage(), args => dispatcher.Invoke(async () => await args()), key); - public Task PublishUI(TMessage message, - CancellationToken cancellationToken = default) + public void PublishUI(TMessage message) where TMessage : notnull => - Publish(message, args => dispatcher.Invoke(async () => await args()), - null, cancellationToken); + Publish(message, args => dispatcher.Invoke(async () => await args()), null); - public Task PublishUI(TMessage message, - object key, - CancellationToken cancellationToken = default) + public void PublishUI(TMessage message, + object key) where TMessage : notnull => - Publish(message, args => dispatcher.Invoke(async () => await args()), - key, cancellationToken); + Publish(message, args => dispatcher.Invoke(async () => await args()), key); - public Task PublishUI(CancellationToken cancellationToken = default) + public void PublishUI() where TMessage : new() => - Publish(new TMessage(), args => dispatcher.Invoke(async () => await args()), - null, cancellationToken); + Publish(new TMessage(), args => dispatcher.Invoke(async () => await args()), null); - public Task PublishUI(object message, - CancellationToken cancellationToken = default) => Publish(message, args => - dispatcher.Invoke(async () => await args()), - null, cancellationToken); + public void PublishUI(object message) => Publish(message, args => + dispatcher.Invoke(async () => await args()), null); } \ No newline at end of file diff --git a/Toolkit.UI.Avalonia/NavigateAction.cs b/Toolkit.UI.Avalonia/NavigateAction.cs index 0e4c894..92cc6aa 100644 --- a/Toolkit.UI.Avalonia/NavigateAction.cs +++ b/Toolkit.UI.Avalonia/NavigateAction.cs @@ -74,8 +74,8 @@ public class NavigateAction : ParameterBindings.Select(binding => new KeyValuePair(binding.Key, binding.Value)).ToArray() : Enumerable.Empty>()]; - observableViewModel.Publisher.Publish(new NavigateEventArgs(Route, Context == this ? control : Context, Scope ?? null, - control.DataContext, Navigated, parameters)).ConfigureAwait(false); + observableViewModel.Publisher.Publish(new NavigateEventArgs(Route, Context == this ? control : Context, Scope ?? null, + control.DataContext, Navigated, parameters)); } } diff --git a/Toolkit.UI.Avalonia/NavigateBackAction.cs b/Toolkit.UI.Avalonia/NavigateBackAction.cs index 13a92af..db925ed 100644 --- a/Toolkit.UI.Avalonia/NavigateBackAction.cs +++ b/Toolkit.UI.Avalonia/NavigateBackAction.cs @@ -35,7 +35,7 @@ public class NavigateBackAction : if (control.DataContext is IObservableViewModel observableViewModel) { observableViewModel.Publisher.Publish(new NavigateBackEventArgs(Context - ?? null, Scope ?? null)).ConfigureAwait(false); + ?? null, Scope ?? null)); } } From 32829d1cf370922c51338f5a7c6178a52f769988 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Tue, 14 May 2024 19:49:29 +0100 Subject: [PATCH 046/228] Fixed edge cases with selections --- .../ObservableCollectionViewModel.cs | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/Toolkit.Foundation/ObservableCollectionViewModel.cs b/Toolkit.Foundation/ObservableCollectionViewModel.cs index a4a1743..00f06c8 100644 --- a/Toolkit.Foundation/ObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/ObservableCollectionViewModel.cs @@ -34,10 +34,11 @@ public partial class ObservableCollectionViewModel : [ObservableProperty] private bool initialized; - private bool selfDisposing; + [ObservableProperty] + private int selectedIndex = 0; public ObservableCollectionViewModel(IServiceProvider provider, - IServiceFactory factory, + IServiceFactory factory, IMediator mediator, IPublisher publisher, ISubscriber subscriber, @@ -181,6 +182,7 @@ public partial class ObservableCollectionViewModel : } catch (InvalidCastException) { + } Add(item!); @@ -205,7 +207,6 @@ public partial class ObservableCollectionViewModel : } ClearItems(); - clearing = false; } @@ -229,8 +230,6 @@ public partial class ObservableCollectionViewModel : public virtual void Dispose() { - selfDisposing = true; - GC.SuppressFinalize(this); Disposer.Dispose(this); } @@ -315,15 +314,17 @@ public partial class ObservableCollectionViewModel : IsCompatibleObject(value) ? IndexOf((TViewModel)value!) : -1; - public async Task Initialize() + public Task Initialize() { if (Initialized) { - return; + return Task.CompletedTask; } Initialized = true; Enumerate(); + + return Task.CompletedTask; } public void Insert(int index, TViewModel item) => @@ -360,6 +361,7 @@ public partial class ObservableCollectionViewModel : public virtual Task OnDeactivating() => Task.CompletedTask; + public bool Remove(TViewModel item) { int index = collection.IndexOf(item); @@ -410,13 +412,13 @@ public partial class ObservableCollectionViewModel : Disposer.Add(this, item); Disposer.Add(item, Disposable.Create(() => { - if (item is IList collection) - { - collection.Clear(); - } - if (item is IRemovable && !clearing) { + if (item is IList collection) + { + collection.Clear(); + } + Remove(item); } })); @@ -425,7 +427,8 @@ public partial class ObservableCollectionViewModel : } protected virtual IEnumerate PrepareEnumeration(object? key) => - new EnumerateEventArgs() with { Key = key }; + new EnumerateEventArgs() with { Key = key }; + protected virtual void RemoveItem(int index) => collection.RemoveAt(index); @@ -437,6 +440,19 @@ public partial class ObservableCollectionViewModel : private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args) => CollectionChanged?.Invoke(this, args); + + partial void OnSelectedIndexChanged(int oldValue, int newValue) + { + if (oldValue >= 0 && oldValue <= this.Count -1 && this[oldValue] is ISelectable removed) + { + removed.Selected = false; + } + + if (newValue >= 0 && newValue <= this.Count - 1 && this[newValue] is ISelectable added) + { + added.Selected = true; + } + } } public partial class ObservableCollectionViewModel(IServiceProvider provider, From 7e57c0d28d7f7ccd729a587011c38276bac40e03 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Tue, 14 May 2024 22:49:16 +0100 Subject: [PATCH 047/228] WIP to allow mediter handlers to be subscribed to --- Toolkit.Foundation/CommandValueViewModel.cs | 2 +- Toolkit.Foundation/CommandViewModel.cs | 2 +- Toolkit.Foundation/ComponentBuilder.cs | 9 ++- .../ComponentConfigurationViewModel.cs | 6 +- Toolkit.Foundation/DefaultHostBuilder.cs | 6 +- Toolkit.Foundation/HandlerProvider.cs | 26 +++++++++ Toolkit.Foundation/IHandlerProvider.cs | 8 +++ .../{ISubscriber.cs => ISubscription.cs} | 6 +- Toolkit.Foundation/ISubscriptionManager.cs | 10 ---- .../ObservableCollectionViewModel.cs | 8 +-- Toolkit.Foundation/ObservableViewModel.cs | 6 +- Toolkit.Foundation/Publisher.cs | 6 +- Toolkit.Foundation/Subscriber.cs | 11 ---- ...SubscriptionManager.cs => Subscription.cs} | 56 ++++++------------- Toolkit.Foundation/ValueViewModel.cs | 2 +- 15 files changed, 81 insertions(+), 83 deletions(-) create mode 100644 Toolkit.Foundation/HandlerProvider.cs create mode 100644 Toolkit.Foundation/IHandlerProvider.cs rename Toolkit.Foundation/{ISubscriber.cs => ISubscription.cs} (77%) delete mode 100644 Toolkit.Foundation/ISubscriptionManager.cs delete mode 100644 Toolkit.Foundation/Subscriber.cs rename Toolkit.Foundation/{SubscriptionManager.cs => Subscription.cs} (75%) diff --git a/Toolkit.Foundation/CommandValueViewModel.cs b/Toolkit.Foundation/CommandValueViewModel.cs index dd8bbb4..ffe6c82 100644 --- a/Toolkit.Foundation/CommandValueViewModel.cs +++ b/Toolkit.Foundation/CommandValueViewModel.cs @@ -6,7 +6,7 @@ public partial class CommandValueViewModel(IServiceProvider provider, IServiceFactory factory, IMediator mediator, IPublisher publisher, - ISubscriber subscriber, + ISubscription subscriber, IDisposer disposer) : ValueViewModel(provider, factory, mediator, publisher, subscriber, disposer) { diff --git a/Toolkit.Foundation/CommandViewModel.cs b/Toolkit.Foundation/CommandViewModel.cs index 8727673..7c1e9ee 100644 --- a/Toolkit.Foundation/CommandViewModel.cs +++ b/Toolkit.Foundation/CommandViewModel.cs @@ -6,7 +6,7 @@ public partial class CommandViewModel(IServiceProvider provider, IServiceFactory factory, IMediator mediator, IPublisher publisher, - ISubscriber subscriber, + ISubscription subscriber, IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) { diff --git a/Toolkit.Foundation/ComponentBuilder.cs b/Toolkit.Foundation/ComponentBuilder.cs index 425e3c9..21e1cbf 100644 --- a/Toolkit.Foundation/ComponentBuilder.cs +++ b/Toolkit.Foundation/ComponentBuilder.cs @@ -27,11 +27,14 @@ public class ComponentBuilder : new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!))); - services.AddScoped(); - services.AddScoped(); services.AddSingleton(); - services.AddTransient(); + services.AddScoped(); + + services.AddTransient(); + services.AddTransient(); + + services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/Toolkit.Foundation/ComponentConfigurationViewModel.cs b/Toolkit.Foundation/ComponentConfigurationViewModel.cs index 83dc531..888d7c1 100644 --- a/Toolkit.Foundation/ComponentConfigurationViewModel.cs +++ b/Toolkit.Foundation/ComponentConfigurationViewModel.cs @@ -12,7 +12,7 @@ public partial class ComponentConfigurationViewModel(); services.AddScoped(); - services.AddTransient(); - services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + + services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/Toolkit.Foundation/HandlerProvider.cs b/Toolkit.Foundation/HandlerProvider.cs new file mode 100644 index 0000000..404a997 --- /dev/null +++ b/Toolkit.Foundation/HandlerProvider.cs @@ -0,0 +1,26 @@ +namespace Toolkit.Foundation; + +public class HandlerProvider(SubscriptionCollection subscriptions) : + IHandlerProvider +{ + public IEnumerable Get(Type type, + object key) + { + string subscriptionKey = $"{(key is not null ? $"{key}:" : "")}{type}"; + if (subscriptions.TryGetValue(subscriptionKey, out List? subscribers)) + { + foreach (WeakReference weakRef in subscribers.ToArray()) + { + object? target = weakRef.Target; + if (target != null) + { + yield return target; + } + else + { + subscribers.Remove(weakRef); + } + } + } + } +} diff --git a/Toolkit.Foundation/IHandlerProvider.cs b/Toolkit.Foundation/IHandlerProvider.cs new file mode 100644 index 0000000..fa01b63 --- /dev/null +++ b/Toolkit.Foundation/IHandlerProvider.cs @@ -0,0 +1,8 @@ + +namespace Toolkit.Foundation +{ + public interface IHandlerProvider + { + IEnumerable Get(Type type, object key); + } +} \ No newline at end of file diff --git a/Toolkit.Foundation/ISubscriber.cs b/Toolkit.Foundation/ISubscription.cs similarity index 77% rename from Toolkit.Foundation/ISubscriber.cs rename to Toolkit.Foundation/ISubscription.cs index 6948bb2..858c85d 100644 --- a/Toolkit.Foundation/ISubscriber.cs +++ b/Toolkit.Foundation/ISubscription.cs @@ -1,8 +1,8 @@ namespace Toolkit.Foundation; -public interface ISubscriber +public interface ISubscription { - void Remove(object subscriber); - void Add(object subscriber); + + void Remove(object subscriber); } \ No newline at end of file diff --git a/Toolkit.Foundation/ISubscriptionManager.cs b/Toolkit.Foundation/ISubscriptionManager.cs deleted file mode 100644 index c6866b8..0000000 --- a/Toolkit.Foundation/ISubscriptionManager.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Toolkit.Foundation; - -public interface ISubscriptionManager -{ - IEnumerable GetHandlers(Type notificationType, object key); - - void Remove(object subscriber); - - void Add(object subscriber); -} \ No newline at end of file diff --git a/Toolkit.Foundation/ObservableCollectionViewModel.cs b/Toolkit.Foundation/ObservableCollectionViewModel.cs index 00f06c8..7706ab7 100644 --- a/Toolkit.Foundation/ObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/ObservableCollectionViewModel.cs @@ -41,7 +41,7 @@ public partial class ObservableCollectionViewModel : IServiceFactory factory, IMediator mediator, IPublisher publisher, - ISubscriber subscriber, + ISubscription subscriber, IDisposer disposer) { Provider = provider; @@ -59,7 +59,7 @@ public partial class ObservableCollectionViewModel : IServiceFactory factory, IMediator mediator, IPublisher publisher, - ISubscriber subscriber, + ISubscription subscriber, IDisposer disposer, IEnumerable items) { @@ -459,7 +459,7 @@ public partial class ObservableCollectionViewModel(IServiceP IServiceFactory factory, IMediator mediator, IPublisher publisher, - ISubscriber subscriber, IDisposer disposer) : ObservableCollectionViewModel(provider, factory, mediator, publisher, subscriber, disposer) + ISubscription subscriber, IDisposer disposer) : ObservableCollectionViewModel(provider, factory, mediator, publisher, subscriber, disposer) where TViewModel : notnull { [ObservableProperty] @@ -470,6 +470,6 @@ public class ObservableCollectionViewModel(IServiceProvider provider, IServiceFactory factory, IMediator mediator, IPublisher publisher, - ISubscriber subscriber, + ISubscription subscriber, IDisposer disposer) : ObservableCollectionViewModel(provider, factory, mediator, publisher, subscriber, disposer); \ No newline at end of file diff --git a/Toolkit.Foundation/ObservableViewModel.cs b/Toolkit.Foundation/ObservableViewModel.cs index 22a1c16..35aefce 100644 --- a/Toolkit.Foundation/ObservableViewModel.cs +++ b/Toolkit.Foundation/ObservableViewModel.cs @@ -19,7 +19,7 @@ public partial class ObservableViewModel : IServiceFactory factory, IMediator mediator, IPublisher publisher, - ISubscriber subscriber, + ISubscription subscriber, IDisposer disposer) { Provider = provider; @@ -80,7 +80,7 @@ public partial class ObservableViewModel(IServiceProvider provider, IServiceFactory factory, IMediator mediator, IPublisher publisher, - ISubscriber subscriber, + ISubscription subscriber, IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) where TValue : notnull { @@ -92,7 +92,7 @@ public partial class ObservableViewModel(IServiceProvider provider IServiceFactory factory, IMediator mediator, IPublisher publisher, - ISubscriber subscriber, + ISubscription subscriber, IDisposer disposer, TValue? value = null) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) where TValue : class diff --git a/Toolkit.Foundation/Publisher.cs b/Toolkit.Foundation/Publisher.cs index 26ff1d5..ccaa823 100644 --- a/Toolkit.Foundation/Publisher.cs +++ b/Toolkit.Foundation/Publisher.cs @@ -3,7 +3,7 @@ using System.Reflection; namespace Toolkit.Foundation; -public class Publisher(ISubscriptionManager subscriptionManager, +public class Publisher(IHandlerProvider handlerProvider, IServiceProvider provider, IDispatcher dispatcher) : IPublisher @@ -29,8 +29,8 @@ public class Publisher(ISubscriptionManager subscriptionManager, List handlers = provider.GetServices(typeof(NotificationHandlerWrapper<>) .MakeGenericType(notificationType)).ToList(); - foreach (object? handler in subscriptionManager - .GetHandlers(notificationType, key!)) + foreach (object? handler in handlerProvider + .Get(notificationType, key!)) { handlers.Add(handler); } diff --git a/Toolkit.Foundation/Subscriber.cs b/Toolkit.Foundation/Subscriber.cs deleted file mode 100644 index 83898a2..0000000 --- a/Toolkit.Foundation/Subscriber.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Toolkit.Foundation; - -public class Subscriber(ISubscriptionManager subscriptionManager) : - ISubscriber -{ - public void Remove(object subscriber) => - subscriptionManager.Remove(subscriber); - - public void Add(object subscriber) => - subscriptionManager.Add(subscriber); -} \ No newline at end of file diff --git a/Toolkit.Foundation/SubscriptionManager.cs b/Toolkit.Foundation/Subscription.cs similarity index 75% rename from Toolkit.Foundation/SubscriptionManager.cs rename to Toolkit.Foundation/Subscription.cs index 62b9216..1905d52 100644 --- a/Toolkit.Foundation/SubscriptionManager.cs +++ b/Toolkit.Foundation/Subscription.cs @@ -2,27 +2,26 @@ namespace Toolkit.Foundation; -public class SubscriptionManager(SubscriptionCollection subscriptions, - IDisposer disposer) : - ISubscriptionManager +public class Subscription(SubscriptionCollection subscriptions, + IDisposer disposer) : + ISubscription { - public IEnumerable GetHandlers(Type notificationType, - object key) + public void Add(object subscriber) { - string subscriptionKey = $"{(key is not null ? $"{key}:" : "")}{notificationType}"; - if (subscriptions.TryGetValue(subscriptionKey, out List? subscribers)) + Type handlerType = subscriber.GetType(); + object? key = GetKeyFromHandler(subscriber); + foreach (Type interfaceType in GetHandlerInterfaces(handlerType)) { - foreach (WeakReference weakRef in subscribers.ToArray()) + if (interfaceType.GetGenericArguments().FirstOrDefault() is Type argumentType) { - object? target = weakRef.Target; - if (target != null) + string subscriptionKey = $"{(key is not null ? $"{key}:" : "")}{argumentType}"; + subscriptions.AddOrUpdate(subscriptionKey, _ => new List { new(subscriber) }, (_, collection) => { - yield return target; - } - else - { - subscribers.Remove(weakRef); - } + collection.Add(new WeakReference(subscriber)); + return collection; + }); + + disposer.Add(subscriber, Disposable.Create(() => RemoveSubscriber(subscriber, argumentType))); } } } @@ -50,27 +49,8 @@ public class SubscriptionManager(SubscriptionCollection subscriptions, } } - public void Add(object subscriber) - { - Type handlerType = subscriber.GetType(); - object? key = GetKeyFromHandler(subscriber); - foreach (Type interfaceType in GetHandlerInterfaces(handlerType)) - { - if (interfaceType.GetGenericArguments().FirstOrDefault() is Type argumentType) - { - string subscriptionKey = $"{(key is not null ? $"{key}:" : "")}{argumentType}"; - subscriptions.AddOrUpdate(subscriptionKey, _ => new List { new(subscriber) }, (_, collection) => - { - collection.Add(new WeakReference(subscriber)); - return collection; - }); - disposer.Add(subscriber, Disposable.Create(() => RemoveSubscriber(subscriber, argumentType))); - } - } - } - - private void RemoveSubscriber(object subscriber, + private void RemoveSubscriber(object subscriber, Type argumentType) { string subscriptionKey = $"{argumentType}"; @@ -91,11 +71,11 @@ public class SubscriptionManager(SubscriptionCollection subscriptions, } } - private object? GetKeyFromHandler(object handler) => + private object? GetKeyFromHandler(object handler) => handler.GetAttribute() is NotificationAttribute attribute ? handler.GetPropertyValue(() => attribute.Key) is { } value ? value : attribute.Key : null; private IEnumerable GetHandlerInterfaces(Type handlerType) => handlerType.GetInterfaces().Where(interfaceType => interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(INotificationHandler<>)); -} +} \ No newline at end of file diff --git a/Toolkit.Foundation/ValueViewModel.cs b/Toolkit.Foundation/ValueViewModel.cs index aed8932..e98c488 100644 --- a/Toolkit.Foundation/ValueViewModel.cs +++ b/Toolkit.Foundation/ValueViewModel.cs @@ -6,7 +6,7 @@ public partial class ValueViewModel(IServiceProvider provider, IServiceFactory factory, IMediator mediator, IPublisher publisher, - ISubscriber subscriber, + ISubscription subscriber, IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) { From 8c0436644a0bb9cec855371c975fec6d6706a2fb Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Wed, 15 May 2024 19:28:08 +0100 Subject: [PATCH 048/228] Allow the Mediator to be subscribed to and therefore handled --- Toolkit.Foundation/HandlerProvider.cs | 2 +- Toolkit.Foundation/IHandlerProvider.cs | 3 +- Toolkit.Foundation/IMediator.cs | 8 +++- Toolkit.Foundation/Mediator.cs | 57 +++++++++++++++++++++----- Toolkit.Foundation/Publisher.cs | 12 +++--- Toolkit.Foundation/Subscription.cs | 9 +++- 6 files changed, 67 insertions(+), 24 deletions(-) diff --git a/Toolkit.Foundation/HandlerProvider.cs b/Toolkit.Foundation/HandlerProvider.cs index 404a997..caadcde 100644 --- a/Toolkit.Foundation/HandlerProvider.cs +++ b/Toolkit.Foundation/HandlerProvider.cs @@ -4,7 +4,7 @@ public class HandlerProvider(SubscriptionCollection subscriptions) : IHandlerProvider { public IEnumerable Get(Type type, - object key) + object? key = null) { string subscriptionKey = $"{(key is not null ? $"{key}:" : "")}{type}"; if (subscriptions.TryGetValue(subscriptionKey, out List? subscribers)) diff --git a/Toolkit.Foundation/IHandlerProvider.cs b/Toolkit.Foundation/IHandlerProvider.cs index fa01b63..f155b6c 100644 --- a/Toolkit.Foundation/IHandlerProvider.cs +++ b/Toolkit.Foundation/IHandlerProvider.cs @@ -3,6 +3,7 @@ namespace Toolkit.Foundation { public interface IHandlerProvider { - IEnumerable Get(Type type, object key); + IEnumerable Get(Type type, + object? key = null); } } \ No newline at end of file diff --git a/Toolkit.Foundation/IMediator.cs b/Toolkit.Foundation/IMediator.cs index df84e29..3879384 100644 --- a/Toolkit.Foundation/IMediator.cs +++ b/Toolkit.Foundation/IMediator.cs @@ -3,7 +3,11 @@ namespace Toolkit.Foundation { public interface IMediator { - Task Handle(object message, CancellationToken cancellationToken = default); - Task Handle(TRequest request, CancellationToken cancellationToken = default) where TRequest : notnull; + Task Handle(object message, + CancellationToken cancellationToken = default); + + Task Handle(TMessage message, + CancellationToken cancellationToken = default) + where TMessage : notnull; } } \ No newline at end of file diff --git a/Toolkit.Foundation/Mediator.cs b/Toolkit.Foundation/Mediator.cs index 3e9d354..12c625e 100644 --- a/Toolkit.Foundation/Mediator.cs +++ b/Toolkit.Foundation/Mediator.cs @@ -1,27 +1,62 @@ -using System.Reflection; +using Microsoft.Extensions.DependencyInjection; +using System.Reflection; namespace Toolkit.Foundation; -public class Mediator(IServiceProvider provider) : +public class Mediator(IHandlerProvider handlerProvider, + IServiceProvider provider) : IMediator { - public Task Handle(TRequest request, + public async Task Handle(TMessage message, CancellationToken cancellationToken = default) - where TRequest : notnull + where TMessage : notnull { - Type handlerType = typeof(HandlerWrapper<,>).MakeGenericType(request.GetType(), - typeof(TResponse)); + Type messageType = message.GetType(); + Type handlerWrapperType = typeof(HandlerWrapper<,>).MakeGenericType(messageType, typeof(TResponse)); - if (provider.GetService(handlerType) - is object handler) + Dictionary> handlers = []; + + foreach (object? service in provider.GetServices(handlerWrapperType)) { - if (handlerType.GetMethod("Handle") is MethodInfo handleMethod) + if (service?.GetType() is Type serviceType) { - return (Task)handleMethod.Invoke(handler, new object[] { request, cancellationToken })!; + if (!handlers.TryGetValue(serviceType, out List? handlerList)) + { + handlerList = []; + handlers.Add(serviceType, handlerList); + } + + handlerList.Add(service); } } - return Task.FromResult(default); + foreach (object? handler in handlerProvider.Get(messageType)) + { + if (handler is not null) + { + Type handlerType = handler.GetType(); + if (!handlers.TryGetValue(handlerType, out List? handlerList)) + { + handlerList = []; + handlers.Add(handlerType, handlerList); + } + handlerList.Add(handler); + } + } + + foreach (KeyValuePair> handlerEntry in handlers) + { + foreach (object? handler in handlerEntry.Value) + { + if (handler?.GetType().GetMethod("Handle") is MethodInfo handleMethod) + { + return await (Task)handleMethod.Invoke(handler, + new object[] { message, cancellationToken })!; + } + } + } + + return default; } public Task Handle(object message, diff --git a/Toolkit.Foundation/Publisher.cs b/Toolkit.Foundation/Publisher.cs index ccaa823..cecca5c 100644 --- a/Toolkit.Foundation/Publisher.cs +++ b/Toolkit.Foundation/Publisher.cs @@ -25,12 +25,11 @@ public class Publisher(IHandlerProvider handlerProvider, object? key = null) { Type notificationType = message.GetType(); + Type handlerType = typeof(NotificationHandlerWrapper<>) + .MakeGenericType(notificationType); - List handlers = provider.GetServices(typeof(NotificationHandlerWrapper<>) - .MakeGenericType(notificationType)).ToList(); - - foreach (object? handler in handlerProvider - .Get(notificationType, key!)) + List handlers = provider.GetServices(handlerType).ToList(); + foreach (object? handler in handlerProvider.Get(notificationType, key)) { handlers.Add(handler); } @@ -39,8 +38,7 @@ public class Publisher(IHandlerProvider handlerProvider, { if (handler is not null) { - Type? handlerType = handler.GetType(); - MethodInfo? handleMethod = handlerType.GetMethod("Handle", + MethodInfo? handleMethod = handler.GetType().GetMethod("Handle", [notificationType]); if (handleMethod is not null) diff --git a/Toolkit.Foundation/Subscription.cs b/Toolkit.Foundation/Subscription.cs index 1905d52..299b9d9 100644 --- a/Toolkit.Foundation/Subscription.cs +++ b/Toolkit.Foundation/Subscription.cs @@ -76,6 +76,11 @@ public class Subscription(SubscriptionCollection subscriptions, ? handler.GetPropertyValue(() => attribute.Key) is { } value ? value : attribute.Key : null; private IEnumerable GetHandlerInterfaces(Type handlerType) => - handlerType.GetInterfaces().Where(interfaceType => interfaceType.IsGenericType && - interfaceType.GetGenericTypeDefinition() == typeof(INotificationHandler<>)); + handlerType.GetInterfaces().Where(interfaceType => + { + Type? definition = interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : null; + return definition == typeof(INotificationHandler<>) || + definition == typeof(IHandler<>) || + definition == typeof(IHandler<,>); + }); } \ No newline at end of file From 7f3c4c53ccb592b1eefead03b947d1fb426afd31 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Thu, 16 May 2024 21:41:48 +0100 Subject: [PATCH 049/228] Fixed more edge cases --- Toolkit.Foundation/ComponentBuilder.cs | 3 +- Toolkit.Foundation/HandlerProvider.cs | 1 + Toolkit.Foundation/IDisposerRequired.cs | 6 +++ Toolkit.Foundation/IHandlerProvider.cs | 11 +++-- Toolkit.Foundation/IMediatorRequired.cs | 6 +++ Toolkit.Foundation/IPublisherRequired.cs | 6 +++ Toolkit.Foundation/IServiceFactoryRequired.cs | 6 +++ .../IServiceProviderRequired.cs | 6 +++ Toolkit.Foundation/ISubscriptionRequired.cs | 6 +++ Toolkit.Foundation/Mediator.cs | 3 +- Toolkit.Foundation/NavigateHandler.cs | 7 ++-- Toolkit.Foundation/NavigationScope.cs | 40 ++++++++++--------- .../ObservableCollectionViewModel.cs | 5 +++ Toolkit.Foundation/ObservableViewModel.cs | 7 +++- Toolkit.Foundation/Validation.cs | 10 +++++ Toolkit.Foundation/ValidationEventArgs.cs | 3 ++ 16 files changed, 93 insertions(+), 33 deletions(-) create mode 100644 Toolkit.Foundation/IDisposerRequired.cs create mode 100644 Toolkit.Foundation/IMediatorRequired.cs create mode 100644 Toolkit.Foundation/IPublisherRequired.cs create mode 100644 Toolkit.Foundation/IServiceFactoryRequired.cs create mode 100644 Toolkit.Foundation/IServiceProviderRequired.cs create mode 100644 Toolkit.Foundation/ISubscriptionRequired.cs create mode 100644 Toolkit.Foundation/Validation.cs create mode 100644 Toolkit.Foundation/ValidationEventArgs.cs diff --git a/Toolkit.Foundation/ComponentBuilder.cs b/Toolkit.Foundation/ComponentBuilder.cs index 21e1cbf..b3dc932 100644 --- a/Toolkit.Foundation/ComponentBuilder.cs +++ b/Toolkit.Foundation/ComponentBuilder.cs @@ -32,9 +32,8 @@ public class ComponentBuilder : services.AddScoped(); services.AddTransient(); - services.AddTransient(); + services.AddScoped(); - services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/Toolkit.Foundation/HandlerProvider.cs b/Toolkit.Foundation/HandlerProvider.cs index caadcde..cadc785 100644 --- a/Toolkit.Foundation/HandlerProvider.cs +++ b/Toolkit.Foundation/HandlerProvider.cs @@ -7,6 +7,7 @@ public class HandlerProvider(SubscriptionCollection subscriptions) : object? key = null) { string subscriptionKey = $"{(key is not null ? $"{key}:" : "")}{type}"; + var d = subscriptions; if (subscriptions.TryGetValue(subscriptionKey, out List? subscribers)) { foreach (WeakReference weakRef in subscribers.ToArray()) diff --git a/Toolkit.Foundation/IDisposerRequired.cs b/Toolkit.Foundation/IDisposerRequired.cs new file mode 100644 index 0000000..a5a9cc7 --- /dev/null +++ b/Toolkit.Foundation/IDisposerRequired.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; + +public interface IDisposerRequired +{ + IDisposer Disposer { get; } +} diff --git a/Toolkit.Foundation/IHandlerProvider.cs b/Toolkit.Foundation/IHandlerProvider.cs index f155b6c..a62ea93 100644 --- a/Toolkit.Foundation/IHandlerProvider.cs +++ b/Toolkit.Foundation/IHandlerProvider.cs @@ -1,9 +1,8 @@  -namespace Toolkit.Foundation +namespace Toolkit.Foundation; + +public interface IHandlerProvider { - public interface IHandlerProvider - { - IEnumerable Get(Type type, - object? key = null); - } + IEnumerable Get(Type type, + object? key = null); } \ No newline at end of file diff --git a/Toolkit.Foundation/IMediatorRequired.cs b/Toolkit.Foundation/IMediatorRequired.cs new file mode 100644 index 0000000..c6c138c --- /dev/null +++ b/Toolkit.Foundation/IMediatorRequired.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; + +public interface IMediatorRequired +{ + IMediator Mediator { get; } +} diff --git a/Toolkit.Foundation/IPublisherRequired.cs b/Toolkit.Foundation/IPublisherRequired.cs new file mode 100644 index 0000000..beb8728 --- /dev/null +++ b/Toolkit.Foundation/IPublisherRequired.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; + +public interface IPublisherRequired +{ + IPublisher Publisher { get; } +} diff --git a/Toolkit.Foundation/IServiceFactoryRequired.cs b/Toolkit.Foundation/IServiceFactoryRequired.cs new file mode 100644 index 0000000..8a77d43 --- /dev/null +++ b/Toolkit.Foundation/IServiceFactoryRequired.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; + +public interface IServiceFactoryRequired +{ + IServiceFactory Factory { get; } +} diff --git a/Toolkit.Foundation/IServiceProviderRequired.cs b/Toolkit.Foundation/IServiceProviderRequired.cs new file mode 100644 index 0000000..edb0f13 --- /dev/null +++ b/Toolkit.Foundation/IServiceProviderRequired.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; + +public interface IServiceProviderRequired +{ + IServiceProvider Provider { get; } +} diff --git a/Toolkit.Foundation/ISubscriptionRequired.cs b/Toolkit.Foundation/ISubscriptionRequired.cs new file mode 100644 index 0000000..250433e --- /dev/null +++ b/Toolkit.Foundation/ISubscriptionRequired.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; + +public interface ISubscriptionRequired +{ + ISubscription Subscription { get; } +} diff --git a/Toolkit.Foundation/Mediator.cs b/Toolkit.Foundation/Mediator.cs index 12c625e..3bebf3b 100644 --- a/Toolkit.Foundation/Mediator.cs +++ b/Toolkit.Foundation/Mediator.cs @@ -40,6 +40,7 @@ public class Mediator(IHandlerProvider handlerProvider, handlerList = []; handlers.Add(handlerType, handlerList); } + handlerList.Add(handler); } } @@ -48,7 +49,7 @@ public class Mediator(IHandlerProvider handlerProvider, { foreach (object? handler in handlerEntry.Value) { - if (handler?.GetType().GetMethod("Handle") is MethodInfo handleMethod) + if (handler?.GetType().GetMethod("Handle", [messageType, typeof(CancellationToken)]) is MethodInfo handleMethod) { return await (Task)handleMethod.Invoke(handler, new object[] { message, cancellationToken })!; diff --git a/Toolkit.Foundation/NavigateHandler.cs b/Toolkit.Foundation/NavigateHandler.cs index 7a06f42..7706346 100644 --- a/Toolkit.Foundation/NavigateHandler.cs +++ b/Toolkit.Foundation/NavigateHandler.cs @@ -3,16 +3,15 @@ namespace Toolkit.Foundation; public class NavigateHandler(NamedComponent scope, - IComponentScopeProvider componentScopeProvider, - IServiceProvider provider) : + IComponentScopeProvider componentScopeProvider) : INotificationHandler { public Task Handle(NavigateEventArgs args) { INavigationScope? navigationScope; - if (args.Scope == "self") + if (args.Scope == "self" && args.Sender is IServiceProviderRequired requireServiceProvider) { - navigationScope = provider.GetRequiredService(); + navigationScope = requireServiceProvider.Provider.GetRequiredService(); } else { diff --git a/Toolkit.Foundation/NavigationScope.cs b/Toolkit.Foundation/NavigationScope.cs index 7a3430d..2a27790 100644 --- a/Toolkit.Foundation/NavigationScope.cs +++ b/Toolkit.Foundation/NavigationScope.cs @@ -10,8 +10,11 @@ public class NavigationScope(IPublisher publisher, IContentTemplateDescriptorProvider contentTemplateDescriptorProvider) : INavigationScope { - public void Navigate(string route, object? sender = null, object? context = null, - EventHandler? navigated = null, object[]? parameters = null) + public void Navigate(string route, + object? sender = null, + object? context = null, + EventHandler? navigated = null, + object[]? parameters = null) { string[] segments = route.Split('/'); int segmentCount = segments.Length; @@ -41,26 +44,25 @@ public class NavigationScope(IPublisher publisher, if (provider.GetRequiredKeyedService(descriptor.TemplateType, segment) is object view) { - if ((parameters is { Length: > 0 } + if (context is not null) + { + if (navigationContextProvider.TryGet(context, out object? scopedContext)) + { + context = scopedContext; + } + } + else + { + context = view; + } + + if (context is not null) + { + if ((parameters is { Length: > 0 } ? factory.Create(descriptor.ContentType, parameters) : provider.GetRequiredKeyedService(descriptor.ContentType, segment)) is object viewModel) - { - if (context is not null) { - if (navigationContextProvider.TryGet(context, out object? scopedContext)) - { - context = scopedContext; - } - } - else - { - context = view; - } - - if (context is not null) - { - if (navigationProvider.Get(context is Type type ? type : context.GetType()) - is INavigation navigation) + if (navigationProvider.Get(context is Type type ? type : context.GetType()) is INavigation navigation) { Type navigateType = typeof(NavigateEventArgs<>).MakeGenericType(navigation.Type); if (Activator.CreateInstance(navigateType, [context, view, viewModel, sender, parameters]) is object navigate) diff --git a/Toolkit.Foundation/ObservableCollectionViewModel.cs b/Toolkit.Foundation/ObservableCollectionViewModel.cs index 7706ab7..383cbf1 100644 --- a/Toolkit.Foundation/ObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/ObservableCollectionViewModel.cs @@ -19,6 +19,11 @@ public partial class ObservableCollectionViewModel : IList, IReadOnlyList, INotifyCollectionChanged, + IServiceProviderRequired, + IServiceFactoryRequired, + IMediatorRequired, + IPublisherRequired, + IDisposerRequired, INotificationHandler>, INotificationHandler>, INotificationHandler>, diff --git a/Toolkit.Foundation/ObservableViewModel.cs b/Toolkit.Foundation/ObservableViewModel.cs index 35aefce..adaa76d 100644 --- a/Toolkit.Foundation/ObservableViewModel.cs +++ b/Toolkit.Foundation/ObservableViewModel.cs @@ -10,7 +10,12 @@ public partial class ObservableViewModel : IDeactivating, IDeactivated, IDeactivatable, - IDisposable + IDisposable, + IServiceProviderRequired, + IServiceFactoryRequired, + IMediatorRequired, + IPublisherRequired, + IDisposerRequired { [ObservableProperty] private bool isInitialized; diff --git a/Toolkit.Foundation/Validation.cs b/Toolkit.Foundation/Validation.cs new file mode 100644 index 0000000..d3c35ed --- /dev/null +++ b/Toolkit.Foundation/Validation.cs @@ -0,0 +1,10 @@ +namespace Toolkit.Foundation; + +public record Validation +{ + public static ValidationEventArgs As(TValue value) => + new(value); + + public static ValidationEventArgs As() where TValue : new() => + new(new TValue()); +} \ No newline at end of file diff --git a/Toolkit.Foundation/ValidationEventArgs.cs b/Toolkit.Foundation/ValidationEventArgs.cs new file mode 100644 index 0000000..e225895 --- /dev/null +++ b/Toolkit.Foundation/ValidationEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record ValidationEventArgs(TValue Value); From 85a14a8259e72850722cc1d69f53eb196a3b51a7 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Thu, 16 May 2024 22:30:01 +0100 Subject: [PATCH 050/228] Improve navigation --- Toolkit.Avalonia/ContentControlHandler.cs | 2 +- Toolkit.Avalonia/ContentDialogHandler.cs | 2 +- Toolkit.Avalonia/FrameHandler.cs | 2 +- Toolkit.Foundation/INavigationScope.cs | 9 ++++-- Toolkit.Foundation/NavigateEventArgs.cs | 4 +-- Toolkit.Foundation/NavigateHandler.cs | 2 +- Toolkit.Foundation/NavigationScope.cs | 37 +++++++++++++++-------- Toolkit.UI.Avalonia/NavigateAction.cs | 21 +++++++------ Toolkit.UI.Avalonia/NavigateBackAction.cs | 12 ++++---- 9 files changed, 53 insertions(+), 38 deletions(-) diff --git a/Toolkit.Avalonia/ContentControlHandler.cs b/Toolkit.Avalonia/ContentControlHandler.cs index 0c69bc6..46f5c62 100644 --- a/Toolkit.Avalonia/ContentControlHandler.cs +++ b/Toolkit.Avalonia/ContentControlHandler.cs @@ -9,7 +9,7 @@ public class ContentControlHandler : { public async Task Handle(NavigateEventArgs args) { - if (args.Context is ContentControl contentControl) + if (args.Region is ContentControl contentControl) { if (args.Template is Control control) { diff --git a/Toolkit.Avalonia/ContentDialogHandler.cs b/Toolkit.Avalonia/ContentDialogHandler.cs index b7856e6..a637460 100644 --- a/Toolkit.Avalonia/ContentDialogHandler.cs +++ b/Toolkit.Avalonia/ContentDialogHandler.cs @@ -8,7 +8,7 @@ public class ContentDialogHandler(IDispatcher dispatcher) : { public async Task Handle(NavigateEventArgs args) { - if (args.Context is ContentDialog contentDialog) + if (args.Region is ContentDialog contentDialog) { contentDialog.DataContext = args.Content; diff --git a/Toolkit.Avalonia/FrameHandler.cs b/Toolkit.Avalonia/FrameHandler.cs index 04b5ab8..466b7fb 100644 --- a/Toolkit.Avalonia/FrameHandler.cs +++ b/Toolkit.Avalonia/FrameHandler.cs @@ -14,7 +14,7 @@ public class FrameHandler : { public Task Handle(NavigateEventArgs args) { - if (args.Context is Frame frame) + if (args.Region is Frame frame) { frame.NavigationPageFactory ??= new NavigationPageFactory(); if (args.Template is Control control) diff --git a/Toolkit.Foundation/INavigationScope.cs b/Toolkit.Foundation/INavigationScope.cs index b6add22..24223d5 100644 --- a/Toolkit.Foundation/INavigationScope.cs +++ b/Toolkit.Foundation/INavigationScope.cs @@ -2,8 +2,11 @@ public interface INavigationScope { - void Navigate(string route, object? sender = null, object? context = null, - EventHandler? navigated = null, object[]? parameters = null); + void Navigate(string route, + object? sender = null, + object? region = null, + EventHandler? navigated = null, + object[]? parameters = null); - void Back(object? context); + void Back(object? region); } \ No newline at end of file diff --git a/Toolkit.Foundation/NavigateEventArgs.cs b/Toolkit.Foundation/NavigateEventArgs.cs index b6d2ff3..432cd3e 100644 --- a/Toolkit.Foundation/NavigateEventArgs.cs +++ b/Toolkit.Foundation/NavigateEventArgs.cs @@ -1,13 +1,13 @@ namespace Toolkit.Foundation; public record NavigateEventArgs(string Route, - object? Context = null, + object? Region = null, string? Scope = null, object? Sender = null, EventHandler? Navigated = null, object[]? Parameters = null); -public record NavigateEventArgs(object Context, +public record NavigateEventArgs(object Region, object Template, object Content, object? Sender = null, diff --git a/Toolkit.Foundation/NavigateHandler.cs b/Toolkit.Foundation/NavigateHandler.cs index 7706346..0310642 100644 --- a/Toolkit.Foundation/NavigateHandler.cs +++ b/Toolkit.Foundation/NavigateHandler.cs @@ -20,7 +20,7 @@ public class NavigateHandler(NamedComponent scope, } navigationScope?.Navigate(args.Route, args.Sender, - args.Context, args.Navigated, args.Parameters); + args.Region, args.Navigated, args.Parameters); return Task.CompletedTask; } diff --git a/Toolkit.Foundation/NavigationScope.cs b/Toolkit.Foundation/NavigationScope.cs index 2a27790..40b1162 100644 --- a/Toolkit.Foundation/NavigationScope.cs +++ b/Toolkit.Foundation/NavigationScope.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using System.Data.SqlTypes; namespace Toolkit.Foundation; @@ -6,16 +7,21 @@ public class NavigationScope(IPublisher publisher, IServiceProvider provider, IServiceFactory factory, INavigationProvider navigationProvider, - INavigationRegionProvider navigationContextProvider, + INavigationRegionProvider navigationRegionProvider, IContentTemplateDescriptorProvider contentTemplateDescriptorProvider) : INavigationScope { public void Navigate(string route, object? sender = null, - object? context = null, + object? region = null, EventHandler? navigated = null, object[]? parameters = null) { + if (region is null) + { + return; + } + string[] segments = route.Split('/'); int segmentCount = segments.Length; int currentSegmentIndex = 0; @@ -44,28 +50,33 @@ public class NavigationScope(IPublisher publisher, if (provider.GetRequiredKeyedService(descriptor.TemplateType, segment) is object view) { - if (context is not null) + if (region is not null) { - if (navigationContextProvider.TryGet(context, out object? scopedContext)) + switch (region) { - context = scopedContext; + case "self": + region = view; + break; + default: + if (navigationRegionProvider.TryGet(region, out object? value)) + { + region = value; + } + + break; } } - else - { - context = view; - } - if (context is not null) + if (region is not null) { if ((parameters is { Length: > 0 } ? factory.Create(descriptor.ContentType, parameters) : provider.GetRequiredKeyedService(descriptor.ContentType, segment)) is object viewModel) { - if (navigationProvider.Get(context is Type type ? type : context.GetType()) is INavigation navigation) + if (navigationProvider.Get(region is Type type ? type : region.GetType()) is INavigation navigation) { Type navigateType = typeof(NavigateEventArgs<>).MakeGenericType(navigation.Type); - if (Activator.CreateInstance(navigateType, [context, view, viewModel, sender, parameters]) is object navigate) + if (Activator.CreateInstance(navigateType, [region, view, viewModel, sender, parameters]) is object navigate) { publisher.Publish(navigate); if (currentSegmentIndex == segmentCount) @@ -85,7 +96,7 @@ public class NavigationScope(IPublisher publisher, { if (context is not null) { - navigationContextProvider.TryGet(context, out context); + navigationRegionProvider.TryGet(context, out context); } if (context is not null) diff --git a/Toolkit.UI.Avalonia/NavigateAction.cs b/Toolkit.UI.Avalonia/NavigateAction.cs index 92cc6aa..be04ddc 100644 --- a/Toolkit.UI.Avalonia/NavigateAction.cs +++ b/Toolkit.UI.Avalonia/NavigateAction.cs @@ -1,4 +1,5 @@ using Avalonia; +using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Metadata; using Avalonia.Xaml.Interactivity; @@ -10,9 +11,6 @@ public class NavigateAction : AvaloniaObject, IAction { - public static readonly StyledProperty ContextProperty = - AvaloniaProperty.Register(nameof(Context)); - public static readonly DirectProperty ParameterBindingsProperty = AvaloniaProperty.RegisterDirect(nameof(ParameterBindings), x => x.ParameterBindings); @@ -20,6 +18,9 @@ public class NavigateAction : public static readonly StyledProperty ParametersProperty = AvaloniaProperty.Register(nameof(Parameters)); + public static readonly StyledProperty RegionProperty = + AvaloniaProperty.Register(nameof(Region)); + public static readonly StyledProperty RouteProperty = AvaloniaProperty.Register(nameof(Route)); @@ -30,10 +31,10 @@ public class NavigateAction : public event EventHandler? Navigated; - public object Context + public object Region { - get => GetValue(ContextProperty); - set => SetValue(ContextProperty, value); + get => GetValue(RegionProperty); + set => SetValue(RegionProperty, value); } [Content] @@ -61,12 +62,12 @@ public class NavigateAction : public object Execute(object? sender, object? parameter) { - if (sender is TemplatedControl control) + if (sender is Control content) { Dictionary arguments = new(StringComparer.InvariantCultureIgnoreCase); - if (control.DataContext is IObservableViewModel observableViewModel) + if (content.DataContext is IObservableViewModel observableViewModel) { object[] parameters = [.. Parameters ?? Enumerable.Empty(), .. @@ -74,8 +75,8 @@ public class NavigateAction : ParameterBindings.Select(binding => new KeyValuePair(binding.Key, binding.Value)).ToArray() : Enumerable.Empty>()]; - observableViewModel.Publisher.Publish(new NavigateEventArgs(Route, Context == this ? control : Context, Scope ?? null, - control.DataContext, Navigated, parameters)); + observableViewModel.Publisher.Publish(new NavigateEventArgs(Route, Region == this ? content : Region, Scope ?? null, + content.DataContext, Navigated, parameters)); } } diff --git a/Toolkit.UI.Avalonia/NavigateBackAction.cs b/Toolkit.UI.Avalonia/NavigateBackAction.cs index db925ed..62e6dbd 100644 --- a/Toolkit.UI.Avalonia/NavigateBackAction.cs +++ b/Toolkit.UI.Avalonia/NavigateBackAction.cs @@ -9,16 +9,16 @@ public class NavigateBackAction : AvaloniaObject, IAction { - public static readonly StyledProperty ContextProperty = - AvaloniaProperty.Register(nameof(Context)); + public static readonly StyledProperty RegionProperty = + AvaloniaProperty.Register(nameof(Region)); public static readonly StyledProperty ScopeProperty = AvaloniaProperty.Register(nameof(Scope)); - public string Context + public string Region { - get => GetValue(ContextProperty); - set => SetValue(ContextProperty, value); + get => GetValue(RegionProperty); + set => SetValue(RegionProperty, value); } public string Scope @@ -34,7 +34,7 @@ public class NavigateBackAction : { if (control.DataContext is IObservableViewModel observableViewModel) { - observableViewModel.Publisher.Publish(new NavigateBackEventArgs(Context + observableViewModel.Publisher.Publish(new NavigateBackEventArgs(Region ?? null, Scope ?? null)); } } From 1b60711ec472dae1728c16c45ad6d9b35b90e7bd Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sat, 18 May 2024 20:40:12 +0100 Subject: [PATCH 051/228] WIP --- Toolkit.Foundation/CommandViewModel.cs | 2 +- Toolkit.Foundation/EnumerateEventArgs.cs | 2 - Toolkit.Foundation/IEnumerate.cs | 2 - Toolkit.Foundation/Notify.cs | 10 ++ Toolkit.Foundation/NotifyEventArgs.cs | 3 + .../{ObservableViewModel.cs => Observable.cs} | 12 +- ...onViewModel.cs => ObservableCollection.cs} | 125 +++++++++--------- Toolkit.Foundation/Request.cs | 2 +- Toolkit.Foundation/ValueViewModel.cs | 2 +- 9 files changed, 84 insertions(+), 76 deletions(-) create mode 100644 Toolkit.Foundation/Notify.cs create mode 100644 Toolkit.Foundation/NotifyEventArgs.cs rename Toolkit.Foundation/{ObservableViewModel.cs => Observable.cs} (81%) rename Toolkit.Foundation/{ObservableCollectionViewModel.cs => ObservableCollection.cs} (72%) diff --git a/Toolkit.Foundation/CommandViewModel.cs b/Toolkit.Foundation/CommandViewModel.cs index 7c1e9ee..6022cbf 100644 --- a/Toolkit.Foundation/CommandViewModel.cs +++ b/Toolkit.Foundation/CommandViewModel.cs @@ -8,7 +8,7 @@ public partial class CommandViewModel(IServiceProvider provider, IPublisher publisher, ISubscription subscriber, IDisposer disposer) : - ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) + Observable(provider, factory, mediator, publisher, subscriber, disposer) { public IRelayCommand InvokeCommand => new AsyncRelayCommand(InvokeAsync); diff --git a/Toolkit.Foundation/EnumerateEventArgs.cs b/Toolkit.Foundation/EnumerateEventArgs.cs index 1895294..a4c519c 100644 --- a/Toolkit.Foundation/EnumerateEventArgs.cs +++ b/Toolkit.Foundation/EnumerateEventArgs.cs @@ -5,8 +5,6 @@ public record EnumerateEventArgs : { public object? Key { get; init; } - public EnumerateMode Mode { get; init; } - public static Enumerate With(TOptions options) where TOptions : class { return new Enumerate(options); diff --git a/Toolkit.Foundation/IEnumerate.cs b/Toolkit.Foundation/IEnumerate.cs index 88782ca..8be8bac 100644 --- a/Toolkit.Foundation/IEnumerate.cs +++ b/Toolkit.Foundation/IEnumerate.cs @@ -3,6 +3,4 @@ public interface IEnumerate { object? Key { get; init; } - - EnumerateMode Mode { get; init; } } diff --git a/Toolkit.Foundation/Notify.cs b/Toolkit.Foundation/Notify.cs new file mode 100644 index 0000000..fec1dbe --- /dev/null +++ b/Toolkit.Foundation/Notify.cs @@ -0,0 +1,10 @@ +namespace Toolkit.Foundation; + +public class Notify +{ + public static NotifyEventArgs As(TValue value) => + new(value); + + public static NotifyEventArgs As() where TValue : new() => + new(new TValue()); +} diff --git a/Toolkit.Foundation/NotifyEventArgs.cs b/Toolkit.Foundation/NotifyEventArgs.cs new file mode 100644 index 0000000..b7b03c7 --- /dev/null +++ b/Toolkit.Foundation/NotifyEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record NotifyEventArgs(TValue Value); diff --git a/Toolkit.Foundation/ObservableViewModel.cs b/Toolkit.Foundation/Observable.cs similarity index 81% rename from Toolkit.Foundation/ObservableViewModel.cs rename to Toolkit.Foundation/Observable.cs index adaa76d..7a156cb 100644 --- a/Toolkit.Foundation/ObservableViewModel.cs +++ b/Toolkit.Foundation/Observable.cs @@ -2,7 +2,7 @@ namespace Toolkit.Foundation; -public partial class ObservableViewModel : +public partial class Observable : ObservableObject, IObservableViewModel, IInitializer, @@ -20,7 +20,7 @@ public partial class ObservableViewModel : [ObservableProperty] private bool isInitialized; - public ObservableViewModel(IServiceProvider provider, + public Observable(IServiceProvider provider, IServiceFactory factory, IMediator mediator, IPublisher publisher, @@ -81,25 +81,25 @@ public partial class ObservableViewModel : } } -public partial class ObservableViewModel(IServiceProvider provider, +public partial class Observable(IServiceProvider provider, IServiceFactory factory, IMediator mediator, IPublisher publisher, ISubscription subscriber, - IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) + IDisposer disposer) : Observable(provider, factory, mediator, publisher, subscriber, disposer) where TValue : notnull { [ObservableProperty] private TValue? value; } -public partial class ObservableViewModel(IServiceProvider provider, +public partial class Observable(IServiceProvider provider, IServiceFactory factory, IMediator mediator, IPublisher publisher, ISubscription subscriber, IDisposer disposer, - TValue? value = null) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) + TValue? value = null) : Observable(provider, factory, mediator, publisher, subscriber, disposer) where TValue : class { [ObservableProperty] diff --git a/Toolkit.Foundation/ObservableCollectionViewModel.cs b/Toolkit.Foundation/ObservableCollection.cs similarity index 72% rename from Toolkit.Foundation/ObservableCollectionViewModel.cs rename to Toolkit.Foundation/ObservableCollection.cs index 383cbf1..8db9f96 100644 --- a/Toolkit.Foundation/ObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/ObservableCollection.cs @@ -1,38 +1,37 @@ using CommunityToolkit.Mvvm.ComponentModel; using Microsoft.Extensions.DependencyInjection; using System.Collections; -using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Reactive.Disposables; namespace Toolkit.Foundation; -public partial class ObservableCollectionViewModel : +public partial class ObservableCollection : ObservableObject, - IObservableCollectionViewModel, + IObservableCollectionViewModel, IInitializer, IActivated, IDeactivating, IDeactivated, IDeactivatable, - IList, + IList, IList, - IReadOnlyList, + IReadOnlyList, INotifyCollectionChanged, IServiceProviderRequired, IServiceFactoryRequired, IMediatorRequired, IPublisherRequired, IDisposerRequired, - INotificationHandler>, - INotificationHandler>, - INotificationHandler>, - INotificationHandler>, - INotificationHandler> - where TViewModel : + INotificationHandler>, + INotificationHandler>, + INotificationHandler>, + INotificationHandler>, + INotificationHandler> + where TItem : notnull { - private readonly ObservableCollection collection = []; + private readonly System.Collections.ObjectModel.ObservableCollection collection = []; private bool clearing; @@ -42,7 +41,7 @@ public partial class ObservableCollectionViewModel : [ObservableProperty] private int selectedIndex = 0; - public ObservableCollectionViewModel(IServiceProvider provider, + public ObservableCollection(IServiceProvider provider, IServiceFactory factory, IMediator mediator, IPublisher publisher, @@ -60,13 +59,13 @@ public partial class ObservableCollectionViewModel : collection.CollectionChanged += OnCollectionChanged; } - public ObservableCollectionViewModel(IServiceProvider provider, + public ObservableCollection(IServiceProvider provider, IServiceFactory factory, IMediator mediator, IPublisher publisher, ISubscription subscriber, IDisposer disposer, - IEnumerable items) + IEnumerable items) { Provider = provider; Factory = factory; @@ -92,7 +91,7 @@ public partial class ObservableCollectionViewModel : bool IList.IsFixedSize => false; - bool ICollection.IsReadOnly => false; + bool ICollection.IsReadOnly => false; bool IList.IsReadOnly => false; @@ -106,7 +105,7 @@ public partial class ObservableCollectionViewModel : object ICollection.SyncRoot => this; - public TViewModel this[int index] + public TItem this[int index] { get => collection[index]; set => SetItem(index, value); @@ -117,11 +116,11 @@ public partial class ObservableCollectionViewModel : get => collection[index]; set { - TViewModel? item = default; + TItem? item = default; try { - item = (TViewModel)value!; + item = (TItem)value!; } catch (InvalidCastException) { @@ -131,16 +130,16 @@ public partial class ObservableCollectionViewModel : } } - public TViewModel Add() + public TItem Add() { - TViewModel? item = Factory.Create(); + TItem? item = Factory.Create(); Add(item); return item; } - public TViewModel Add(params object?[] parameters) - where T : TViewModel + public TItem Add(params object?[] parameters) + where T : TItem { T? item = Factory.Create(parameters); Add(item); @@ -148,9 +147,9 @@ public partial class ObservableCollectionViewModel : return item; } - public TViewModel Add(bool scope = false) + public TItem Add(bool scope = false) where T : - TViewModel + TItem { IServiceFactory? factory = null; if (scope) @@ -165,7 +164,7 @@ public partial class ObservableCollectionViewModel : return item; } - public void Add(TViewModel item) + public void Add(TItem item) { int index = collection.Count; InsertItem(index, item); @@ -174,16 +173,16 @@ public partial class ObservableCollectionViewModel : public void Add(object item) { int index = collection.Count; - InsertItem(index, (TViewModel)item); + InsertItem(index, (TItem)item); } int IList.Add(object? value) { - TViewModel? item = default; + TItem? item = default; try { - item = (TViewModel)value!; + item = (TItem)value!; } catch (InvalidCastException) { @@ -194,9 +193,9 @@ public partial class ObservableCollectionViewModel : return Count - 1; } - public void AddRange(IEnumerable items) + public void AddRange(IEnumerable items) { - foreach (TViewModel? item in items) + foreach (TItem? item in items) { Add(item); } @@ -206,7 +205,7 @@ public partial class ObservableCollectionViewModel : { clearing = true; - foreach (TViewModel item in this.ToList()) + foreach (TItem item in this.ToList()) { Disposer.Dispose(item); } @@ -215,17 +214,17 @@ public partial class ObservableCollectionViewModel : clearing = false; } - public bool Contains(TViewModel item) => + public bool Contains(TItem item) => collection.Contains(item); bool IList.Contains(object? value) => - IsCompatibleObject(value) && Contains((TViewModel)value!); + IsCompatibleObject(value) && Contains((TItem)value!); - public void CopyTo(TViewModel[] array, int index) => + public void CopyTo(TItem[] array, int index) => collection.CopyTo(array, index); void ICollection.CopyTo(Array array, int index) => - collection.CopyTo((TViewModel[])array, index); + collection.CopyTo((TItem[])array, index); public Task Deactivate() { @@ -253,15 +252,15 @@ public partial class ObservableCollectionViewModel : } } - public IEnumerator GetEnumerator() => + public IEnumerator GetEnumerator() => collection.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)collection).GetEnumerator(); - public Task Handle(RemoveEventArgs args) + public Task Handle(RemoveEventArgs args) { - foreach (TViewModel item in this.ToList()) + foreach (TItem item in this.ToList()) { if (args.Value is not null && args.Value.Equals(item)) { @@ -272,9 +271,9 @@ public partial class ObservableCollectionViewModel : return Task.CompletedTask; } - public Task Handle(CreateEventArgs args) + public Task Handle(CreateEventArgs args) { - if (args.Value is TViewModel item) + if (args.Value is TItem item) { Add(item); } @@ -282,9 +281,9 @@ public partial class ObservableCollectionViewModel : return Task.CompletedTask; } - public Task Handle(InsertEventArgs args) + public Task Handle(InsertEventArgs args) { - if (args.Value is TViewModel item) + if (args.Value is TItem item) { Insert(args.Index, item); } @@ -292,9 +291,9 @@ public partial class ObservableCollectionViewModel : return Task.CompletedTask; } - public Task Handle(MoveEventArgs args) + public Task Handle(MoveEventArgs args) { - if (args.Value is TViewModel item) + if (args.Value is TItem item) { Move(args.Index, item); } @@ -302,9 +301,9 @@ public partial class ObservableCollectionViewModel : return Task.CompletedTask; } - public Task Handle(ReplaceEventArgs args) + public Task Handle(ReplaceEventArgs args) { - if (args.Value is TViewModel item) + if (args.Value is TItem item) { Replace(args.Index, item); } @@ -312,12 +311,12 @@ public partial class ObservableCollectionViewModel : return Task.CompletedTask; } - public int IndexOf(TViewModel item) => + public int IndexOf(TItem item) => collection.IndexOf(item); int IList.IndexOf(object? value) => IsCompatibleObject(value) ? - IndexOf((TViewModel)value!) : -1; + IndexOf((TItem)value!) : -1; public Task Initialize() { @@ -332,19 +331,19 @@ public partial class ObservableCollectionViewModel : return Task.CompletedTask; } - public void Insert(int index, TViewModel item) => + public void Insert(int index, TItem item) => InsertItem(index, item); void IList.Insert(int index, object? value) { - if (value is TViewModel item) + if (value is TItem item) { Insert(index, item); } } - public bool Move(int index, TViewModel item) + public bool Move(int index, TItem item) { int oldIndex = collection.IndexOf(item); if (oldIndex < 0) @@ -367,7 +366,7 @@ public partial class ObservableCollectionViewModel : public virtual Task OnDeactivating() => Task.CompletedTask; - public bool Remove(TViewModel item) + public bool Remove(TItem item) { int index = collection.IndexOf(item); if (index < 0) @@ -385,7 +384,7 @@ public partial class ObservableCollectionViewModel : { if (IsCompatibleObject(value)) { - Remove((TViewModel)value!); + Remove((TItem)value!); } } @@ -393,7 +392,7 @@ public partial class ObservableCollectionViewModel : RemoveItem(index); public bool Replace(int index, - TViewModel item) + TItem item) { if (index <= Count - 1) { @@ -412,7 +411,7 @@ public partial class ObservableCollectionViewModel : collection.Clear(); protected virtual void InsertItem(int index, - TViewModel item) + TItem item) { Disposer.Add(this, item); Disposer.Add(item, Disposable.Create(() => @@ -432,16 +431,16 @@ public partial class ObservableCollectionViewModel : } protected virtual IEnumerate PrepareEnumeration(object? key) => - new EnumerateEventArgs() with { Key = key }; + new EnumerateEventArgs() with { Key = key }; protected virtual void RemoveItem(int index) => collection.RemoveAt(index); - protected virtual void SetItem(int index, TViewModel item) => + protected virtual void SetItem(int index, TItem item) => collection[index] = item; private static bool IsCompatibleObject(object? value) => - (value is TViewModel) || (value == null && default(TViewModel) == null); + (value is TItem) || (value == null && default(TItem) == null); private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args) => CollectionChanged?.Invoke(this, args); @@ -460,21 +459,21 @@ public partial class ObservableCollectionViewModel : } } -public partial class ObservableCollectionViewModel(IServiceProvider provider, +public partial class ObservableCollection(IServiceProvider provider, IServiceFactory factory, IMediator mediator, IPublisher publisher, - ISubscription subscriber, IDisposer disposer) : ObservableCollectionViewModel(provider, factory, mediator, publisher, subscriber, disposer) + ISubscription subscriber, IDisposer disposer) : ObservableCollection(provider, factory, mediator, publisher, subscriber, disposer) where TViewModel : notnull { [ObservableProperty] private TValue? value; } -public class ObservableCollectionViewModel(IServiceProvider provider, +public class ObservableCollection(IServiceProvider provider, IServiceFactory factory, IMediator mediator, IPublisher publisher, ISubscription subscriber, IDisposer disposer) : - ObservableCollectionViewModel(provider, factory, mediator, publisher, subscriber, disposer); \ No newline at end of file + ObservableCollection(provider, factory, mediator, publisher, subscriber, disposer); \ No newline at end of file diff --git a/Toolkit.Foundation/Request.cs b/Toolkit.Foundation/Request.cs index cbf71d6..555318e 100644 --- a/Toolkit.Foundation/Request.cs +++ b/Toolkit.Foundation/Request.cs @@ -7,4 +7,4 @@ public class Request public static RequestEventArgs As() where TValue : new() => new(new TValue()); -} \ No newline at end of file +} diff --git a/Toolkit.Foundation/ValueViewModel.cs b/Toolkit.Foundation/ValueViewModel.cs index e98c488..4bfdb4f 100644 --- a/Toolkit.Foundation/ValueViewModel.cs +++ b/Toolkit.Foundation/ValueViewModel.cs @@ -8,7 +8,7 @@ public partial class ValueViewModel(IServiceProvider provider, IPublisher publisher, ISubscription subscriber, IDisposer disposer) : - ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) + Observable(provider, factory, mediator, publisher, subscriber, disposer) { [ObservableProperty] private TValue? value; From 41c7f71a9da4f5b378fff358aebf407425c829c4 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 19 May 2024 14:18:08 +0100 Subject: [PATCH 052/228] Add contextual commands --- Toolkit.Foundation/Aggerate.cs | 10 ++++ Toolkit.Foundation/AggerateEventArgs.cs | 12 +++++ .../{EnumerateMode.cs => AggerateMode.cs} | 2 +- Toolkit.Foundation/Enumerate.cs | 10 ---- Toolkit.Foundation/EnumerateEventArgs.cs | 12 ----- .../{IEnumerate.cs => IAggerate.cs} | 2 +- Toolkit.Foundation/IPublisher.cs | 21 ++++---- .../IServiceCollectionExtensions.cs | 29 +++++++--- Toolkit.Foundation/IServiceFactory.cs | 3 ++ Toolkit.Foundation/NotificationAttribute.cs | 4 +- Toolkit.Foundation/ObservableCollection.cs | 54 +++++++++++-------- Toolkit.Foundation/Publisher.cs | 7 +-- Toolkit.Foundation/ServiceFactory.cs | 9 ++++ 13 files changed, 107 insertions(+), 68 deletions(-) create mode 100644 Toolkit.Foundation/Aggerate.cs create mode 100644 Toolkit.Foundation/AggerateEventArgs.cs rename Toolkit.Foundation/{EnumerateMode.cs => AggerateMode.cs} (69%) delete mode 100644 Toolkit.Foundation/Enumerate.cs delete mode 100644 Toolkit.Foundation/EnumerateEventArgs.cs rename Toolkit.Foundation/{IEnumerate.cs => IAggerate.cs} (71%) diff --git a/Toolkit.Foundation/Aggerate.cs b/Toolkit.Foundation/Aggerate.cs new file mode 100644 index 0000000..b88032f --- /dev/null +++ b/Toolkit.Foundation/Aggerate.cs @@ -0,0 +1,10 @@ +namespace Toolkit.Foundation; + +public record Aggerate(TOptions? Options = null) : + IAggerate + where TOptions : class +{ + public object? Key { get; init; } + + public AggerateMode Mode { get; init; } +} \ No newline at end of file diff --git a/Toolkit.Foundation/AggerateEventArgs.cs b/Toolkit.Foundation/AggerateEventArgs.cs new file mode 100644 index 0000000..1693463 --- /dev/null +++ b/Toolkit.Foundation/AggerateEventArgs.cs @@ -0,0 +1,12 @@ +namespace Toolkit.Foundation; + +public record AggerateEventArgs : + IAggerate +{ + public object? Key { get; init; } + + public static Aggerate With(TOptions options) where TOptions : class + { + return new Aggerate(options); + } +} \ No newline at end of file diff --git a/Toolkit.Foundation/EnumerateMode.cs b/Toolkit.Foundation/AggerateMode.cs similarity index 69% rename from Toolkit.Foundation/EnumerateMode.cs rename to Toolkit.Foundation/AggerateMode.cs index 1827b0a..e7f4d54 100644 --- a/Toolkit.Foundation/EnumerateMode.cs +++ b/Toolkit.Foundation/AggerateMode.cs @@ -1,6 +1,6 @@ namespace Toolkit.Foundation; -public enum EnumerateMode +public enum AggerateMode { Append, Reset diff --git a/Toolkit.Foundation/Enumerate.cs b/Toolkit.Foundation/Enumerate.cs deleted file mode 100644 index 9a90559..0000000 --- a/Toolkit.Foundation/Enumerate.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Toolkit.Foundation; - -public record Enumerate(TOptions? Options = null) : - IEnumerate - where TOptions : class -{ - public object? Key { get; init; } - - public EnumerateMode Mode { get; init; } -} \ No newline at end of file diff --git a/Toolkit.Foundation/EnumerateEventArgs.cs b/Toolkit.Foundation/EnumerateEventArgs.cs deleted file mode 100644 index a4c519c..0000000 --- a/Toolkit.Foundation/EnumerateEventArgs.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Toolkit.Foundation; - -public record EnumerateEventArgs : - IEnumerate -{ - public object? Key { get; init; } - - public static Enumerate With(TOptions options) where TOptions : class - { - return new Enumerate(options); - } -} \ No newline at end of file diff --git a/Toolkit.Foundation/IEnumerate.cs b/Toolkit.Foundation/IAggerate.cs similarity index 71% rename from Toolkit.Foundation/IEnumerate.cs rename to Toolkit.Foundation/IAggerate.cs index 8be8bac..3510f94 100644 --- a/Toolkit.Foundation/IEnumerate.cs +++ b/Toolkit.Foundation/IAggerate.cs @@ -1,6 +1,6 @@ namespace Toolkit.Foundation; -public interface IEnumerate +public interface IAggerate { object? Key { get; init; } } diff --git a/Toolkit.Foundation/IPublisher.cs b/Toolkit.Foundation/IPublisher.cs index c01d127..0a05a60 100644 --- a/Toolkit.Foundation/IPublisher.cs +++ b/Toolkit.Foundation/IPublisher.cs @@ -11,10 +11,18 @@ public interface IPublisher void Publish(TMessage message, object key) where TMessage : notnull; + + void Publish(object message, + Func, Task> marshal, + object? key = null); + + void Publish() + where TMessage : new(); + + void Publish(object message); void PublishUI(TMessage message, - object key) - where TMessage : notnull; + object key) where TMessage : notnull; void PublishUI(object key) where TMessage : new(); @@ -24,15 +32,6 @@ public interface IPublisher void PublishUI(object message); - void Publish(object message, - Func, Task> marshal, - object? key = null); - void PublishUI() where TMessage : new(); - - void Publish() - where TMessage : new(); - - void Publish(object message); } \ No newline at end of file diff --git a/Toolkit.Foundation/IServiceCollectionExtensions.cs b/Toolkit.Foundation/IServiceCollectionExtensions.cs index e53b5c1..370643c 100644 --- a/Toolkit.Foundation/IServiceCollectionExtensions.cs +++ b/Toolkit.Foundation/IServiceCollectionExtensions.cs @@ -129,7 +129,8 @@ public static class IServiceCollectionExtensions } public static IServiceCollection AddTemplate(this IServiceCollection services, - object? key = null, + object? key = null, + ServiceLifetime serviceLifetime = ServiceLifetime.Transient, params object[]? parameters) { Type viewModelType = typeof(TViewModel); @@ -137,15 +138,29 @@ public static class IServiceCollectionExtensions key ??= viewModelType.Name.Replace("ViewModel", ""); - services.AddTransient(viewModelType, provider => - provider.GetRequiredService().Create(parameters)!); + if (parameters is { Length: 0 }) + { + services.Add(new ServiceDescriptor(viewModelType, viewModelType, serviceLifetime)); + } + else + { + services.Add(new ServiceDescriptor(viewModelType, provider => + provider.GetRequiredService().Create(parameters)!, serviceLifetime)); + } - services.AddTransient(viewType); + services.Add(new ServiceDescriptor(viewType, viewType, serviceLifetime)); - services.AddKeyedTransient(viewModelType, key, (provider, key) => - provider.GetRequiredService().Create(parameters)!); + if (parameters is { Length: 0 }) + { + services.Add(new ServiceDescriptor(viewModelType, key, viewModelType, serviceLifetime)); + } + else + { + services.Add(new ServiceDescriptor(viewModelType, key, (provider, key) => + provider.GetRequiredService().Create(parameters)!, serviceLifetime)); + } - services.AddKeyedTransient(viewType, key); + services.Add(new ServiceDescriptor(viewType, key, viewType, serviceLifetime)); services.AddTransient(provider => new ContentTemplateDescriptor(key, viewModelType, viewType, parameters)); diff --git a/Toolkit.Foundation/IServiceFactory.cs b/Toolkit.Foundation/IServiceFactory.cs index c69fb20..5cf1845 100644 --- a/Toolkit.Foundation/IServiceFactory.cs +++ b/Toolkit.Foundation/IServiceFactory.cs @@ -4,5 +4,8 @@ public interface IServiceFactory { object Create(Type type, params object?[]? parameters); + TService Create(Action serviceDelegate, + params object?[]? parameters); + TService Create(params object?[]? parameters); } \ No newline at end of file diff --git a/Toolkit.Foundation/NotificationAttribute.cs b/Toolkit.Foundation/NotificationAttribute.cs index cf48a5f..50178f7 100644 --- a/Toolkit.Foundation/NotificationAttribute.cs +++ b/Toolkit.Foundation/NotificationAttribute.cs @@ -7,7 +7,7 @@ public class NotificationAttribute(object key) : Attribute } public class EnumerateAttribute(object key, - EnumerateMode mode = EnumerateMode.Reset) : NotificationAttribute(key) + AggerateMode mode = AggerateMode.Reset) : NotificationAttribute(key) { - public EnumerateMode Mode => mode; + public AggerateMode Mode => mode; } \ No newline at end of file diff --git a/Toolkit.Foundation/ObservableCollection.cs b/Toolkit.Foundation/ObservableCollection.cs index 8db9f96..9eb094a 100644 --- a/Toolkit.Foundation/ObservableCollection.cs +++ b/Toolkit.Foundation/ObservableCollection.cs @@ -111,6 +111,12 @@ public partial class ObservableCollection : set => SetItem(index, value); } + public void ResetAndAddRange(Action> args) + { + Clear(); + args.Invoke(this); + } + object? IList.this[int index] { get => collection[index]; @@ -130,24 +136,19 @@ public partial class ObservableCollection : } } - public TItem Add() - { - TItem? item = Factory.Create(); - - Add(item); - return item; - } + public TItem Add() => + Add(null, false); public TItem Add(params object?[] parameters) - where T : TItem - { - T? item = Factory.Create(parameters); - Add(item); + where T : TItem => Add(null, false, parameters); - return item; - } + public TItem Add(IDisposable? owner, + params object?[] parameters) + where T : TItem => Add(owner, false, parameters); - public TItem Add(bool scope = false) + public TItem Add(IDisposable? owner = null, + bool scope = false, + params object?[] parameters) where T : TItem { @@ -158,9 +159,20 @@ public partial class ObservableCollection : factory = serviceScope.ServiceProvider.GetRequiredService(); } - T? item = factory is not null ? factory.Create() : Factory.Create(); + T? item = factory is not null ? factory.Create(parameters) : Factory.Create(parameters); Add(item); + if (owner is not null) + { + Disposer.Add(owner, Disposable.Create(() => + { + if (item is IRemovable) + { + Remove(item); + } + })); + } + return item; } @@ -238,17 +250,17 @@ public partial class ObservableCollection : Disposer.Dispose(this); } - public void Enumerate() + public void Aggerate() { if (this.GetAttribute() is EnumerateAttribute attribute) { - if (attribute.Mode == EnumerateMode.Reset) + if (attribute.Mode == AggerateMode.Reset) { Clear(); } object? key = this.GetPropertyValue(() => attribute.Key) is { } value ? value : attribute.Key; - Publisher.PublishUI(PrepareEnumeration(key)); + Publisher.PublishUI(OnAggerate(key)); } } @@ -326,7 +338,7 @@ public partial class ObservableCollection : } Initialized = true; - Enumerate(); + Aggerate(); return Task.CompletedTask; } @@ -430,8 +442,8 @@ public partial class ObservableCollection : collection.Insert(index, item); } - protected virtual IEnumerate PrepareEnumeration(object? key) => - new EnumerateEventArgs() with { Key = key }; + protected virtual IAggerate OnAggerate(object? key) => + new AggerateEventArgs() with { Key = key }; protected virtual void RemoveItem(int index) => collection.RemoveAt(index); diff --git a/Toolkit.Foundation/Publisher.cs b/Toolkit.Foundation/Publisher.cs index cecca5c..0489146 100644 --- a/Toolkit.Foundation/Publisher.cs +++ b/Toolkit.Foundation/Publisher.cs @@ -4,13 +4,14 @@ using System.Reflection; namespace Toolkit.Foundation; public class Publisher(IHandlerProvider handlerProvider, - IServiceProvider provider, + IServiceFactory serviceFactory, + IServiceProvider serviceProvider, IDispatcher dispatcher) : IPublisher { public void Publish(object key) where TMessage : new() => - Publish(new TMessage(), async args => await args(), key); + Publish(serviceFactory.Create() ?? new TMessage(), async args => await args(), key); public void Publish(TMessage message) where TMessage : notnull => @@ -28,7 +29,7 @@ public class Publisher(IHandlerProvider handlerProvider, Type handlerType = typeof(NotificationHandlerWrapper<>) .MakeGenericType(notificationType); - List handlers = provider.GetServices(handlerType).ToList(); + List handlers = serviceProvider.GetServices(handlerType).ToList(); foreach (object? handler in handlerProvider.Get(notificationType, key)) { handlers.Add(handler); diff --git a/Toolkit.Foundation/ServiceFactory.cs b/Toolkit.Foundation/ServiceFactory.cs index d98b2bc..88cd537 100644 --- a/Toolkit.Foundation/ServiceFactory.cs +++ b/Toolkit.Foundation/ServiceFactory.cs @@ -6,6 +6,15 @@ public class ServiceFactory(Func factory) : public TService Create(params object?[]? parameters) => (TService)factory(typeof(TService), parameters); + public TService Create(Action serviceDelegate, + params object?[]? parameters) + { + TService service = (TService)factory(typeof(TService), parameters); + serviceDelegate.Invoke(service); + + return service; + } + public object Create(Type type, params object?[]? parameters) => factory(type, parameters); } \ No newline at end of file From 718a1f92ca59fe93e1dbd76d9a32b5d31e639143 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Sun, 19 May 2024 16:13:45 +0100 Subject: [PATCH 053/228] Support unarchiving --- Toolkit.Foundation/Cancel.cs | 10 ++++++++++ Toolkit.Foundation/CancelEventArgs.cs | 3 +++ Toolkit.Foundation/EditEventArgs.cs | 2 +- Toolkit.UI.Controls.Avalonia/Class1.cs | 17 ----------------- .../Fonts/FluentSystemIcons-Regular.ttf | Bin 0 -> 2262148 bytes .../Themes/ControlResources.axaml | 1 + .../Toolkit.UI.Controls.Avalonia.csproj | 6 ++++++ 7 files changed, 21 insertions(+), 18 deletions(-) create mode 100644 Toolkit.Foundation/Cancel.cs create mode 100644 Toolkit.Foundation/CancelEventArgs.cs delete mode 100644 Toolkit.UI.Controls.Avalonia/Class1.cs create mode 100644 Toolkit.UI.Controls.Avalonia/Fonts/FluentSystemIcons-Regular.ttf diff --git a/Toolkit.Foundation/Cancel.cs b/Toolkit.Foundation/Cancel.cs new file mode 100644 index 0000000..d9cdcf2 --- /dev/null +++ b/Toolkit.Foundation/Cancel.cs @@ -0,0 +1,10 @@ +namespace Toolkit.Foundation; + +public record Cancel +{ + public static CancelEventArgs As(TValue value) => + new(value); + + public static CancelEventArgs As() where TValue : new() => + new(new TValue()); +} diff --git a/Toolkit.Foundation/CancelEventArgs.cs b/Toolkit.Foundation/CancelEventArgs.cs new file mode 100644 index 0000000..5792b3a --- /dev/null +++ b/Toolkit.Foundation/CancelEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record CancelEventArgs(TValue Value); diff --git a/Toolkit.Foundation/EditEventArgs.cs b/Toolkit.Foundation/EditEventArgs.cs index 9d1ff77..2e3b649 100644 --- a/Toolkit.Foundation/EditEventArgs.cs +++ b/Toolkit.Foundation/EditEventArgs.cs @@ -1,3 +1,3 @@ namespace Toolkit.Foundation; -public record EditEventArgs(TValue Value); +public record EditEventArgs(TValue Value); \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/Class1.cs b/Toolkit.UI.Controls.Avalonia/Class1.cs deleted file mode 100644 index cef80b6..0000000 --- a/Toolkit.UI.Controls.Avalonia/Class1.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Avalonia.Controls; -using Avalonia.Controls.Primitives; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Toolkit.UI.Controls; - -public class AttachedFlyout : PopupFlyoutBase -{ - protected override Control CreatePresenter() - { - throw new NotImplementedException(); - } -} diff --git a/Toolkit.UI.Controls.Avalonia/Fonts/FluentSystemIcons-Regular.ttf b/Toolkit.UI.Controls.Avalonia/Fonts/FluentSystemIcons-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..b7dfdc2760052821383317b0c153acfe3db44d42 GIT binary patch literal 2262148 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