| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 |
- using System.Reactive.Linq;
- using Avalonia;
- using Avalonia.Controls;
- using Avalonia.Controls.ApplicationLifetimes;
- using Avalonia.Input;
- using Avalonia.Threading;
- using PicView.Avalonia.FileSystem;
- using PicView.Avalonia.Navigation;
- using PicView.Avalonia.Resizing;
- using PicView.Avalonia.UI;
- using PicView.Avalonia.ViewModels;
- using PicView.Core.ImageDecoding;
- using PicView.Core.Localization;
- using ReactiveUI;
- namespace PicView.Avalonia.Views;
- public partial class SingleImageResizeView : UserControl
- {
- private double _aspectRatio;
- private IDisposable? _imageUpdateSubscription;
- private bool _isKeepingAspectRatio = true;
- public SingleImageResizeView()
- {
- InitializeComponent();
- Loaded += OnLoaded;
- Unloaded += OnUnloaded;
- }
- private void OnLoaded(object? sender, EventArgs e)
- {
- if (DataContext is not MainViewModel vm)
- {
- return;
- }
- _aspectRatio = (double)vm.PicViewer.PixelWidth / vm.PicViewer.PixelHeight;
- RegisterEventHandlers(vm);
- _imageUpdateSubscription = vm.PicViewer.WhenAnyValue(x => x.FileInfo)
- .ObserveOn(RxApp.MainThreadScheduler)
- .Select(x => x is not null)
- .Subscribe(_ =>
- {
- UpdateQualitySliderState();
- ShowCancelButton();
- });
- }
- private void OnUnloaded(object? sender, EventArgs e)
- {
- _imageUpdateSubscription?.Dispose();
- }
- private void RegisterEventHandlers(MainViewModel vm)
- {
- UpdateQualitySliderState();
- QualitySlider.ValueChanged += (_, _) => ShowResetButton();
- SaveButton.Click += async (_, _) => await SaveImage(vm).ConfigureAwait(false);
- SaveAsButton.Click += async (_, _) => await SaveImageAs(vm).ConfigureAwait(false);
- PixelWidthTextBox.KeyDown += async (_, e) => await SaveImageOnEnter(e, vm);
- PixelHeightTextBox.KeyDown += async (_, e) => await SaveImageOnEnter(e, vm);
- PixelWidthTextBox.KeyUp += (_, _) => AdjustAspectRatio(PixelWidthTextBox);
- PixelHeightTextBox.KeyUp += (_, _) => AdjustAspectRatio(PixelHeightTextBox);
- ConversionComboBox.SelectionChanged += (_, _) =>
- {
- UpdateQualitySliderState();
- ShowResetButton();
- };
- ResetButton.Click += (_, _) => ResetSettings(vm);
- CancelButton.Click += (_, _) => (VisualRoot as Window)?.Close();
- LinkChainButton.Click += (_, _) => ToggleAspectRatio();
- }
- private void ShowResetButton()
- {
- CancelButton.IsVisible = false;
- ResetButton.IsVisible = true;
- }
- private void ShowCancelButton()
- {
- CancelButton.IsVisible = true;
- ResetButton.IsVisible = false;
- }
- private void AdjustAspectRatio(TextBox sender)
- {
- if (!_isKeepingAspectRatio)
- {
- return;
- }
- AspectRatioHelper.SetAspectRatioForTextBox(
- PixelWidthTextBox, PixelHeightTextBox, sender == PixelWidthTextBox,
- _aspectRatio, DataContext as MainViewModel);
- ShowResetButton();
- }
- private void UpdateQualitySliderState()
- {
- if (DataContext is not MainViewModel vm)
- {
- return;
- }
- try
- {
- if (IsConversionToQualityFormat())
- {
- QualitySlider.IsEnabled = true;
- QualitySlider.Value = 75;
- }
- else if (IsOriginalFileQualityFormat(vm.PicViewer.FileInfo.Extension))
- {
- QualitySlider.IsEnabled = true;
- QualitySlider.Value = ImageAnalyzer.GetCompressionQuality(vm.PicViewer.FileInfo.FullName);
- }
- else
- {
- QualitySlider.IsEnabled = false;
- }
- }
- catch (Exception e)
- {
- #if DEBUG
- Console.WriteLine(e);
- #endif
- }
- }
- private bool IsConversionToQualityFormat()
- => JpgItem.IsSelected || PngItem.IsSelected;
- private static bool IsOriginalFileQualityFormat(string ext)
- => ext.Equals(".jpg", StringComparison.OrdinalIgnoreCase)
- || ext.Equals(".jpeg", StringComparison.OrdinalIgnoreCase)
- || ext.Equals(".png", StringComparison.OrdinalIgnoreCase);
- private async Task SaveImageOnEnter(KeyEventArgs e, MainViewModel vm)
- {
- if (e.Key == Key.Enter)
- {
- await SaveImage(vm).ConfigureAwait(false);
- }
- }
- private async Task SaveImageAs(MainViewModel vm)
- {
- if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop
- || desktop.MainWindow?.StorageProvider is not { } provider)
- {
- return;
- }
- var fileInfoFullName = vm.PicViewer.FileInfo.FullName;
- var ext = GetSelectedFileExtension(vm, ref fileInfoFullName);
- var file = await FilePicker.PickFileForSavingAsync(vm.PicViewer.FileInfo?.FullName, ext);
- if (file is null)
- {
- return;
- }
- await DoSaveImage(vm, file).ConfigureAwait(false);
- }
- private async Task SaveImage(MainViewModel vm)
- {
- await DoSaveImage(vm, vm.PicViewer.FileInfo.FullName).ConfigureAwait(false);
- }
- private async Task DoSaveImage(MainViewModel vm, string destination)
- {
- if (!uint.TryParse(PixelWidthTextBox.Text, out var width) ||
- !uint.TryParse(PixelHeightTextBox.Text, out var height))
- {
- return;
- }
- await Dispatcher.UIThread.InvokeAsync(() => SetLoadingState(true));
- const int rotationAngle = 0; // TODO: Add rotation control
- var file = vm.PicViewer.FileInfo.FullName;
- var ext = GetSelectedFileExtension(vm, ref destination);
- destination = Path.ChangeExtension(destination, ext);
- var sameFile = file.Equals(destination, StringComparison.OrdinalIgnoreCase);
- var quality = GetQualityValue(ext, destination);
- var success = await SaveImageFileHelper.SaveImageAsync(
- null, file, sameFile ? null : destination, width, height, quality,
- ext, rotationAngle, null, _isKeepingAspectRatio).ConfigureAwait(false);
- await Dispatcher.UIThread.InvokeAsync(() => SetLoadingState(false));
- if (!success)
- {
- await TooltipHelper.ShowTooltipMessageAsync(TranslationManager.Translation.SavingFileFailed);
- return;
- }
- await HandlePostSaveActions(vm, file, destination);
- if (Path.GetExtension(file) != ext)
- {
- await vm.PlatformService.DeleteFile(file, true);
- }
- }
- private void SetLoadingState(bool isLoading)
- {
- ParentContainer.Opacity = isLoading ? 0.1 : 1;
- ParentContainer.IsHitTestVisible = !isLoading;
- SpinWaiter.IsVisible = isLoading;
- }
- private string GetSelectedFileExtension(MainViewModel vm, ref string destination)
- {
- var ext = vm.PicViewer.FileInfo.Extension;
- if (NoConversion.IsSelected)
- {
- return ext;
- }
- ext = GetExtensionFromSelectedItem() ?? ext;
- destination = Path.ChangeExtension(destination, ext);
- return ext;
- }
- private string? GetExtensionFromSelectedItem()
- {
- if (PngItem.IsSelected)
- {
- return ".png";
- }
- if (JpgItem.IsSelected)
- {
- return ".jpg";
- }
- if (WebpItem.IsSelected)
- {
- return ".webp";
- }
- if (AvifItem.IsSelected)
- {
- return ".avif";
- }
- if (HeicItem.IsSelected)
- {
- return ".heic";
- }
- if (JxlItem.IsSelected)
- {
- return ".jxl";
- }
- return null;
- }
- private uint? GetQualityValue(string ext, string destination)
- {
- if (QualitySlider.IsEnabled && (
- ext.Equals(".jpg", StringComparison.OrdinalIgnoreCase) ||
- Path.GetExtension(destination).Equals(".jpg", StringComparison.OrdinalIgnoreCase) ||
- Path.GetExtension(destination).Equals(".jpeg", StringComparison.OrdinalIgnoreCase)))
- {
- return (uint)QualitySlider.Value;
- }
- return null;
- }
- private static async Task HandlePostSaveActions(MainViewModel vm, string file, string destination)
- {
- if (destination == file)
- {
- await NavigationManager.QuickReload().ConfigureAwait(false);
- }
- else if (Path.GetDirectoryName(file) == Path.GetDirectoryName(destination))
- {
- await NavigationManager.LoadPicFromFile(destination, vm).ConfigureAwait(false);
- }
- }
- private void ResetSettings(MainViewModel vm)
- {
- PixelWidthTextBox.Text = vm.PicViewer.PixelWidth.ToString();
- PixelHeightTextBox.Text = vm.PicViewer.PixelHeight.ToString();
- if (IsOriginalFileQualityFormat(vm.PicViewer.FileInfo.Extension))
- {
- QualitySlider.IsEnabled = true;
- QualitySlider.Value = ImageAnalyzer.GetCompressionQuality(vm.PicViewer.FileInfo.FullName);
- }
- else
- {
- QualitySlider.IsEnabled = false;
- }
- ConversionComboBox.SelectedItem = NoConversion;
- _isKeepingAspectRatio = true;
- LinkChainImage.IsVisible = true;
- UnlinkChainImage.IsVisible = false;
- ShowCancelButton();
- }
- private void ToggleAspectRatio()
- {
- _isKeepingAspectRatio = !_isKeepingAspectRatio;
- LinkChainImage.IsVisible = _isKeepingAspectRatio;
- UnlinkChainImage.IsVisible = !_isKeepingAspectRatio;
- if (_isKeepingAspectRatio)
- {
- AdjustAspectRatio(PixelWidthTextBox);
- }
- if (!_isKeepingAspectRatio)
- {
- ShowResetButton();
- }
- }
- ~SingleImageResizeView()
- {
- _imageUpdateSubscription?.Dispose();
- }
- }
|