From 7d6000e3b62ed96b9532efb8c8c8a39a00b3fca8 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Fri, 16 Dec 2022 16:41:24 -0700 Subject: [PATCH] Bring Hangover Chardonnay into feature parity with Classic (#409) --- Source/HangoverAvalonia/App.axaml.cs | 6 +- .../Controls/CheckedListBox.axaml | 30 +++++ .../Controls/CheckedListBox.axaml.cs | 106 ++++++++++++++++++ .../HangoverAvalonia/HangoverAvalonia.csproj | 7 +- .../ViewModels/MainVM.Database.cs | 38 +++++++ .../ViewModels/MainVM.Deleted.cs | 43 +++++++ Source/HangoverAvalonia/ViewModels/MainVM.cs | 18 +++ .../ViewModels/MainWindowViewModel.cs | 37 ------ .../HangoverAvalonia/Views/MainWindow.CLI.cs | 17 +++ .../Views/MainWindow.Database.cs | 23 ++++ .../Views/MainWindow.Deleted.cs | 41 +++++++ .../HangoverAvalonia/Views/MainWindow.axaml | 65 +++++++++-- .../Views/MainWindow.axaml.cs | 19 ++-- 13 files changed, 391 insertions(+), 59 deletions(-) create mode 100644 Source/HangoverAvalonia/Controls/CheckedListBox.axaml create mode 100644 Source/HangoverAvalonia/Controls/CheckedListBox.axaml.cs create mode 100644 Source/HangoverAvalonia/ViewModels/MainVM.Database.cs create mode 100644 Source/HangoverAvalonia/ViewModels/MainVM.Deleted.cs create mode 100644 Source/HangoverAvalonia/ViewModels/MainVM.cs delete mode 100644 Source/HangoverAvalonia/ViewModels/MainWindowViewModel.cs create mode 100644 Source/HangoverAvalonia/Views/MainWindow.CLI.cs create mode 100644 Source/HangoverAvalonia/Views/MainWindow.Database.cs create mode 100644 Source/HangoverAvalonia/Views/MainWindow.Deleted.cs diff --git a/Source/HangoverAvalonia/App.axaml.cs b/Source/HangoverAvalonia/App.axaml.cs index a64b8e22..158a427e 100644 --- a/Source/HangoverAvalonia/App.axaml.cs +++ b/Source/HangoverAvalonia/App.axaml.cs @@ -17,10 +17,12 @@ namespace HangoverAvalonia { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - desktop.MainWindow = new MainWindow + var mainWindow = new MainWindow { - DataContext = new MainWindowViewModel(), + DataContext = new MainVM(), }; + desktop.MainWindow = mainWindow; + mainWindow.OnLoad(); } base.OnFrameworkInitializationCompleted(); diff --git a/Source/HangoverAvalonia/Controls/CheckedListBox.axaml b/Source/HangoverAvalonia/Controls/CheckedListBox.axaml new file mode 100644 index 00000000..f797e06b --- /dev/null +++ b/Source/HangoverAvalonia/Controls/CheckedListBox.axaml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + diff --git a/Source/HangoverAvalonia/Controls/CheckedListBox.axaml.cs b/Source/HangoverAvalonia/Controls/CheckedListBox.axaml.cs new file mode 100644 index 00000000..79f899e8 --- /dev/null +++ b/Source/HangoverAvalonia/Controls/CheckedListBox.axaml.cs @@ -0,0 +1,106 @@ +using Avalonia; +using Avalonia.Collections; +using Avalonia.Controls; +using DataLayer; +using HangoverAvalonia.ViewModels; +using NPOI.XSSF.Streaming.Properties; +using ReactiveUI; +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +namespace HangoverAvalonia.Controls +{ + public partial class CheckedListBox : UserControl + { + public event EventHandler ItemCheck; + + public static readonly StyledProperty ItemsProperty = + AvaloniaProperty.Register(nameof(Items)); + + public IEnumerable Items { get => GetValue(ItemsProperty); set => SetValue(ItemsProperty, value); } + private CheckedListBoxViewModel _viewModel = new(); + + public IEnumerable CheckedItems => + _viewModel + .CheckboxItems + .Where(i => i.IsChecked) + .Select(i => i.Item); + + public void SetItemChecked(int i, bool isChecked) => _viewModel.CheckboxItems[i].IsChecked = isChecked; + public void SetItemChecked(object item, bool isChecked) + { + var obj = _viewModel.CheckboxItems.SingleOrDefault(i => i.Item == item); + if (obj is not null) + obj.IsChecked = isChecked; + } + + public CheckedListBox() + { + InitializeComponent(); + scroller.DataContext = _viewModel; + _viewModel.CheckedChanged += _viewModel_CheckedChanged; + } + + private void _viewModel_CheckedChanged(object sender, CheckBoxViewModel e) + { + var args = new ItemCheckEventArgs { Item = e.Item, ItemIndex = _viewModel.CheckboxItems.IndexOf(e), IsChecked = e.IsChecked }; + ItemCheck?.Invoke(this, args); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + if (change.Property.Name == nameof(Items) && Items != null) + _viewModel.SetItems(Items); + base.OnPropertyChanged(change); + } + + public class CheckedListBoxViewModel : ViewModelBase + { + public event EventHandler CheckedChanged; + public AvaloniaList CheckboxItems { get; private set; } + + public void SetItems(IEnumerable items) + { + UnsubscribeFromItems(CheckboxItems); + CheckboxItems = new(items.OfType().Select(o => new CheckBoxViewModel { Item = o })); + SubscribeToItems(CheckboxItems); + this.RaisePropertyChanged(nameof(CheckboxItems)); + } + + private void SubscribeToItems(IEnumerable objects) + { + foreach (var i in objects.OfType()) + i.PropertyChanged += I_PropertyChanged; + } + + private void UnsubscribeFromItems(AvaloniaList objects) + { + if (objects is null) return; + + foreach (var i in objects) + i.PropertyChanged -= I_PropertyChanged; + } + private void I_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + CheckedChanged?.Invoke(this, (CheckBoxViewModel)sender); + } + } + public class CheckBoxViewModel : ViewModelBase + { + private bool _isChecked; + public bool IsChecked { get => _isChecked; set => this.RaiseAndSetIfChanged(ref _isChecked, value); } + private object _bookText; + public object Item { get => _bookText; set => this.RaiseAndSetIfChanged(ref _bookText, value); } + } + } + + public class ItemCheckEventArgs : EventArgs + { + public int ItemIndex { get; init; } + public bool IsChecked { get; init; } + public object Item { get; init; } + } +} diff --git a/Source/HangoverAvalonia/HangoverAvalonia.csproj b/Source/HangoverAvalonia/HangoverAvalonia.csproj index 999e7fe2..40795aff 100644 --- a/Source/HangoverAvalonia/HangoverAvalonia.csproj +++ b/Source/HangoverAvalonia/HangoverAvalonia.csproj @@ -50,7 +50,12 @@ - + + + + MainVM.cs + + diff --git a/Source/HangoverAvalonia/ViewModels/MainVM.Database.cs b/Source/HangoverAvalonia/ViewModels/MainVM.Database.cs new file mode 100644 index 00000000..070a96fa --- /dev/null +++ b/Source/HangoverAvalonia/ViewModels/MainVM.Database.cs @@ -0,0 +1,38 @@ +using HangoverBase; +using ReactiveUI; +using System; +using System.Linq; + +namespace HangoverAvalonia.ViewModels +{ + public partial class MainVM + { + private DatabaseTab _tab; + + private string _databaseFileText; + private bool _databaseFound; + private string _sqlResults; + public string DatabaseFileText { get => _databaseFileText; set => this.RaiseAndSetIfChanged(ref _databaseFileText, value); } + public string SqlQuery { get; set; } + public bool DatabaseFound { get => _databaseFound; set => this.RaiseAndSetIfChanged(ref _databaseFound, value); } + public string SqlResults { get => _sqlResults; set => this.RaiseAndSetIfChanged(ref _sqlResults, value); } + + private void Load_databaseVM() + { + _tab = new(new(() => SqlQuery, s => SqlResults = s, s => SqlResults = s)); + + _tab.LoadDatabaseFile(); + if (_tab.DbFile is null) + { + DatabaseFileText = $"Database file not found"; + DatabaseFound = false; + return; + } + + DatabaseFileText = $"Database file: {_tab.DbFile}"; + DatabaseFound = true; + } + + public void ExecuteQuery() => _tab.ExecuteQuery(); + } +} diff --git a/Source/HangoverAvalonia/ViewModels/MainVM.Deleted.cs b/Source/HangoverAvalonia/ViewModels/MainVM.Deleted.cs new file mode 100644 index 00000000..482c98a1 --- /dev/null +++ b/Source/HangoverAvalonia/ViewModels/MainVM.Deleted.cs @@ -0,0 +1,43 @@ +using ApplicationServices; +using DataLayer; +using ReactiveUI; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace HangoverAvalonia.ViewModels +{ + public partial class MainVM + { + private List _deletedBooks; + public List DeletedBooks { get => _deletedBooks; set => this.RaiseAndSetIfChanged(ref _deletedBooks, value); } + public string CheckedCountText => $"Checked : {_checkedBooksCount} of {_totalBooksCount}"; + + private int _totalBooksCount = 0; + private int _checkedBooksCount = 0; + public int CheckedBooksCount + { + get => _checkedBooksCount; + set + { + if (_checkedBooksCount != value) + { + _checkedBooksCount = value; + this.RaisePropertyChanged(nameof(CheckedCountText)); + } + } + } + private void Load_deletedVM() + { + reload(); + } + + public void reload() + { + DeletedBooks = DbContexts.GetContext().GetDeletedLibraryBooks(); + _checkedBooksCount = 0; + _totalBooksCount = DeletedBooks.Count; + this.RaisePropertyChanged(nameof(CheckedCountText)); + } + } +} diff --git a/Source/HangoverAvalonia/ViewModels/MainVM.cs b/Source/HangoverAvalonia/ViewModels/MainVM.cs new file mode 100644 index 00000000..9cc1fea6 --- /dev/null +++ b/Source/HangoverAvalonia/ViewModels/MainVM.cs @@ -0,0 +1,18 @@ +using System; +using ApplicationServices; +using DataLayer; +using System.Collections.Generic; +using HangoverBase; +using ReactiveUI; + +namespace HangoverAvalonia.ViewModels +{ + public partial class MainVM : ViewModelBase + { + public MainVM() + { + Load_databaseVM(); + Load_deletedVM(); + } + } +} diff --git a/Source/HangoverAvalonia/ViewModels/MainWindowViewModel.cs b/Source/HangoverAvalonia/ViewModels/MainWindowViewModel.cs deleted file mode 100644 index a578811b..00000000 --- a/Source/HangoverAvalonia/ViewModels/MainWindowViewModel.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using HangoverBase; -using ReactiveUI; - -namespace HangoverAvalonia.ViewModels -{ - public class MainWindowViewModel : ViewModelBase - { - private DatabaseTab _tab; - - private string _databaseFileText; - private bool _databaseFound; - private string _sqlResults; - public string DatabaseFileText { get => _databaseFileText; set => this.RaiseAndSetIfChanged(ref _databaseFileText, value); } - public string SqlQuery { get; set; } - public bool DatabaseFound { get => _databaseFound; set => this.RaiseAndSetIfChanged(ref _databaseFound, value); } - public string SqlResults { get => _sqlResults; set => this.RaiseAndSetIfChanged(ref _sqlResults, value); } - - public MainWindowViewModel() - { - _tab = new(new(() => SqlQuery, s => SqlResults = s, s => SqlResults = s)); - - _tab.LoadDatabaseFile(); - if (_tab.DbFile is null) - { - DatabaseFileText = $"Database file not found"; - DatabaseFound = false; - return; - } - - DatabaseFileText = $"Database file: {_tab.DbFile}"; - DatabaseFound = true; - } - - public void ExecuteQuery() => _tab.ExecuteQuery(); - } -} diff --git a/Source/HangoverAvalonia/Views/MainWindow.CLI.cs b/Source/HangoverAvalonia/Views/MainWindow.CLI.cs new file mode 100644 index 00000000..6002bb80 --- /dev/null +++ b/Source/HangoverAvalonia/Views/MainWindow.CLI.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HangoverAvalonia.Views +{ + public partial class MainWindow + { + private void cliTab_VisibleChanged(bool isVisible) + { + if (!isVisible) + return; + } + } +} diff --git a/Source/HangoverAvalonia/Views/MainWindow.Database.cs b/Source/HangoverAvalonia/Views/MainWindow.Database.cs new file mode 100644 index 00000000..0a9e6ed4 --- /dev/null +++ b/Source/HangoverAvalonia/Views/MainWindow.Database.cs @@ -0,0 +1,23 @@ +using HangoverAvalonia.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HangoverAvalonia.Views +{ + public partial class MainWindow + { + private void databaseTab_VisibleChanged(bool isVisible) + { + if (!isVisible) + return; + } + + public void Execute_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { + _viewModel.ExecuteQuery(); + } + } +} diff --git a/Source/HangoverAvalonia/Views/MainWindow.Deleted.cs b/Source/HangoverAvalonia/Views/MainWindow.Deleted.cs new file mode 100644 index 00000000..aa352552 --- /dev/null +++ b/Source/HangoverAvalonia/Views/MainWindow.Deleted.cs @@ -0,0 +1,41 @@ +using ApplicationServices; +using DataLayer; +using HangoverAvalonia.Controls; +using System; +using System.Linq; + +namespace HangoverAvalonia.Views +{ + public partial class MainWindow + { + private void deletedTab_VisibleChanged(bool isVisible) + { + if (!isVisible) + return; + + if (_viewModel.DeletedBooks.Count == 0) + _viewModel.reload(); + } + public void Deleted_CheckedListBox_ItemCheck(object sender, ItemCheckEventArgs args) + { + _viewModel.CheckedBooksCount = deletedCbl.CheckedItems.Count(); + } + public void Deleted_CheckAll_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { + foreach (var item in deletedCbl.Items) + deletedCbl.SetItemChecked(item, true); + } + public void Deleted_UncheckAll_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { + foreach (var item in deletedCbl.Items) + deletedCbl.SetItemChecked(item, false); + } + public void Deleted_Save_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { + var libraryBooksToRestore = deletedCbl.CheckedItems.Cast().ToList(); + var qtyChanges = libraryBooksToRestore.RestoreBooks(); + if (qtyChanges > 0) + _viewModel.reload(); + } + } +} diff --git a/Source/HangoverAvalonia/Views/MainWindow.axaml b/Source/HangoverAvalonia/Views/MainWindow.axaml index b32eb8de..d407fbc7 100644 --- a/Source/HangoverAvalonia/Views/MainWindow.axaml +++ b/Source/HangoverAvalonia/Views/MainWindow.axaml @@ -3,33 +3,74 @@ xmlns:vm="using:HangoverAvalonia.ViewModels" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="500" + xmlns:controls="clr-namespace:HangoverAvalonia.Controls" + mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="500" Width="800" Height="500" x:Class="HangoverAvalonia.Views.MainWindow" Icon="/Assets/hangover.ico " Title="Hangover: Libation debug and recovery tool"> - - + - - + + + + + + + + Deleted Books + + + + + + + + + + +