From 4a65d6bbd3faa9579193217fd29ecbebacfb5f70 Mon Sep 17 00:00:00 2001 From: MBucari Date: Sat, 1 Apr 2023 11:52:14 -0600 Subject: [PATCH 1/7] Add native menu for mac and refactor MainWindow --- Source/LibationAvalonia/App.axaml | 8 +- Source/LibationAvalonia/App.axaml.cs | 8 +- .../Controls/DirectorySelectControl.axaml | 2 +- .../LibationAvalonia/Controls/GroupBox.axaml | 3 - .../Dialogs/AboutDialog.axaml | 13 + .../Dialogs/AboutDialog.axaml.cs | 12 + .../Dialogs/AccountsDialog.axaml | 11 +- .../Dialogs/MessageBoxWindow.axaml | 2 +- .../LibationAvalonia/LibationAvalonia.csproj | 4 +- ...KeyHandlerEx.cs => MacAccessKeyHandler.cs} | 40 ++- .../ViewModels/MainVM.BackupCounts.cs | 58 ++++ .../MainVM.Export.cs} | 13 +- .../ViewModels/MainVM.Filters.cs | 108 ++++++++ .../ViewModels/MainVM.Import.cs | 254 ++++++++++++++++++ .../ViewModels/MainVM.Liberate.cs | 64 +++++ .../MainVM.ProcessQueue.cs} | 67 +++-- .../MainVM.ScanAuto.cs} | 35 +-- .../ViewModels/MainVM.Settings.cs | 38 +++ .../ViewModels/MainVM.VisibleBooks.cs | 202 ++++++++++++++ .../MainVM._NoUI.cs} | 4 +- Source/LibationAvalonia/ViewModels/MainVM.cs | 39 +++ .../ViewModels/MainWindowViewModel.cs | 226 ---------------- .../ViewModels/ProductsDisplayViewModel.cs | 2 +- .../Views/MainWindow.BackupCounts.cs | 23 -- .../Views/MainWindow.Filter.cs | 47 ---- .../Views/MainWindow.Liberate.cs | 59 ---- .../Views/MainWindow.QuickFilters.cs | 90 ------- .../Views/MainWindow.RemoveBooks.cs | 100 ------- .../Views/MainWindow.ScanManual.cs | 90 ------- .../Views/MainWindow.ScanNotification.cs | 24 -- .../Views/MainWindow.Settings.cs | 34 --- .../Views/MainWindow.Upgrade.cs | 34 --- .../Views/MainWindow.VisibleBooks.cs | 158 ----------- .../LibationAvalonia/Views/MainWindow.axaml | 159 +++++++---- .../Views/MainWindow.axaml.cs | 100 ++++--- .../Views/ProcessQueueControl.axaml | 3 - Source/LibationAvalonia/Walkthrough.cs | 6 +- 37 files changed, 1056 insertions(+), 1084 deletions(-) create mode 100644 Source/LibationAvalonia/Dialogs/AboutDialog.axaml create mode 100644 Source/LibationAvalonia/Dialogs/AboutDialog.axaml.cs rename Source/LibationAvalonia/{AccessKeyHandlerEx.cs => MacAccessKeyHandler.cs} (52%) create mode 100644 Source/LibationAvalonia/ViewModels/MainVM.BackupCounts.cs rename Source/LibationAvalonia/{Views/MainWindow.Export.cs => ViewModels/MainVM.Export.cs} (76%) create mode 100644 Source/LibationAvalonia/ViewModels/MainVM.Filters.cs create mode 100644 Source/LibationAvalonia/ViewModels/MainVM.Import.cs create mode 100644 Source/LibationAvalonia/ViewModels/MainVM.Liberate.cs rename Source/LibationAvalonia/{Views/MainWindow.ProcessQueue.cs => ViewModels/MainVM.ProcessQueue.cs} (60%) rename Source/LibationAvalonia/{Views/MainWindow.ScanAuto.cs => ViewModels/MainVM.ScanAuto.cs} (65%) create mode 100644 Source/LibationAvalonia/ViewModels/MainVM.Settings.cs create mode 100644 Source/LibationAvalonia/ViewModels/MainVM.VisibleBooks.cs rename Source/LibationAvalonia/{Views/MainWindow.NoUI.cs => ViewModels/MainVM._NoUI.cs} (92%) create mode 100644 Source/LibationAvalonia/ViewModels/MainVM.cs delete mode 100644 Source/LibationAvalonia/ViewModels/MainWindowViewModel.cs delete mode 100644 Source/LibationAvalonia/Views/MainWindow.BackupCounts.cs delete mode 100644 Source/LibationAvalonia/Views/MainWindow.Filter.cs delete mode 100644 Source/LibationAvalonia/Views/MainWindow.Liberate.cs delete mode 100644 Source/LibationAvalonia/Views/MainWindow.QuickFilters.cs delete mode 100644 Source/LibationAvalonia/Views/MainWindow.RemoveBooks.cs delete mode 100644 Source/LibationAvalonia/Views/MainWindow.ScanManual.cs delete mode 100644 Source/LibationAvalonia/Views/MainWindow.ScanNotification.cs delete mode 100644 Source/LibationAvalonia/Views/MainWindow.Settings.cs delete mode 100644 Source/LibationAvalonia/Views/MainWindow.Upgrade.cs delete mode 100644 Source/LibationAvalonia/Views/MainWindow.VisibleBooks.cs diff --git a/Source/LibationAvalonia/App.axaml b/Source/LibationAvalonia/App.axaml index da3b6fad..7677075b 100644 --- a/Source/LibationAvalonia/App.axaml +++ b/Source/LibationAvalonia/App.axaml @@ -1,7 +1,8 @@  + x:Class="LibationAvalonia.App" + Name="Libation"> @@ -69,4 +70,9 @@ + + + + + \ No newline at end of file diff --git a/Source/LibationAvalonia/App.axaml.cs b/Source/LibationAvalonia/App.axaml.cs index c5e309ea..9d1c5159 100644 --- a/Source/LibationAvalonia/App.axaml.cs +++ b/Source/LibationAvalonia/App.axaml.cs @@ -14,6 +14,8 @@ using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; +using ReactiveUI; +using DataLayer; namespace LibationAvalonia { @@ -45,9 +47,6 @@ namespace LibationAvalonia { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - var acceleratorKey = Configuration.IsMacOs ? KeyModifiers.Meta : KeyModifiers.Alt; - AvaloniaLocator.CurrentMutable.Bind().ToFunc(() => new AccessKeyHandlerEx(acceleratorKey)); - var config = Configuration.Instance; if (!config.LibationSettingsAreValid) @@ -219,9 +218,8 @@ namespace LibationAvalonia LoadStyles(); var mainWindow = new MainWindow(); desktop.MainWindow = MainWindow = mainWindow; - mainWindow.RestoreSizeAndLocation(Configuration.Instance); - mainWindow.OnLoad(); mainWindow.OnLibraryLoaded(LibraryTask.GetAwaiter().GetResult()); + mainWindow.RestoreSizeAndLocation(Configuration.Instance); mainWindow.Show(); } diff --git a/Source/LibationAvalonia/Controls/DirectorySelectControl.axaml b/Source/LibationAvalonia/Controls/DirectorySelectControl.axaml index 45f6fe02..c66c7a1b 100644 --- a/Source/LibationAvalonia/Controls/DirectorySelectControl.axaml +++ b/Source/LibationAvalonia/Controls/DirectorySelectControl.axaml @@ -27,7 +27,7 @@ + Text="{Binding Converter={StaticResource KnownDirectoryConverter}}" /> diff --git a/Source/LibationAvalonia/Controls/GroupBox.axaml b/Source/LibationAvalonia/Controls/GroupBox.axaml index 91246b19..e94dfe41 100644 --- a/Source/LibationAvalonia/Controls/GroupBox.axaml +++ b/Source/LibationAvalonia/Controls/GroupBox.axaml @@ -6,9 +6,6 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="LibationAvalonia.Controls.GroupBox"> - - - - + - + - + - - - - + + + + + - - - + + + - + @@ -67,10 +130,10 @@ - - - - + + + + @@ -82,12 +145,12 @@ - + - + - - - - - - + + + + + + @@ -122,19 +185,19 @@ - - + + - - + + - - + + - + - + @@ -155,23 +218,23 @@ - @@ -179,17 +242,17 @@ - + - + @@ -203,10 +266,10 @@ - - - - + + + + diff --git a/Source/LibationAvalonia/Views/MainWindow.axaml.cs b/Source/LibationAvalonia/Views/MainWindow.axaml.cs index 20dfe906..21e2b87a 100644 --- a/Source/LibationAvalonia/Views/MainWindow.axaml.cs +++ b/Source/LibationAvalonia/Views/MainWindow.axaml.cs @@ -1,58 +1,32 @@ -using ApplicationServices; +using Avalonia.Controls; +using Avalonia.Input; using Avalonia.ReactiveUI; using DataLayer; using LibationAvalonia.ViewModels; using LibationFileManager; +using LibationUiBase.GridView; using System; using System.Collections.Generic; using System.Linq; namespace LibationAvalonia.Views { - public partial class MainWindow : ReactiveWindow + public partial class MainWindow : ReactiveWindow { - public event EventHandler Load; public event EventHandler> LibraryLoaded; - private readonly MainWindowViewModel _viewModel; - public MainWindow() { - this.DataContext = _viewModel = new MainWindowViewModel(); + DataContext = new MainVM(this); InitializeComponent(); - - // eg: if one of these init'd productsGrid, then another can't reliably subscribe to it - Configure_BackupCounts(); - Configure_ScanAuto(); - Configure_ScanNotification(); - Configure_VisibleBooks(); - Configure_QuickFilters(); - Configure_ScanManual(); - Configure_RemoveBooks(); - Configure_Liberate(); - Configure_Export(); - Configure_Settings(); - Configure_ProcessQueue(); Configure_Upgrade(); - Configure_Filter(); - // misc which belongs in winforms app but doesn't have a UI element - Configure_NonUI(); - _viewModel.ProductsDisplay.RemovableCountChanged += ProductsDisplay_RemovableCountChanged; - _viewModel.ProductsDisplay.VisibleCountChanged += ProductsDisplay_VisibleCountChanged; - - { - this.LibraryLoaded += MainWindow_LibraryLoaded; - - LibraryCommands.LibrarySizeChanged += async (_, _) => await _viewModel.ProductsDisplay.UpdateGridAsync(DbContexts.GetLibrary_Flat_NoTracking(includeParents: true)); - Closing += (_, _) => this.SaveSizeAndLocation(Configuration.Instance); - } + Loaded += MainWindow_Loaded; Closing += MainWindow_Closing; - - Opened += MainWindow_Opened; + LibraryLoaded += MainWindow_LibraryLoaded; } - private async void MainWindow_Opened(object sender, EventArgs e) + private async void MainWindow_Loaded(object sender, EventArgs e) { if (Configuration.Instance.FirstLaunch) { @@ -66,21 +40,71 @@ namespace LibationAvalonia.Views Configuration.Instance.FirstLaunch = false; } } - + private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) { productsDisplay?.CloseImageDisplay(); + this.SaveSizeAndLocation(Configuration.Instance); } private async void MainWindow_LibraryLoaded(object sender, List dbBooks) { if (QuickFilters.UseDefault) - await performFilter(QuickFilters.Filters.FirstOrDefault()); + await ViewModel.PerformFilter(QuickFilters.Filters.FirstOrDefault()); - _viewModel.ProductsDisplay.BindToGrid(dbBooks); + ViewModel.ProductsDisplay.BindToGrid(dbBooks); } - public void OnLoad() => Load?.Invoke(this, EventArgs.Empty); public void OnLibraryLoaded(List initialLibrary) => LibraryLoaded?.Invoke(this, initialLibrary); + public void ProductsDisplay_LiberateClicked(object _, LibraryBook libraryBook) => ViewModel.LiberateClicked(libraryBook); + public void ProductsDisplay_LiberateSeriesClicked(object _, ISeriesEntry series) => ViewModel.LiberateSeriesClicked(series); + public void ProductsDisplay_ConvertToMp3Clicked(object _, LibraryBook libraryBook) => ViewModel.ConvertToMp3Clicked(libraryBook); + + public async void filterSearchTb_KeyPress(object _, KeyEventArgs e) + { + if (e.Key == Key.Return) + { + await ViewModel.PerformFilter(ViewModel.FilterString); + + // silence the 'ding' + e.Handled = true; + } + } + + private void QuickFiltersMenuItem_KeyDown(object _, KeyEventArgs e) + { + int keyNum = (int)e.Key - 34; + + if (keyNum <= 9 && keyNum >= 1) + { + ViewModel.QuickFilterMenuItems + .OfType() + .FirstOrDefault(i => i.Header is string h && h.StartsWith($"_{keyNum}")) + ?.Command + ?.Execute(null); + } + } + private void Configure_Upgrade() + { + setProgressVisible(false); +#if DEBUG + async System.Threading.Tasks.Task upgradeAvailable(LibationUiBase.UpgradeEventArgs e) + { + var notificationResult = await new LibationAvalonia.Dialogs.UpgradeNotificationDialog(e.UpgradeProperties, e.CapUpgrade).ShowDialogAsync(this); + + e.Ignore = notificationResult == DialogResult.Ignore; + e.InstallUpgrade = notificationResult == DialogResult.OK; + } + + var upgrader = new LibationUiBase.Upgrader(); + upgrader.DownloadProgress += async (_, e) => await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => ViewModel.DownloadProgress = e.ProgressPercentage); + upgrader.DownloadBegin += async (_, _) => await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => setProgressVisible(true)); + upgrader.DownloadCompleted += async (_, _) => await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => setProgressVisible(false)); + + Opened += async (_, _) => await upgrader.CheckForUpgradeAsync(upgradeAvailable); +#endif + } + + private void setProgressVisible(bool visible) => ViewModel.DownloadProgress = visible ? 0 : null; } } diff --git a/Source/LibationAvalonia/Views/ProcessQueueControl.axaml b/Source/LibationAvalonia/Views/ProcessQueueControl.axaml index 87b50442..555576dc 100644 --- a/Source/LibationAvalonia/Views/ProcessQueueControl.axaml +++ b/Source/LibationAvalonia/Views/ProcessQueueControl.axaml @@ -11,9 +11,6 @@ - - - diff --git a/Source/LibationAvalonia/Walkthrough.cs b/Source/LibationAvalonia/Walkthrough.cs index b7bd49d1..8cee6272 100644 --- a/Source/LibationAvalonia/Walkthrough.cs +++ b/Source/LibationAvalonia/Walkthrough.cs @@ -149,7 +149,7 @@ namespace LibationAvalonia await displayControlAsync(MainForm.importToolStripMenuItem); await displayControlAsync(scanItem); - scanItem.RaiseEvent(new RoutedEventArgs(MenuItem.ClickEvent)); + scanItem.Command.Execute(null); MainForm.importToolStripMenuItem.Close(); var tcs = new TaskCompletionSource(); @@ -193,7 +193,7 @@ namespace LibationAvalonia await displayControlAsync(MainForm.filterBtn); - MainForm.filterBtn.RaiseEvent(new RoutedEventArgs(Button.ClickEvent)); + MainForm.filterBtn.Command.Execute(null); await Task.Delay(1000); @@ -222,7 +222,7 @@ namespace LibationAvalonia await Task.Delay(750); await displayControlAsync(MainForm.addQuickFilterBtn); - MainForm.addQuickFilterBtn.RaiseEvent(new RoutedEventArgs(Button.ClickEvent)); + MainForm.addQuickFilterBtn.Command.Execute(null); await displayControlAsync(MainForm.quickFiltersToolStripMenuItem); await displayControlAsync(editQuickFiltersToolStripMenuItem); From 8d73f5cc7e0ee3e4b9bd587d00006c521335eda6 Mon Sep 17 00:00:00 2001 From: MBucari Date: Sun, 2 Apr 2023 11:29:28 -0600 Subject: [PATCH 2/7] Add About dialog --- Source/LibationAvalonia/App.axaml | 4 +- .../Assets/LibationVectorIcons.xaml | 27 +++ .../Dialogs/AboutDialog.axaml | 61 ++++++- .../Dialogs/AboutDialog.axaml.cs | 68 +++++++- .../ViewModels/MainVM.Settings.cs | 10 +- .../Views/MainWindow.axaml.cs | 4 +- .../Dialogs/AboutDialog.Designer.cs | 161 ++++++++++++++++++ .../LibationWinForms/Dialogs/AboutDialog.cs | 66 +++++++ .../LibationWinForms/Dialogs/AboutDialog.resx | 60 +++++++ Source/LibationWinForms/Form1.Designer.cs | 4 +- Source/LibationWinForms/Form1.Settings.cs | 4 +- .../Properties/Resources.Designer.cs | 10 ++ .../Properties/Resources.resx | 3 + Source/LibationWinForms/Resources/cheers.png | Bin 0 -> 34171 bytes 14 files changed, 466 insertions(+), 16 deletions(-) create mode 100644 Source/LibationWinForms/Dialogs/AboutDialog.Designer.cs create mode 100644 Source/LibationWinForms/Dialogs/AboutDialog.cs create mode 100644 Source/LibationWinForms/Dialogs/AboutDialog.resx create mode 100644 Source/LibationWinForms/Resources/cheers.png diff --git a/Source/LibationAvalonia/App.axaml b/Source/LibationAvalonia/App.axaml index 7677075b..9c4b6240 100644 --- a/Source/LibationAvalonia/App.axaml +++ b/Source/LibationAvalonia/App.axaml @@ -72,7 +72,9 @@ - + + + \ No newline at end of file diff --git a/Source/LibationAvalonia/Assets/LibationVectorIcons.xaml b/Source/LibationAvalonia/Assets/LibationVectorIcons.xaml index d2e2d278..9c9d7ec2 100644 --- a/Source/LibationAvalonia/Assets/LibationVectorIcons.xaml +++ b/Source/LibationAvalonia/Assets/LibationVectorIcons.xaml @@ -64,6 +64,33 @@ M7.2,0.8 a 0.8,0.8 0 0 1 1.6,0 v8 l0.9929,-0.9929 a 0.8,0.8 0 0 1 1.1314,1.1314 l-2.3586,2.3586 a 0.8,0.8 0 0 1 -1.1314,0 l-2.3586,-2.3586 a 0.8,0.8 0 0 1 1.1314,-1.1314 l0.9929,0.9929 v8 + + + M139,2 + A 192,200 0 0 0 103,84 + A 222,334 41 0 0 241,320 + V478 + H160 + A 16,16 0 0 0 160,510 + H352 + A16 16 0 0 0 352,478 + H271 + V320 + A 222,334 -41 0 0 409,84 + A 192,200 0 0 0 373,2 + M355,32 + A 192,200 0 0 1 381,127 + A 187.5,334 -35 0 1 256,286 + A 187.5,334 35 0 1 131,127 + A 192,200 0 0 1 157,32 + H355 + M146,147 + A 168,300 35 0 0 256,270 + A 168,300 -35 0 0 366,128 + S 360,50 280,110 + S 192,128 147,147 + + diff --git a/Source/LibationAvalonia/Dialogs/AboutDialog.axaml b/Source/LibationAvalonia/Dialogs/AboutDialog.axaml index 0468bebb..b41144df 100644 --- a/Source/LibationAvalonia/Dialogs/AboutDialog.axaml +++ b/Source/LibationAvalonia/Dialogs/AboutDialog.axaml @@ -2,12 +2,65 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" d:DesignWidth="550" d:DesignHeight="450" - MinWidth="550" MinHeight="450" - Width="650" Height="500" + mc:Ignorable="d" d:DesignWidth="450" d:DesignHeight="600" + MinWidth="450" MinHeight="550" + Width="450" Height="600" x:Class="LibationAvalonia.Dialogs.AboutDialog" xmlns:controls="clr-namespace:LibationAvalonia.Controls" Title="About Libation" Icon="/Assets/libation.ico"> - Welcome to Avalonia! + + + + + + + +