Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c7427eb22c | |||
| e9fde97453 | |||
| 441f8b6e26 | |||
| 8042b917a0 | |||
| 343e7281fe | |||
| c64e6a4554 | |||
| 8190bf275c | |||
| e792f2637d | |||
| 3cd26323dc | |||
| 40e1c4d467 | |||
| 86c22d373a | |||
| cb7a76efc5 | |||
| 545425c4d3 | |||
| eb1aaa10e4 | |||
| daf0db312b | |||
| 8b2bc2f064 | |||
| 2e343cbbf9 | |||
| a75f0470bc | |||
| 287d31a3a9 | |||
| 430fff0515 | |||
| fbbb7b8ad7 | |||
| 54b74d7411 | |||
| 82dba31b2a | |||
| 38d7e782e0 | |||
| b17e73db90 | |||
| da4beb5dcd | |||
| 72d871ecb8 | |||
| 86676d40fa | |||
| 5a5d4c1d50 | |||
| 52a99179cd | |||
| 05440248a1 |
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user