Skip to content

Commit

Permalink
Resizing and file conversion updates
Browse files Browse the repository at this point in the history
  • Loading branch information
Ruben2776 committed Oct 22, 2024
1 parent c4c6a36 commit 7ca92f3
Show file tree
Hide file tree
Showing 9 changed files with 664 additions and 284 deletions.
23 changes: 13 additions & 10 deletions src/PicView.Avalonia/FileSystem/FilePicker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,18 @@ public static async Task SelectAndLoadFile(MainViewModel vm)
};

public static async Task PickAndSaveFileAsAsync(string? fileName, MainViewModel vm)
{
var file = await PickFileForSavingAsync(fileName, vm);
if (file is null)
{
return;
}

var destination = file.Path.LocalPath; // TODO: Handle macOS
await FileSaverHelper.SaveFileAsync(fileName, destination, vm);
}

public static async Task<IStorageFile?> PickFileForSavingAsync(string? fileName, MainViewModel vm)
{
if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop ||
desktop.MainWindow?.StorageProvider is not { } provider)
Expand All @@ -188,15 +200,6 @@ public static async Task PickAndSaveFileAsAsync(string? fileName, MainViewModel
SuggestedStartLocation = await desktop.MainWindow.StorageProvider.TryGetFolderFromPathAsync(fileName)

};
var file = await provider.SaveFilePickerAsync(options);

if (file is null)
{
// User exited
return;
}

var destination = file.Path.LocalPath; // TODO: Handle macOS
await FileSaverHelper.SaveFileAsync(fileName, destination, vm);
return await provider.SaveFilePickerAsync(options);
}
}
72 changes: 72 additions & 0 deletions src/PicView.Avalonia/Input/InputHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using Avalonia.Input;

namespace PicView.Avalonia.Input;

public static class InputHelper
{
public static async Task OnKeyDownVerifyInput(KeyEventArgs e, Func<bool> focus, Task saveImage)
{
switch (e.Key)
{
case Key.D0:
case Key.D1:
case Key.D2:
case Key.D3:
case Key.D4:
case Key.D5:
case Key.D6:
case Key.D7:
case Key.D8:
case Key.D9:
case Key.NumPad0:
case Key.NumPad1:
case Key.NumPad2:
case Key.NumPad3:
case Key.NumPad4:
case Key.NumPad5:
case Key.NumPad6:
case Key.NumPad7:
case Key.NumPad8:
case Key.NumPad9:
case Key.Back:
case Key.Delete:
break; // Allow numbers and basic operations

case Key.Left:
case Key.Right:
case Key.Tab:
case Key.OemBackTab:
break; // Allow navigation keys

case Key.A:
case Key.C:
case Key.X:
case Key.V:
if (e.KeyModifiers == KeyModifiers.Control)
{
// Allow Ctrl + A, Ctrl + C, Ctrl + X, and Ctrl + V (paste)
break;
}

e.Handled = true; // Only allow with Ctrl
return;

case Key.Oem5: // Key for `%` symbol (may vary based on layout)
break; // Allow the percentage symbol (%)

case Key.Escape: // Handle Escape key
focus();
e.Handled = true;
return;

case Key.Enter: // Handle Enter key for function

await saveImage.ConfigureAwait(false);
return;

default:
e.Handled = true; // Block all other inputs
return;
}
}
}
2 changes: 1 addition & 1 deletion src/PicView.Avalonia/Navigation/ImageIterator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ await PreLoader.PreLoadAsync(CurrentIndex, ImagePaths.Count, IsReversed, ImagePa
.ConfigureAwait(false);
}
await AddAsync(index, preloadValue.ImageModel).ConfigureAwait(false);
await AddAsync(index, preloadValue?.ImageModel).ConfigureAwait(false);
// Add recent files, except when browsing archive
if (string.IsNullOrWhiteSpace(TempFileHelper.TempFilePath) && ImagePaths.Count > index)
Expand Down
1 change: 0 additions & 1 deletion src/PicView.Avalonia/PicView.Avalonia.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@

<ItemGroup>
<Folder Include="Assets\" />
<Folder Include="Resizing\" />
</ItemGroup>

<ItemGroup>
Expand Down
67 changes: 67 additions & 0 deletions src/PicView.Avalonia/Resizing/AspectRatioHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System.Globalization;
using Avalonia.Controls;
using PicView.Avalonia.ViewModels;
using PicView.Core.Extensions;

namespace PicView.Avalonia.Resizing;

public static class AspectRatioHelper
{
public static void SetAspectRatioForTextBox(TextBox widthTextBox, TextBox heightTextBox, bool isWidth, double aspectRatio, MainViewModel vm)
{
var percentage = isWidth ? widthTextBox.Text.GetPercentage() : heightTextBox.Text.GetPercentage();
if (percentage > 0)
{
var newWidth = vm.PixelWidth * (percentage / 100);
var newHeight = vm.PixelHeight * (percentage / 100);

widthTextBox.Text = newWidth.ToString("# ", CultureInfo.CurrentCulture);
heightTextBox.Text = newHeight.ToString("# ", CultureInfo.CurrentCulture);

if (isWidth)
{
heightTextBox.Text = newHeight.ToString(CultureInfo.CurrentCulture);
}
else
{
widthTextBox.Text = newWidth.ToString(CultureInfo.CurrentCulture);
}
}
else
{
if (!uint.TryParse(widthTextBox.Text, out var width) || !uint.TryParse(heightTextBox.Text, out var height))
{
// Invalid input, delete last character
try
{
if (isWidth && widthTextBox.Text.Length > 1)
{
widthTextBox.Text = widthTextBox.Text[..^1];
}
else if (heightTextBox.Text.Length > 1)
{
heightTextBox.Text = heightTextBox.Text[..^1];
}

}
catch (Exception e)
{
#if DEBUG
Console.WriteLine(e);
#endif
}
return;
}
if (isWidth)
{
var newHeight = Math.Round(width / aspectRatio);
heightTextBox.Text = newHeight.ToString(CultureInfo.CurrentCulture);
}
else
{
var newWidth = Math.Round(height * aspectRatio);
widthTextBox.Text = newWidth.ToString(CultureInfo.CurrentCulture);
}
}
}
}
165 changes: 111 additions & 54 deletions src/PicView.Avalonia/Views/ExifView.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Avalonia.Controls;
using Avalonia.Input;
using PicView.Avalonia.Converters;
using PicView.Avalonia.Resizing;
using PicView.Avalonia.ViewModels;

namespace PicView.Avalonia.Views;
Expand All @@ -10,77 +11,133 @@ public partial class ExifView : UserControl
public ExifView()
{
InitializeComponent();
PixelHeightTextBox.KeyDown += async (s, e) => await PixelHeightTextBox_OnKeyDown(s, e);
PixelWidthTextBox.KeyDown += async (s, e) => await PixelWidthTextBox_OnKeyDown(s, e);
}
Loaded += (_, _) =>
{
PixelWidthTextBox.KeyDown += async (s, e) => await OnKeyDownVerifyInput(s,e);
PixelHeightTextBox.KeyDown += async (s, e) => await OnKeyDownVerifyInput(s,e);
private async Task PixelHeightTextBox_OnKeyDown(object? sender, KeyEventArgs e)
PixelWidthTextBox.KeyUp += delegate { AdjustAspectRatio(PixelWidthTextBox); };
PixelHeightTextBox.KeyUp += delegate { AdjustAspectRatio(PixelHeightTextBox); };
};
}

private void AdjustAspectRatio(TextBox sender)
{
if (DataContext is null)
{
return;
}
var vm = (MainViewModel)DataContext;
var textBox = (TextBox)sender!;
if (textBox is null)
{
return;
}
var text = ((TextBox)sender!).Text;
if (e.Key != Key.Enter)
if (DataContext is not MainViewModel vm)
{
return;
}
var aspectRatio = (double)vm.PixelWidth / vm.PixelHeight;
AspectRatioHelper.SetAspectRatioForTextBox(PixelWidthTextBox, PixelHeightTextBox, sender == PixelWidthTextBox,
aspectRatio, DataContext as MainViewModel);
}

if (!double.TryParse(text, out var height))
private static async Task DoResize(MainViewModel vm, bool isWidth, object width, object height)
{
if (isWidth)
{
return;
if (!double.TryParse((string?)width, out var widthValue))
{
return;
}
if (widthValue > 0)
{
var success = await ConversionHelper.ResizeByWidth(vm.FileInfo, widthValue).ConfigureAwait(false);
if (success)
{
if (vm.ImageIterator is not null)
{
await vm.ImageIterator.QuickReload().ConfigureAwait(false);
}
}
}
}

if (height > 0)
else
{
var success = await ConversionHelper.ResizeByHeight(vm.FileInfo, height).ConfigureAwait(false);
if (success)
if (!double.TryParse((string?)height, out var heightValue))
{
vm.ImageIterator?.RemoveCurrentItemFromPreLoader();
await vm.ImageIterator?.IterateToIndex(vm.ImageIterator.CurrentIndex);
return;
}
if (heightValue > 0)
{
var success = await ConversionHelper.ResizeByHeight(vm.FileInfo, heightValue).ConfigureAwait(false);
if (success)
{
vm.ImageIterator?.RemoveCurrentItemFromPreLoader();
await vm.ImageIterator?.IterateToIndex(vm.ImageIterator.CurrentIndex);
}
}
}
}

private async Task PixelWidthTextBox_OnKeyDown(object? sender, KeyEventArgs e)
private async Task OnKeyDownVerifyInput(object? sender, KeyEventArgs? e)
{
if (DataContext is null)
{
return;
}
var vm = (MainViewModel)DataContext;
var textBox = (TextBox)sender!;
if (textBox is null)
switch (e.Key)
{
return;
}
var text = ((TextBox)sender!).Text;
if (e.Key != Key.Enter)
{
return;
}

if (!double.TryParse(text, out var width))
{
return;
}

if (width > 0)
{
var success = await ConversionHelper.ResizeByWidth(vm.FileInfo, width).ConfigureAwait(false);
if (success)
{
if (vm.ImageIterator is not null)
case Key.D0:
case Key.D1:
case Key.D2:
case Key.D3:
case Key.D4:
case Key.D5:
case Key.D6:
case Key.D7:
case Key.D8:
case Key.D9:
case Key.NumPad0:
case Key.NumPad1:
case Key.NumPad2:
case Key.NumPad3:
case Key.NumPad4:
case Key.NumPad5:
case Key.NumPad6:
case Key.NumPad7:
case Key.NumPad8:
case Key.NumPad9:
case Key.Back:
case Key.Delete:
break; // Allow numbers and basic operations

case Key.Left:
case Key.Right:
case Key.Tab:
case Key.OemBackTab:
break; // Allow navigation keys

case Key.A:
case Key.C:
case Key.X:
case Key.V:
if (e.KeyModifiers == KeyModifiers.Control)
{
await vm.ImageIterator.QuickReload().ConfigureAwait(false);
// Allow Ctrl + A, Ctrl + C, Ctrl + X, and Ctrl + V (paste)
break;
}
}

e.Handled = true; // Only allow with Ctrl
return;

case Key.Oem5: // Key for `%` symbol (may vary based on layout)
break; // Allow the percentage symbol (%)

case Key.Escape: // Handle Escape key
Focus();
e.Handled = true;
return;

case Key.Enter: // Handle Enter key for saving
if (DataContext is not MainViewModel vm)
{
return;
}

await DoResize(vm, Equals(sender, PixelWidthTextBox), PixelWidthTextBox.Text, PixelHeightTextBox.Text).ConfigureAwait(false);
return;

default:
e.Handled = true; // Block all other inputs
return;
}
}
}
Loading

0 comments on commit 7ca92f3

Please sign in to comment.