using System.Drawing; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.IO; namespace AxCopilot.Views; /// /// 전체 화면 위에 반투명 오버레이를 표시하고 마우스 드래그로 캡처 영역을 선택합니다. /// ShowDialog() 후 SelectedRect로 결과를 얻습니다. /// public partial class RegionSelectWindow : Window { public System.Drawing.Rectangle? SelectedRect { get; private set; } private readonly System.Drawing.Rectangle _screenBounds; private System.Windows.Point _startPoint; private System.Windows.Point _endPoint; private bool _isDragging; public RegionSelectWindow(Bitmap fullScreenshot, System.Drawing.Rectangle screenBounds) { InitializeComponent(); _screenBounds = screenBounds; // 창을 모든 모니터 전체에 걸쳐 표시 Left = screenBounds.X; Top = screenBounds.Y; Width = screenBounds.Width; Height = screenBounds.Height; // 전체 화면 스크린샷을 배경으로 렌더링 RootCanvas.Background = CreateFrozenBrush(fullScreenshot); // 오버레이 초기 크기 설정 OverlayTop.Width = screenBounds.Width; OverlayTop.Height = screenBounds.Height; OverlayBottom.Width = screenBounds.Width; OverlayLeft.Width = 0; OverlayRight.Width = 0; // 가이드 텍스트 중앙 배치 (Loaded 이후) Loaded += (_, _) => { Canvas.SetLeft(GuideText, (Width - GuideText.ActualWidth) / 2); Canvas.SetTop(GuideText, (Height - GuideText.ActualHeight) / 2); }; } private static ImageBrush CreateFrozenBrush(Bitmap bmp) { using var ms = new MemoryStream(); bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); ms.Position = 0; var img = new BitmapImage(); img.BeginInit(); img.StreamSource = ms; img.CacheOption = BitmapCacheOption.OnLoad; img.EndInit(); img.Freeze(); var brush = new ImageBrush(img) { Stretch = Stretch.None, AlignmentX = AlignmentX.Left, AlignmentY = AlignmentY.Top }; brush.Freeze(); return brush; } private void Window_MouseDown(object sender, MouseButtonEventArgs e) { if (e.ChangedButton != MouseButton.Left) return; _startPoint = e.GetPosition(RootCanvas); _isDragging = true; GuideText.Visibility = Visibility.Collapsed; SelectionBorder.Visibility = Visibility.Visible; SizeLabel.Visibility = Visibility.Visible; CaptureMouse(); } private void Window_MouseMove(object sender, MouseEventArgs e) { if (!_isDragging) return; var cur = e.GetPosition(RootCanvas); UpdateSelection(_startPoint, cur); } private void Window_MouseUp(object sender, MouseButtonEventArgs e) { if (!_isDragging || e.ChangedButton != MouseButton.Left) return; _isDragging = false; ReleaseMouseCapture(); var cur = e.GetPosition(RootCanvas); _endPoint = cur; var rect = MakeRect(_startPoint, cur); if (rect.Width < 4 || rect.Height < 4) { // 너무 작으면 무시, 다시 드래그 가능 return; } // 선택 완료 — 화살표 키로 미세 조정 후 Enter/더블클릭으로 확정, Esc로 취소 // SizeLabel 하단에 힌트 표시 SizeLabelText.Text += " · ↑↓←→ 미세조정 · Enter 확정"; } private void Window_KeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Escape) { SelectedRect = null; Close(); return; } // 드래그 완료 후 화살표 키로 선택 영역 미세 조정 if (!_isDragging && SelectionBorder.Visibility == Visibility.Visible) { double dx = 0, dy = 0; bool shift = Keyboard.Modifiers.HasFlag(ModifierKeys.Shift); double step = shift ? 10 : 1; // Shift 누르면 10px 단위 switch (e.Key) { case Key.Left: dx = -step; break; case Key.Right: dx = step; break; case Key.Up: dy = -step; break; case Key.Down: dy = step; break; case Key.Enter: // Enter 키로 현재 선택 확정 var r = MakeRect(_startPoint, _endPoint); if (r.Width >= 4 && r.Height >= 4) { var dpi = VisualTreeHelper.GetDpi(this); SelectedRect = new System.Drawing.Rectangle( (int)(r.X * dpi.DpiScaleX) + _screenBounds.X, (int)(r.Y * dpi.DpiScaleY) + _screenBounds.Y, (int)(r.Width * dpi.DpiScaleX), (int)(r.Height * dpi.DpiScaleY)); } Close(); return; default: return; } _endPoint = new System.Windows.Point(_endPoint.X + dx, _endPoint.Y + dy); UpdateSelection(_startPoint, _endPoint); e.Handled = true; } } private void UpdateSelection(System.Windows.Point a, System.Windows.Point b) { var rect = MakeRect(a, b); Canvas.SetLeft(SelectionBorder, rect.X); Canvas.SetTop(SelectionBorder, rect.Y); SelectionBorder.Width = rect.Width; SelectionBorder.Height = rect.Height; // 바깥 마스크 업데이트 OverlayTop.Width = Width; OverlayTop.Height = rect.Y; OverlayBottom.Width = Width; OverlayBottom.Height = Height - rect.Y - rect.Height; Canvas.SetTop(OverlayBottom, rect.Y + rect.Height); OverlayLeft.Width = rect.X; OverlayLeft.Height = rect.Height; Canvas.SetTop(OverlayLeft, rect.Y); OverlayRight.Width = Width - rect.X - rect.Width; OverlayRight.Height = rect.Height; Canvas.SetLeft(OverlayRight, rect.X + rect.Width); Canvas.SetTop(OverlayRight, rect.Y); // 크기 레이블 var dpiScale = VisualTreeHelper.GetDpi(this); var pw = (int)(rect.Width * dpiScale.DpiScaleX); var ph = (int)(rect.Height * dpiScale.DpiScaleY); SizeLabelText.Text = $"{pw} × {ph}"; Canvas.SetLeft(SizeLabel, rect.X + rect.Width + 8); Canvas.SetTop(SizeLabel, rect.Y + rect.Height + 4); } private static Rect MakeRect(System.Windows.Point a, System.Windows.Point b) => new(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Abs(b.X - a.X), Math.Abs(b.Y - a.Y)); }