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

193 lines
6.7 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.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;
/// <summary>
/// 전체 화면 위에 반투명 오버레이를 표시하고 마우스 드래그로 캡처 영역을 선택합니다.
/// ShowDialog() 후 SelectedRect로 결과를 얻습니다.
/// </summary>
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));
}