diff --git a/Source/LibationWinForms/AvaloniaUI/Views/Dialogs/DescriptionDisplayDialog.axaml b/Source/LibationWinForms/AvaloniaUI/Views/Dialogs/DescriptionDisplayDialog.axaml new file mode 100644 index 00000000..76aeb1d2 --- /dev/null +++ b/Source/LibationWinForms/AvaloniaUI/Views/Dialogs/DescriptionDisplayDialog.axaml @@ -0,0 +1,19 @@ + + + + + diff --git a/Source/LibationWinForms/AvaloniaUI/Views/Dialogs/DescriptionDisplayDialog.axaml.cs b/Source/LibationWinForms/AvaloniaUI/Views/Dialogs/DescriptionDisplayDialog.axaml.cs new file mode 100644 index 00000000..54f7e257 --- /dev/null +++ b/Source/LibationWinForms/AvaloniaUI/Views/Dialogs/DescriptionDisplayDialog.axaml.cs @@ -0,0 +1,62 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using System; + +namespace LibationWinForms.AvaloniaUI.Views.Dialogs +{ + public partial class DescriptionDisplayDialog : Window + { + public Point SpawnLocation { get; set; } + public string DescriptionText { get; init; } + public DescriptionDisplayDialog() + { + InitializeComponent(); +#if DEBUG + this.AttachDevTools(); +#endif + DescriptionTextBox = this.FindControl(nameof(DescriptionTextBox)); + this.Activated += DescriptionDisplay_Activated; + Opened += DescriptionDisplay_Opened; + } + + private void DescriptionDisplay_Opened(object sender, EventArgs e) + { + DescriptionTextBox.Focus(); + } + + private void DescriptionDisplay_Activated(object sender, EventArgs e) + { + DataContext = this; + var workingHeight = this.Screens.Primary.WorkingArea.Height; + DescriptionTextBox.Measure(new Size(DescriptionTextBox.MinWidth, workingHeight * 0.8)); + + this.Width = DescriptionTextBox.DesiredSize.Width; + this.Height = DescriptionTextBox.DesiredSize.Height; + this.MinWidth = this.Width; + this.MaxWidth = this.Width; + this.MinHeight = this.Height; + this.MaxHeight = this.Height; + + DescriptionTextBox.Width = this.Width; + DescriptionTextBox.Height = this.Height; + DescriptionTextBox.MinWidth = this.Width; + DescriptionTextBox.MaxWidth = this.Width; + DescriptionTextBox.MinHeight = this.Height; + DescriptionTextBox.MaxHeight = this.Height; + + this.Position = new PixelPoint((int)SpawnLocation.X, (int)Math.Min(SpawnLocation.Y, (double)workingHeight - DescriptionTextBox.DesiredSize.Height)); + } + + private void DescriptionTextBox_LostFocus(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { + Close(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + } +} diff --git a/Source/LibationWinForms/AvaloniaUI/Views/Dialogs/ImageDisplayDialog.axaml b/Source/LibationWinForms/AvaloniaUI/Views/Dialogs/ImageDisplayDialog.axaml new file mode 100644 index 00000000..81e87a14 --- /dev/null +++ b/Source/LibationWinForms/AvaloniaUI/Views/Dialogs/ImageDisplayDialog.axaml @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/Source/LibationWinForms/AvaloniaUI/Views/Dialogs/ImageDisplayDialog.axaml.cs b/Source/LibationWinForms/AvaloniaUI/Views/Dialogs/ImageDisplayDialog.axaml.cs new file mode 100644 index 00000000..41c62b39 --- /dev/null +++ b/Source/LibationWinForms/AvaloniaUI/Views/Dialogs/ImageDisplayDialog.axaml.cs @@ -0,0 +1,84 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Media.Imaging; +using Avalonia.Platform; +using System; +using System.ComponentModel; +using System.IO; +using ReactiveUI; + +namespace LibationWinForms.AvaloniaUI.Views.Dialogs +{ + public partial class ImageDisplayDialog : DialogWindow, INotifyPropertyChanged + { + public string PictureFileName { get; set; } + public string BookSaveDirectory { get; set; } + + private byte[] _coverBytes; + public byte[] CoverBytes + { + get => _coverBytes; + set + { + _coverBytes = value; + var ms = new MemoryStream(_coverBytes); + ms.Position = 0; + _bitmapHolder.CoverImage = new Bitmap(ms); + } + } + + + private readonly BitmapHolder _bitmapHolder = new BitmapHolder(); + + + public ImageDisplayDialog() + { + InitializeComponent(); + DataContext = _bitmapHolder; + } + + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + public async void SaveImage_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { + + SaveFileDialog saveFileDialog = new(); + saveFileDialog.Filters.Add(new FileDialogFilter { Name = "Jpeg", Extensions = new System.Collections.Generic.List() { "jpg" } }); + saveFileDialog.Directory = Directory.Exists(BookSaveDirectory) ? BookSaveDirectory : Path.GetDirectoryName(BookSaveDirectory); + saveFileDialog.InitialFileName = PictureFileName; + + var fileName = await saveFileDialog.ShowAsync(this); + + if (fileName is null) + return; + + try + { + File.WriteAllBytes(fileName, CoverBytes); + } + catch (Exception ex) + { + Serilog.Log.Logger.Error(ex, $"Failed to save picture to {fileName}"); + await MessageBox.Show(this, $"An error was encountered while trying to save the picture\r\n\r\n{ex.Message}", "Failed to save picture", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); + } + } + + public class BitmapHolder : ViewModels.ViewModelBase + { + private Bitmap _coverImage; + public Bitmap CoverImage + { + get => _coverImage; + set + { + this.RaiseAndSetIfChanged(ref _coverImage, value); + } + } + } + } +} diff --git a/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml.cs b/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml.cs index fddeef83..bdd96e90 100644 --- a/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml.cs +++ b/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml.cs @@ -18,7 +18,7 @@ namespace LibationWinForms.AvaloniaUI.Views public event EventHandler LiberateClicked; private ProductsDisplayViewModel _viewModel => DataContext as ProductsDisplayViewModel; - private GridView.ImageDisplay imageDisplay; + ImageDisplayDialog imageDisplayDialog; public ProductsDisplay2() { @@ -211,12 +211,18 @@ namespace LibationWinForms.AvaloniaUI.Views if (sender is not Image tblock || tblock.DataContext is not GridEntry2 gEntry) return; + + if (imageDisplayDialog is null || !imageDisplayDialog.IsVisible) + { + imageDisplayDialog = new ImageDisplayDialog(); + } + var picDef = new PictureDefinition(gEntry.LibraryBook.Book.PictureLarge ?? gEntry.LibraryBook.Book.PictureId, PictureSize.Native); void PictureCached(object sender, PictureCachedEventArgs e) { if (e.Definition.PictureId == picDef.PictureId) - imageDisplay.CoverPicture = e.Picture; + imageDisplayDialog.CoverBytes = e.Picture; PictureStorage.PictureCached -= PictureCached; } @@ -224,24 +230,20 @@ namespace LibationWinForms.AvaloniaUI.Views PictureStorage.PictureCached += PictureCached; (bool isDefault, byte[] initialImageBts) = PictureStorage.GetPicture(picDef); + var windowTitle = $"{gEntry.Title} - Cover"; - if (imageDisplay is null || imageDisplay.IsDisposed || !imageDisplay.Visible) - { - imageDisplay = new GridView.ImageDisplay(); - imageDisplay.RestoreSizeAndLocation(Configuration.Instance); - imageDisplay.FormClosed += (_, _) => imageDisplay.SaveSizeAndLocation(Configuration.Instance); - } - imageDisplay.BookSaveDirectory = AudibleFileStorage.Audio.GetDestinationDirectory(gEntry.LibraryBook); - imageDisplay.PictureFileName = System.IO.Path.GetFileName(AudibleFileStorage.Audio.GetBooksDirectoryFilename(gEntry.LibraryBook, ".jpg")); - imageDisplay.Text = windowTitle; - imageDisplay.CoverPicture = initialImageBts; + imageDisplayDialog.BookSaveDirectory = AudibleFileStorage.Audio.GetDestinationDirectory(gEntry.LibraryBook); + imageDisplayDialog.PictureFileName = System.IO.Path.GetFileName(AudibleFileStorage.Audio.GetBooksDirectoryFilename(gEntry.LibraryBook, ".jpg")); + imageDisplayDialog.Title = windowTitle; + imageDisplayDialog.CoverBytes = initialImageBts; + if (!isDefault) PictureStorage.PictureCached -= PictureCached; - if (!imageDisplay.Visible) - imageDisplay.Show(null); + if (!imageDisplayDialog.IsVisible) + imageDisplayDialog.Show(); } public void Description_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args) @@ -249,11 +251,10 @@ namespace LibationWinForms.AvaloniaUI.Views if (sender is TextBlock tblock && tblock.DataContext is GridEntry2 gEntry) { var pt = tblock.Parent.PointToScreen(tblock.Parent.Bounds.TopRight); - var displayWindow = new GridView.DescriptionDisplay + var displayWindow = new DescriptionDisplayDialog { - SpawnLocation = new System.Drawing.Point(pt.X, pt.Y), + SpawnLocation = new Point(pt.X, pt.Y), DescriptionText = gEntry.LongDescription, - BorderThickness = 2, }; void CloseWindow(object o, DataGridRowEventArgs e) @@ -261,7 +262,7 @@ namespace LibationWinForms.AvaloniaUI.Views displayWindow.Close(); } productsGrid.LoadingRow += CloseWindow; - displayWindow.FormClosed += (_, _) => + displayWindow.Closing += (_, _) => { productsGrid.LoadingRow -= CloseWindow; }; diff --git a/Source/LibationWinForms/LibationWinForms.csproj b/Source/LibationWinForms/LibationWinForms.csproj index 0905273d..81ecbe2b 100644 --- a/Source/LibationWinForms/LibationWinForms.csproj +++ b/Source/LibationWinForms/LibationWinForms.csproj @@ -109,6 +109,9 @@ + + DescriptionDisplayDialog.axaml + EditQuickFilters.axaml @@ -118,6 +121,9 @@ SearchSyntaxDialog.axaml + + ImageDisplayDialog.axaml + True True