Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3298,6 +3298,9 @@ private void ResetTouchStates()
// 清空触摸点计数器
dec.Clear();

if (isPalmEraserActive)
isPalmEraserActive = false;

// 确保触摸事件能正常响应
inkCanvas.IsHitTestVisible = true;
inkCanvas.IsManipulationEnabled = true;
Expand Down
12 changes: 12 additions & 0 deletions Ink Canvas/MainWindow_cs/MW_Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3605,6 +3605,11 @@
RestoreNonStrokeElements(preservedElements);
isInMultiTouchMode = true;

palmEraserWasEnabledBeforeMultiTouch = Settings.Canvas.EnablePalmEraser;
Settings.Canvas.EnablePalmEraser = false;
if (ToggleSwitchEnablePalmEraser != null)
ToggleSwitchEnablePalmEraser.IsOn = false;

// 恢复到之前的编辑状态
inkCanvas.EditingMode = currentEditingMode;
drawingShapeMode = currentDrawingShapeMode;
Expand Down Expand Up @@ -3635,6 +3640,13 @@
RestoreNonStrokeElements(preservedElements);
isInMultiTouchMode = false;

if (palmEraserWasEnabledBeforeMultiTouch)
{
Settings.Canvas.EnablePalmEraser = true;
if (ToggleSwitchEnablePalmEraser != null)
ToggleSwitchEnablePalmEraser.IsOn = true;
}

// 恢复到之前的编辑状态
inkCanvas.EditingMode = currentEditingMode;
drawingShapeMode = currentDrawingShapeMode;
Expand Down Expand Up @@ -5212,7 +5224,7 @@
MessageBoxImage.Warning);

Settings.Startup.UpdateChannel = oldChannel;
Dispatcher.BeginInvoke(new Action(() =>

Check warning on line 5227 in Ink Canvas/MainWindow_cs/MW_Settings.cs

View workflow job for this annotation

GitHub Actions / Build & Package

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
{
_isChangingUpdateChannelInternally = true;
try
Expand Down Expand Up @@ -5263,7 +5275,7 @@
else
{
Settings.Startup.UpdateChannel = oldChannel;
Dispatcher.BeginInvoke(new Action(() =>

Check warning on line 5278 in Ink Canvas/MainWindow_cs/MW_Settings.cs

View workflow job for this annotation

GitHub Actions / Build & Package

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
{
_isChangingUpdateChannelInternally = true;
try
Expand Down
224 changes: 185 additions & 39 deletions Ink Canvas/MainWindow_cs/MW_TouchEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ public partial class MainWindow : Window
/// 多点触控延迟时间(毫秒)
/// </summary>
private const double MULTI_TOUCH_DELAY_MS = 100;
private bool isMultiTouchTimerActive;
private bool isPalmEraserActive;
private bool palmEraserWasEnabledBeforeMultiTouch;

/// <summary>
/// 保存画布上的非笔画元素(如图片、媒体元素等)
Expand Down Expand Up @@ -227,6 +230,12 @@ private void BorderMultiTouchMode_MouseUp(object sender, MouseButtonEventArgs e)
RestoreNonStrokeElements(preservedElements);
isInMultiTouchMode = false;

if (palmEraserWasEnabledBeforeMultiTouch)
{
Settings.Canvas.EnablePalmEraser = true;
if (ToggleSwitchEnablePalmEraser != null)
ToggleSwitchEnablePalmEraser.IsOn = true;
}
}
else
{
Expand All @@ -247,6 +256,11 @@ private void BorderMultiTouchMode_MouseUp(object sender, MouseButtonEventArgs e)
// 恢复非笔画元素
RestoreNonStrokeElements(preservedElements);
isInMultiTouchMode = true;

palmEraserWasEnabledBeforeMultiTouch = Settings.Canvas.EnablePalmEraser;
Settings.Canvas.EnablePalmEraser = false;
if (ToggleSwitchEnablePalmEraser != null)
ToggleSwitchEnablePalmEraser.IsOn = false;
}
}

Expand Down Expand Up @@ -703,21 +717,13 @@ private void Main_Grid_TouchDown(object sender, TouchEventArgs e)
/// <param name="e">触摸事件参数</param>
/// <returns>返回触摸边界宽度</returns>
/// <remarks>
/// 根据触摸事件参数计算触摸边界宽度,包括以下逻辑:
/// 1. 获取触摸点的边界
/// 2. 如果不是四边红外屏幕,使用边界宽度
/// 3. 如果是四边红外屏幕,使用边界宽度和高度的平方根
/// 4. 如果是特殊屏幕,乘以触摸倍数
/// 5. 返回计算得到的触摸边界宽度
/// 手掌擦阈值与特殊屏 <c>TouchMultiplier</c> 在激活逻辑中单独参与计算,此处仅返回几何接触尺寸。
/// </remarks>
public double GetTouchBoundWidth(TouchEventArgs e)
{
var args = e.GetTouchPoint(null).Bounds;
double value;
if (!Settings.Advanced.IsQuadIR) value = args.Width;
else value = Math.Sqrt(args.Width * args.Height); //四边红外
if (Settings.Advanced.IsSpecialScreen) value *= Settings.Advanced.TouchMultiplier;
return value;
if (!Settings.Advanced.IsQuadIR) return args.Width;
return Math.Sqrt(args.Width * args.Height);
}

/// <summary>
Expand All @@ -741,28 +747,146 @@ public double GetTouchBoundWidth(TouchEventArgs e)
/// </remarks>
private void InkCanvas_PreviewTouchDown(object sender, TouchEventArgs e)
{
var touchPointForBar = e.GetTouchPoint(this);
var floatingBarBounds = ViewboxFloatingBar.TransformToAncestor(this).TransformBounds(
new Rect(0, 0, ViewboxFloatingBar.ActualWidth, ViewboxFloatingBar.ActualHeight));
if (floatingBarBounds.Contains(touchPointForBar.Position))
return;

if ((inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint
|| inkCanvas.EditingMode == InkCanvasEditingMode.EraseByStroke)
&& !isPalmEraserActive)
{
return;
}

if (drawingShapeMode != 0)
{
inkCanvas.EditingMode = InkCanvasEditingMode.None;
SetCursorBasedOnEditingMode(inkCanvas);
inkCanvas.CaptureTouch(e.TouchDevice);
ViewboxFloatingBar.IsHitTestVisible = false;
BlackboardUIGridForInkReplay.IsHitTestVisible = false;

isTouchDown = true;

if (dec.Count == 0)
{
var inkTouchPoint = e.GetTouchPoint(inkCanvas);
if (drawingShapeMode == 24 || drawingShapeMode == 25)
{
if (drawMultiStepShapeCurrentStep == 0)
iniP = inkTouchPoint.Position;
}
else
{
iniP = inkTouchPoint.Position;
}
lastTouchDownStrokeCollection = inkCanvas.Strokes.Clone();
}
dec.Add(e.TouchDevice.Id);
return;
}

SetCursorBasedOnEditingMode(inkCanvas);
inkCanvas.CaptureTouch(e.TouchDevice);
ViewboxFloatingBar.IsHitTestVisible = false;
BlackboardUIGridForInkReplay.IsHitTestVisible = false;

lastTouchDownTime = DateTime.Now;
dec.Add(e.TouchDevice.Id);
//设备1个的时候,记录中心点

if (Settings.Canvas.EnablePalmEraser && !isPalmEraserActive && drawingShapeMode == 0)
{
var touchPoint = e.GetTouchPoint(inkCanvas);
double boundWidth = GetTouchBoundWidth(e);

if ((Settings.Advanced.TouchMultiplier != 0 || !Settings.Advanced.IsSpecialScreen)
&& (boundWidth > BoundsWidth))
{
double thresholdMultiplier;
switch (Settings.Canvas.PalmEraserSensitivity)
{
case 0:
thresholdMultiplier = 3.0;
break;
case 1:
thresholdMultiplier = 2.5;
break;
case 2:
default:
thresholdMultiplier = 2.0;
break;
}

double EraserThresholdValue = Settings.Startup.IsEnableNibMode
? Settings.Advanced.NibModeBoundsWidthThresholdValue
: Settings.Advanced.FingerModeBoundsWidthThresholdValue;

if (boundWidth > BoundsWidth * EraserThresholdValue * thresholdMultiplier)
{
boundWidth *= Settings.Startup.IsEnableNibMode
? Settings.Advanced.NibModeBoundsWidthEraserSize
: Settings.Advanced.FingerModeBoundsWidthEraserSize;

if (Settings.Advanced.IsSpecialScreen)
boundWidth *= Settings.Advanced.TouchMultiplier;
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
isPalmEraserActive = true;

EnableEraserOverlay();
eraserWidth = boundWidth;
UpdateEraserStyle();
touchPoint = e.GetTouchPoint(inkCanvas);
EraserOverlay_PointerDown(sender);
EraserOverlay_PointerMove(sender, touchPoint.Position);
if (Settings.Canvas.IsShowCursor)
{
inkCanvas.ForceCursor = false;
inkCanvas.UseCustomCursor = false;
}
}
}
}

if (dec.Count == 1)
{
var touchPoint = e.GetTouchPoint(inkCanvas);
centerPoint = touchPoint.Position;

//记录第一根手指点击时的 StrokeCollection
lastTouchDownStrokeCollection = inkCanvas.Strokes.Clone();
}
//设备两个及两个以上,将画笔功能关闭

if (dec.Count > 1 || isSingleFingerDragMode || !Settings.Gesture.IsEnableTwoFingerGesture)
{
if (isInMultiTouchMode || !Settings.Gesture.IsEnableTwoFingerGesture) return;
if (inkCanvas.EditingMode == InkCanvasEditingMode.None ||
inkCanvas.EditingMode == InkCanvasEditingMode.Select) return;
var timeSinceLastTouch = (DateTime.Now - lastTouchDownTime).TotalMilliseconds;
if (timeSinceLastTouch < MULTI_TOUCH_DELAY_MS && inkCanvas.EditingMode == InkCanvasEditingMode.Ink)
{
if (!isMultiTouchTimerActive)
{
isMultiTouchTimerActive = true;
var remainingTime = MULTI_TOUCH_DELAY_MS - timeSinceLastTouch;
Task.Delay((int)remainingTime).ContinueWith(_ =>
{
Dispatcher.Invoke(() =>
{
if (dec.Count > 1 && inkCanvas.EditingMode == InkCanvasEditingMode.Ink)
inkCanvas.EditingMode = InkCanvasEditingMode.None;
isMultiTouchTimerActive = false;
});
});
}
return;
}

lastInkCanvasEditingMode = inkCanvas.EditingMode;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke
&& drawingShapeMode == 0)
{
inkCanvas.EditingMode = InkCanvasEditingMode.None;
}
}
}

Expand All @@ -776,6 +900,11 @@ private void InkCanvas_PreviewTouchDown(object sender, TouchEventArgs e)
/// </remarks>
private void InkCanvas_PreviewTouchMove(object sender, TouchEventArgs e)
{
if (isPalmEraserActive)
{
var touchPoint = e.GetTouchPoint(inkCanvas);
EraserOverlay_PointerMove(sender, touchPoint.Position);
}
}

/// <summary>
Expand All @@ -802,32 +931,18 @@ private void InkCanvas_PreviewTouchMove(object sender, TouchEventArgs e)
/// </remarks>
private void InkCanvas_PreviewTouchUp(object sender, TouchEventArgs e)
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint && !isPalmEraserActive)
{
return;
}
inkCanvas.ReleaseAllTouchCaptures();
ViewboxFloatingBar.IsHitTestVisible = true;
BlackboardUIGridForInkReplay.IsHitTestVisible = true;

//手势完成后切回之前的状态
if (dec.Count > 1)
if (inkCanvas.EditingMode == InkCanvasEditingMode.None)
inkCanvas.EditingMode = lastInkCanvasEditingMode;
dec.Remove(e.TouchDevice.Id);

if (dec.Count == 0)
{
isSingleFingerDragMode = false;
isWaitUntilNextTouchDown = false;
if (drawingShapeMode == 0
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke
&& inkCanvas.EditingMode != InkCanvasEditingMode.Select
&& inkCanvas.EditingMode != InkCanvasEditingMode.None)
{
if (lastInkCanvasEditingMode != InkCanvasEditingMode.None)
{
inkCanvas.EditingMode = lastInkCanvasEditingMode;
}
}
}
if (dec.Count <= 1)
isMultiTouchTimerActive = false;

if (drawingShapeMode != 0)
{
Expand All @@ -839,12 +954,10 @@ private void InkCanvas_PreviewTouchUp(object sender, TouchEventArgs e)
{
if (drawMultiStepShapeCurrentStep == 0)
{
// 第一笔完成,进入第二笔
drawMultiStepShapeCurrentStep = 1;
}
else
{
// 第二笔完成,完成绘制
var mouseArgs = new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left)
{
RoutedEvent = MouseLeftButtonUpEvent,
Expand All @@ -864,6 +977,36 @@ private void InkCanvas_PreviewTouchUp(object sender, TouchEventArgs e)
}
}

if (drawingShapeMode == 0)
{
if (dec.Count > 1)
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.None)
{
if (lastInkCanvasEditingMode != InkCanvasEditingMode.EraseByPoint)
inkCanvas.EditingMode = lastInkCanvasEditingMode;
}
}
else if (dec.Count == 0)
{
isSingleFingerDragMode = false;
isWaitUntilNextTouchDown = false;

if (inkCanvas.EditingMode == InkCanvasEditingMode.None &&
lastInkCanvasEditingMode != InkCanvasEditingMode.None &&
lastInkCanvasEditingMode != InkCanvasEditingMode.EraseByPoint)
{
inkCanvas.EditingMode = lastInkCanvasEditingMode;
}

if (isPalmEraserActive)
{
isPalmEraserActive = false;
Comment on lines +1002 to +1004
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore previous editing mode when palm erasing ends

When the last touch is released, this block only clears isPalmEraserActive and never restores inkCanvas.EditingMode from EraseByPoint. Because PreviewTouchDown now returns early whenever mode is EraseByPoint and isPalmEraserActive is false, touch input can get stuck after one palm-erase gesture (no further touch inking/gesture handling until another path changes the mode). Persist and restore the pre-palm mode (typically Ink) when deactivating palm eraser.

Useful? React with 👍 / 👎.

DisableEraserOverlay();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Commit palm eraser history before disabling overlay

The palm-eraser teardown calls DisableEraserOverlay() directly, which skips the EraserOverlay_PointerUp path that commits ReplacedStroke/AddedStroke to timeMachine in MW_Eraser.cs. In sessions where palm erase actually removes strokes, those erases are not recorded as a discrete undo/redo operation, so undo behavior becomes inconsistent compared with normal eraser input.

Useful? React with 👍 / 👎.

}
}
}

inkCanvas.Opacity = 1;

if (dec.Count == 0)
Expand Down Expand Up @@ -961,6 +1104,9 @@ private void Main_Grid_ManipulationCompleted(object sender, ManipulationComplete
/// </remarks>
private void Main_Grid_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
return;

if (isInMultiTouchMode || !Settings.Gesture.IsEnableTwoFingerGesture) return;

bool hasMultipleManipulators = e.Manipulators.Count() >= 2;
Expand Down
Loading