Merge pull request #316 from InkCanvasForClass/beta

ICC CE 1.7.18.1
This commit is contained in:
CJK_mkp
2025-12-13 21:03:41 +08:00
committed by GitHub
16 changed files with 1184 additions and 1342 deletions
+20 -11
View File
@@ -12,6 +12,7 @@ on:
- patch
- minor
- major
- build
prerelease:
description: 'Create as pre-release'
required: true
@@ -51,7 +52,7 @@ jobs:
echo "Found latest tag: $latestTag"
} else {
# 如果没有tag,使用默认版本
$version = "1.0.0"
$version = "1.0.0.0"
echo "No tags found, using default version"
}
echo "current_version=$version" >> $env:GITHUB_OUTPUT
@@ -63,16 +64,18 @@ jobs:
$currentVersion = "${{ steps.get_version.outputs.current_version }}"
$versionParts = $currentVersion.Split('.')
# 确保版本号格式正确(至少3部分)
# 确保版本号格式正确(支持4部分)
if ($versionParts.Length -ge 3) {
$major = [int]$versionParts[0]
$minor = [int]$versionParts[1]
$patch = [int]$versionParts[2]
$build = [int]$versionParts[3]
} else {
# 如果版本号格式不正确,使用默认值
$major = 1
$minor = 0
$patch = 0
$build = 0
}
$versionType = "${{ github.event.inputs.version_type }}"
@@ -82,18 +85,24 @@ jobs:
$major++
$minor = 0
$patch = 0
$build = 0
}
"minor" {
$minor++
$patch = 0
$build = 0
}
"patch" {
$patch++
$build = 0
}
"build" {
$build++
}
}
# 生成新版本号(保持3位格式,如1.7.13
$newVersion = "$major.$minor.$patch"
# 生成新版本号(4位格式,如1.7.18.0
$newVersion = "$major.$minor.$patch.$build"
echo "new_version=$newVersion" >> $env:GITHUB_OUTPUT
echo "New version: $newVersion"
@@ -150,7 +159,7 @@ jobs:
# 生成changelog内容
$version = "${{ steps.calc_version.outputs.new_version }}"
$changelog = "# ICC CE $version.0 更新日志`n`n## 修复 (Fixes)"
$changelog = "# ICC CE $version 更新日志`n`n## 修复 (Fixes)"
if ($fixes.Count -gt 0) {
foreach ($fix in $fixes) {
@@ -236,7 +245,7 @@ jobs:
- name: Create Release Archive
run: |
$version = "${{ steps.calc_version.outputs.new_version }}"
$archiveName = "InkCanvasForClass.CE.$version.0.zip"
$archiveName = "InkCanvasForClass.CE.$version.zip"
# 创建发布目录
New-Item -ItemType Directory -Path "release" -Force
@@ -254,7 +263,7 @@ jobs:
with:
name: release-files-${{ steps.calc_version.outputs.new_version }}
path: |
InkCanvasForClass.CE.${{ steps.calc_version.outputs.new_version }}.0.zip
InkCanvasForClass.CE.${{ steps.calc_version.outputs.new_version }}.zip
CHANGELOG_${{ steps.calc_version.outputs.new_version }}.md
- name: Prepare Release Info
@@ -265,14 +274,14 @@ jobs:
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.calc_version.outputs.new_version }}.0
name: ICC CE ${{ steps.calc_version.outputs.new_version }}.0
tag_name: ${{ steps.calc_version.outputs.new_version }}
name: ICC CE ${{ steps.calc_version.outputs.new_version }}
body: |
${{ steps.changelog.outputs.changelog }}
draft: false
prerelease: ${{ github.event.inputs.prerelease }}
files: |
InkCanvasForClass.CE.${{ steps.calc_version.outputs.new_version }}.0.zip
InkCanvasForClass.CE.${{ steps.calc_version.outputs.new_version }}.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -285,7 +294,7 @@ jobs:
$changelogContent = Get-Content $changelogFile -Raw
# 生成预览内容
$previewContent = "ICC CE $version.0 更新日志`n" + $changelogContent
$previewContent = "ICC CE $version 更新日志`n" + $changelogContent
echo "UpdateLog preview generated (not written to file):"
echo $previewContent
+15 -2
View File
@@ -273,7 +273,9 @@ namespace Ink_Canvas.Helpers
/// <summary>
/// 保存所有墨迹到文件
/// </summary>
public void SaveAllStrokesToFile(Presentation presentation)
/// <param name="presentation">演示文稿对象</param>
/// <param name="currentSlideIndex">当前播放的页码,如果提供则使用此值保存位置,否则使用_lockedSlideIndex</param>
public void SaveAllStrokesToFile(Presentation presentation, int currentSlideIndex = -1)
{
if (!IsAutoSaveEnabled || string.IsNullOrEmpty(AutoSaveLocation) || presentation == null) return;
@@ -290,7 +292,18 @@ namespace Ink_Canvas.Helpers
// 保存位置信息
try
{
File.WriteAllText(Path.Combine(folderPath, "Position"), _lockedSlideIndex.ToString());
// 优先使用传入的当前页码,否则使用锁定的页码
int positionToSave = currentSlideIndex > 0 ? currentSlideIndex : _lockedSlideIndex;
// 如果都没有有效值,尝试使用最后切换的页码
if (positionToSave <= 0 && _lastSwitchSlideIndex > 0)
{
positionToSave = _lastSwitchSlideIndex;
}
if (positionToSave > 0)
{
File.WriteAllText(Path.Combine(folderPath, "Position"), positionToSave.ToString());
}
}
catch (Exception ex)
{
+9 -3
View File
@@ -12,7 +12,6 @@
AllowsTransparency="True"
WindowStyle="None"
ResizeMode="NoResize"
WindowState="Maximized"
Loaded="Window_Loaded"
Background="Transparent"
ShowInTaskbar="False"
@@ -605,6 +604,13 @@
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchNoFocusMode_Toggled" />
</ui:SimpleStackPanel>
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="窗口无边框模式" VerticalAlignment="Center"
FontSize="14" Margin="0,0,16,0" />
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchWindowMode"
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchWindowMode_Toggled" />
</ui:SimpleStackPanel>
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="窗口置顶" VerticalAlignment="Center"
FontSize="14" Margin="0,0,16,0" />
@@ -3214,14 +3220,14 @@
</ui:SimpleStackPanel>
<TextBlock Text="# 开启后,退出收纳模式时将自动切换至批注模式,便于快速批注" TextWrapping="Wrap" Foreground="#a1a1aa" />
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="退出PPT放映后自动恢复浮动栏状态"
<TextBlock Foreground="#fafafa" Text="退出PPT放映后自动收纳浮动栏"
VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
<ui:ToggleSwitch OnContent="" OffContent=""
Name="ToggleSwitchAutoFoldAfterPPTSlideShow"
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchAutoFoldAfterPPTSlideShow_Toggled" />
</ui:SimpleStackPanel>
<TextBlock Text="# 开启后,如果进入PPT放映前为收纳模式则退出后也为收纳模式,如果进入前不是收纳模式则退出后也不是收纳模式" TextWrapping="Wrap" Foreground="#a1a1aa" />
<TextBlock Text="# 开启后,退出PPT放映后会自动收纳浮动栏" TextWrapping="Wrap" Foreground="#a1a1aa" />
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="退出白板时自动收纳"
VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
+19 -1
View File
@@ -514,6 +514,9 @@ namespace Ink_Canvas
// 加载自定义背景颜色
LoadCustomBackgroundColor();
// 设置窗口模式
SetWindowMode();
// 注册设置面板滚动事件
if (SettingsPanelScrollViewer != null)
{
@@ -769,6 +772,22 @@ namespace Ink_Canvas
}
}
private void SetWindowMode()
{
if (Settings.Advanced.WindowMode)
{
WindowState = WindowState.Normal;
Left = 0.0;
Top = 0.0;
Height = SystemParameters.PrimaryScreenHeight;
Width = SystemParameters.PrimaryScreenWidth;
}
else // 全屏
{
WindowState = WindowState.Maximized;
}
}
private void Window_Closing(object sender, CancelEventArgs e)
{
LogHelper.WriteLogToFile("Ink Canvas closing", LogHelper.LogType.Event);
@@ -3003,7 +3022,6 @@ namespace Ink_Canvas
else if (!isInSlideShow && IsVisible)
{
Hide();
LogHelper.WriteLogToFile("PPT放映结束,隐藏主窗口(仅PPT模式)", LogHelper.LogType.Trace);
}
}
else
@@ -206,9 +206,6 @@ namespace Ink_Canvas
// 检查是否保存了多指书写模式状态
if (savedMultiTouchModeStates[pageIndex])
{
// 恢复多指书写模式
EnterMultiTouchModeIfNeeded();
// 更新UI状态
if (ToggleSwitchEnableMultiTouchMode != null)
{
@@ -219,9 +216,6 @@ namespace Ink_Canvas
}
else
{
// 确保多指书写模式关闭
ExitMultiTouchModeIfNeeded();
// 更新UI状态
if (ToggleSwitchEnableMultiTouchMode != null)
{
+164 -88
View File
@@ -73,80 +73,67 @@ namespace Ink_Canvas
/// <summary>
/// 用於更新浮動工具欄的"手勢"按鈕和白板工具欄的"手勢"按鈕的樣式(開啟和關閉狀態)
/// </summary>
private void CheckEnableTwoFingerGestureBtnColorPrompt()
{
if (ToggleSwitchEnableMultiTouchMode.IsOn)
{
// 多指书写模式启用时,手势功能被禁用
private void CheckEnableTwoFingerGestureBtnColorPrompt() {
// 根据主题选择手势图标和颜色
bool isDarkTheme = Settings.Appearance.Theme == 1 ||
(Settings.Appearance.Theme == 2 && !IsSystemThemeLight());
bool isLightTheme = !isDarkTheme;
string gestureIconPath = isLightTheme ? "/Resources/new-icons/gesture.png" : "/Resources/new-icons/gesture_white.png";
// 根据主题设置白板模式下的颜色
Color boardBgColor, boardIconColor, boardTextColor, boardBorderColor;
if (isLightTheme) {
// 浅色主题
boardBgColor = Color.FromRgb(244, 244, 245);
boardIconColor = Color.FromRgb(24, 24, 27);
boardTextColor = Color.FromRgb(24, 24, 27);
boardBorderColor = Color.FromRgb(161, 161, 170);
} else {
// 深色主题
boardBgColor = Color.FromRgb(39, 39, 42);
boardIconColor = Color.FromRgb(244, 244, 245);
boardTextColor = Color.FromRgb(244, 244, 245);
boardBorderColor = Color.FromRgb(113, 113, 122);
}
if (ToggleSwitchEnableMultiTouchMode.IsOn) {
TwoFingerGestureSimpleStackPanel.Opacity = 0.5;
TwoFingerGestureSimpleStackPanel.IsHitTestVisible = false;
EnableTwoFingerGestureBtn.Source = (BitmapImage)Application.Current.FindResource("GestureIcon");
// 根据主题设置颜色
if (Settings.Appearance.Theme == 1) // 深色主题
{
BoardGesture.Background = new SolidColorBrush(Color.FromRgb(42, 42, 42));
BoardGestureGeometry.Brush = new SolidColorBrush(Color.FromRgb(255, 255, 255));
BoardGestureGeometry2.Brush = new SolidColorBrush(Color.FromRgb(255, 255, 255));
BoardGestureLabel.Foreground = new SolidColorBrush(Color.FromRgb(255, 255, 255));
BoardGesture.BorderBrush = new SolidColorBrush(Color.FromRgb(85, 85, 85));
}
else // 浅色主题或跟随系统
{
BoardGesture.Background = new SolidColorBrush(Color.FromRgb(244, 244, 245));
BoardGestureGeometry.Brush = new SolidColorBrush(Color.FromRgb(24, 24, 27));
BoardGestureGeometry2.Brush = new SolidColorBrush(Color.FromRgb(24, 24, 27));
BoardGestureLabel.Foreground = new SolidColorBrush(Color.FromRgb(24, 24, 27));
BoardGesture.BorderBrush = new SolidColorBrush(Color.FromRgb(161, 161, 170));
}
EnableTwoFingerGestureBtn.Source =
new BitmapImage(new Uri(gestureIconPath, UriKind.Relative));
BoardGesture.Background = new SolidColorBrush(boardBgColor);
BoardGestureGeometry.Brush = new SolidColorBrush(boardIconColor);
BoardGestureGeometry2.Brush = new SolidColorBrush(boardIconColor);
BoardGestureLabel.Foreground = new SolidColorBrush(boardTextColor);
BoardGesture.BorderBrush = new SolidColorBrush(boardBorderColor);
BoardGestureGeometry.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.DisabledGestureIcon);
BoardGestureGeometry2.Geometry = Geometry.Parse("F0 M24,24z M0,0z");
// 强制禁用所有双指手势功能
ForceDisableTwoFingerGestures();
}
else
{
// 多指书写模式禁用时,根据实际手势功能状态显示
else {
TwoFingerGestureSimpleStackPanel.Opacity = 1;
TwoFingerGestureSimpleStackPanel.IsHitTestVisible = true;
// 检查是否有任何手势功能启用
bool hasGestureEnabled = Settings.Gesture.IsEnableTwoFingerGesture;
if (hasGestureEnabled)
{
EnableTwoFingerGestureBtn.Source = (BitmapImage)Application.Current.FindResource("GestureIconEnabled");
if (Settings.Gesture.IsEnableTwoFingerGesture) {
EnableTwoFingerGestureBtn.Source =
new BitmapImage(new Uri("/Resources/new-icons/gesture-enabled.png", UriKind.Relative));
BoardGesture.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235));
BoardGestureGeometry.Brush = new SolidColorBrush(Colors.GhostWhite);
BoardGestureGeometry2.Brush = new SolidColorBrush(Colors.GhostWhite);
BoardGestureLabel.Foreground = new SolidColorBrush(Colors.GhostWhite);
BoardGesture.BorderBrush = new SolidColorBrush(Color.FromRgb(37, 99, 235));
BoardGestureGeometry.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.EnabledGestureIcon);
BoardGestureGeometry2.Geometry = Geometry.Parse("F0 M24,24z M0,0z " + XamlGraphicsIconGeometries.EnabledGestureIconBadgeCheck);
BoardGestureGeometry2.Geometry = Geometry.Parse("F0 M24,24z M0,0z "+XamlGraphicsIconGeometries.EnabledGestureIconBadgeCheck);
}
else
{
EnableTwoFingerGestureBtn.Source = (BitmapImage)Application.Current.FindResource("GestureIcon");
// 根据主题设置颜色
if (Settings.Appearance.Theme == 1) // 深色主题
{
BoardGesture.Background = new SolidColorBrush(Color.FromRgb(42, 42, 42));
BoardGestureGeometry.Brush = new SolidColorBrush(Color.FromRgb(255, 255, 255));
BoardGestureGeometry2.Brush = new SolidColorBrush(Color.FromRgb(255, 255, 255));
BoardGestureLabel.Foreground = new SolidColorBrush(Color.FromRgb(255, 255, 255));
BoardGesture.BorderBrush = new SolidColorBrush(Color.FromRgb(85, 85, 85));
}
else // 浅色主题或跟随系统
{
BoardGesture.Background = new SolidColorBrush(Color.FromRgb(244, 244, 245));
BoardGestureGeometry.Brush = new SolidColorBrush(Color.FromRgb(24, 24, 27));
BoardGestureGeometry2.Brush = new SolidColorBrush(Color.FromRgb(24, 24, 27));
BoardGestureLabel.Foreground = new SolidColorBrush(Color.FromRgb(24, 24, 27));
BoardGesture.BorderBrush = new SolidColorBrush(Color.FromRgb(161, 161, 170));
}
else {
EnableTwoFingerGestureBtn.Source =
new BitmapImage(new Uri(gestureIconPath, UriKind.Relative));
BoardGesture.Background = new SolidColorBrush(boardBgColor);
BoardGestureGeometry.Brush = new SolidColorBrush(boardIconColor);
BoardGestureGeometry2.Brush = new SolidColorBrush(boardIconColor);
BoardGestureLabel.Foreground = new SolidColorBrush(boardTextColor);
BoardGesture.BorderBrush = new SolidColorBrush(boardBorderColor);
BoardGestureGeometry.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.DisabledGestureIcon);
BoardGestureGeometry2.Geometry = Geometry.Parse("F0 M24,24z M0,0z");
}
@@ -486,13 +473,23 @@ namespace Ink_Canvas
highlightColor = Color.FromRgb(30, 58, 138); // Keep current color for light theme
}
bool useLegacyUI = Settings.Appearance.UseLegacyFloatingBarUI;
switch (mode)
{
case "pen":
case "color":
{
PenIconGeometry.Brush = new SolidColorBrush(highlightColor);
PenIconGeometry.Geometry = Geometry.Parse(GetCorrectIcon("pen", true));
if (useLegacyUI)
{
PenIconGeometry.Brush = new SolidColorBrush(highlightColor);
PenIconGeometry.Geometry = Geometry.Parse(GetCorrectIcon("pen", false));
}
else
{
PenIconGeometry.Brush = new SolidColorBrush(highlightColor);
PenIconGeometry.Geometry = Geometry.Parse(GetCorrectIcon("pen", true));
}
BoardPen.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235));
BoardPen.BorderBrush = new SolidColorBrush(Color.FromRgb(37, 99, 235));
BoardPenGeometry.Brush = new SolidColorBrush(Colors.GhostWhite);
@@ -503,9 +500,18 @@ namespace Ink_Canvas
}
case "eraser":
{
CircleEraserIconGeometry.Brush = new SolidColorBrush(highlightColor);
CircleEraserIconGeometry.Geometry =
Geometry.Parse(GetCorrectIcon("eraserCircle", true));
if (useLegacyUI)
{
CircleEraserIconGeometry.Brush = new SolidColorBrush(highlightColor);
CircleEraserIconGeometry.Geometry =
Geometry.Parse(GetCorrectIcon("eraserCircle", false));
}
else
{
CircleEraserIconGeometry.Brush = new SolidColorBrush(highlightColor);
CircleEraserIconGeometry.Geometry =
Geometry.Parse(GetCorrectIcon("eraserCircle", true));
}
BoardEraser.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235));
BoardEraser.BorderBrush = new SolidColorBrush(Color.FromRgb(37, 99, 235));
BoardEraserGeometry.Brush = new SolidColorBrush(Colors.GhostWhite);
@@ -516,9 +522,18 @@ namespace Ink_Canvas
}
case "eraserByStrokes":
{
StrokeEraserIconGeometry.Brush = new SolidColorBrush(highlightColor);
StrokeEraserIconGeometry.Geometry =
Geometry.Parse(GetCorrectIcon("eraserStroke", true));
if (useLegacyUI)
{
StrokeEraserIconGeometry.Brush = new SolidColorBrush(highlightColor);
StrokeEraserIconGeometry.Geometry =
Geometry.Parse(GetCorrectIcon("eraserStroke", false));
}
else
{
StrokeEraserIconGeometry.Brush = new SolidColorBrush(highlightColor);
StrokeEraserIconGeometry.Geometry =
Geometry.Parse(GetCorrectIcon("eraserStroke", true));
}
BoardEraser.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235));
BoardEraser.BorderBrush = new SolidColorBrush(Color.FromRgb(37, 99, 235));
BoardEraserGeometry.Brush = new SolidColorBrush(Colors.GhostWhite);
@@ -529,9 +544,18 @@ namespace Ink_Canvas
}
case "select":
{
LassoSelectIconGeometry.Brush = new SolidColorBrush(highlightColor);
LassoSelectIconGeometry.Geometry =
Geometry.Parse(GetCorrectIcon("lassoSelect", true));
if (useLegacyUI)
{
LassoSelectIconGeometry.Brush = new SolidColorBrush(highlightColor);
LassoSelectIconGeometry.Geometry =
Geometry.Parse(GetCorrectIcon("lassoSelect", false));
}
else
{
LassoSelectIconGeometry.Brush = new SolidColorBrush(highlightColor);
LassoSelectIconGeometry.Geometry =
Geometry.Parse(GetCorrectIcon("lassoSelect", true));
}
BoardSelect.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235));
BoardSelect.BorderBrush = new SolidColorBrush(Color.FromRgb(37, 99, 235));
BoardSelectGeometry.Brush = new SolidColorBrush(Colors.GhostWhite);
@@ -542,9 +566,18 @@ namespace Ink_Canvas
}
case "cursor":
{
CursorIconGeometry.Brush = new SolidColorBrush(highlightColor);
CursorIconGeometry.Geometry =
Geometry.Parse(GetCorrectIcon("cursor", true));
if (useLegacyUI)
{
CursorIconGeometry.Brush = new SolidColorBrush(highlightColor);
CursorIconGeometry.Geometry =
Geometry.Parse(GetCorrectIcon("cursor", false));
}
else
{
CursorIconGeometry.Brush = new SolidColorBrush(highlightColor);
CursorIconGeometry.Geometry =
Geometry.Parse(GetCorrectIcon("cursor", true));
}
// 根据主题设置颜色
if (Settings.Appearance.Theme == 1) // 深色主题
{
@@ -1055,6 +1088,8 @@ namespace Ink_Canvas
{
if (TimerContainer != null && TimerControl != null)
{
// 每次打开计时器窗口时重置计时器
TimerControl.ResetTimerState();
TimerContainer.Visibility = Visibility.Visible;
if (MinimizedTimerContainer != null)
{
@@ -1823,7 +1858,7 @@ namespace Ink_Canvas
});
}
public async void PureViewboxFloatingBarMarginAnimationInPPTMode()
public async void PureViewboxFloatingBarMarginAnimationInPPTMode(bool isRetry = false)
{
// 新增:在白板模式下不执行浮动栏动画
if (currentMode == 1)
@@ -1919,6 +1954,25 @@ namespace Ink_Canvas
{
ViewboxFloatingBar.Margin = new Thickness(pos.X, pos.Y, -2000, -200);
});
if (Settings.ModeSettings.IsPPTOnlyMode && !isRetry)
{
await Task.Delay(2000); // 等待动画完成后再检查
bool isFloatingBarVisible = false;
await Dispatcher.InvokeAsync(() =>
{
// 检查浮动栏是否真的显示了
isFloatingBarVisible = ViewboxFloatingBar.Visibility == Visibility.Visible &&
ViewboxFloatingBar.Margin.Left >= 0 &&
ViewboxFloatingBar.Margin.Top >= 0;
});
if (!isFloatingBarVisible)
{
PureViewboxFloatingBarMarginAnimationInPPTMode(true);
}
}
}
internal async void CursorIcon_Click(object sender, RoutedEventArgs e)
@@ -2062,7 +2116,6 @@ namespace Ink_Canvas
{
drawingShapeMode = 0;
isLongPressSelected = false;
ResetAllShapeButtonsOpacity();
}
// 使用集中化的工具模式切换方法
@@ -2270,7 +2323,6 @@ namespace Ink_Canvas
internal void EraserIcon_Click(object sender, RoutedEventArgs e)
{
EnterMultiTouchModeIfNeeded();
bool isAlreadyEraser = inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint;
forceEraser = false;
forcePointEraser = true;
@@ -2322,7 +2374,6 @@ namespace Ink_Canvas
private void BoardEraserIcon_Click(object sender, RoutedEventArgs e)
{
EnterMultiTouchModeIfNeeded();
bool isAlreadyEraser = inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint;
forceEraser = false;
forcePointEraser = true;
@@ -2361,7 +2412,6 @@ namespace Ink_Canvas
private void EraserIconByStrokes_Click(object sender, RoutedEventArgs e)
{
EnterMultiTouchModeIfNeeded();
if (lastBorderMouseDownObject != null && lastBorderMouseDownObject is Panel)
((Panel)lastBorderMouseDownObject).Background = new SolidColorBrush(Colors.Transparent);
@@ -2914,12 +2964,6 @@ namespace Ink_Canvas
// 清空触摸点计数器
dec.Clear();
// 重置手掌擦状态
if (isPalmEraserActive)
{
isPalmEraserActive = false;
}
// 确保触摸事件能正常响应
inkCanvas.IsHitTestVisible = true;
inkCanvas.IsManipulationEnabled = true;
@@ -3017,10 +3061,26 @@ namespace Ink_Canvas
ClearStrokes(true);
RestoreStrokes(true);
// 新增:在屏幕模式下恢复基础浮动栏的显示
// 退出白板模式时取消全屏
if (Settings.Advanced.IsEnableAvoidFullScreenHelper)
{
// 恢复为非画板模式,重新启用全屏限制
AvoidFullScreenHelper.SetBoardMode(false);
Dispatcher.BeginInvoke(new Action(() =>
{
// 退出白板模式,恢复到工作区域大小
var workingArea = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea;
MainWindow.MoveWindow(new WindowInteropHelper(this).Handle,
workingArea.Left, workingArea.Top,
workingArea.Width, workingArea.Height, true);
}), DispatcherPriority.ApplicationIdle);
}
// 在屏幕模式下恢复基础浮动栏的显示
ViewboxFloatingBar.Visibility = Visibility.Visible;
// 新增:退出白板时自动收纳功能 - 等待浮动栏完全展开后再收纳
// 退出白板时自动收纳功能 - 等待浮动栏完全展开后再收纳
if (Settings.Automation.IsAutoFoldWhenExitWhiteboard && !isFloatingBarFolded)
{
// 使用异步延迟,等待浮动栏展开动画完成后再收纳
@@ -3029,7 +3089,10 @@ namespace Ink_Canvas
await Task.Delay(700);
await Dispatcher.InvokeAsync(() =>
{
FoldFloatingBar_MouseUp(new object(), null);
if (_pptManager?.IsInSlideShow != true)
{
FoldFloatingBar_MouseUp(new object(), null);
}
});
});
}
@@ -3070,6 +3133,19 @@ namespace Ink_Canvas
RestoreStrokes();
// 进入白板模式时全屏
if (Settings.Advanced.IsEnableAvoidFullScreenHelper)
{
// 设置为画板模式,允许全屏操作
AvoidFullScreenHelper.SetBoardMode(true);
Dispatcher.BeginInvoke(new Action(() =>
{
MainWindow.MoveWindow(new WindowInteropHelper(this).Handle, 0, 0,
System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width,
System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height, true);
}), DispatcherPriority.ApplicationIdle);
}
ViewboxFloatingBar.Visibility = Visibility.Collapsed;
BtnSwitch.Content = "屏幕";
+288 -203
View File
@@ -77,7 +77,6 @@ namespace Ink_Canvas
#endregion
#region PPT State Management
private bool wasFloatingBarFoldedWhenEnterSlideShow;
private bool isEnteredSlideShowEndEvent;
private bool isPresentationHaveBlackSpace;
@@ -94,12 +93,16 @@ namespace Ink_Canvas
// 上次播放位置相关字段
private int _lastPlaybackPage = 0;
private bool _shouldNavigateToLastPage = false;
// 当前播放页码跟踪
private int _currentSlideShowPosition = 0;
// 页面切换防抖机制
private DateTime _lastSlideSwitchTime = DateTime.MinValue;
private int _pendingSlideIndex = -1;
private System.Timers.Timer _slideSwitchDebounceTimer;
private const int SlideSwitchDebounceMs = 150; // 防抖延迟150毫秒
private const int SlideSwitchDebounceMs = 150;
private bool _isInkClearedByButton = false;
#endregion
#region PPT Managers
@@ -612,9 +615,6 @@ namespace Ink_Canvas
{
try
{
// 始终记录进入放映时浮动栏收纳状态,用于退出时恢复
wasFloatingBarFoldedWhenEnterSlideShow = isFloatingBarFolded;
if (Settings.Automation.IsAutoFoldInPPTSlideShow)
{
if (!isFloatingBarFolded)
@@ -641,12 +641,16 @@ namespace Ink_Canvas
activePresentation = wn.Presentation;
currentSlide = wn.View.CurrentShowPosition;
totalSlides = activePresentation.Slides.Count;
// 初始化当前播放页码跟踪
_currentSlideShowPosition = currentSlide;
}
else
{
activePresentation = _pptManager?.GetCurrentActivePresentation();
currentSlide = _pptManager?.GetCurrentSlideNumber() ?? 0;
totalSlides = _pptManager?.SlidesCount ?? 0;
// 初始化当前播放页码跟踪
_currentSlideShowPosition = currentSlide;
}
if (activePresentation != null)
@@ -765,19 +769,50 @@ namespace Ink_Canvas
if (!isFloatingBarFolded)
{
new Thread(() =>
_ = Task.Run(async () =>
{
Thread.Sleep(100);
Application.Current.Dispatcher.Invoke(() =>
try
{
ViewboxFloatingBarMarginAnimation(60);
});
}).Start();
await Task.Delay(100);
await Application.Current.Dispatcher.InvokeAsync(() =>
{
ViewboxFloatingBar.UpdateLayout();
// 如果浮动栏宽度仍未计算好,再等待一段时间
if (ViewboxFloatingBar.ActualWidth <= 0)
{
LogHelper.WriteLogToFile("浮动栏宽度未准备好,等待布局完成", LogHelper.LogType.Trace);
}
});
await Task.Delay(100);
await Application.Current.Dispatcher.InvokeAsync(() =>
{
PureViewboxFloatingBarMarginAnimationInPPTMode(false);
});
}
catch (Exception)
{
try
{
await Task.Delay(100);
await Application.Current.Dispatcher.InvokeAsync(() =>
{
ViewboxFloatingBarMarginAnimation(60);
});
}
catch (Exception)
{
}
}
});
}
}
catch (Exception ex)
catch (Exception)
{
LogHelper.WriteLogToFile($"处理幻灯片放映开始事件失败: {ex}", LogHelper.LogType.Error);
}
}
@@ -785,7 +820,7 @@ namespace Ink_Canvas
{
try
{
Application.Current.Dispatcher.InvokeAsync(() =>
Application.Current.Dispatcher.Invoke(() =>
{
if (wn?.View == null || wn.Presentation == null)
{
@@ -796,8 +831,58 @@ namespace Ink_Canvas
var activePresentation = wn.Presentation;
var totalSlides = activePresentation.Slides.Count;
// 使用防抖机制处理页面切换
HandleSlideSwitchWithDebounce(currentSlide, totalSlides);
// 获取之前的页码(用于保存墨迹)
var previousSlide = _currentSlideShowPosition > 0 ? _currentSlideShowPosition :
(_pptManager?.GetCurrentSlideNumber() ?? 0);
if (_isInkClearedByButton)
{
_isInkClearedByButton = false;
}
else
{
StrokeCollection strokesToSave = null;
if (previousSlide > 0 && previousSlide != currentSlide && inkCanvas.Strokes.Count > 0)
{
strokesToSave = inkCanvas.Strokes.Clone();
}
// 清除墨迹
if (inkCanvas.Strokes.Count > 0)
{
ClearStrokes(true);
timeMachine.ClearStrokeHistory();
}
// 异步保存之前页面的墨迹
if (strokesToSave != null && previousSlide > 0 && previousSlide != currentSlide)
{
Task.Run(() =>
{
try
{
Application.Current.Dispatcher.Invoke(() =>
{
bool canWrite = _singlePPTInkManager?.CanWriteInk(previousSlide) == true;
if (canWrite)
{
_singlePPTInkManager?.SaveCurrentSlideStrokes(previousSlide, strokesToSave);
}
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"异步保存PPT页面墨迹失败: {ex}", LogHelper.LogType.Error);
}
});
}
}
// 更新当前播放页码
_currentSlideShowPosition = currentSlide;
LoadCurrentSlideInk(currentSlide, skipClear: true);
_pptUIManager?.UpdateCurrentSlideNumber(currentSlide, totalSlides);
});
}
@@ -811,39 +896,39 @@ namespace Ink_Canvas
{
try
{
if (Settings.Automation.IsAutoFoldAfterPPTSlideShow)
// PPT退出时自动收纳浮动栏
if (!isFloatingBarFolded)
{
if (wasFloatingBarFoldedWhenEnterSlideShow)
{
if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(new object(), null);
}
else
{
if (isFloatingBarFolded) await UnFoldFloatingBar(new object());
}
}
else
{
if (Settings.Automation.IsAutoFoldInPPTSlideShow)
{
if (isFloatingBarFolded)
{
await UnFoldFloatingBar(new object());
}
}
else
{
if (isFloatingBarFolded)
{
await UnFoldFloatingBar(new object());
}
}
FoldFloatingBar_MouseUp(new object(), null);
}
if (isEnteredSlideShowEndEvent) return;
isEnteredSlideShowEndEvent = true;
_singlePPTInkManager?.SaveAllStrokesToFile(pres);
// 获取当前播放页码,优先使用跟踪的页码,否则尝试从PPT管理器获取
int currentPage = _currentSlideShowPosition;
if (currentPage <= 0)
{
try
{
currentPage = _pptManager?.GetCurrentSlideNumber() ?? 0;
}
catch
{
// 如果无法获取,尝试从演示文稿的SlideShowWindow获取
try
{
if (pres.SlideShowWindow != null && pres.SlideShowWindow.View != null)
{
currentPage = pres.SlideShowWindow.View.CurrentShowPosition;
}
}
catch { }
}
}
// 保存墨迹和位置信息
_singlePPTInkManager?.SaveAllStrokesToFile(pres, currentPage);
await Application.Current.Dispatcher.InvokeAsync(() =>
{
@@ -917,7 +1002,7 @@ namespace Ink_Canvas
await Application.Current.Dispatcher.InvokeAsync(() =>
{
PureViewboxFloatingBarMarginAnimationInDesktopMode();
ViewboxFloatingBarMarginAnimation(100, true);
ViewboxFloatingBarMarginAnimation(-60);
});
}
catch (Exception ex)
@@ -1098,13 +1183,16 @@ namespace Ink_Canvas
}
}
private void LoadCurrentSlideInk(int slideIndex)
private void LoadCurrentSlideInk(int slideIndex, bool skipClear = false)
{
try
{
ClearStrokes(true);
timeMachine.ClearStrokeHistory();
// 如果未跳过清除,则清除当前墨迹
if (!skipClear)
{
ClearStrokes(true);
timeMachine.ClearStrokeHistory();
}
StrokeCollection strokes = _singlePPTInkManager?.LoadSlideStrokes(slideIndex);
if (strokes != null && strokes.Count > 0)
@@ -1140,9 +1228,6 @@ namespace Ink_Canvas
{
try
{
// 重置进入PPT时的浮动栏收纳状态记录
wasFloatingBarFoldedWhenEnterSlideShow = false;
// 重置PPT放映结束事件标志
isEnteredSlideShowEndEvent = false;
@@ -1152,6 +1237,9 @@ namespace Ink_Canvas
// 重置上次播放位置相关字段
_lastPlaybackPage = 0;
_shouldNavigateToLastPage = false;
// 重置当前播放页码跟踪
_currentSlideShowPosition = 0;
// 重置页面切换防抖机制
_lastSlideSwitchTime = DateTime.MinValue;
@@ -1170,51 +1258,14 @@ namespace Ink_Canvas
/// </summary>
private void HandleSlideSwitchWithDebounce(int currentSlide, int totalSlides)
{
try
{
var now = DateTime.Now;
// 如果距离上次切换时间太短,使用防抖机制
if (now - _lastSlideSwitchTime < TimeSpan.FromMilliseconds(SlideSwitchDebounceMs))
{
_pendingSlideIndex = currentSlide;
// 停止之前的定时器
_slideSwitchDebounceTimer?.Stop();
// 创建新的定时器
_slideSwitchDebounceTimer = new System.Timers.Timer(SlideSwitchDebounceMs);
_slideSwitchDebounceTimer.Elapsed += (sender, e) =>
{
Application.Current.Dispatcher.Invoke(() =>
{
if (_pendingSlideIndex > 0)
{
SwitchSlideInk(_pendingSlideIndex);
_pptUIManager?.UpdateCurrentSlideNumber(_pendingSlideIndex, totalSlides);
_pendingSlideIndex = -1;
}
});
_slideSwitchDebounceTimer?.Stop();
};
_slideSwitchDebounceTimer.Start();
}
else
{
// 直接处理页面切换
SwitchSlideInk(currentSlide);
_pptUIManager?.UpdateCurrentSlideNumber(currentSlide, totalSlides);
}
_lastSlideSwitchTime = now;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理页面切换防抖失败: {ex}", LogHelper.LogType.Error);
}
}
private void SwitchSlideInk(int newSlideIndex)
/// <summary>
/// 切换页面墨迹
/// </summary>
/// <param name="newSlideIndex">新页面索引</param>
/// <param name="skipClear">是否跳过清除操作(如果已在翻页时立即清除,则设为true</param>
private void SwitchSlideInk(int newSlideIndex, bool skipClear = false)
{
try
{
@@ -1235,18 +1286,23 @@ namespace Ink_Canvas
}
// 如果有当前墨迹且不是第一次切换,先保存到当前页面
if (inkCanvas.Strokes.Count > 0 && currentSlideIndex > 0 && currentSlideIndex != newSlideIndex)
if (currentSlideIndex > 0 && currentSlideIndex != newSlideIndex)
{
bool canWrite = _singlePPTInkManager?.CanWriteInk(currentSlideIndex) == true;
if (canWrite)
if (canWrite && inkCanvas.Strokes.Count > 0)
{
_singlePPTInkManager?.SaveCurrentSlideStrokes(currentSlideIndex, inkCanvas.Strokes);
}
}
ClearStrokes(true);
timeMachine.ClearStrokeHistory();
if (!skipClear)
{
ClearStrokes(true);
timeMachine.ClearStrokeHistory();
}
// 加载新页面的墨迹
StrokeCollection newStrokes = _singlePPTInkManager?.SwitchToSlide(newSlideIndex, null);
if (newStrokes != null && newStrokes.Count > 0)
@@ -1383,25 +1439,72 @@ namespace Ink_Canvas
{
try
{
// 保存当前页墨迹
var currentSlide = _pptManager?.GetCurrentSlideNumber() ?? 0;
if (currentSlide > 0)
var previousSlideBeforeNavigate = _pptManager?.GetCurrentSlideNumber() ?? 0;
StrokeCollection strokesToSave = null;
if (previousSlideBeforeNavigate > 0 && inkCanvas.Strokes.Count > 0)
{
_singlePPTInkManager?.SaveCurrentSlideStrokes(currentSlide, inkCanvas.Strokes);
strokesToSave = inkCanvas.Strokes.Clone();
}
// 保存截图(如果启用)
if (inkCanvas.Strokes.Count > Settings.Automation.MinimumAutomationStrokeNumber &&
Settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint)
{
var presentationName = _pptManager?.GetPresentationName() ?? "";
SaveScreenShot(true, $"{presentationName}/{currentSlide}");
}
// 执行翻页
if (_pptManager?.TryNavigatePrevious() == true)
{
// 翻页成功,等待事件处理墨迹切换
var currentSlideAfterNavigate = _pptManager?.GetCurrentSlideNumber() ?? 0;
if (previousSlideBeforeNavigate == currentSlideAfterNavigate && previousSlideBeforeNavigate > 0)
{
Thread.Sleep(50);
currentSlideAfterNavigate = _pptManager?.GetCurrentSlideNumber() ?? 0;
}
if (previousSlideBeforeNavigate != currentSlideAfterNavigate && previousSlideBeforeNavigate > 0)
{
if (inkCanvas.Strokes.Count > 0)
{
ClearStrokes(true);
timeMachine.ClearStrokeHistory();
_isInkClearedByButton = true;
}
if (strokesToSave != null && previousSlideBeforeNavigate > 0)
{
Task.Run(() =>
{
try
{
Application.Current.Dispatcher.Invoke(() =>
{
_singlePPTInkManager?.SaveCurrentSlideStrokes(previousSlideBeforeNavigate, strokesToSave);
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"异步保存PPT上一页墨迹失败: {ex}", LogHelper.LogType.Error);
}
});
// 异步保存截图(如果启用)
if (strokesToSave.Count > Settings.Automation.MinimumAutomationStrokeNumber &&
Settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint)
{
Task.Run(() =>
{
try
{
Application.Current.Dispatcher.Invoke(() =>
{
var presentationName = _pptManager?.GetPresentationName() ?? "";
SaveScreenShot(true, $"{presentationName}/{previousSlideBeforeNavigate}");
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"异步保存PPT上一页截图失败: {ex}", LogHelper.LogType.Error);
}
});
}
}
}
}
else
{
@@ -1423,25 +1526,72 @@ namespace Ink_Canvas
{
try
{
// 保存当前页墨迹
var currentSlide = _pptManager?.GetCurrentSlideNumber() ?? 0;
if (currentSlide > 0)
var previousSlideBeforeNavigate = _pptManager?.GetCurrentSlideNumber() ?? 0;
StrokeCollection strokesToSave = null;
if (previousSlideBeforeNavigate > 0 && inkCanvas.Strokes.Count > 0)
{
_singlePPTInkManager?.SaveCurrentSlideStrokes(currentSlide, inkCanvas.Strokes);
strokesToSave = inkCanvas.Strokes.Clone();
}
// 保存截图(如果启用)
if (inkCanvas.Strokes.Count > Settings.Automation.MinimumAutomationStrokeNumber &&
Settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint)
{
var presentationName = _pptManager?.GetPresentationName() ?? "";
SaveScreenShot(true, $"{presentationName}/{currentSlide}");
}
// 执行翻页
if (_pptManager?.TryNavigateNext() == true)
{
// 翻页成功,等待事件处理墨迹切换
var currentSlideAfterNavigate = _pptManager?.GetCurrentSlideNumber() ?? 0;
if (previousSlideBeforeNavigate == currentSlideAfterNavigate && previousSlideBeforeNavigate > 0)
{
Thread.Sleep(50);
currentSlideAfterNavigate = _pptManager?.GetCurrentSlideNumber() ?? 0;
}
if (previousSlideBeforeNavigate != currentSlideAfterNavigate && previousSlideBeforeNavigate > 0)
{
if (inkCanvas.Strokes.Count > 0)
{
ClearStrokes(true);
timeMachine.ClearStrokeHistory();
_isInkClearedByButton = true;
}
if (strokesToSave != null && previousSlideBeforeNavigate > 0)
{
Task.Run(() =>
{
try
{
Application.Current.Dispatcher.Invoke(() =>
{
_singlePPTInkManager?.SaveCurrentSlideStrokes(previousSlideBeforeNavigate, strokesToSave);
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"异步保存PPT下一页墨迹失败: {ex}", LogHelper.LogType.Error);
}
});
// 异步保存截图(如果启用)
if (strokesToSave.Count > Settings.Automation.MinimumAutomationStrokeNumber &&
Settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint)
{
Task.Run(() =>
{
try
{
Application.Current.Dispatcher.Invoke(() =>
{
var presentationName = _pptManager?.GetPresentationName() ?? "";
SaveScreenShot(true, $"{presentationName}/{previousSlideBeforeNavigate}");
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"异步保存PPT下一页截图失败: {ex}", LogHelper.LogType.Error);
}
});
}
}
}
}
else
{
@@ -1609,7 +1759,7 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile("手动更新放映结束UI状态", LogHelper.LogType.Trace);
});
// 手动处理收纳状态恢复,因为OnPPTSlideShowEnd事件可能未触发
// 手动处理自动收纳,因为OnPPTSlideShowEnd事件可能未触发
await HandleManualSlideShowEnd();
}
@@ -1617,28 +1767,8 @@ namespace Ink_Canvas
SetCurrentToolMode(InkCanvasEditingMode.None);
await Task.Delay(150);
if (Settings.Automation.IsAutoFoldAfterPPTSlideShow)
{
if (wasFloatingBarFoldedWhenEnterSlideShow)
{
ViewboxFloatingBarMarginAnimation(-60);
}
else
{
ViewboxFloatingBarMarginAnimation(100, true);
}
}
else
{
if (isFloatingBarFolded)
{
ViewboxFloatingBarMarginAnimation(-60);
}
else
{
ViewboxFloatingBarMarginAnimation(100, true);
}
}
// PPT退出时自动收纳,使用收纳状态的边距动画
ViewboxFloatingBarMarginAnimation(-60);
}
catch (Exception ex)
{
@@ -1651,76 +1781,31 @@ namespace Ink_Canvas
_pptUIManager?.UpdateSidebarExitButtons(false);
});
// 异常情况下也手动处理收纳状态恢复
// 异常情况下也手动处理自动收纳
await HandleManualSlideShowEnd();
// 异常情况下也要根据设置决定浮动栏边距
// 异常情况下也要自动收纳,使用收纳状态的边距动画
await Task.Delay(150);
if (Settings.Automation.IsAutoFoldAfterPPTSlideShow)
{
if (wasFloatingBarFoldedWhenEnterSlideShow)
{
ViewboxFloatingBarMarginAnimation(-60);
}
else
{
ViewboxFloatingBarMarginAnimation(100, true);
}
}
else
{
if (isFloatingBarFolded)
{
ViewboxFloatingBarMarginAnimation(-60);
}
else
{
ViewboxFloatingBarMarginAnimation(100, true);
}
}
ViewboxFloatingBarMarginAnimation(-60);
}
}
/// <summary>
/// 手动处理PPT放映结束时的收纳状态恢复
/// 手动处理PPT放映结束时的自动收纳
/// </summary>
private async Task HandleManualSlideShowEnd()
{
try
{
if (Settings.Automation.IsAutoFoldAfterPPTSlideShow)
// PPT退出时自动收纳浮动栏
if (!isFloatingBarFolded)
{
if (wasFloatingBarFoldedWhenEnterSlideShow)
{
if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(new object(), null);
}
else
{
if (isFloatingBarFolded) await UnFoldFloatingBar(new object());
}
}
else
{
if (Settings.Automation.IsAutoFoldInPPTSlideShow)
{
if (isFloatingBarFolded)
{
await UnFoldFloatingBar(new object());
}
}
else
{
// 如果两个功能都关闭,确保浮动栏展开
if (isFloatingBarFolded)
{
await UnFoldFloatingBar(new object());
}
}
FoldFloatingBar_MouseUp(new object(), null);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"手动处理PPT放映结束收纳状态恢复失败: {ex}", LogHelper.LogType.Error);
LogHelper.WriteLogToFile($"手动处理PPT放映结束自动收纳失败: {ex}", LogHelper.LogType.Error);
}
}
@@ -354,7 +354,6 @@ namespace Ink_Canvas
private void BtnSelect_Click(object sender, RoutedEventArgs e)
{
ExitMultiTouchModeIfNeeded();
forceEraser = true;
drawingShapeMode = 0;
inkCanvas.IsManipulationEnabled = false;
@@ -709,7 +708,6 @@ namespace Ink_Canvas
private void LassoSelect_Click(object sender, RoutedEventArgs e)
{
ExitMultiTouchModeIfNeeded();
forceEraser = false;
forcePointEraser = false;
drawingShapeMode = 0;
@@ -720,7 +718,6 @@ namespace Ink_Canvas
private void BtnLassoSelect_Click(object sender, RoutedEventArgs e)
{
ExitMultiTouchModeIfNeeded();
forceEraser = false;
forcePointEraser = false;
drawingShapeMode = 0;
+8
View File
@@ -2532,6 +2532,14 @@ namespace Ink_Canvas
SaveSettingsToFile();
}
private void ToggleSwitchWindowMode_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
Settings.Advanced.WindowMode = ToggleSwitchWindowMode.IsOn;
SaveSettingsToFile();
SetWindowMode();
}
private void ToggleSwitchIsAutoBackupBeforeUpdate_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
@@ -795,6 +795,7 @@ namespace Ink_Canvas
ToggleSwitchIsLogEnabled.IsOn = Settings.Advanced.IsLogEnabled;
ToggleSwitchIsSaveLogByDate.IsOn = Settings.Advanced.IsSaveLogByDate;
ToggleSwitchIsSecondConfimeWhenShutdownApp.IsOn = Settings.Advanced.IsSecondConfirmWhenShutdownApp;
ToggleSwitchWindowMode.IsOn = Settings.Advanced.WindowMode;
ToggleSwitchIsSpecialScreen.IsOn = Settings.Advanced.IsSpecialScreen;
ToggleSwitchIsQuadIR.IsOn = Settings.Advanced.IsQuadIR;
ToggleSwitchEraserBindTouchMultiplier.IsOn = Settings.Advanced.EraserBindTouchMultiplier;
File diff suppressed because it is too large Load Diff
@@ -18,21 +18,19 @@ namespace Ink_Canvas
{
private StrokeCollection newStrokes = new StrokeCollection();
private List<Circle> circles = new List<Circle>();
private const double LINE_STRAIGHTEN_THRESHOLD = 0.20; // 默认灵敏度阈值,与UI默认值对应
private const double LINE_STRAIGHTEN_THRESHOLD = 0.20;
// 矩形参考线系统
private List<RectangleGuideLine> rectangleGuideLines = new List<RectangleGuideLine>();
private const double RECTANGLE_ENDPOINT_THRESHOLD = 30.0; // 端点相交判断阈值
private const double RECTANGLE_ANGLE_THRESHOLD = 15.0; // 角度判断阈值(度)
private const double RECTANGLE_ENDPOINT_THRESHOLD = 30.0;
private const double RECTANGLE_ANGLE_THRESHOLD = 15.0;
// 矩形参考线数据结构
private class RectangleGuideLine
{
public Stroke OriginalStroke { get; set; }
public Point StartPoint { get; set; }
public Point EndPoint { get; set; }
public DateTime CreatedTime { get; set; }
public double Angle { get; set; } // 直线角度(弧度)
public double Angle { get; set; }
public bool IsHorizontal { get; set; }
public bool IsVertical { get; set; }
@@ -64,7 +62,6 @@ namespace Ink_Canvas
var startPoint = e.Stroke.StylusPoints.Count > 0 ? e.Stroke.StylusPoints[0].ToPoint() : new Point();
var endPoint = e.Stroke.StylusPoints.Count > 0 ? e.Stroke.StylusPoints[e.Stroke.StylusPoints.Count - 1].ToPoint() : new Point();
// 确保InkCanvas保持Ink编辑模式,防止自动切换到鼠标模式
if (inkCanvas.EditingMode != InkCanvasEditingMode.Ink)
{
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
@@ -80,19 +77,15 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile("StrokeCollected: 墨迹渐隐管理器为空,无法添加墨迹", LogHelper.LogType.Error);
}
// 延迟移除墨迹,避免立即移除导致模式切换
// 使用Dispatcher.BeginInvoke确保在UI线程上异步执行
Dispatcher.BeginInvoke(new Action(() =>
{
try
{
// 再次确保InkCanvas保持Ink编辑模式
if (inkCanvas.EditingMode != InkCanvasEditingMode.Ink)
{
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
// 从InkCanvas中移除墨迹,因为我们要用渐隐管理器来管理它
if (inkCanvas.Strokes.Contains(e.Stroke))
{
inkCanvas.Strokes.Remove(e.Stroke);
@@ -104,21 +97,18 @@ namespace Ink_Canvas
}
}), DispatcherPriority.Background);
// 墨迹渐隐模式下不参与墨迹纠正和其他处理,直接返回
return;
}
// 标记是否进行了直线拉直
bool wasStraightened = false;
// 禁用原有的FitToCurve,使用新的高级贝塞尔曲线平滑
if (Settings.Canvas.FitToCurve) drawingAttributes.FitToCurve = false;
try
{
inkCanvas.Opacity = 1;
// 应用屏蔽压感功能 - 如果启用,所有笔画都使用统一粗细
if (Settings.Canvas.DisablePressure)
{
var uniformPoints = new StylusPointCollection();
@@ -129,13 +119,11 @@ namespace Ink_Canvas
}
e.Stroke.StylusPoints = uniformPoints;
}
// 应用压感触屏模式 - 如果启用并且检测到触屏输入
else if (Settings.Canvas.EnablePressureTouchMode)
{
bool isTouchInput = true;
foreach (StylusPoint point in e.Stroke.StylusPoints)
{
// 检测是否为压感笔输入(压感笔的PressureFactor不等于0.5或0
if ((point.PressureFactor > 0.501 || point.PressureFactor < 0.5) && point.PressureFactor != 0)
{
isTouchInput = false;
@@ -143,7 +131,6 @@ namespace Ink_Canvas
}
}
// 如果是触屏输入,则应用模拟压感
if (isTouchInput)
{
switch (Settings.Canvas.InkStyle)
@@ -859,30 +846,61 @@ namespace Ink_Canvas
// 快速检查:计算几个关键点与直线的距离
if (stroke.StylusPoints.Count >= 10)
{
// 取中点和1/4、3/4位置的点,快速检查偏差
int quarterIdx = stroke.StylusPoints.Count / 4;
int midIdx = stroke.StylusPoints.Count / 2;
int threeQuarterIdx = quarterIdx * 3;
Point quarterPoint = stroke.StylusPoints[quarterIdx].ToPoint();
Point midPoint = stroke.StylusPoints[midIdx].ToPoint();
Point threeQuarterPoint = stroke.StylusPoints[threeQuarterIdx].ToPoint();
double quarterDeviation = DistanceFromLineToPoint(start, end, quarterPoint);
double midDeviation = DistanceFromLineToPoint(start, end, midPoint);
double threeQuarterDeviation = DistanceFromLineToPoint(start, end, threeQuarterPoint);
// 使用相对偏差:偏差与线长的比例,并使用灵敏度进行调整
double quickRelativeThreshold = lineLength * quickThreshold;
// 记录检测到的偏差
Debug.WriteLine($"Deviations: q={quarterDeviation}, m={midDeviation}, tq={threeQuarterDeviation}, threshold={quickRelativeThreshold}");
if (quarterDeviation > quickRelativeThreshold ||
midDeviation > quickRelativeThreshold ||
threeQuarterDeviation > quickRelativeThreshold)
List<Point> checkPoints;
// 使用采样点进行更准确的判断
if (Settings.Canvas.HighPrecisionLineStraighten)
{
return false;
var allPoints = stroke.StylusPoints.Select(p => p.ToPoint()).ToList();
checkPoints = SamplePointsByDistance(allPoints, 10.0);
Debug.WriteLine($"高精度模式快速检查:原始点数={allPoints.Count}, 采样点数={checkPoints.Count}");
}
else
{
// 取中点和1/4、3/4位置的点
int quarterIdx = stroke.StylusPoints.Count / 4;
int midIdx = stroke.StylusPoints.Count / 2;
int threeQuarterIdx = quarterIdx * 3;
checkPoints = new List<Point>
{
stroke.StylusPoints[quarterIdx].ToPoint(),
stroke.StylusPoints[midIdx].ToPoint(),
stroke.StylusPoints[threeQuarterIdx].ToPoint()
};
}
// 计算所有检查点与直线的平均偏差
double totalDeviation = 0;
double maxDeviation = 0;
int validPointCount = 0;
foreach (Point checkPoint in checkPoints)
{
double deviation = DistanceFromLineToPoint(start, end, checkPoint);
totalDeviation += deviation;
maxDeviation = Math.Max(maxDeviation, deviation);
validPointCount++;
}
if (validPointCount > 0)
{
double avgDeviation = totalDeviation / validPointCount;
// 使用相对偏差:偏差与线长的比例,并使用灵敏度进行调整
double quickRelativeThreshold = lineLength * quickThreshold;
// 使用平均偏差和最大偏差的综合判断
double deviationThreshold = Settings.Canvas.HighPrecisionLineStraighten
? Math.Max(avgDeviation, maxDeviation * 0.7) // 高精度模式更严格
: maxDeviation;
// 记录检测到的偏差
Debug.WriteLine($"Deviations: avg={avgDeviation:F2}, max={maxDeviation:F2}, threshold={quickRelativeThreshold:F2}, highPrecision={Settings.Canvas.HighPrecisionLineStraighten}");
if (deviationThreshold > quickRelativeThreshold)
{
return false;
}
}
}
@@ -1177,14 +1195,29 @@ namespace Ink_Canvas
return false;
}
List<Point> workingPoints = points;
if (Settings.Canvas.HighPrecisionLineStraighten)
{
workingPoints = SamplePointsByDistance(points, 10.0);
Debug.WriteLine($"高精度模式:原始点数={points.Count}, 采样后点数={workingPoints.Count}");
}
// 使用总最小二乘法(TLS/PCA)进行直线拟合
int n = points.Count - 8;
int n = workingPoints.Count - 8;
if (n < 1)
{
// 如果采样后点数太少,回退到原始方法
n = points.Count - 8;
workingPoints = points;
}
List<Point> filteredPoints = new List<Point>();
// 收集过滤后的点(跳过前 4 个和后 4 个点,用于计算直线方向)
for (int i = 4; i < n + 4; i++)
int skipCount = Math.Min(4, n / 2); // 确保跳过数量不超过一半
for (int i = skipCount; i < n + skipCount && i < workingPoints.Count; i++)
{
filteredPoints.Add(points[i]);
filteredPoints.Add(workingPoints[i]);
}
// 计算中心点(使用过滤后的点)
@@ -1244,7 +1277,8 @@ namespace Ink_Canvas
double maxProjection = double.MinValue;
// 计算所有点在直线方向上的投影
foreach (Point p in points)
List<Point> pointsForProjection = Settings.Canvas.HighPrecisionLineStraighten ? workingPoints : points;
foreach (Point p in pointsForProjection)
{
// 相对于过滤点中心的投影
double projection = (p.X - centerX) * directionX + (p.Y - centerY) * directionY;
@@ -1453,6 +1487,43 @@ namespace Ink_Canvas
return points;
}
/// <summary>
/// 高精度模式
/// </summary>
private List<Point> SamplePointsByDistance(List<Point> points, double sampleInterval = 10.0)
{
if (points == null || points.Count < 2)
return points;
List<Point> sampledPoints = new List<Point>();
sampledPoints.Add(points[0]); // 总是包含起点
double accumulatedDistance = 0;
Point lastSampledPoint = points[0];
for (int i = 1; i < points.Count; i++)
{
double segmentDistance = GetDistance(lastSampledPoint, points[i]);
accumulatedDistance += segmentDistance;
// 当累积距离达到采样间隔时,添加当前点
if (accumulatedDistance >= sampleInterval)
{
sampledPoints.Add(points[i]);
lastSampledPoint = points[i];
accumulatedDistance = 0; // 重置累积距离
}
}
// 总是包含终点(如果还没有包含)
if (sampledPoints.Count == 0 || GetDistance(sampledPoints.Last(), points.Last()) > 1.0)
{
sampledPoints.Add(points.Last());
}
return sampledPoints;
}
// New method: Gets distance from point to a line defined by two points
private double DistanceFromLineToPoint(Point lineStart, Point lineEnd, Point point)
{
@@ -1467,11 +1538,66 @@ namespace Ink_Canvas
return distance;
}
/// <summary>
/// 判断一个 stroke 是否是直线(排除虚线和点线)
/// </summary>
/// <param name="stroke">要检查的 stroke</param>
/// <returns>如果是直线返回 true,否则返回 false</returns>
private bool IsStraightLine(Stroke stroke)
{
if (stroke == null || stroke.StylusPoints.Count == 0)
return false;
int pointCount = stroke.StylusPoints.Count;
if (pointCount == 1)
return false;
// 最简单的直线:只有2个点
if (pointCount == 2)
{
Point p1 = stroke.StylusPoints[0].ToPoint();
Point p2 = stroke.StylusPoints[1].ToPoint();
double lineLength = GetDistance(p1, p2);
if (lineLength < 10)
return false;
return true;
}
if (pointCount > 3)
return false;
// 对于3个点的情况,检查它们是否基本在一条直线上
if (pointCount == 3)
{
Point p1 = stroke.StylusPoints[0].ToPoint();
Point p2 = stroke.StylusPoints[1].ToPoint();
Point p3 = stroke.StylusPoints[2].ToPoint();
double totalLength = GetDistance(p1, p3);
if (totalLength < 10)
return false;
// 计算点到直线的距离
// 使用 p1 和 p3 作为直线端点,检查 p2 是否在这条直线上
double distance = DistanceFromLineToPoint(p1, p3, p2);
// 如果点到直线的距离相对于线段长度很小,认为是直线
// 使用相对误差阈值(比如 1%
if (totalLength > 0 && distance / totalLength < 0.01)
return true;
return false;
}
return false;
}
// New method: Attempts to snap endpoints to existing stroke endpoints
private Point[] GetSnappedEndpoints(Point start, Point end)
{
// 如果端点吸附功能关闭,直接返回null
// 这里不再返回原始点,因为调用此方法的地方会判断返回值是否为null
if (!Settings.Canvas.LineEndpointSnapping)
return null;
@@ -1488,6 +1614,10 @@ namespace Ink_Canvas
{
if (stroke.StylusPoints.Count == 0) continue;
// 只对直线进行端点吸附,跳过虚线和点线
if (!IsStraightLine(stroke))
continue;
// Get stroke endpoints
Point strokeStart = stroke.StylusPoints.First().ToPoint();
Point strokeEnd = stroke.StylusPoints.Last().ToPoint();
+80 -400
View File
@@ -325,7 +325,7 @@ namespace Ink_Canvas
// 根据当前编辑模式设置不同的光标
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
inkCanvas.Cursor = Cursors.Cross;
inkCanvas.Cursor = Cursors.Arrow;
}
else if (inkCanvas.EditingMode == InkCanvasEditingMode.Ink)
{
@@ -533,13 +533,6 @@ namespace Ink_Canvas
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
// 橡皮状态下只return,保证橡皮状态可保持
return;
}
if (inkCanvas.EditingMode == InkCanvasEditingMode.Select)
{
// 套索选状态下不直接return,允许触摸事件继续处理
dec.Add(e.TouchDevice.Id);
return;
}
if (drawingShapeMode != 0)
@@ -556,6 +549,11 @@ namespace Ink_Canvas
return;
}
if (inkCanvas.EditingMode == InkCanvasEditingMode.Select)
{
dec.Add(e.TouchDevice.Id);
return;
}
if (inkCanvas.EditingMode == InkCanvasEditingMode.Ink)
{
return;
@@ -571,171 +569,30 @@ namespace Ink_Canvas
}
}
// 手掌擦相关变量
private bool isPalmEraserActive;
private InkCanvasEditingMode palmEraserLastEditingMode = InkCanvasEditingMode.Ink;
private bool palmEraserLastIsHighlighter;
private bool palmEraserWasEnabledBeforeMultiTouch;
public double GetTouchBoundWidth(TouchEventArgs e)
{
var args = e.GetTouchPoint(null).Bounds;
if (!Settings.Advanced.IsQuadIR) return args.Width;
else return Math.Sqrt(args.Width * args.Height); // 四边红外
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;
}
private void inkCanvas_PreviewTouchDown(object sender, TouchEventArgs e)
{
// 检查触摸是否发生在浮动栏区域,如果是则允许事件传播到浮动栏按钮
var touchPoint = e.GetTouchPoint(this);
var floatingBarBounds = ViewboxFloatingBar.TransformToAncestor(this).TransformBounds(
new Rect(0, 0, ViewboxFloatingBar.ActualWidth, ViewboxFloatingBar.ActualHeight));
// 如果触摸发生在浮动栏区域,不阻止事件传播,让浮动栏按钮能够接收触摸事件
if (floatingBarBounds.Contains(touchPoint.Position))
{
// 不设置 ViewboxFloatingBar.IsHitTestVisible = false,让浮动栏按钮能够接收触摸事件
return;
}
// 橡皮状态下不做任何切换,直接return,保证橡皮可持续
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint
|| inkCanvas.EditingMode == InkCanvasEditingMode.EraseByStroke)
{
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;
}
// 第二笔时不更新iniP,保持第一笔的起点
}
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);
// Palm Eraser 逻辑
if (Settings.Canvas.EnablePalmEraser && !isPalmEraserActive)
{
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)
{
// 记录当前编辑模式和高光状态
palmEraserLastEditingMode = inkCanvas.EditingMode;
palmEraserLastIsHighlighter = drawingAttributes.IsHighlighter;
// 动态调整橡皮大小
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;
}
}
}
}
// 设备1个的时候,记录中心点
//设备1个的时候,记录中心点
if (dec.Count == 1)
{
touchPoint = e.GetTouchPoint(inkCanvas);
var touchPoint = e.GetTouchPoint(inkCanvas);
centerPoint = touchPoint.Position;
if (drawingShapeMode != 0)
{
// 对于双曲线绘制,第一笔时记录起点,第二笔时不更新起点
if (drawingShapeMode == 24 || drawingShapeMode == 25)
{
// 双曲线绘制:第一笔记录起点,第二笔保持第一笔的起点
if (drawMultiStepShapeCurrentStep == 0)
{
iniP = touchPoint.Position;
}
// 第二笔时不更新iniP,保持第一笔的起点
}
else
{
// 其他图形正常记录起点
iniP = touchPoint.Position;
}
}
// 记录第一根手指点击时的 StrokeCollection
//记录第一根手指点击时的 StrokeCollection
lastTouchDownStrokeCollection = inkCanvas.Strokes.Clone();
}
//设备两个及两个以上,将画笔功能关闭
@@ -744,61 +601,25 @@ namespace Ink_Canvas
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;
System.Threading.Tasks.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;
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke
&& drawingShapeMode == 0)
{
inkCanvas.EditingMode = InkCanvasEditingMode.None;
}
inkCanvas.EditingMode = InkCanvasEditingMode.None;
}
}
private void inkCanvas_PreviewTouchMove(object sender, TouchEventArgs e)
{
// 如果手掌擦激活,更新橡皮擦反馈位置
if (isPalmEraserActive)
{
var touchPoint = e.GetTouchPoint(inkCanvas);
EraserOverlay_PointerMove(sender, touchPoint.Position);
}
}
private void inkCanvas_PreviewTouchUp(object sender, TouchEventArgs e)
{
// 橡皮状态下不做任何切换,直接return,保证橡皮可持续
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint && !isPalmEraserActive)
{
return;
}
inkCanvas.ReleaseAllTouchCaptures();
ViewboxFloatingBar.IsHitTestVisible = true;
BlackboardUIGridForInkReplay.IsHitTestVisible = true;
// Palm Eraser 逻辑
//手势完成后切回之前的状态
if (dec.Count > 1)
if (inkCanvas.EditingMode == InkCanvasEditingMode.None)
inkCanvas.EditingMode = lastInkCanvasEditingMode;
dec.Remove(e.TouchDevice.Id);
// 重置多触控点定时器状态
@@ -807,17 +628,30 @@ namespace Ink_Canvas
isMultiTouchTimerActive = false;
}
if (dec.Count == 0)
{
isSingleFingerDragMode = 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 (drawingShapeMode != 0)
{
isTouchDown = false;
ViewboxFloatingBar.IsHitTestVisible = true;
BlackboardUIGridForInkReplay.IsHitTestVisible = true;
// 对于双曲线等需要多步绘制的图形,触摸抬手时应该进入下一步
if (drawingShapeMode == 24 || drawingShapeMode == 25)
{
// 双曲线绘制:触摸抬手时进入下一步,但不自动触发鼠标抬起事件
// 让用户继续绘制第二笔
if (drawMultiStepShapeCurrentStep == 0)
{
// 第一笔完成,进入第二笔
@@ -836,7 +670,6 @@ namespace Ink_Canvas
}
else
{
// 其他单步绘制的图形,触摸抬手时完成绘制
var mouseArgs = new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left)
{
RoutedEvent = MouseLeftButtonUpEvent,
@@ -846,82 +679,6 @@ namespace Ink_Canvas
}
}
// 手势完成后切回之前的状态
if (drawingShapeMode == 0)
{
if (dec.Count > 1)
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.None)
{
if (lastInkCanvasEditingMode != InkCanvasEditingMode.EraseByPoint)
{
inkCanvas.EditingMode = lastInkCanvasEditingMode;
}
}
}
else if (dec.Count == 0)
{
// 当所有触摸点都抬起时,确保正确恢复编辑模式
// 这对于从橡皮擦切换到笔后恢复多指手势功能很重要
if (inkCanvas.EditingMode == InkCanvasEditingMode.None &&
lastInkCanvasEditingMode != InkCanvasEditingMode.None &&
lastInkCanvasEditingMode != InkCanvasEditingMode.EraseByPoint)
{
inkCanvas.EditingMode = lastInkCanvasEditingMode;
}
if (isPalmEraserActive)
{
LogHelper.WriteLogToFile("Palm eraser force recovery - all touch points cleared");
// 恢复高光状态
drawingAttributes.IsHighlighter = palmEraserLastIsHighlighter;
// 恢复编辑模式
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;
}
// 如果手掌擦还在激活状态但触摸点已清空,强制重置状态
isPalmEraserActive = false;
inkCanvas.IsHitTestVisible = true;
inkCanvas.IsManipulationEnabled = true;
ViewboxFloatingBar.IsHitTestVisible = true;
BlackboardUIGridForInkReplay.IsHitTestVisible = true;
DisableEraserOverlay();
if (Settings.Canvas.IsShowCursor)
{
inkCanvas.ForceCursor = true;
inkCanvas.UseCustomCursor = true;
}
LogHelper.WriteLogToFile("Palm eraser force recovery completed");
}
}
}
inkCanvas.Opacity = 1;
if (dec.Count == 0)
@@ -943,61 +700,37 @@ namespace Ink_Canvas
private void Main_Grid_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
if (e.Manipulators.Count() != 0) return;
if (drawingShapeMode == 0
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke
&& inkCanvas.EditingMode != InkCanvasEditingMode.Select)
if (e.Manipulators.Count() == 0)
{
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
lastInkCanvasEditingMode = InkCanvasEditingMode.Ink;
if (dec.Count > 0)
{
dec.Clear();
}
isSingleFingerDragMode = false;
if (drawingShapeMode == 0
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke
&& inkCanvas.EditingMode != InkCanvasEditingMode.Select)
{
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
lastInkCanvasEditingMode = InkCanvasEditingMode.Ink;
}
}
}
private void Main_Grid_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
// 手掌擦时禁止移动/缩放
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
return;
// 三指及以上禁止缩放
bool disableScale = dec.Count >= 3;
if (isInMultiTouchMode) return;
if (dec.Count == 0 && (isSingleFingerDragMode || isInMultiTouchMode))
{
ResetTouchStates();
return;
}
// 如果是单指拖动选中的墨迹,允许处理
if (dec.Count == 1 && inkCanvas.GetSelectedStrokes().Count > 0)
{
var md = e.DeltaManipulation;
var trans = md.Translation; // 获得位移矢量
if (trans.X != 0 || trans.Y != 0)
{
var m = new Matrix();
m.Translate(trans.X, trans.Y); // 移动
var strokes = inkCanvas.GetSelectedStrokes();
foreach (var stroke in strokes)
{
stroke.Transform(m, false);
}
// 更新选择框位置
updateBorderStrokeSelectionControlLocation();
}
return;
}
if (!Settings.Gesture.IsEnableTwoFingerGesture) return;
if ((dec.Count >= 2 && (Settings.PowerPointSettings.IsEnableTwoFingerGestureInPresentationMode ||
StackPanelPPTControls.Visibility != Visibility.Visible ||
StackPanelPPTButtons.Visibility == Visibility.Collapsed)) ||
isSingleFingerDragMode)
if (isInMultiTouchMode || !Settings.Gesture.IsEnableTwoFingerGesture) return;
bool hasMultipleManipulators = e.Manipulators.Count() >= 2;
bool shouldUseTwoFingerGesture = (dec.Count >= 2 && hasMultipleManipulators &&
(Settings.PowerPointSettings.IsEnableTwoFingerGestureInPresentationMode ||
StackPanelPPTControls.Visibility != Visibility.Visible ||
StackPanelPPTButtons.Visibility == Visibility.Collapsed)) ||
isSingleFingerDragMode;
if (shouldUseTwoFingerGesture)
{
var md = e.DeltaManipulation;
var trans = md.Translation; // 获得位移矢量
@@ -1007,20 +740,23 @@ namespace Ink_Canvas
if (Settings.Gesture.IsEnableTwoFingerTranslate)
m.Translate(trans.X, trans.Y); // 移动
// 计算中心点(用于缩放和旋转)
var fe = e.Source as FrameworkElement;
var center = new Point(fe.ActualWidth / 2, fe.ActualHeight / 2);
center = m.Transform(center); // 转换为矩阵缩放和旋转的中心点
if (Settings.Gesture.IsEnableTwoFingerGestureTranslateOrRotation)
{
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); // 旋转
if (Settings.Gesture.IsEnableTwoFingerZoom && !disableScale)
m.ScaleAt(scale.X, scale.Y, center.X, center.Y); // 缩放
}
if (Settings.Gesture.IsEnableTwoFingerZoom)
{
var scale = md.Scale; // 获得缩放倍数
m.ScaleAt(scale.X, scale.Y, center.X, center.Y); // 缩放
}
var strokes = inkCanvas.GetSelectedStrokes();
@@ -1043,6 +779,13 @@ namespace Ink_Canvas
break;
}
if (!Settings.Gesture.IsEnableTwoFingerZoom) continue;
try
{
stroke.DrawingAttributes.Width *= md.Scale.X;
stroke.DrawingAttributes.Height *= md.Scale.Y;
}
catch { }
}
}
else
@@ -1165,69 +908,6 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile($"应用媒体元素变换失败: {ex.Message}", LogHelper.LogType.Error);
}
}
// 退出多指书写模式,恢复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;
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke
&& drawingShapeMode == 0)
{
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
// 保存非笔画元素(如图片)
var preservedElements = PreserveNonStrokeElements();
inkCanvas.Children.Clear();
// 恢复非笔画元素
RestoreNonStrokeElements(preservedElements);
isInMultiTouchMode = false;
// 关闭多指书写时,恢复手掌擦开关
if (palmEraserWasEnabledBeforeMultiTouch)
{
Settings.Canvas.EnablePalmEraser = true;
if (ToggleSwitchEnablePalmEraser != null)
ToggleSwitchEnablePalmEraser.IsOn = true;
}
}
}
// 进入多指书写模式,绑定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;
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke
&& drawingShapeMode == 0)
{
inkCanvas.EditingMode = InkCanvasEditingMode.None;
}
// 保存非笔画元素(如图片)
var preservedElements = PreserveNonStrokeElements();
inkCanvas.Children.Clear();
// 恢复非笔画元素
RestoreNonStrokeElements(preservedElements);
isInMultiTouchMode = true;
// 启用多指书写时,自动禁用手掌擦
palmEraserWasEnabledBeforeMultiTouch = Settings.Canvas.EnablePalmEraser;
Settings.Canvas.EnablePalmEraser = false;
if (ToggleSwitchEnablePalmEraser != null)
ToggleSwitchEnablePalmEraser.IsOn = false;
}
}
}
}
+3
View File
@@ -602,6 +602,9 @@ namespace Ink_Canvas
[JsonProperty("enableUIAccessTopMost")]
public bool EnableUIAccessTopMost { get; set; } = false;
[JsonProperty("windowMode")]
public bool WindowMode { get; set; } = true;
}
public class InkToShape
+42 -3
View File
@@ -598,13 +598,52 @@ namespace Ink_Canvas
historyData.NameProbabilities = new Dictionary<string, double>();
}
// 过滤掉已选择的人员
var candidateNames = availableNames.Where(name => !alreadySelected.Contains(name)).ToList();
if (candidateNames.Count == 0) return null;
if (candidateNames.Count == 1) return candidateNames[0];
// 检查极差:当极差达到3时,从被抽选次数最少的人中抽选
if (historyData.NameFrequency != null && historyData.NameFrequency.Count > 0)
{
// 获取所有候选人员的被抽选次数
var candidateFrequencies = new Dictionary<string, int>();
foreach (string name in candidateNames)
{
int count = historyData.NameFrequency.ContainsKey(name) ? historyData.NameFrequency[name] : 0;
candidateFrequencies[name] = count;
}
// 计算极差(最大值 - 最小值)
if (candidateFrequencies.Count > 0)
{
int maxCount = candidateFrequencies.Values.Max();
int minCount = candidateFrequencies.Values.Min();
int range = maxCount - minCount;
// 当极差达到3时,只从被抽选次数最少的人中抽选
if (range >= 3)
{
var leastSelectedNames = candidateFrequencies
.Where(kvp => kvp.Value == minCount)
.Select(kvp => kvp.Key)
.ToList();
if (leastSelectedNames.Count > 0)
{
// 只从被抽选次数最少的人中随机选择
int randomIndex = random.Next(0, leastSelectedNames.Count);
return leastSelectedNames[randomIndex];
}
}
}
}
// 获取每个人员的概率
var nameProbabilities = new Dictionary<string, double>();
foreach (string name in availableNames)
foreach (string name in candidateNames)
{
if (alreadySelected.Contains(name)) continue;
// 获取基础概率
double baseProbability = GetNameProbability(name);
+67 -1
View File
@@ -70,7 +70,7 @@ namespace Ink_Canvas.Windows
}
/// <summary>
/// 刷新主题(供外部调用)
/// 刷新主题
/// </summary>
public void RefreshTheme()
{
@@ -1505,6 +1505,72 @@ namespace Ink_Canvas.Windows
private void HandleTimerCompletion()
{
// 计时器结束时,如果显示的是最小化视图,恢复到主窗口视图
Application.Current.Dispatcher.Invoke(() =>
{
var mainWindow = Application.Current.MainWindow as MainWindow;
if (mainWindow != null)
{
var timerContainer = mainWindow.FindName("TimerContainer") as FrameworkElement;
var minimizedContainer = mainWindow.FindName("MinimizedTimerContainer") as FrameworkElement;
// 如果最小化视图可见,恢复到主窗口视图
if (minimizedContainer != null && minimizedContainer.Visibility == Visibility.Visible)
{
HideMinimizedRequested?.Invoke(this, EventArgs.Empty);
}
}
});
// 重置计时器状态
ResetTimerState();
}
/// <summary>
/// 重置计时器状态
/// </summary>
public void ResetTimerState()
{
Application.Current.Dispatcher.Invoke(() =>
{
// 停止计时器
if (isTimerRunning)
{
timer.Stop();
isTimerRunning = false;
isPaused = false;
if (hideTimer != null)
{
hideTimer.Stop();
}
}
// 重置时间到默认值
hour = 0;
minute = 5;
second = 0;
// 更新显示
UpdateDigitDisplays();
SetColonDisplay(false);
// 重置图标
if (StartPauseIcon != null)
{
StartPauseIcon.Data = Geometry.Parse(PlayIconData);
}
// 重置状态标志
isOvertimeMode = false;
hasPlayedProgressiveReminder = false;
// 禁用全屏按钮
if (FullscreenBtn != null)
{
FullscreenBtn.IsEnabled = false;
}
});
}
private void HideTimer_Elapsed(object sender, ElapsedEventArgs e)