feat: 浮动栏截图修改为选区截图,添加“添加到白板”按钮 (#377)
* 调整截图按钮默认行为为选区/白板全屏 * 截图选择器新增添加到白板按钮并支持新建白板页插入 * 修复添加到白板不生效:改为剪贴板复制粘贴插入 * 修复添加到白板截图时序:先截到内存再进白板 * Fix screenshot insert flow honoring add-to-whiteboard option * Refine screenshot selector freehand confirm and blank double-click full select * Fix freehand whiteboard insert alpha transparency * Fix screenshot insert notification text for whiteboard flow * Adjust screenshot selection: right-click/double-click full-select and keep selector on single click * Show unmasked freehand selection area like rectangle mode * Remove freehand cutout mask rendering from screenshot selector * Restore freehand selected-area unmask behavior in selector
This commit is contained in:
@@ -1130,7 +1130,16 @@ namespace Ink_Canvas
|
|||||||
{
|
{
|
||||||
HideSubPanelsImmediately();
|
HideSubPanelsImmediately();
|
||||||
await Task.Delay(50);
|
await Task.Delay(50);
|
||||||
SaveScreenShotToDesktop();
|
|
||||||
|
// 白板模式下默认全屏截图到桌面;其余模式默认调用可选区截图
|
||||||
|
if (currentMode == 1)
|
||||||
|
{
|
||||||
|
SaveScreenShotToDesktop();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await SaveAreaScreenShotToDesktop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -4309,4 +4318,4 @@ namespace Ink_Canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,13 +29,16 @@ namespace Ink_Canvas
|
|||||||
public List<Point> Path;
|
public List<Point> Path;
|
||||||
public Bitmap CameraImage;
|
public Bitmap CameraImage;
|
||||||
public BitmapSource CameraBitmapSource;
|
public BitmapSource CameraBitmapSource;
|
||||||
|
public bool AddToWhiteboard;
|
||||||
|
|
||||||
public ScreenshotResult(Rectangle area, List<Point> path = null, Bitmap cameraImage = null, BitmapSource cameraBitmapSource = null)
|
public ScreenshotResult(Rectangle area, List<Point> path = null, Bitmap cameraImage = null,
|
||||||
|
BitmapSource cameraBitmapSource = null, bool addToWhiteboard = false)
|
||||||
{
|
{
|
||||||
Area = area;
|
Area = area;
|
||||||
Path = path;
|
Path = path;
|
||||||
CameraImage = cameraImage;
|
CameraImage = cameraImage;
|
||||||
CameraBitmapSource = cameraBitmapSource;
|
CameraBitmapSource = cameraBitmapSource;
|
||||||
|
AddToWhiteboard = addToWhiteboard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,11 +75,17 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
if (screenshotResult.HasValue)
|
if (screenshotResult.HasValue)
|
||||||
{
|
{
|
||||||
|
if (screenshotResult.Value.AddToWhiteboard)
|
||||||
|
{
|
||||||
|
await AddScreenshotToNewWhiteboardPage(screenshotResult.Value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 检查是否是摄像头截图
|
// 检查是否是摄像头截图
|
||||||
if (screenshotResult.Value.CameraBitmapSource != null)
|
if (screenshotResult.Value.CameraBitmapSource != null)
|
||||||
{
|
{
|
||||||
// 摄像头截图(使用BitmapSource)
|
// 摄像头截图(使用BitmapSource)
|
||||||
await InsertBitmapSourceToCanvas(screenshotResult.Value.CameraBitmapSource);
|
await InsertBitmapSourceToCanvas(screenshotResult.Value.CameraBitmapSource, "摄像头截图已插入到画布", "插入摄像头截图失败");
|
||||||
}
|
}
|
||||||
else if (screenshotResult.Value.CameraImage != null)
|
else if (screenshotResult.Value.CameraImage != null)
|
||||||
{
|
{
|
||||||
@@ -208,7 +217,8 @@ namespace Ink_Canvas
|
|||||||
Rectangle.Empty, // 摄像头截图不需要区域
|
Rectangle.Empty, // 摄像头截图不需要区域
|
||||||
null, // 摄像头截图不需要路径
|
null, // 摄像头截图不需要路径
|
||||||
null, // 不再使用Bitmap
|
null, // 不再使用Bitmap
|
||||||
selectorWindow.CameraBitmapSource // 摄像头BitmapSource
|
selectorWindow.CameraBitmapSource, // 摄像头BitmapSource
|
||||||
|
selectorWindow.ShouldAddToWhiteboard
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (selectorWindow.CameraImage != null)
|
else if (selectorWindow.CameraImage != null)
|
||||||
@@ -216,14 +226,19 @@ namespace Ink_Canvas
|
|||||||
result = new ScreenshotResult(
|
result = new ScreenshotResult(
|
||||||
Rectangle.Empty, // 摄像头截图不需要区域
|
Rectangle.Empty, // 摄像头截图不需要区域
|
||||||
null, // 摄像头截图不需要路径
|
null, // 摄像头截图不需要路径
|
||||||
selectorWindow.CameraImage // 摄像头图像
|
selectorWindow.CameraImage, // 摄像头图像
|
||||||
|
null,
|
||||||
|
selectorWindow.ShouldAddToWhiteboard
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = new ScreenshotResult(
|
result = new ScreenshotResult(
|
||||||
selectorWindow.SelectedArea.Value,
|
selectorWindow.SelectedArea.Value,
|
||||||
selectorWindow.SelectedPath
|
selectorWindow.SelectedPath,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
selectorWindow.ShouldAddToWhiteboard
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -403,7 +418,7 @@ namespace Ink_Canvas
|
|||||||
/// 8. 提交历史记录
|
/// 8. 提交历史记录
|
||||||
/// 9. 插入图片后切换到选择模式并刷新浮动栏高光显示
|
/// 9. 插入图片后切换到选择模式并刷新浮动栏高光显示
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private async Task InsertBitmapSourceToCanvas(BitmapSource bitmapSource)
|
private async Task InsertBitmapSourceToCanvas(BitmapSource bitmapSource, string successMessage = "截图已插入到画布", string failureMessagePrefix = "插入截图失败")
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -452,11 +467,11 @@ namespace Ink_Canvas
|
|||||||
UpdateCurrentToolMode("select");
|
UpdateCurrentToolMode("select");
|
||||||
HideSubPanels("select");
|
HideSubPanels("select");
|
||||||
|
|
||||||
ShowNotification("摄像头截图已插入到画布");
|
ShowNotification(successMessage);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
ShowNotification($"插入摄像头截图失败: {ex.Message}");
|
ShowNotification($"{failureMessagePrefix}: {ex.Message}");
|
||||||
LogHelper.WriteLogToFile($"插入摄像头截图失败: {ex.Message}", LogHelper.LogType.Error);
|
LogHelper.WriteLogToFile($"插入摄像头截图失败: {ex.Message}", LogHelper.LogType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -841,11 +856,12 @@ namespace Ink_Canvas
|
|||||||
if (bitmap == null || bitmap.Width <= 0 || bitmap.Height <= 0)
|
if (bitmap == null || bitmap.Width <= 0 || bitmap.Height <= 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// 创建一个新的位图,确保格式正确
|
// 创建一个新的位图,确保保留Alpha通道
|
||||||
using (var convertedBitmap = new Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format24bppRgb))
|
using (var convertedBitmap = new Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format32bppArgb))
|
||||||
{
|
{
|
||||||
using (var graphics = Graphics.FromImage(convertedBitmap))
|
using (var graphics = Graphics.FromImage(convertedBitmap))
|
||||||
{
|
{
|
||||||
|
graphics.CompositingMode = CompositingMode.SourceCopy;
|
||||||
graphics.DrawImage(bitmap, 0, 0);
|
graphics.DrawImage(bitmap, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -951,4 +967,4 @@ namespace Ink_Canvas
|
|||||||
return 1.0; // 默认DPI
|
return 1.0; // 默认DPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.IO;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
using System.Windows.Ink;
|
using System.Windows.Ink;
|
||||||
using Ink_Canvas.Helpers;
|
using Ink_Canvas.Helpers;
|
||||||
|
|
||||||
@@ -159,6 +160,160 @@ namespace Ink_Canvas
|
|||||||
SaveInkCanvasStrokes(false);
|
SaveInkCanvasStrokes(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal async Task SaveAreaScreenShotToDesktop()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var originalVisibility = Visibility;
|
||||||
|
Visibility = Visibility.Hidden;
|
||||||
|
await Task.Delay(200);
|
||||||
|
|
||||||
|
var screenshotResult = await ShowScreenshotSelector();
|
||||||
|
Visibility = originalVisibility;
|
||||||
|
|
||||||
|
if (!screenshotResult.HasValue)
|
||||||
|
{
|
||||||
|
ShowNotification("截图已取消");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (screenshotResult.Value.AddToWhiteboard)
|
||||||
|
{
|
||||||
|
await AddScreenshotToNewWhiteboardPage(screenshotResult.Value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (screenshotResult.Value.Area.Width <= 0 || screenshotResult.Value.Area.Height <= 0)
|
||||||
|
{
|
||||||
|
ShowNotification("未选择有效截图区域");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var desktopPath = Path.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),
|
||||||
|
$"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.png");
|
||||||
|
|
||||||
|
using (var originalBitmap = CaptureScreenArea(screenshotResult.Value.Area))
|
||||||
|
{
|
||||||
|
if (originalBitmap == null)
|
||||||
|
{
|
||||||
|
ShowNotification("截图失败");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap finalBitmap = originalBitmap;
|
||||||
|
bool needDisposeFinalBitmap = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (screenshotResult.Value.Path != null && screenshotResult.Value.Path.Count > 0)
|
||||||
|
{
|
||||||
|
finalBitmap = ApplyShapeMask(originalBitmap, screenshotResult.Value.Path, screenshotResult.Value.Area);
|
||||||
|
needDisposeFinalBitmap = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var directory = Path.GetDirectoryName(desktopPath);
|
||||||
|
if (!Directory.Exists(directory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
finalBitmap.Save(desktopPath, ImageFormat.Png);
|
||||||
|
ShowNotification($"截图成功保存至 {desktopPath}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (needDisposeFinalBitmap && finalBitmap != originalBitmap)
|
||||||
|
{
|
||||||
|
finalBitmap.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings.Automation.IsAutoSaveStrokesAtScreenshot)
|
||||||
|
SaveInkCanvasStrokes(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Visibility = Visibility.Visible;
|
||||||
|
ShowNotification($"截图失败: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AddScreenshotToNewWhiteboardPage(ScreenshotResult screenshotResult)
|
||||||
|
{
|
||||||
|
// 先在当前场景准备截图数据,再进白板,避免误截到白板页面
|
||||||
|
BitmapSource bitmapSourceForClipboard = null;
|
||||||
|
|
||||||
|
// 摄像头截图(BitmapSource)
|
||||||
|
if (screenshotResult.CameraBitmapSource != null)
|
||||||
|
{
|
||||||
|
bitmapSourceForClipboard = screenshotResult.CameraBitmapSource;
|
||||||
|
}
|
||||||
|
// 摄像头截图(Bitmap)
|
||||||
|
else if (screenshotResult.CameraImage != null)
|
||||||
|
{
|
||||||
|
bitmapSourceForClipboard = ConvertBitmapToBitmapSource(screenshotResult.CameraImage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (screenshotResult.Area.Width <= 0 || screenshotResult.Area.Height <= 0)
|
||||||
|
{
|
||||||
|
ShowNotification("未选择有效截图区域");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var originalBitmap = CaptureScreenArea(screenshotResult.Area))
|
||||||
|
{
|
||||||
|
if (originalBitmap == null)
|
||||||
|
{
|
||||||
|
ShowNotification("截图失败");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap finalBitmap = originalBitmap;
|
||||||
|
bool needDisposeFinalBitmap = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (screenshotResult.Path != null && screenshotResult.Path.Count > 0)
|
||||||
|
{
|
||||||
|
finalBitmap = ApplyShapeMask(originalBitmap, screenshotResult.Path, screenshotResult.Area);
|
||||||
|
needDisposeFinalBitmap = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmapSourceForClipboard = ConvertBitmapToBitmapSource(finalBitmap);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (needDisposeFinalBitmap && finalBitmap != originalBitmap)
|
||||||
|
{
|
||||||
|
finalBitmap.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitmapSourceForClipboard == null)
|
||||||
|
{
|
||||||
|
ShowNotification("截图转换失败");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图像已拷贝到内存后再进入白板
|
||||||
|
bitmapSourceForClipboard.Freeze();
|
||||||
|
|
||||||
|
if (currentMode != 1)
|
||||||
|
{
|
||||||
|
SwitchToBoardMode();
|
||||||
|
await Task.Delay(150);
|
||||||
|
}
|
||||||
|
|
||||||
|
BtnWhiteBoardAdd_Click(null, EventArgs.Empty);
|
||||||
|
|
||||||
|
await InsertBitmapSourceToCanvas(bitmapSourceForClipboard);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 提取公共的截图和保存逻辑
|
/// 提取公共的截图和保存逻辑
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
Cursor="Cross"
|
Cursor="Cross"
|
||||||
KeyDown="Window_KeyDown"
|
KeyDown="Window_KeyDown"
|
||||||
MouseLeftButtonDown="Window_MouseLeftButtonDown"
|
MouseLeftButtonDown="Window_MouseLeftButtonDown"
|
||||||
|
MouseRightButtonDown="Window_MouseRightButtonDown"
|
||||||
MouseMove="Window_MouseMove"
|
MouseMove="Window_MouseMove"
|
||||||
MouseLeftButtonUp="Window_MouseLeftButtonUp">
|
MouseLeftButtonUp="Window_MouseLeftButtonUp">
|
||||||
|
|
||||||
@@ -149,6 +150,15 @@
|
|||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
FontWeight="Medium"
|
FontWeight="Medium"
|
||||||
Click="ConfirmButton_Click" />
|
Click="ConfirmButton_Click" />
|
||||||
|
<Button Name="AddToWhiteboardButton"
|
||||||
|
Content="添加到白板"
|
||||||
|
Margin="4,0"
|
||||||
|
Padding="12,6"
|
||||||
|
Background="#2563eb"
|
||||||
|
Foreground="White"
|
||||||
|
BorderThickness="0"
|
||||||
|
FontWeight="Medium"
|
||||||
|
Click="AddToWhiteboardButton_Click" />
|
||||||
<Button Name="CancelButton"
|
<Button Name="CancelButton"
|
||||||
Content="取消"
|
Content="取消"
|
||||||
Margin="4,0"
|
Margin="4,0"
|
||||||
@@ -270,7 +280,7 @@
|
|||||||
Margin="0,0,0,140"
|
Margin="0,0,0,140"
|
||||||
Panel.ZIndex="1000">
|
Panel.ZIndex="1000">
|
||||||
<TextBlock Name="HintText"
|
<TextBlock Name="HintText"
|
||||||
Text="拖拽鼠标选择矩形区域,或使用自由绘制模式"
|
Text="拖拽选择区域,右键或双击可全选屏幕;自由绘制松开后需点击确认"
|
||||||
Foreground="White"
|
Foreground="White"
|
||||||
FontSize="14"
|
FontSize="14"
|
||||||
FontWeight="Medium" />
|
FontWeight="Medium" />
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ namespace Ink_Canvas
|
|||||||
private ControlPointType _activeControlPoint = ControlPointType.None;
|
private ControlPointType _activeControlPoint = ControlPointType.None;
|
||||||
private CameraService _cameraService;
|
private CameraService _cameraService;
|
||||||
private Bitmap _capturedCameraImage = null;
|
private Bitmap _capturedCameraImage = null;
|
||||||
|
private DateTime _lastBlankClickTime = DateTime.MinValue;
|
||||||
|
private WpfPoint _lastBlankClickPosition;
|
||||||
|
|
||||||
|
private const int DoubleClickTimeThresholdMs = 300; // 双击判定时间阈值(常见范围 200~500ms)
|
||||||
|
private const double DoubleClickDistanceThresholdPx = 12; // 双击判定位置阈值(像素)
|
||||||
|
|
||||||
// 控制点类型枚举
|
// 控制点类型枚举
|
||||||
private enum ControlPointType
|
private enum ControlPointType
|
||||||
@@ -49,6 +54,7 @@ namespace Ink_Canvas
|
|||||||
public List<WpfPoint> SelectedPath { get; private set; }
|
public List<WpfPoint> SelectedPath { get; private set; }
|
||||||
public Bitmap CameraImage { get; private set; }
|
public Bitmap CameraImage { get; private set; }
|
||||||
public System.Windows.Media.Imaging.BitmapSource CameraBitmapSource { get; private set; }
|
public System.Windows.Media.Imaging.BitmapSource CameraBitmapSource { get; private set; }
|
||||||
|
public bool ShouldAddToWhiteboard { get; private set; }
|
||||||
|
|
||||||
public ScreenshotSelectorWindow()
|
public ScreenshotSelectorWindow()
|
||||||
{
|
{
|
||||||
@@ -377,7 +383,7 @@ namespace Ink_Canvas
|
|||||||
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235)); // 蓝色
|
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235)); // 蓝色
|
||||||
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
||||||
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
||||||
HintText.Text = "按住鼠标左键绘制任意形状,松开直接截图";
|
HintText.Text = "按住鼠标左键自由绘制,松开后可继续调整或重新绘制,确认后再截图";
|
||||||
HintTextBorder.Visibility = Visibility.Visible;
|
HintTextBorder.Visibility = Visibility.Visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -484,9 +490,12 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
|
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
// 在自由绘制模式下,确认按钮不执行任何操作
|
ShouldAddToWhiteboard = false;
|
||||||
|
|
||||||
|
// 在自由绘制模式下,按当前自由选区执行确认
|
||||||
if (_isFreehandMode)
|
if (_isFreehandMode)
|
||||||
{
|
{
|
||||||
|
ConfirmFreehandSelection();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,6 +509,26 @@ namespace Ink_Canvas
|
|||||||
ConfirmSelection();
|
ConfirmSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddToWhiteboardButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
ShouldAddToWhiteboard = true;
|
||||||
|
|
||||||
|
// 在自由绘制模式下,按当前自由选区执行确认
|
||||||
|
if (_isFreehandMode)
|
||||||
|
{
|
||||||
|
ConfirmFreehandSelection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isCameraMode)
|
||||||
|
{
|
||||||
|
ConfirmCameraCapture();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfirmSelection();
|
||||||
|
}
|
||||||
|
|
||||||
private void ConfirmCameraCapture()
|
private void ConfirmCameraCapture()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -564,6 +593,25 @@ namespace Ink_Canvas
|
|||||||
// 如果正在调整,忽略新的选择
|
// 如果正在调整,忽略新的选择
|
||||||
if (_isAdjusting) return;
|
if (_isAdjusting) return;
|
||||||
|
|
||||||
|
// 空白区域双击可快速全选(时间阈值 300ms,位置阈值 12px)
|
||||||
|
if (!_isCameraMode)
|
||||||
|
{
|
||||||
|
var clickPoint = e.GetPosition(this);
|
||||||
|
var now = DateTime.Now;
|
||||||
|
if ((e.ClickCount >= 2 || (now - _lastBlankClickTime).TotalMilliseconds <= DoubleClickTimeThresholdMs) &&
|
||||||
|
Math.Abs(clickPoint.X - _lastBlankClickPosition.X) <= DoubleClickDistanceThresholdPx &&
|
||||||
|
Math.Abs(clickPoint.Y - _lastBlankClickPosition.Y) <= DoubleClickDistanceThresholdPx)
|
||||||
|
{
|
||||||
|
SelectFullScreenArea();
|
||||||
|
_lastBlankClickTime = DateTime.MinValue;
|
||||||
|
e.Handled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastBlankClickTime = now;
|
||||||
|
_lastBlankClickPosition = clickPoint;
|
||||||
|
}
|
||||||
|
|
||||||
// 如果正在选择,先重置状态
|
// 如果正在选择,先重置状态
|
||||||
if (_isSelecting)
|
if (_isSelecting)
|
||||||
{
|
{
|
||||||
@@ -584,11 +632,14 @@ namespace Ink_Canvas
|
|||||||
// 自由绘制模式:开始绘制
|
// 自由绘制模式:开始绘制
|
||||||
_freehandPoints.Clear();
|
_freehandPoints.Clear();
|
||||||
_freehandPolyline.Points.Clear();
|
_freehandPolyline.Points.Clear();
|
||||||
|
SelectedArea = null;
|
||||||
|
SelectedPath = null;
|
||||||
_freehandPoints.Add(_startPoint);
|
_freehandPoints.Add(_startPoint);
|
||||||
_freehandPolyline.Points.Add(_startPoint);
|
_freehandPolyline.Points.Add(_startPoint);
|
||||||
|
|
||||||
// 确保自由绘制路径可见
|
// 确保自由绘制路径可见
|
||||||
_freehandPolyline.Visibility = Visibility.Visible;
|
_freehandPolyline.Visibility = Visibility.Visible;
|
||||||
|
UpdateFreehandSelectionMask(_freehandPolyline.Points);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -606,6 +657,42 @@ namespace Ink_Canvas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Window_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (_isCameraMode)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击到工具栏等UI元素时不触发全选
|
||||||
|
var hitElement = e.Source as FrameworkElement;
|
||||||
|
if (hitElement != null && (
|
||||||
|
hitElement is Ellipse ||
|
||||||
|
hitElement is System.Windows.Controls.Button ||
|
||||||
|
hitElement is Border ||
|
||||||
|
hitElement is TextBlock ||
|
||||||
|
hitElement is StackPanel ||
|
||||||
|
hitElement is Separator ||
|
||||||
|
hitElement.Name == "SizeInfoBorder" ||
|
||||||
|
hitElement.Name == "HintText" ||
|
||||||
|
hitElement.Name == "AdjustModeHint"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isSelecting)
|
||||||
|
{
|
||||||
|
_isSelecting = false;
|
||||||
|
if (IsMouseCaptured)
|
||||||
|
{
|
||||||
|
ReleaseMouseCapture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectFullScreenArea();
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void Window_MouseMove(object sender, MouseEventArgs e)
|
private void Window_MouseMove(object sender, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
// 如果正在调整模式且正在移动,不处理窗口级别的鼠标移动
|
// 如果正在调整模式且正在移动,不处理窗口级别的鼠标移动
|
||||||
@@ -626,6 +713,7 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
// 确保自由绘制路径可见
|
// 确保自由绘制路径可见
|
||||||
_freehandPolyline.Visibility = Visibility.Visible;
|
_freehandPolyline.Visibility = Visibility.Visible;
|
||||||
|
UpdateFreehandSelectionMask(_freehandPolyline.Points);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -650,50 +738,48 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
if (_isFreehandMode)
|
if (_isFreehandMode)
|
||||||
{
|
{
|
||||||
// 自由绘制模式:一笔完成,直接截图
|
// 自由绘制模式:松开后先保留选区,等待用户点击“确认截图/添加到白板”
|
||||||
if (_freehandPoints.Count > 1) // 只要有点就可以截图
|
if (_freehandPoints.Count > 1)
|
||||||
{
|
{
|
||||||
// 创建路径的副本,避免修改原始列表
|
// 创建路径副本并闭合
|
||||||
var pathPoints = new List<WpfPoint>(_freehandPoints);
|
var pathPoints = new List<WpfPoint>(_freehandPoints);
|
||||||
|
|
||||||
// 简化路径处理,不强制闭合
|
|
||||||
// 如果路径没有闭合,自动添加起始点
|
|
||||||
if (pathPoints.Count > 0)
|
if (pathPoints.Count > 0)
|
||||||
{
|
{
|
||||||
pathPoints.Add(_startPoint);
|
pathPoints.Add(_startPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 优化路径:移除重复点和过于接近的点,提高路径质量
|
|
||||||
var optimizedPath = OptimizePath(pathPoints);
|
var optimizedPath = OptimizePath(pathPoints);
|
||||||
|
|
||||||
// 保存选择的路径
|
|
||||||
SelectedPath = optimizedPath;
|
|
||||||
|
|
||||||
// 计算边界矩形用于截图
|
|
||||||
var bounds = CalculatePathBounds(optimizedPath);
|
var bounds = CalculatePathBounds(optimizedPath);
|
||||||
|
|
||||||
// 确保边界矩形有效
|
if (bounds.Width > 5 && bounds.Height > 5)
|
||||||
if (bounds.Width >= 0 && bounds.Height >= 0)
|
|
||||||
{
|
{
|
||||||
|
SelectedPath = optimizedPath;
|
||||||
|
|
||||||
var dpiScale = GetDpiScale();
|
var dpiScale = GetDpiScale();
|
||||||
var virtualScreen = SystemInformation.VirtualScreen;
|
var virtualScreen = SystemInformation.VirtualScreen;
|
||||||
|
|
||||||
int screenX = (int)((bounds.X * dpiScale) + virtualScreen.Left);
|
int screenX = (int)((bounds.X * dpiScale) + virtualScreen.Left);
|
||||||
int screenY = (int)((bounds.Y * dpiScale) + virtualScreen.Top);
|
int screenY = (int)((bounds.Y * dpiScale) + virtualScreen.Top);
|
||||||
int screenWidth = (int)(bounds.Width * dpiScale);
|
int screenWidth = (int)(bounds.Width * dpiScale);
|
||||||
int screenHeight = (int)(bounds.Height * dpiScale);
|
int screenHeight = (int)(bounds.Height * dpiScale);
|
||||||
|
|
||||||
SelectedArea = new DrawingRectangle(screenX, screenY, screenWidth, screenHeight);
|
SelectedArea = new DrawingRectangle(screenX, screenY, screenWidth, screenHeight);
|
||||||
DialogResult = true;
|
UpdateFreehandSelectionMask(optimizedPath);
|
||||||
Close();
|
HintText.Text = "自由选区已完成,可点击确认截图/添加到白板;重新拖动可重画";
|
||||||
|
HintTextBorder.Visibility = Visibility.Visible;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果自由绘制失败,清除路径并继续
|
// 自由绘制无效则清理本次路径,允许继续重绘
|
||||||
_freehandPoints.Clear();
|
_freehandPoints.Clear();
|
||||||
_freehandPolyline.Points.Clear();
|
_freehandPolyline.Points.Clear();
|
||||||
_freehandPolyline.Visibility = Visibility.Collapsed;
|
_freehandPolyline.Visibility = Visibility.Collapsed;
|
||||||
|
TransparentSelectionMask.Visibility = Visibility.Collapsed;
|
||||||
|
OverlayRectangle.Visibility = Visibility.Visible;
|
||||||
|
SelectedArea = null;
|
||||||
|
SelectedPath = null;
|
||||||
|
HintText.Text = "自由选区过小,请重新绘制";
|
||||||
|
HintTextBorder.Visibility = Visibility.Visible;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -711,14 +797,13 @@ namespace Ink_Canvas
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
SelectedArea = null;
|
SelectedArea = null;
|
||||||
DialogResult = false;
|
SelectionRectangle.Visibility = Visibility.Collapsed;
|
||||||
|
SizeInfoBorder.Visibility = Visibility.Collapsed;
|
||||||
|
HintText.Text = "请拖拽选择区域,右键或双击可全选";
|
||||||
|
HintTextBorder.Visibility = Visibility.Visible;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_isAdjusting)
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -907,11 +992,46 @@ namespace Ink_Canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateTransparentSelectionMask(Rect selectionRect)
|
private void UpdateTransparentSelectionMask(Rect selectionRect)
|
||||||
|
{
|
||||||
|
SetSelectionMaskGeometry(new RectangleGeometry(selectionRect));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateFreehandSelectionMask(IList<WpfPoint> points)
|
||||||
|
{
|
||||||
|
if (points == null || points.Count < 3)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var figure = new PathFigure
|
||||||
|
{
|
||||||
|
StartPoint = points[0],
|
||||||
|
IsClosed = true,
|
||||||
|
IsFilled = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var segments = new PolyLineSegment();
|
||||||
|
for (int i = 1; i < points.Count; i++)
|
||||||
|
{
|
||||||
|
segments.Points.Add(points[i]);
|
||||||
|
}
|
||||||
|
figure.Segments.Add(segments);
|
||||||
|
|
||||||
|
var geometry = new PathGeometry();
|
||||||
|
geometry.Figures.Add(figure);
|
||||||
|
SetSelectionMaskGeometry(geometry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetSelectionMaskGeometry(Geometry selectionGeometry)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 更新选择区域的几何体
|
if (!(TransparentSelectionMask.Clip is CombinedGeometry combinedGeometry))
|
||||||
SelectionClipGeometry.Rect = selectionRect;
|
{
|
||||||
|
throw new InvalidOperationException("TransparentSelectionMask.Clip 不是 CombinedGeometry");
|
||||||
|
}
|
||||||
|
|
||||||
|
combinedGeometry.Geometry2 = selectionGeometry;
|
||||||
|
|
||||||
// 显示透明遮罩,隐藏原始遮罩
|
// 显示透明遮罩,隐藏原始遮罩
|
||||||
TransparentSelectionMask.Visibility = Visibility.Visible;
|
TransparentSelectionMask.Visibility = Visibility.Visible;
|
||||||
@@ -972,9 +1092,10 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
private void ConfirmSelection()
|
private void ConfirmSelection()
|
||||||
{
|
{
|
||||||
// 在自由绘制模式下,不执行确认操作
|
// 在自由绘制模式下,走自由选区确认
|
||||||
if (_isFreehandMode)
|
if (_isFreehandMode)
|
||||||
{
|
{
|
||||||
|
ConfirmFreehandSelection();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -996,6 +1117,44 @@ namespace Ink_Canvas
|
|||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ConfirmFreehandSelection()
|
||||||
|
{
|
||||||
|
if (!SelectedArea.HasValue || SelectedPath == null || SelectedPath.Count < 3)
|
||||||
|
{
|
||||||
|
HintText.Text = "请先拖动鼠标绘制自由选区";
|
||||||
|
HintTextBorder.Visibility = Visibility.Visible;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogResult = true;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectFullScreenArea()
|
||||||
|
{
|
||||||
|
var virtualScreen = SystemInformation.VirtualScreen;
|
||||||
|
_currentSelection = new Rect(0, 0, ActualWidth, ActualHeight);
|
||||||
|
|
||||||
|
SelectionRectangle.Visibility = Visibility.Visible;
|
||||||
|
SelectionRectangle.IsHitTestVisible = false;
|
||||||
|
ControlPointsCanvas.Visibility = Visibility.Collapsed;
|
||||||
|
SizeInfoBorder.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
_isAdjusting = false;
|
||||||
|
_isSelecting = false;
|
||||||
|
_isFreehandMode = false;
|
||||||
|
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235));
|
||||||
|
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128));
|
||||||
|
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128));
|
||||||
|
|
||||||
|
UpdateSelectionDisplay();
|
||||||
|
SelectedPath = null;
|
||||||
|
SelectedArea = new DrawingRectangle(virtualScreen.X, virtualScreen.Y, virtualScreen.Width, virtualScreen.Height);
|
||||||
|
|
||||||
|
HintText.Text = "已全选屏幕,点击确认截图或添加到白板";
|
||||||
|
HintTextBorder.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
|
||||||
private void CancelSelection()
|
private void CancelSelection()
|
||||||
{
|
{
|
||||||
// 停止摄像头预览
|
// 停止摄像头预览
|
||||||
@@ -1007,6 +1166,7 @@ namespace Ink_Canvas
|
|||||||
SelectedArea = null;
|
SelectedArea = null;
|
||||||
SelectedPath = null;
|
SelectedPath = null;
|
||||||
CameraImage = null;
|
CameraImage = null;
|
||||||
|
ShouldAddToWhiteboard = false;
|
||||||
DialogResult = false;
|
DialogResult = false;
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user