Files
community/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs
T

1316 lines
58 KiB
C#
Raw Normal View History

2025-08-31 11:43:52 +08:00
using Ink_Canvas.Helpers;
2025-05-25 09:29:48 +08:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
2025-09-13 11:08:56 +08:00
using System.Windows.Media.Imaging;
2025-08-31 09:54:13 +08:00
using System.Windows.Threading;
2025-05-25 09:29:48 +08:00
using Point = System.Windows.Point;
2025-08-03 16:46:33 +08:00
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
2025-05-25 09:29:48 +08:00
#region Multi-Touch
2025-07-28 14:40:44 +08:00
private bool isInMultiTouchMode;
2025-07-18 16:12:04 +08:00
private List<int> dec = new List<int>();
2025-07-28 14:40:44 +08:00
private bool isSingleFingerDragMode;
2025-07-18 16:12:04 +08:00
private Point centerPoint = new Point(0, 0);
private InkCanvasEditingMode lastInkCanvasEditingMode = InkCanvasEditingMode.Ink;
2025-05-25 09:29:48 +08:00
2025-07-27 22:00:37 +08:00
/// <summary>
/// 保存画布上的非笔画元素(如图片、媒体元素等)
/// </summary>
private List<UIElement> PreserveNonStrokeElements()
{
var preservedElements = new List<UIElement>();
2025-09-13 11:08:56 +08:00
// 遍历inkCanvas的所有子元素,创建副本而不是直接引用
2025-07-27 22:00:37 +08:00
for (int i = inkCanvas.Children.Count - 1; i >= 0; i--)
{
var child = inkCanvas.Children[i];
// 保存图片、媒体元素等非笔画相关的UI元素
if (child is Image || child is MediaElement ||
(child is Border border && border.Name != "AdvancedEraserOverlay"))
{
2025-09-13 11:08:56 +08:00
// 创建元素的深拷贝,避免直接引用导致的问题
var clonedElement = CloneUIElement(child);
if (clonedElement != null)
{
preservedElements.Add(clonedElement);
}
2025-07-27 22:00:37 +08:00
}
}
return preservedElements;
}
2025-09-13 11:08:56 +08:00
/// <summary>
/// 克隆UI元素,创建深拷贝
/// </summary>
private UIElement CloneUIElement(UIElement originalElement)
{
try
{
if (originalElement is Image originalImage)
{
var clonedImage = new Image();
// 复制图片源
if (originalImage.Source is BitmapSource bitmapSource)
{
clonedImage.Source = bitmapSource;
}
// 复制属性
clonedImage.Width = originalImage.Width;
clonedImage.Height = originalImage.Height;
clonedImage.Stretch = originalImage.Stretch;
clonedImage.StretchDirection = originalImage.StretchDirection;
clonedImage.Name = originalImage.Name;
clonedImage.IsHitTestVisible = originalImage.IsHitTestVisible;
clonedImage.Focusable = originalImage.Focusable;
clonedImage.Cursor = originalImage.Cursor;
clonedImage.IsManipulationEnabled = originalImage.IsManipulationEnabled;
// 复制位置
InkCanvas.SetLeft(clonedImage, InkCanvas.GetLeft(originalImage));
InkCanvas.SetTop(clonedImage, InkCanvas.GetTop(originalImage));
// 复制变换
if (originalImage.RenderTransform != null)
{
clonedImage.RenderTransform = originalImage.RenderTransform.Clone();
}
return clonedImage;
}
else if (originalElement is MediaElement originalMedia)
{
var clonedMedia = new MediaElement();
// 复制媒体属性
clonedMedia.Source = originalMedia.Source;
clonedMedia.Width = originalMedia.Width;
clonedMedia.Height = originalMedia.Height;
clonedMedia.Name = originalMedia.Name;
clonedMedia.IsHitTestVisible = originalMedia.IsHitTestVisible;
clonedMedia.Focusable = originalMedia.Focusable;
// 复制位置
InkCanvas.SetLeft(clonedMedia, InkCanvas.GetLeft(originalMedia));
InkCanvas.SetTop(clonedMedia, InkCanvas.GetTop(originalMedia));
// 复制变换
if (originalMedia.RenderTransform != null)
{
clonedMedia.RenderTransform = originalMedia.RenderTransform.Clone();
}
return clonedMedia;
}
else if (originalElement is Border originalBorder)
{
var clonedBorder = new Border();
// 复制边框属性
clonedBorder.Width = originalBorder.Width;
clonedBorder.Height = originalBorder.Height;
clonedBorder.Name = originalBorder.Name;
clonedBorder.IsHitTestVisible = originalBorder.IsHitTestVisible;
clonedBorder.Focusable = originalBorder.Focusable;
clonedBorder.Background = originalBorder.Background;
clonedBorder.BorderBrush = originalBorder.BorderBrush;
clonedBorder.BorderThickness = originalBorder.BorderThickness;
clonedBorder.CornerRadius = originalBorder.CornerRadius;
// 复制位置
InkCanvas.SetLeft(clonedBorder, InkCanvas.GetLeft(originalBorder));
InkCanvas.SetTop(clonedBorder, InkCanvas.GetTop(originalBorder));
// 复制变换
if (originalBorder.RenderTransform != null)
{
clonedBorder.RenderTransform = originalBorder.RenderTransform.Clone();
}
return clonedBorder;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"克隆UI元素失败: {ex.Message}", LogHelper.LogType.Error);
}
return null;
}
2025-07-27 22:00:37 +08:00
/// <summary>
/// 恢复之前保存的非笔画元素到画布
/// </summary>
private void RestoreNonStrokeElements(List<UIElement> preservedElements)
{
if (preservedElements == null) return;
foreach (var element in preservedElements)
{
2025-09-13 11:08:56 +08:00
try
2025-07-27 22:00:37 +08:00
{
2025-09-13 11:08:56 +08:00
// 由于现在使用的是克隆的元素,不需要检查Parent属性
2025-07-27 22:00:37 +08:00
inkCanvas.Children.Add(element);
}
2025-09-13 11:08:56 +08:00
catch (Exception ex)
{
LogHelper.WriteLogToFile($"恢复非笔画元素失败: {ex.Message}", LogHelper.LogType.Error);
}
2025-07-27 22:00:37 +08:00
}
}
2025-08-03 16:46:33 +08:00
private void BorderMultiTouchMode_MouseUp(object sender, MouseButtonEventArgs e)
{
if (isInMultiTouchMode)
{
2025-05-25 09:29:48 +08:00
inkCanvas.StylusDown -= MainWindow_StylusDown;
inkCanvas.StylusMove -= MainWindow_StylusMove;
inkCanvas.StylusUp -= MainWindow_StylusUp;
inkCanvas.TouchDown -= MainWindow_TouchDown;
inkCanvas.TouchDown += Main_Grid_TouchDown;
2025-08-11 09:13:12 +08:00
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke)
2025-08-03 16:46:33 +08:00
{
2025-07-18 16:28:50 +08:00
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
2025-07-27 22:00:37 +08:00
// 保存非笔画元素(如图片)
var preservedElements = PreserveNonStrokeElements();
2025-05-25 09:29:48 +08:00
inkCanvas.Children.Clear();
2025-07-27 22:00:37 +08:00
// 恢复非笔画元素
RestoreNonStrokeElements(preservedElements);
2025-05-25 09:29:48 +08:00
isInMultiTouchMode = false;
2025-07-27 22:00:37 +08:00
2025-05-25 09:29:48 +08:00
}
2025-08-03 16:46:33 +08:00
else
{
2025-07-27 22:00:37 +08:00
2025-05-25 09:29:48 +08:00
inkCanvas.StylusDown += MainWindow_StylusDown;
inkCanvas.StylusMove += MainWindow_StylusMove;
inkCanvas.StylusUp += MainWindow_StylusUp;
inkCanvas.TouchDown += MainWindow_TouchDown;
inkCanvas.TouchDown -= Main_Grid_TouchDown;
2025-08-11 09:13:12 +08:00
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke)
2025-08-03 16:46:33 +08:00
{
2025-07-18 16:28:50 +08:00
inkCanvas.EditingMode = InkCanvasEditingMode.None;
}
2025-07-27 22:00:37 +08:00
// 保存非笔画元素(如图片)
var preservedElements = PreserveNonStrokeElements();
2025-05-25 09:29:48 +08:00
inkCanvas.Children.Clear();
2025-07-27 22:00:37 +08:00
// 恢复非笔画元素
RestoreNonStrokeElements(preservedElements);
2025-05-25 09:29:48 +08:00
isInMultiTouchMode = true;
}
}
2025-08-03 16:46:33 +08:00
private void MainWindow_TouchDown(object sender, TouchEventArgs e)
{
2025-08-30 18:10:55 +08:00
// 检查触摸是否发生在浮动栏区域,如果是则允许事件传播到浮动栏按钮
var touchPoint = e.GetTouchPoint(this);
var floatingBarBounds = ViewboxFloatingBar.TransformToAncestor(this).TransformBounds(
new Rect(0, 0, ViewboxFloatingBar.ActualWidth, ViewboxFloatingBar.ActualHeight));
2025-08-31 11:43:52 +08:00
2025-08-30 18:10:55 +08:00
// 如果触摸发生在浮动栏区域,不阻止事件传播,让浮动栏按钮能够接收触摸事件
if (floatingBarBounds.Contains(touchPoint.Position))
{
// 不设置 ViewboxFloatingBar.IsHitTestVisible = false,让浮动栏按钮能够接收触摸事件
return;
}
2025-08-31 11:43:52 +08:00
2025-07-06 22:07:05 +08:00
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint
|| inkCanvas.EditingMode == InkCanvasEditingMode.EraseByStroke
|| inkCanvas.EditingMode == InkCanvasEditingMode.Select) return;
2025-05-25 09:29:48 +08:00
2025-08-03 16:46:33 +08:00
if (!isHidingSubPanelsWhenInking)
{
2025-05-25 09:29:48 +08:00
isHidingSubPanelsWhenInking = true;
HideSubPanels(); // 书写时自动隐藏二级菜单
}
2025-08-10 11:58:58 +08:00
// 修复:几何绘制模式下完全禁止触摸轨迹收集
if (drawingShapeMode != 0)
{
// 确保几何绘制模式下不切换到Ink模式,避免触摸轨迹被收集
inkCanvas.EditingMode = InkCanvasEditingMode.None;
return;
}
2025-07-23 22:57:16 +08:00
// 只保留普通橡皮逻辑
TouchDownPointsList[e.TouchDevice.Id] = InkCanvasEditingMode.None;
inkCanvas.EraserShape = new EllipseStylusShape(50, 50);
2025-08-31 11:43:52 +08:00
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
2025-08-11 09:13:12 +08:00
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke)
2025-08-03 16:46:33 +08:00
{
inkCanvas.EditingMode = InkCanvasEditingMode.None;
2025-05-25 09:29:48 +08:00
}
}
2025-08-03 16:46:33 +08:00
private void MainWindow_StylusDown(object sender, StylusDownEventArgs e)
{
2025-08-30 18:10:55 +08:00
// 检查手写笔点击是否发生在浮动栏区域,如果是则允许事件传播到浮动栏按钮
var stylusPoint = e.GetPosition(this);
var floatingBarBounds = ViewboxFloatingBar.TransformToAncestor(this).TransformBounds(
new Rect(0, 0, ViewboxFloatingBar.ActualWidth, ViewboxFloatingBar.ActualHeight));
2025-08-31 11:43:52 +08:00
2025-08-30 18:10:55 +08:00
// 如果手写笔点击发生在浮动栏区域,不阻止事件传播,让浮动栏按钮能够接收手写笔事件
if (floatingBarBounds.Contains(stylusPoint))
{
// 不设置 ViewboxFloatingBar.IsHitTestVisible = false,让浮动栏按钮能够接收手写笔事件
return;
}
2025-08-31 11:43:52 +08:00
2025-08-10 11:58:58 +08:00
// 新增:根据是否为笔尾自动切换橡皮擦/画笔模式
if (e.StylusDevice.Inverted)
{
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
}
else
{
// 修复:几何绘制模式下完全禁止触摸轨迹收集
if (drawingShapeMode != 0)
{
// 确保几何绘制模式下不切换到Ink模式,避免触摸轨迹被收集
inkCanvas.EditingMode = InkCanvasEditingMode.None;
return;
}
2025-08-11 09:13:12 +08:00
// 修复:保持当前的线擦模式,不要强制切换到Ink模式
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke)
{
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
2025-08-23 23:13:39 +08:00
}
else
{
2025-08-31 07:54:43 +08:00
LogHelper.WriteLogToFile("保持当前线擦模式");
2025-08-11 09:13:12 +08:00
}
2025-08-10 11:58:58 +08:00
}
2025-07-18 16:12:04 +08:00
SetCursorBasedOnEditingMode(inkCanvas);
2025-07-06 22:07:05 +08:00
2025-05-25 09:29:48 +08:00
inkCanvas.CaptureStylus();
ViewboxFloatingBar.IsHitTestVisible = false;
BlackboardUIGridForInkReplay.IsHitTestVisible = false;
2025-06-19 11:25:15 +08:00
// 确保手写笔模式下显示光标
2025-08-03 16:46:33 +08:00
if (Settings.Canvas.IsShowCursor)
{
2025-06-19 11:25:15 +08:00
inkCanvas.ForceCursor = true;
inkCanvas.UseCustomCursor = true;
2025-08-03 16:46:33 +08:00
2025-06-19 11:25:15 +08:00
// 根据当前编辑模式设置不同的光标
2025-08-03 16:46:33 +08:00
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
2025-06-19 11:25:15 +08:00
inkCanvas.Cursor = Cursors.Cross;
2025-08-03 16:46:33 +08:00
}
else if (inkCanvas.EditingMode == InkCanvasEditingMode.Ink)
{
2025-06-19 11:25:15 +08:00
var sri = Application.GetResourceStream(new Uri("Resources/Cursors/Pen.cur", UriKind.Relative));
if (sri != null)
inkCanvas.Cursor = new Cursor(sri.Stream);
}
2025-08-03 16:46:33 +08:00
2025-06-19 11:25:15 +08:00
// 强制显示光标
System.Windows.Forms.Cursor.Show();
}
2025-05-25 09:29:48 +08:00
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint
2025-07-06 22:07:05 +08:00
|| inkCanvas.EditingMode == InkCanvasEditingMode.EraseByStroke
|| inkCanvas.EditingMode == InkCanvasEditingMode.Select) return;
2025-05-25 09:29:48 +08:00
TouchDownPointsList[e.StylusDevice.Id] = InkCanvasEditingMode.None;
}
2025-08-03 16:46:33 +08:00
private async void MainWindow_StylusUp(object sender, StylusEventArgs e)
{
try
{
2025-08-23 23:13:39 +08:00
var stroke = GetStrokeVisual(e.StylusDevice.Id).Stroke;
2025-08-31 11:43:52 +08:00
2025-08-23 23:13:39 +08:00
// 正常模式:添加到画布并参与墨迹纠正
inkCanvas.Strokes.Add(stroke);
2025-09-13 21:38:03 +08:00
await Task.Delay(5);
2025-05-25 09:29:48 +08:00
inkCanvas.Children.Remove(GetVisualCanvas(e.StylusDevice.Id));
inkCanvas_StrokeCollected(inkCanvas,
2025-08-23 23:13:39 +08:00
new InkCanvasStrokeCollectedEventArgs(stroke));
2025-05-25 09:29:48 +08:00
}
2025-08-03 16:46:33 +08:00
catch (Exception ex)
{
2025-08-23 23:13:39 +08:00
LogHelper.WriteLogToFile($"MainWindow_StylusUp 出错: {ex}", LogHelper.LogType.Error);
2025-05-25 09:29:48 +08:00
Label.Content = ex.ToString();
}
2025-08-03 16:46:33 +08:00
try
{
2025-05-25 09:29:48 +08:00
StrokeVisualList.Remove(e.StylusDevice.Id);
VisualCanvasList.Remove(e.StylusDevice.Id);
TouchDownPointsList.Remove(e.StylusDevice.Id);
2025-08-03 16:46:33 +08:00
if (StrokeVisualList.Count == 0 || VisualCanvasList.Count == 0 || TouchDownPointsList.Count == 0)
{
2025-07-27 22:00:37 +08:00
// 只清除手写笔预览相关的Canvas,不清除所有子元素
2025-08-03 16:46:33 +08:00
foreach (var canvas in VisualCanvasList.Values.ToList())
{
if (inkCanvas.Children.Contains(canvas))
{
2025-07-27 22:00:37 +08:00
inkCanvas.Children.Remove(canvas);
}
}
2025-05-25 09:29:48 +08:00
StrokeVisualList.Clear();
VisualCanvasList.Clear();
TouchDownPointsList.Clear();
}
}
catch { }
inkCanvas.ReleaseStylusCapture();
ViewboxFloatingBar.IsHitTestVisible = true;
BlackboardUIGridForInkReplay.IsHitTestVisible = true;
}
2025-08-03 16:46:33 +08:00
private void MainWindow_StylusMove(object sender, StylusEventArgs e)
{
try
{
2025-05-25 09:29:48 +08:00
if (GetTouchDownPointsList(e.StylusDevice.Id) != InkCanvasEditingMode.None) return;
2025-08-03 16:46:33 +08:00
try
{
2025-05-25 09:29:48 +08:00
if (e.StylusDevice.StylusButtons[1].StylusButtonState == StylusButtonState.Down) return;
}
catch { }
2025-06-19 11:25:15 +08:00
// 确保手写笔移动时光标保持可见
2025-08-03 16:46:33 +08:00
if (Settings.Canvas.IsShowCursor)
{
2025-06-19 11:25:15 +08:00
inkCanvas.ForceCursor = true;
inkCanvas.UseCustomCursor = true;
System.Windows.Forms.Cursor.Show();
}
2025-05-25 09:29:48 +08:00
var strokeVisual = GetStrokeVisual(e.StylusDevice.Id);
var stylusPointCollection = e.GetStylusPoints(this);
foreach (var stylusPoint in stylusPointCollection)
strokeVisual.Add(new StylusPoint(stylusPoint.X, stylusPoint.Y, stylusPoint.PressureFactor));
strokeVisual.Redraw();
}
catch { }
}
2025-08-03 16:46:33 +08:00
private StrokeVisual GetStrokeVisual(int id)
{
2025-05-25 09:29:48 +08:00
if (StrokeVisualList.TryGetValue(id, out var visual)) return visual;
var strokeVisual = new StrokeVisual(inkCanvas.DefaultDrawingAttributes.Clone());
StrokeVisualList[id] = strokeVisual;
StrokeVisualList[id] = strokeVisual;
var visualCanvas = new VisualCanvas(strokeVisual);
VisualCanvasList[id] = visualCanvas;
inkCanvas.Children.Add(visualCanvas);
return strokeVisual;
}
2025-08-03 16:46:33 +08:00
private VisualCanvas GetVisualCanvas(int id)
{
2025-05-25 09:29:48 +08:00
return VisualCanvasList.TryGetValue(id, out var visualCanvas) ? visualCanvas : null;
}
2025-08-03 16:46:33 +08:00
private InkCanvasEditingMode GetTouchDownPointsList(int id)
{
2025-05-25 09:29:48 +08:00
return TouchDownPointsList.TryGetValue(id, out var inkCanvasEditingMode) ? inkCanvasEditingMode : inkCanvas.EditingMode;
}
private Dictionary<int, InkCanvasEditingMode> TouchDownPointsList { get; } =
new Dictionary<int, InkCanvasEditingMode>();
private Dictionary<int, StrokeVisual> StrokeVisualList { get; } = new Dictionary<int, StrokeVisual>();
private Dictionary<int, VisualCanvas> VisualCanvasList { get; } = new Dictionary<int, VisualCanvas>();
#endregion
2025-08-31 09:00:32 +08:00
2025-05-25 09:29:48 +08:00
private Point iniP = new Point(0, 0);
2025-06-29 15:35:28 +08:00
2025-08-03 16:46:33 +08:00
private void Main_Grid_TouchDown(object sender, TouchEventArgs e)
{
2025-08-30 18:10:55 +08:00
// 检查触摸是否发生在浮动栏区域,如果是则允许事件传播到浮动栏按钮
var touchPoint = e.GetTouchPoint(this);
var floatingBarBounds = ViewboxFloatingBar.TransformToAncestor(this).TransformBounds(
new Rect(0, 0, ViewboxFloatingBar.ActualWidth, ViewboxFloatingBar.ActualHeight));
2025-08-31 11:43:52 +08:00
2025-08-30 18:10:55 +08:00
// 如果触摸发生在浮动栏区域,不阻止事件传播,让浮动栏按钮能够接收触摸事件
if (floatingBarBounds.Contains(touchPoint.Position))
{
// 不设置 ViewboxFloatingBar.IsHitTestVisible = false,让浮动栏按钮能够接收触摸事件
return;
}
2025-08-31 11:43:52 +08:00
2025-07-18 16:12:04 +08:00
SetCursorBasedOnEditingMode(inkCanvas);
2025-05-25 09:29:48 +08:00
inkCanvas.CaptureTouch(e.TouchDevice);
2025-08-03 16:46:33 +08:00
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
2025-07-18 16:12:04 +08:00
// 橡皮状态下只return,保证橡皮状态可保持
2025-07-16 08:03:08 +08:00
return;
}
2025-08-03 16:46:33 +08:00
if (inkCanvas.EditingMode == InkCanvasEditingMode.Select)
{
2025-09-07 07:53:05 +08:00
// 套索选状态下不直接return,允许触摸事件继续处理
dec.Add(e.TouchDevice.Id);
2025-06-20 11:23:57 +08:00
return;
}
2025-08-10 11:58:58 +08:00
// 修复:几何绘制模式下完全禁止触摸轨迹收集
2025-08-03 16:46:33 +08:00
if (drawingShapeMode != 0)
{
2025-08-10 11:58:58 +08:00
// 确保几何绘制模式下不切换到Ink模式,避免触摸轨迹被收集
inkCanvas.EditingMode = InkCanvasEditingMode.None;
2025-07-18 16:12:04 +08:00
return;
2025-05-25 09:29:48 +08:00
}
2025-08-03 16:46:33 +08:00
if (inkCanvas.EditingMode == InkCanvasEditingMode.Ink)
{
2025-07-18 16:12:04 +08:00
return;
}
2025-08-11 09:13:12 +08:00
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByStroke)
{
return;
}
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke)
2025-08-03 16:46:33 +08:00
{
2025-07-18 16:28:50 +08:00
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
2025-05-25 09:29:48 +08:00
}
2025-07-23 23:05:28 +08:00
// 手掌擦相关变量
2025-07-28 14:40:44 +08:00
private bool isPalmEraserActive;
2025-07-23 23:05:28 +08:00
private InkCanvasEditingMode palmEraserLastEditingMode = InkCanvasEditingMode.Ink;
2025-07-28 14:40:44 +08:00
private bool palmEraserLastIsHighlighter;
private bool palmEraserWasEnabledBeforeMultiTouch;
2025-08-31 09:54:13 +08:00
private bool palmEraserTouchDownHandled; // 新增:标记手掌擦触摸按下是否已处理
2025-08-23 18:24:24 +08:00
private DateTime palmEraserActivationTime; // 新增:记录手掌擦激活时间
2025-08-30 14:53:50 +08:00
private const int PALM_ERASER_TIMEOUT_MS = 3000; // 修改:减少手掌擦超时时间(3秒)
2025-08-31 09:54:13 +08:00
private DispatcherTimer palmEraserRecoveryTimer; // 新增:手掌擦恢复定时器
2025-08-30 14:53:50 +08:00
private HashSet<int> palmEraserTouchIds = new HashSet<int>(); // 新增:记录参与手掌擦的触摸点ID
2025-07-23 23:05:28 +08:00
2025-08-03 16:46:33 +08:00
private void inkCanvas_PreviewTouchDown(object sender, TouchEventArgs e)
{
2025-08-30 18:10:55 +08:00
// 检查触摸是否发生在浮动栏区域,如果是则允许事件传播到浮动栏按钮
var touchPoint = e.GetTouchPoint(this);
var floatingBarBounds = ViewboxFloatingBar.TransformToAncestor(this).TransformBounds(
new Rect(0, 0, ViewboxFloatingBar.ActualWidth, ViewboxFloatingBar.ActualHeight));
2025-08-31 11:43:52 +08:00
2025-08-30 18:10:55 +08:00
// 如果触摸发生在浮动栏区域,不阻止事件传播,让浮动栏按钮能够接收触摸事件
if (floatingBarBounds.Contains(touchPoint.Position))
{
// 不设置 ViewboxFloatingBar.IsHitTestVisible = false,让浮动栏按钮能够接收触摸事件
return;
}
2025-08-31 11:43:52 +08:00
2025-07-18 16:28:50 +08:00
// 橡皮状态下不做任何切换,直接return,保证橡皮可持续
2025-08-11 09:13:12 +08:00
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint
|| inkCanvas.EditingMode == InkCanvasEditingMode.EraseByStroke)
2025-08-03 16:46:33 +08:00
{
2025-07-18 16:28:50 +08:00
return;
}
2025-08-10 11:58:58 +08:00
// 修复:几何绘制模式下完全禁止触摸轨迹收集
2025-08-04 20:25:42 +08:00
if (drawingShapeMode != 0)
{
2025-08-11 09:13:12 +08:00
// 确保几何绘制模式下不切换到Ink模式,避免触摸轨迹收集
2025-08-04 20:25:42 +08:00
inkCanvas.EditingMode = InkCanvasEditingMode.None;
2025-08-10 11:58:58 +08:00
// 几何绘制模式下不记录触摸点,避免触摸轨迹被收集
SetCursorBasedOnEditingMode(inkCanvas);
inkCanvas.CaptureTouch(e.TouchDevice);
ViewboxFloatingBar.IsHitTestVisible = false;
BlackboardUIGridForInkReplay.IsHitTestVisible = false;
2025-08-31 11:43:52 +08:00
2025-08-11 09:13:12 +08:00
// 修复:几何绘制模式下,只记录几何绘制的起点,不记录触摸轨迹
if (dec.Count == 0)
2025-08-10 12:20:51 +08:00
{
2025-08-30 18:10:55 +08:00
var inkTouchPoint = e.GetTouchPoint(inkCanvas);
2025-08-11 09:13:12 +08:00
// 对于双曲线绘制,第一笔时记录起点,第二笔时不更新起点
if (drawingShapeMode == 24 || drawingShapeMode == 25)
2025-08-10 12:20:51 +08:00
{
2025-08-11 09:13:12 +08:00
// 双曲线绘制:第一笔记录起点,第二笔保持第一笔的起点
if (drawMultiStepShapeCurrentStep == 0)
{
2025-08-30 18:10:55 +08:00
iniP = inkTouchPoint.Position;
2025-08-11 09:13:12 +08:00
}
// 第二笔时不更新iniP,保持第一笔的起点
}
else
{
// 其他图形正常记录起点
2025-08-30 18:10:55 +08:00
iniP = inkTouchPoint.Position;
2025-08-10 12:20:51 +08:00
}
2025-08-11 09:13:12 +08:00
lastTouchDownStrokeCollection = inkCanvas.Strokes.Clone();
2025-08-10 11:58:58 +08:00
}
2025-08-11 09:13:12 +08:00
dec.Add(e.TouchDevice.Id);
return;
2025-08-04 20:25:42 +08:00
}
2025-08-31 11:43:52 +08:00
2025-08-11 09:13:12 +08:00
// 非几何绘制模式下的正常触摸处理
SetCursorBasedOnEditingMode(inkCanvas);
inkCanvas.CaptureTouch(e.TouchDevice);
ViewboxFloatingBar.IsHitTestVisible = false;
BlackboardUIGridForInkReplay.IsHitTestVisible = false;
dec.Add(e.TouchDevice.Id);
2025-08-31 11:43:52 +08:00
2025-09-06 15:50:16 +08:00
// Palm Eraser 逻辑 - 优化:改进手掌判定条件,使用设备提供的触摸面积信息
2025-08-31 11:43:52 +08:00
if (Settings.Canvas.EnablePalmEraser && dec.Count >= 2 && !isPalmEraserActive && !palmEraserTouchDownHandled)
2025-08-03 16:46:33 +08:00
{
2025-09-06 15:50:16 +08:00
touchPoint = e.GetTouchPoint(inkCanvas);
var size = touchPoint.Size; // 使用设备提供的触摸面积信息
var bounds = touchPoint.Bounds; // 保留bounds用于宽高比计算
2025-08-31 11:43:52 +08:00
// 根据敏感度设置调整判定参数
2025-09-06 15:50:16 +08:00
double palmAreaThreshold; // 改为面积阈值
2025-08-31 11:43:52 +08:00
double aspectRatioThreshold;
int minTouchPoints;
switch (Settings.Canvas.PalmEraserSensitivity)
2025-08-30 14:53:50 +08:00
{
2025-08-31 11:43:52 +08:00
case 0: // 低敏感度 - 更严格的判定
2025-09-06 15:50:16 +08:00
palmAreaThreshold = 6400; // 80*80的面积
2025-08-31 11:43:52 +08:00
aspectRatioThreshold = 0.4;
minTouchPoints = 4;
break;
case 1: // 中敏感度 - 平衡的判定
2025-09-06 15:50:16 +08:00
palmAreaThreshold = 3600; // 60*60的面积
2025-08-31 11:43:52 +08:00
aspectRatioThreshold = 0.3;
minTouchPoints = 3;
break;
case 2: // 高敏感度 - 较宽松的判定
default:
2025-09-06 15:50:16 +08:00
palmAreaThreshold = 2500; // 50*50的面积
2025-08-31 11:43:52 +08:00
aspectRatioThreshold = 0.25;
minTouchPoints = 2;
break;
}
2025-09-06 15:50:16 +08:00
// 计算触摸面积(使用设备提供的Size)
double touchArea = size.Width * size.Height;
2025-09-07 13:30:46 +08:00
2025-09-06 15:50:16 +08:00
// 计算宽高比(使用Bounds确保准确性)
2025-08-31 11:43:52 +08:00
double aspectRatio = Math.Min(bounds.Width, bounds.Height) / Math.Max(bounds.Width, bounds.Height);
2025-09-06 15:50:16 +08:00
// 改进的手掌判定条件:使用面积而不是单独的宽高
bool isLargeTouch = touchArea >= palmAreaThreshold;
2025-08-31 11:43:52 +08:00
bool isPalmLikeShape = aspectRatio >= aspectRatioThreshold;
bool hasMultipleTouchPoints = dec.Count >= minTouchPoints;
2025-09-07 13:30:46 +08:00
2025-09-06 15:50:16 +08:00
// 新增:额外的判定条件提高准确性
bool isReasonableSize = size.Width >= 20 && size.Height >= 20 && size.Width <= 200 && size.Height <= 200; // 合理的触摸尺寸范围
bool isNotTooElongated = aspectRatio >= 0.2; // 避免过于细长的触摸(可能是手指)
bool hasEnoughArea = touchArea >= 400; // 最小面积要求,避免小面积误判
2025-08-31 11:43:52 +08:00
2025-09-06 15:50:16 +08:00
if (isLargeTouch && isPalmLikeShape && hasMultipleTouchPoints && isReasonableSize && isNotTooElongated && hasEnoughArea)
2025-08-31 11:43:52 +08:00
{
// 记录当前编辑模式和高光状态
palmEraserLastEditingMode = inkCanvas.EditingMode;
palmEraserLastIsHighlighter = drawingAttributes.IsHighlighter;
// 记录参与手掌擦的触摸点ID
palmEraserTouchIds.Clear();
foreach (int touchId in dec)
{
palmEraserTouchIds.Add(touchId);
}
// 切换为橡皮擦
EraserIcon_Click(null, null);
isPalmEraserActive = true;
palmEraserActivationTime = DateTime.Now; // 记录激活时间
palmEraserTouchDownHandled = true; // 标记已处理
// 启动恢复定时器,防止卡死
StartPalmEraserRecoveryTimer();
// 记录日志
2025-09-06 15:50:16 +08:00
LogHelper.WriteLogToFile($"Palm eraser activated - Sensitivity: {Settings.Canvas.PalmEraserSensitivity}, Touch area: {touchArea:F0}, Size: {size.Width}x{size.Height}, Bounds: {bounds.Width}x{bounds.Height}, Aspect ratio: {aspectRatio:F2}, Touch points: {dec.Count}, Reasonable size: {isReasonableSize}, Not elongated: {isNotTooElongated}, Enough area: {hasEnoughArea}");
2025-08-30 14:53:50 +08:00
}
2025-07-23 23:05:28 +08:00
}
2025-05-25 09:29:48 +08:00
2025-08-31 11:43:52 +08:00
// 设备1个的时候,记录中心点
if (dec.Count == 1)
2025-08-10 12:20:51 +08:00
{
2025-08-31 11:43:52 +08:00
touchPoint = e.GetTouchPoint(inkCanvas);
centerPoint = touchPoint.Position;
// 修复:只允许在此处赋值iniP,防止TouchMove等其他地方覆盖,保证几何绘制起点一致
if (drawingShapeMode != 0)
2025-08-03 16:46:33 +08:00
{
2025-08-31 11:43:52 +08:00
// 对于双曲线绘制,第一笔时记录起点,第二笔时不更新起点
if (drawingShapeMode == 24 || drawingShapeMode == 25)
2025-08-10 12:20:51 +08:00
{
2025-08-31 11:43:52 +08:00
// 双曲线绘制:第一笔记录起点,第二笔保持第一笔的起点
if (drawMultiStepShapeCurrentStep == 0)
{
iniP = touchPoint.Position;
}
// 第二笔时不更新iniP,保持第一笔的起点
}
else
{
// 其他图形正常记录起点
2025-08-10 12:20:51 +08:00
iniP = touchPoint.Position;
}
2025-07-18 17:02:17 +08:00
}
2025-08-10 12:20:51 +08:00
2025-08-31 11:43:52 +08:00
// 记录第一根手指点击时的 StrokeCollection
lastTouchDownStrokeCollection = inkCanvas.Strokes.Clone();
}
2025-07-06 22:07:05 +08:00
//设备两个及两个以上,将画笔功能关闭
2025-08-03 16:46:33 +08:00
if (dec.Count > 1 || isSingleFingerDragMode || !Settings.Gesture.IsEnableTwoFingerGesture)
{
2025-07-06 22:07:05 +08:00
if (isInMultiTouchMode || !Settings.Gesture.IsEnableTwoFingerGesture) return;
if (inkCanvas.EditingMode == InkCanvasEditingMode.None ||
inkCanvas.EditingMode == InkCanvasEditingMode.Select) return;
lastInkCanvasEditingMode = inkCanvas.EditingMode;
2025-08-10 11:58:58 +08:00
// 修复:几何绘制模式下禁止切回Ink
2025-08-11 09:13:12 +08:00
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
2025-08-31 11:43:52 +08:00
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke
2025-08-11 09:13:12 +08:00
&& drawingShapeMode == 0)
2025-08-03 16:46:33 +08:00
{
2025-07-18 16:28:50 +08:00
inkCanvas.EditingMode = InkCanvasEditingMode.None;
}
2025-05-25 09:29:48 +08:00
}
}
2025-08-03 16:46:33 +08:00
private void inkCanvas_PreviewTouchUp(object sender, TouchEventArgs e)
{
2025-07-18 16:28:50 +08:00
// 橡皮状态下不做任何切换,直接return,保证橡皮可持续
2025-08-03 16:46:33 +08:00
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint && !isPalmEraserActive)
{
2025-07-18 16:28:50 +08:00
return;
}
2025-05-25 09:29:48 +08:00
inkCanvas.ReleaseAllTouchCaptures();
ViewboxFloatingBar.IsHitTestVisible = true;
BlackboardUIGridForInkReplay.IsHitTestVisible = true;
2025-08-30 14:53:50 +08:00
// Palm Eraser 逻辑:优化状态恢复机制
2025-07-23 23:05:28 +08:00
dec.Remove(e.TouchDevice.Id);
2025-08-31 11:43:52 +08:00
2025-08-30 14:53:50 +08:00
// 如果是手掌擦的触摸点,从记录中移除
if (palmEraserTouchIds.Contains(e.TouchDevice.Id))
{
palmEraserTouchIds.Remove(e.TouchDevice.Id);
}
2025-08-31 11:43:52 +08:00
2025-08-30 14:53:50 +08:00
// 当所有手掌擦触摸点都抬起时,恢复原编辑模式
if (isPalmEraserActive && palmEraserTouchIds.Count == 0)
2025-08-03 16:46:33 +08:00
{
2025-09-06 15:50:16 +08:00
LogHelper.WriteLogToFile($"Palm eraser recovery triggered - Touch points remaining: {palmEraserTouchIds.Count}, dec.Count: {dec.Count}");
2025-09-07 13:30:46 +08:00
2025-07-23 23:05:28 +08:00
// 恢复高光状态
drawingAttributes.IsHighlighter = palmEraserLastIsHighlighter;
2025-08-31 11:43:52 +08:00
2025-08-30 14:53:50 +08:00
// 恢复编辑模式 - 优化:改进状态恢复逻辑
2025-08-23 18:24:24 +08:00
try
2025-08-03 16:46:33 +08:00
{
2025-08-23 18:24:24 +08:00
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
2025-08-03 16:46:33 +08:00
{
2025-08-30 14:53:50 +08:00
// 根据之前的状态恢复
switch (palmEraserLastEditingMode)
2025-08-23 18:24:24 +08:00
{
2025-08-30 14:53:50 +08:00
case InkCanvasEditingMode.Ink:
PenIcon_Click(null, null);
break;
case InkCanvasEditingMode.Select:
SymbolIconSelect_MouseUp(null, null);
break;
default:
inkCanvas.EditingMode = palmEraserLastEditingMode;
break;
2025-08-23 18:24:24 +08:00
}
2025-08-31 11:43:52 +08:00
2025-08-31 07:54:43 +08:00
LogHelper.WriteLogToFile($"Palm eraser recovered to mode: {palmEraserLastEditingMode}");
2025-08-03 16:46:33 +08:00
}
2025-08-23 18:24:24 +08:00
}
catch (Exception ex)
{
// 如果恢复失败,强制切换到批注模式
2025-08-30 14:53:50 +08:00
LogHelper.WriteLogToFile($"Palm eraser recovery failed: {ex.Message}, forcing to Ink mode", LogHelper.LogType.Error);
2025-08-23 18:24:24 +08:00
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
2025-08-31 11:43:52 +08:00
2025-08-23 18:24:24 +08:00
// 重置手掌擦状态
isPalmEraserActive = false;
palmEraserTouchDownHandled = false;
2025-08-30 14:53:50 +08:00
palmEraserTouchIds.Clear();
2025-08-31 11:43:52 +08:00
2025-08-23 18:24:24 +08:00
// 停止恢复定时器
StopPalmEraserRecoveryTimer();
2025-08-31 11:43:52 +08:00
2025-08-23 18:24:24 +08:00
// 确保触摸事件能正常响应
inkCanvas.IsHitTestVisible = true;
inkCanvas.IsManipulationEnabled = true;
2025-08-31 11:43:52 +08:00
2025-08-24 16:09:14 +08:00
ViewboxFloatingBar.IsHitTestVisible = true;
BlackboardUIGridForInkReplay.IsHitTestVisible = true;
2025-08-31 11:43:52 +08:00
2025-08-31 07:54:43 +08:00
LogHelper.WriteLogToFile("Palm eraser state reset completed");
2025-08-23 18:24:24 +08:00
}
2025-08-31 11:43:52 +08:00
2025-08-23 18:24:24 +08:00
// 新增:超时检测 - 如果手掌擦激活时间过长,强制重置状态
2025-08-30 14:53:50 +08:00
if (isPalmEraserActive)
2025-08-23 18:24:24 +08:00
{
var timeSinceActivation = DateTime.Now - palmEraserActivationTime;
if (timeSinceActivation.TotalMilliseconds > PALM_ERASER_TIMEOUT_MS)
{
2025-08-30 14:53:50 +08:00
LogHelper.WriteLogToFile($"Palm eraser timeout detected ({timeSinceActivation.TotalMilliseconds}ms), forcing recovery", LogHelper.LogType.Warning);
2025-08-31 11:43:52 +08:00
2025-08-23 18:24:24 +08:00
// 强制恢复状态
try
2025-08-03 16:46:33 +08:00
{
2025-08-23 18:24:24 +08:00
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
2025-08-30 14:53:50 +08:00
switch (palmEraserLastEditingMode)
2025-08-23 18:24:24 +08:00
{
2025-08-30 14:53:50 +08:00
case InkCanvasEditingMode.Ink:
PenIcon_Click(null, null);
break;
case InkCanvasEditingMode.Select:
SymbolIconSelect_MouseUp(null, null);
break;
default:
inkCanvas.EditingMode = palmEraserLastEditingMode;
break;
2025-08-23 18:24:24 +08:00
}
}
2025-08-03 16:46:33 +08:00
}
2025-08-23 18:24:24 +08:00
catch (Exception ex)
2025-08-03 16:46:33 +08:00
{
2025-08-30 14:53:50 +08:00
LogHelper.WriteLogToFile($"Palm eraser timeout recovery failed: {ex.Message}, forcing to Ink mode", LogHelper.LogType.Error);
2025-08-23 18:24:24 +08:00
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
2025-07-23 23:05:28 +08:00
}
2025-08-31 11:43:52 +08:00
2025-08-23 18:24:24 +08:00
// 重置所有手掌擦状态
isPalmEraserActive = false;
palmEraserTouchDownHandled = false;
2025-08-30 14:53:50 +08:00
palmEraserTouchIds.Clear();
2025-08-23 18:24:24 +08:00
inkCanvas.IsHitTestVisible = true;
inkCanvas.IsManipulationEnabled = true;
2025-08-31 11:43:52 +08:00
2025-08-24 16:09:14 +08:00
ViewboxFloatingBar.IsHitTestVisible = true;
BlackboardUIGridForInkReplay.IsHitTestVisible = true;
2025-08-31 11:43:52 +08:00
2025-08-23 18:24:24 +08:00
// 停止恢复定时器
StopPalmEraserRecoveryTimer();
2025-08-31 11:43:52 +08:00
2025-08-31 07:54:43 +08:00
LogHelper.WriteLogToFile("Palm eraser timeout recovery completed");
2025-07-23 23:05:28 +08:00
}
}
2025-08-10 12:20:51 +08:00
// 修复:几何绘制模式下,触摸抬手时应该正确处理,而不是简单模拟鼠标事件
2025-08-03 16:46:33 +08:00
if (drawingShapeMode != 0)
{
2025-08-10 12:20:51 +08:00
// 对于双曲线等需要多步绘制的图形,触摸抬手时应该进入下一步
if (drawingShapeMode == 24 || drawingShapeMode == 25)
2025-07-18 17:01:18 +08:00
{
2025-08-10 12:20:51 +08:00
// 双曲线绘制:触摸抬手时进入下一步,但不自动触发鼠标抬起事件
// 让用户继续绘制第二笔
if (drawMultiStepShapeCurrentStep == 0)
{
// 第一笔完成,进入第二笔
drawMultiStepShapeCurrentStep = 1;
}
else
{
// 第二笔完成,完成绘制
var mouseArgs = new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left)
{
RoutedEvent = MouseLeftButtonUpEvent,
Source = inkCanvas
};
inkCanvas_MouseUp(inkCanvas, mouseArgs);
}
}
else
{
// 其他单步绘制的图形,触摸抬手时完成绘制
var mouseArgs = new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left)
{
RoutedEvent = MouseLeftButtonUpEvent,
Source = inkCanvas
};
inkCanvas_MouseUp(inkCanvas, mouseArgs);
}
2025-07-18 17:01:18 +08:00
}
2025-08-10 12:20:51 +08:00
// 手势完成后切回之前的状态
2025-08-10 11:58:58 +08:00
if (drawingShapeMode == 0)
2025-08-03 16:46:33 +08:00
{
2025-08-10 11:58:58 +08:00
if (dec.Count > 1)
2025-08-03 16:46:33 +08:00
{
2025-08-10 11:58:58 +08:00
if (inkCanvas.EditingMode == InkCanvasEditingMode.None)
2025-08-03 16:46:33 +08:00
{
2025-08-10 11:58:58 +08:00
if (lastInkCanvasEditingMode != InkCanvasEditingMode.EraseByPoint)
{
inkCanvas.EditingMode = lastInkCanvasEditingMode;
}
2025-07-18 16:28:50 +08:00
}
2025-07-29 19:07:59 +08:00
}
2025-08-10 11:58:58 +08:00
else if (dec.Count == 0)
2025-08-03 16:46:33 +08:00
{
2025-08-10 11:58:58 +08:00
// 当所有触摸点都抬起时,确保正确恢复编辑模式
// 这对于从橡皮擦切换到笔后恢复多指手势功能很重要
if (inkCanvas.EditingMode == InkCanvasEditingMode.None &&
lastInkCanvasEditingMode != InkCanvasEditingMode.None &&
lastInkCanvasEditingMode != InkCanvasEditingMode.EraseByPoint)
{
inkCanvas.EditingMode = lastInkCanvasEditingMode;
}
2025-08-31 11:43:52 +08:00
2025-08-23 18:24:24 +08:00
// 修复:确保手掌擦除后触摸事件能正常响应
if (isPalmEraserActive)
{
2025-09-06 15:50:16 +08:00
LogHelper.WriteLogToFile("Palm eraser force recovery - all touch points cleared");
2025-09-07 13:30:46 +08:00
2025-09-06 15:50:16 +08:00
// 恢复高光状态
drawingAttributes.IsHighlighter = palmEraserLastIsHighlighter;
2025-09-07 13:30:46 +08:00
2025-09-06 15:50:16 +08:00
// 恢复编辑模式
try
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
switch (palmEraserLastEditingMode)
{
case InkCanvasEditingMode.Ink:
PenIcon_Click(null, null);
break;
case InkCanvasEditingMode.Select:
SymbolIconSelect_MouseUp(null, null);
break;
default:
inkCanvas.EditingMode = palmEraserLastEditingMode;
break;
}
LogHelper.WriteLogToFile($"Palm eraser force recovered to mode: {palmEraserLastEditingMode}");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"Palm eraser force recovery failed: {ex.Message}, forcing to Ink mode", LogHelper.LogType.Error);
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
2025-09-07 13:30:46 +08:00
2025-08-23 18:24:24 +08:00
// 如果手掌擦还在激活状态但触摸点已清空,强制重置状态
isPalmEraserActive = false;
palmEraserTouchDownHandled = false;
2025-08-30 14:53:50 +08:00
palmEraserTouchIds.Clear(); // 确保清空触摸点ID
2025-08-23 18:24:24 +08:00
inkCanvas.IsHitTestVisible = true;
inkCanvas.IsManipulationEnabled = true;
2025-08-31 11:43:52 +08:00
2025-08-24 16:09:14 +08:00
ViewboxFloatingBar.IsHitTestVisible = true;
BlackboardUIGridForInkReplay.IsHitTestVisible = true;
2025-09-07 13:30:46 +08:00
2025-09-06 15:50:16 +08:00
// 停止恢复定时器
StopPalmEraserRecoveryTimer();
2025-09-07 13:30:46 +08:00
2025-09-06 15:50:16 +08:00
LogHelper.WriteLogToFile("Palm eraser force recovery completed");
2025-08-23 18:24:24 +08:00
}
2025-07-29 19:07:59 +08:00
}
}
2025-05-25 09:29:48 +08:00
inkCanvas.Opacity = 1;
2025-07-29 19:07:59 +08:00
2025-07-06 22:07:05 +08:00
if (dec.Count == 0)
2025-05-25 09:29:48 +08:00
if (lastTouchDownStrokeCollection.Count() != inkCanvas.Strokes.Count() &&
2025-08-03 16:46:33 +08:00
!(drawingShapeMode == 9 && !isFirstTouchCuboid))
{
2025-05-25 09:29:48 +08:00
var whiteboardIndex = CurrentWhiteboardIndex;
if (currentMode == 0) whiteboardIndex = 0;
strokeCollections[whiteboardIndex] = lastTouchDownStrokeCollection;
}
}
2025-08-03 16:46:33 +08:00
private void inkCanvas_ManipulationStarting(object sender, ManipulationStartingEventArgs e)
{
2025-05-25 09:29:48 +08:00
e.Mode = ManipulationModes.All;
}
private void inkCanvas_ManipulationInertiaStarting(object sender, ManipulationInertiaStartingEventArgs e) { }
2025-08-03 16:46:33 +08:00
private void Main_Grid_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
2025-05-25 09:29:48 +08:00
if (e.Manipulators.Count() != 0) return;
2025-08-10 11:58:58 +08:00
// 修复:几何绘制模式下不自动切换到Ink模式,避免触摸轨迹被收集
2025-08-31 11:43:52 +08:00
if (drawingShapeMode == 0
2025-08-11 09:13:12 +08:00
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke)
2025-08-03 16:46:33 +08:00
{
2025-07-18 16:28:50 +08:00
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
2025-07-29 19:07:59 +08:00
// 修复:确保多指手势完成后正确更新lastInkCanvasEditingMode
lastInkCanvasEditingMode = InkCanvasEditingMode.Ink;
2025-07-18 16:28:50 +08:00
}
2025-05-25 09:29:48 +08:00
}
2025-08-03 16:46:33 +08:00
private void Main_Grid_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
2025-06-19 22:49:11 +08:00
// 手掌擦时禁止移动/缩放
2025-07-23 22:57:16 +08:00
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
2025-06-19 22:49:11 +08:00
return;
// 三指及以上禁止缩放
bool disableScale = dec.Count >= 3;
2025-09-07 13:30:46 +08:00
2025-09-07 07:53:05 +08:00
// 修复:允许单指拖动选中的墨迹,即使禁用了多指手势
if (isInMultiTouchMode) return;
2025-09-07 13:30:46 +08:00
2025-09-07 07:53:05 +08:00
// 如果是单指拖动选中的墨迹,允许处理
if (dec.Count == 1 && inkCanvas.GetSelectedStrokes().Count > 0)
{
var md = e.DeltaManipulation;
var trans = md.Translation; // 获得位移矢量
2025-09-07 13:30:46 +08:00
2025-09-07 07:53:05 +08:00
if (trans.X != 0 || trans.Y != 0)
{
var m = new Matrix();
m.Translate(trans.X, trans.Y); // 移动
2025-09-07 13:30:46 +08:00
2025-09-07 07:53:05 +08:00
var strokes = inkCanvas.GetSelectedStrokes();
foreach (var stroke in strokes)
{
stroke.Transform(m, false);
}
2025-09-07 13:30:46 +08:00
2025-09-07 07:53:05 +08:00
// 更新选择框位置
updateBorderStrokeSelectionControlLocation();
}
return;
}
2025-09-07 13:30:46 +08:00
2025-09-07 07:53:05 +08:00
if (!Settings.Gesture.IsEnableTwoFingerGesture) return;
2025-05-25 09:29:48 +08:00
if ((dec.Count >= 2 && (Settings.PowerPointSettings.IsEnableTwoFingerGestureInPresentationMode ||
StackPanelPPTControls.Visibility != Visibility.Visible ||
StackPanelPPTButtons.Visibility == Visibility.Collapsed)) ||
2025-08-03 16:46:33 +08:00
isSingleFingerDragMode)
{
2025-05-25 09:29:48 +08:00
var md = e.DeltaManipulation;
var trans = md.Translation; // 获得位移矢量
var m = new Matrix();
if (Settings.Gesture.IsEnableTwoFingerTranslate)
m.Translate(trans.X, trans.Y); // 移动
2025-08-03 16:46:33 +08:00
if (Settings.Gesture.IsEnableTwoFingerGestureTranslateOrRotation)
{
2025-05-25 09:29:48 +08:00
var rotate = md.Rotation; // 获得旋转角度
var scale = md.Scale; // 获得缩放倍数
// Find center of element and then transform to get current location of center
var fe = e.Source as FrameworkElement;
var center = new Point(fe.ActualWidth / 2, fe.ActualHeight / 2);
center = m.Transform(center); // 转换为矩阵缩放和旋转的中心点
if (Settings.Gesture.IsEnableTwoFingerRotation)
m.RotateAt(rotate, center.X, center.Y); // 旋转
2025-06-19 22:49:11 +08:00
if (Settings.Gesture.IsEnableTwoFingerZoom && !disableScale)
2025-05-25 09:29:48 +08:00
m.ScaleAt(scale.X, scale.Y, center.X, center.Y); // 缩放
}
var strokes = inkCanvas.GetSelectedStrokes();
2025-08-03 16:46:33 +08:00
if (strokes.Count != 0)
{
foreach (var stroke in strokes)
{
2025-05-25 09:29:48 +08:00
stroke.Transform(m, false);
foreach (var circle in circles)
2025-08-03 16:46:33 +08:00
if (stroke == circle.Stroke)
{
2025-05-25 09:29:48 +08:00
circle.R = GetDistance(circle.Stroke.StylusPoints[0].ToPoint(),
circle.Stroke.StylusPoints[circle.Stroke.StylusPoints.Count / 2].ToPoint()) / 2;
circle.Centroid = new Point(
(circle.Stroke.StylusPoints[0].X +
circle.Stroke.StylusPoints[circle.Stroke.StylusPoints.Count / 2].X) / 2,
(circle.Stroke.StylusPoints[0].Y +
circle.Stroke.StylusPoints[circle.Stroke.StylusPoints.Count / 2].Y) / 2);
break;
}
}
}
2025-08-03 16:46:33 +08:00
else
{
if (Settings.Gesture.IsEnableTwoFingerZoom)
{
foreach (var stroke in inkCanvas.Strokes)
{
2025-05-25 09:29:48 +08:00
stroke.Transform(m, false);
2025-08-03 16:46:33 +08:00
try
{
2025-05-25 09:29:48 +08:00
stroke.DrawingAttributes.Width *= md.Scale.X;
stroke.DrawingAttributes.Height *= md.Scale.Y;
}
catch { }
}
2025-09-13 11:38:46 +08:00
// 同时变换画布上的图片元素
TransformCanvasImages(m);
2025-05-25 09:29:48 +08:00
}
2025-08-03 16:46:33 +08:00
else
{
2025-05-25 09:29:48 +08:00
foreach (var stroke in inkCanvas.Strokes) stroke.Transform(m, false);
2025-09-13 11:38:46 +08:00
// 同时变换画布上的图片元素
TransformCanvasImages(m);
2025-05-25 09:29:48 +08:00
}
2025-08-03 16:46:33 +08:00
foreach (var circle in circles)
{
2025-05-25 09:29:48 +08:00
circle.R = GetDistance(circle.Stroke.StylusPoints[0].ToPoint(),
circle.Stroke.StylusPoints[circle.Stroke.StylusPoints.Count / 2].ToPoint()) / 2;
circle.Centroid = new Point(
(circle.Stroke.StylusPoints[0].X +
circle.Stroke.StylusPoints[circle.Stroke.StylusPoints.Count / 2].X) / 2,
(circle.Stroke.StylusPoints[0].Y +
circle.Stroke.StylusPoints[circle.Stroke.StylusPoints.Count / 2].Y) / 2
);
}
}
}
}
2025-07-18 17:59:17 +08:00
2025-09-13 11:38:46 +08:00
/// <summary>
/// 变换画布上的图片元素,使其与墨迹同步移动
/// </summary>
private void TransformCanvasImages(Matrix matrix)
{
try
{
// 遍历inkCanvas的所有子元素,找到图片元素
for (int i = inkCanvas.Children.Count - 1; i >= 0; i--)
{
var child = inkCanvas.Children[i];
if (child is Image image)
{
// 应用矩阵变换到图片
ApplyMatrixTransformToImage(image, matrix);
}
else if (child is MediaElement mediaElement)
{
// 对媒体元素也应用变换
ApplyMatrixTransformToMediaElement(mediaElement, matrix);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"变换画布图片失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 对图片应用矩阵变换
/// </summary>
private void ApplyMatrixTransformToImage(Image image, Matrix matrix)
{
try
{
// 获取图片的RenderTransform,如果不存在则创建新的TransformGroup
TransformGroup transformGroup = image.RenderTransform as TransformGroup;
if (transformGroup == null)
{
transformGroup = new TransformGroup();
image.RenderTransform = transformGroup;
}
// 创建新的MatrixTransform并添加到变换组
var matrixTransform = new MatrixTransform(matrix);
transformGroup.Children.Add(matrixTransform);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"应用图片变换失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 对媒体元素应用矩阵变换
/// </summary>
private void ApplyMatrixTransformToMediaElement(MediaElement mediaElement, Matrix matrix)
{
try
{
// 获取媒体元素的RenderTransform,如果不存在则创建新的TransformGroup
TransformGroup transformGroup = mediaElement.RenderTransform as TransformGroup;
if (transformGroup == null)
{
transformGroup = new TransformGroup();
mediaElement.RenderTransform = transformGroup;
}
// 创建新的MatrixTransform并添加到变换组
var matrixTransform = new MatrixTransform(matrix);
transformGroup.Children.Add(matrixTransform);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"应用媒体元素变换失败: {ex.Message}", LogHelper.LogType.Error);
}
}
2025-07-18 17:59:17 +08:00
// 退出多指书写模式,恢复InkCanvas的TouchDown事件绑定
private void ExitMultiTouchModeIfNeeded()
{
if (isInMultiTouchMode)
{
inkCanvas.StylusDown -= MainWindow_StylusDown;
inkCanvas.StylusMove -= MainWindow_StylusMove;
inkCanvas.StylusUp -= MainWindow_StylusUp;
inkCanvas.TouchDown -= MainWindow_TouchDown;
inkCanvas.TouchDown += Main_Grid_TouchDown;
2025-08-10 11:58:58 +08:00
// 修复:几何绘制模式下不自动切换到Ink模式,避免触摸轨迹被收集
2025-08-31 11:43:52 +08:00
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
2025-08-11 09:13:12 +08:00
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke
&& drawingShapeMode == 0)
2025-07-18 17:59:17 +08:00
{
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
2025-07-27 22:00:37 +08:00
// 保存非笔画元素(如图片)
var preservedElements = PreserveNonStrokeElements();
2025-07-18 17:59:17 +08:00
inkCanvas.Children.Clear();
2025-07-27 22:00:37 +08:00
// 恢复非笔画元素
RestoreNonStrokeElements(preservedElements);
2025-07-18 17:59:17 +08:00
isInMultiTouchMode = false;
2025-07-23 23:12:37 +08:00
// 关闭多指书写时,恢复手掌擦开关
2025-08-03 16:46:33 +08:00
if (palmEraserWasEnabledBeforeMultiTouch)
{
2025-07-23 23:12:37 +08:00
Settings.Canvas.EnablePalmEraser = true;
if (ToggleSwitchEnablePalmEraser != null)
ToggleSwitchEnablePalmEraser.IsOn = true;
}
2025-07-18 17:59:17 +08:00
}
}
// 进入多指书写模式,绑定Main_Grid_TouchDown
private void EnterMultiTouchModeIfNeeded()
{
if (!isInMultiTouchMode)
{
inkCanvas.StylusDown += MainWindow_StylusDown;
inkCanvas.StylusMove += MainWindow_StylusMove;
inkCanvas.StylusUp += MainWindow_StylusUp;
inkCanvas.TouchDown += MainWindow_TouchDown;
inkCanvas.TouchDown -= Main_Grid_TouchDown;
2025-08-10 11:58:58 +08:00
// 修复:几何绘制模式下不自动切换到Ink模式,避免触摸轨迹被收集
2025-08-11 09:13:12 +08:00
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
2025-08-31 11:43:52 +08:00
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke
2025-08-11 09:13:12 +08:00
&& drawingShapeMode == 0)
2025-07-18 17:59:17 +08:00
{
inkCanvas.EditingMode = InkCanvasEditingMode.None;
}
2025-07-27 22:00:37 +08:00
// 保存非笔画元素(如图片)
var preservedElements = PreserveNonStrokeElements();
2025-07-18 17:59:17 +08:00
inkCanvas.Children.Clear();
2025-07-27 22:00:37 +08:00
// 恢复非笔画元素
RestoreNonStrokeElements(preservedElements);
2025-07-18 17:59:17 +08:00
isInMultiTouchMode = true;
2025-07-23 23:12:37 +08:00
// 启用多指书写时,自动禁用手掌擦
palmEraserWasEnabledBeforeMultiTouch = Settings.Canvas.EnablePalmEraser;
Settings.Canvas.EnablePalmEraser = false;
if (ToggleSwitchEnablePalmEraser != null)
ToggleSwitchEnablePalmEraser.IsOn = false;
2025-07-18 17:59:17 +08:00
}
}
2025-08-31 11:43:52 +08:00
2025-08-23 18:24:24 +08:00
/// <summary>
/// 启动手掌擦恢复定时器,防止卡死状态
/// </summary>
private void StartPalmEraserRecoveryTimer()
{
if (palmEraserRecoveryTimer == null)
{
2025-08-31 09:54:13 +08:00
palmEraserRecoveryTimer = new DispatcherTimer();
2025-08-23 18:24:24 +08:00
palmEraserRecoveryTimer.Interval = TimeSpan.FromMilliseconds(1000); // 每秒检查一次
palmEraserRecoveryTimer.Tick += PalmEraserRecoveryTimer_Tick;
}
2025-08-31 11:43:52 +08:00
2025-08-23 18:24:24 +08:00
palmEraserRecoveryTimer.Start();
}
2025-08-31 11:43:52 +08:00
2025-08-23 18:24:24 +08:00
/// <summary>
/// 停止手掌擦恢复定时器
/// </summary>
private void StopPalmEraserRecoveryTimer()
{
if (palmEraserRecoveryTimer != null)
{
palmEraserRecoveryTimer.Stop();
}
}
2025-08-31 11:43:52 +08:00
2025-08-23 18:24:24 +08:00
/// <summary>
/// 手掌擦恢复定时器事件处理
/// </summary>
private void PalmEraserRecoveryTimer_Tick(object sender, EventArgs e)
{
if (!isPalmEraserActive) return;
2025-08-31 11:43:52 +08:00
2025-08-23 18:24:24 +08:00
// 检查是否超时
var timeSinceActivation = DateTime.Now - palmEraserActivationTime;
if (timeSinceActivation.TotalMilliseconds > PALM_ERASER_TIMEOUT_MS)
{
2025-08-30 14:53:50 +08:00
LogHelper.WriteLogToFile($"Palm eraser recovery timer triggered, forcing recovery after {timeSinceActivation.TotalMilliseconds}ms", LogHelper.LogType.Warning);
2025-08-31 11:43:52 +08:00
2025-08-23 18:24:24 +08:00
// 强制恢复状态
try
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
2025-08-30 14:53:50 +08:00
switch (palmEraserLastEditingMode)
2025-08-23 18:24:24 +08:00
{
2025-08-30 14:53:50 +08:00
case InkCanvasEditingMode.Ink:
PenIcon_Click(null, null);
break;
case InkCanvasEditingMode.Select:
SymbolIconSelect_MouseUp(null, null);
break;
default:
inkCanvas.EditingMode = palmEraserLastEditingMode;
break;
2025-08-23 18:24:24 +08:00
}
2025-08-31 11:43:52 +08:00
2025-08-31 07:54:43 +08:00
LogHelper.WriteLogToFile($"Palm eraser timer recovery to mode: {palmEraserLastEditingMode}");
2025-08-23 18:24:24 +08:00
}
}
catch (Exception ex)
{
2025-08-30 14:53:50 +08:00
LogHelper.WriteLogToFile($"Palm eraser recovery timer failed: {ex.Message}, forcing to Ink mode", LogHelper.LogType.Error);
2025-08-23 18:24:24 +08:00
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
2025-08-31 11:43:52 +08:00
2025-08-23 18:24:24 +08:00
// 重置所有手掌擦状态
isPalmEraserActive = false;
palmEraserTouchDownHandled = false;
2025-08-30 14:53:50 +08:00
palmEraserTouchIds.Clear();
2025-08-23 18:24:24 +08:00
inkCanvas.IsHitTestVisible = true;
inkCanvas.IsManipulationEnabled = true;
2025-08-31 11:43:52 +08:00
2025-08-24 16:09:14 +08:00
ViewboxFloatingBar.IsHitTestVisible = true;
BlackboardUIGridForInkReplay.IsHitTestVisible = true;
2025-08-31 11:43:52 +08:00
2025-08-23 18:24:24 +08:00
// 停止定时器
StopPalmEraserRecoveryTimer();
2025-08-31 11:43:52 +08:00
2025-08-31 07:54:43 +08:00
LogHelper.WriteLogToFile("Palm eraser timer recovery completed");
2025-08-23 18:24:24 +08:00
}
}
2025-05-25 09:29:48 +08:00
}
2025-07-06 22:07:05 +08:00
}