Files
AX-Copilot-Codex/src/AxCopilot/Views/ClipboardImagePreviewWindow.xaml.cs

227 lines
7.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.IO;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
namespace AxCopilot.Views;
/// <summary>클립보드 이미지 전용 미리보기 창. 원본 해상도 기준 줌과 저장을 지원합니다.</summary>
public partial class ClipboardImagePreviewWindow : Window
{
private const double MinZoom = 0.1;
private const double MaxZoom = 8.0;
private const double ZoomStep = 0.1;
private readonly BitmapSource _image;
private readonly string? _sourcePath;
private double _zoom = 1.0;
private bool _fitToViewport = true;
public ClipboardImagePreviewWindow(BitmapSource image, string? sourcePath = null)
{
_image = image ?? throw new ArgumentNullException(nameof(image));
_sourcePath = sourcePath;
InitializeComponent();
PreviewImage.Source = _image;
TitleText.Text = string.IsNullOrWhiteSpace(sourcePath)
? "클립보드 이미지 미리보기"
: Path.GetFileName(sourcePath);
MetaText.Text = $"{_image.PixelWidth:N0} × {_image.PixelHeight:N0}px";
Loaded += (_, _) =>
{
ApplyFitToViewport();
UpdateStatusText();
};
}
private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 2)
{
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
return;
}
try { DragMove(); } catch { }
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Escape:
Close();
e.Handled = true;
return;
case Key.Add:
case Key.OemPlus:
IncreaseZoom();
e.Handled = true;
return;
case Key.Subtract:
case Key.OemMinus:
DecreaseZoom();
e.Handled = true;
return;
case Key.D0:
case Key.NumPad0:
ResetZoom();
e.Handled = true;
return;
case Key.F:
ApplyFitToViewport();
e.Handled = true;
return;
case Key.C when Keyboard.Modifiers == ModifierKeys.Control:
CopyImageToClipboard();
e.Handled = true;
return;
}
}
private void ImageScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
if ((Keyboard.Modifiers & ModifierKeys.Control) == 0)
return;
if (e.Delta > 0)
IncreaseZoom();
else if (e.Delta < 0)
DecreaseZoom();
e.Handled = true;
}
private void ImageScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (_fitToViewport)
ApplyFitToViewport();
}
private void BtnClose_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) => Close();
private void BtnResetZoom_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) => ResetZoom();
private void BtnZoomIn_Click(object sender, RoutedEventArgs e) => IncreaseZoom();
private void BtnZoomOut_Click(object sender, RoutedEventArgs e) => DecreaseZoom();
private void BtnFit_Click(object sender, RoutedEventArgs e) => ApplyFitToViewport();
private void BtnCopyImage_Click(object sender, RoutedEventArgs e) => CopyImageToClipboard();
private void BtnSavePng_Click(object sender, RoutedEventArgs e) => SaveImage("png");
private void BtnSaveJpeg_Click(object sender, RoutedEventArgs e) => SaveImage("jpg");
private void BtnSaveBmp_Click(object sender, RoutedEventArgs e) => SaveImage("bmp");
private void IncreaseZoom() => SetZoom(_zoom + ZoomStep, false);
private void DecreaseZoom() => SetZoom(_zoom - ZoomStep, false);
private void ResetZoom() => SetZoom(1.0, false);
private void ApplyFitToViewport()
{
if (ImageScrollViewer.ViewportWidth <= 0 || ImageScrollViewer.ViewportHeight <= 0)
return;
var availableWidth = Math.Max(1, ImageScrollViewer.ViewportWidth - 32);
var availableHeight = Math.Max(1, ImageScrollViewer.ViewportHeight - 32);
var scaleX = availableWidth / _image.PixelWidth;
var scaleY = availableHeight / _image.PixelHeight;
var fitScale = Math.Min(scaleX, scaleY);
if (double.IsNaN(fitScale) || double.IsInfinity(fitScale) || fitScale <= 0)
fitScale = 1.0;
SetZoom(Math.Min(1.0, fitScale), true);
}
private void SetZoom(double zoom, bool fitToViewport)
{
_fitToViewport = fitToViewport;
_zoom = Math.Max(MinZoom, Math.Min(MaxZoom, zoom));
ImageScaleTransform.ScaleX = _zoom;
ImageScaleTransform.ScaleY = _zoom;
UpdateStatusText();
}
private void UpdateStatusText()
{
var mode = _fitToViewport ? "창 맞춤" : "수동 줌";
var source = string.IsNullOrWhiteSpace(_sourcePath) ? "원본 경로 없음" : _sourcePath;
StatusText.Text = $"{_image.PixelWidth:N0} × {_image.PixelHeight:N0}px · 확대 {Math.Round(_zoom * 100)}% · {mode} · {source}";
}
private void CopyImageToClipboard()
{
try
{
var app = Application.Current as App;
app?.ClipboardHistoryService?.SuppressNextCapture();
Clipboard.SetImage(_image);
CustomMessageBox.Show("이미지를 클립보드에 복사했습니다.", "AX Commander", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
Services.LogService.Warn($"클립보드 이미지 복사 실패: {ex.Message}");
CustomMessageBox.Show($"이미지 복사에 실패했습니다.\n{ex.Message}", "AX Commander", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
private void SaveImage(string format)
{
try
{
var dialog = new SaveFileDialog
{
FileName = BuildDefaultFileName(format),
Filter = format switch
{
"png" => "PNG 이미지|*.png",
"jpg" => "JPEG 이미지|*.jpg;*.jpeg",
"bmp" => "BMP 이미지|*.bmp",
_ => "이미지 파일|*.*",
},
AddExtension = true,
OverwritePrompt = true,
};
if (dialog.ShowDialog(this) != true)
return;
BitmapEncoder encoder = format switch
{
"jpg" => new JpegBitmapEncoder { QualityLevel = 95 },
"bmp" => new BmpBitmapEncoder(),
_ => new PngBitmapEncoder(),
};
encoder.Frames.Add(BitmapFrame.Create(_image));
using var stream = new FileStream(dialog.FileName, FileMode.Create, FileAccess.Write);
encoder.Save(stream);
CustomMessageBox.Show($"이미지를 저장했습니다.\n{dialog.FileName}", "AX Commander", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
Services.LogService.Warn($"클립보드 이미지 저장 실패: {ex.Message}");
CustomMessageBox.Show($"이미지 저장에 실패했습니다.\n{ex.Message}", "AX Commander", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
private string BuildDefaultFileName(string format)
{
var sourceName = !string.IsNullOrWhiteSpace(_sourcePath)
? Path.GetFileNameWithoutExtension(_sourcePath)
: $"clipboard-image-{DateTime.Now:yyyyMMdd-HHmmss}";
var extension = format switch
{
"jpg" => ".jpg",
"bmp" => ".bmp",
_ => ".png",
};
return sourceName + extension;
}
}