Merge branch 'net6' into net462

This commit is contained in:
doudou0720
2026-05-01 23:15:07 +08:00
228 changed files with 23683 additions and 26236 deletions
+2 -16
View File
@@ -4,7 +4,6 @@ using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
@@ -64,7 +63,7 @@ namespace Ink_Canvas
/// 处理折叠浮动栏的鼠标点击事件。
/// </summary>
/// <param name="sender">事件发送者。</param>
/// <param name="e">鼠标按钮事件参数。</param>
/// <param name="e">路由事件参数。</param>
public async void FoldFloatingBar_MouseUp(object sender, MouseButtonEventArgs e)
{
await FoldFloatingBar(sender);
@@ -91,19 +90,6 @@ namespace Ink_Canvas
/// </remarks>
public async Task FoldFloatingBar(object sender, bool isAutoFoldCommand = false)
{
var isShouldRejectAction = false;
await Dispatcher.InvokeAsync(() =>
{
if (lastBorderMouseDownObject != null && lastBorderMouseDownObject is Panel)
((Panel)lastBorderMouseDownObject).Background = new SolidColorBrush(Colors.Transparent);
if (sender == Fold_Icon && lastBorderMouseDownObject != Fold_Icon) isShouldRejectAction = true;
});
if (isShouldRejectAction)
{
return;
}
// FloatingBarIcons_MouseUp_New(sender);
if (sender == null)
@@ -338,7 +324,7 @@ namespace Ink_Canvas
/// 处理展开浮动栏的鼠标点击事件。
/// </summary>
/// <param name="sender">事件发送者。</param>
/// <param name="e">鼠标按钮事件参数。</param>
/// <param name="e">路由事件参数。</param>
public async void UnFoldFloatingBar_MouseUp(object sender, MouseButtonEventArgs e)
{
await UnFoldFloatingBar(sender);
-76
View File
@@ -1,76 +0,0 @@
using IWshRuntimeLibrary;
using System;
using System.Windows;
using Application = System.Windows.Forms.Application;
using File = System.IO.File;
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
/// <summary>
/// 创建开机自启动快捷方式。
/// </summary>
/// <param name="exeName">可执行文件名,用于命名快捷方式。</param>
/// <returns>创建成功返回true,失败返回false。</returns>
/// <remarks>
/// 操作包括:
/// 1. 创建Windows Shell对象
/// 2. 在启动文件夹中创建快捷方式
/// 3. 设置快捷方式的目标路径为当前可执行文件路径
/// 4. 设置工作目录为当前目录
/// 5. 设置窗口样式为普通窗口
/// 6. 设置快捷方式描述
/// 7. 保存快捷方式
/// 8. 捕获可能的异常,确保方法不会因异常而崩溃
/// </remarks>
public static bool StartAutomaticallyCreate(string exeName)
{
try
{
var shell = new WshShell();
var shortcut = (IWshShortcut)shell.CreateShortcut(
Environment.GetFolderPath(Environment.SpecialFolder.Startup) + "\\" + exeName + ".lnk");
//设置快捷方式的目标所在的位置(源程序完整路径)
shortcut.TargetPath = Application.ExecutablePath;
//应用程序的工作目录
//当用户没有指定一个具体的目录时,快捷方式的目标应用程序将使用该属性所指定的目录来装载或保存文件。
shortcut.WorkingDirectory = Environment.CurrentDirectory;
//目标应用程序窗口类型(1.Normal window普通窗口,3.Maximized最大化窗口,7.Minimized最小化)
shortcut.WindowStyle = 1;
//快捷方式的描述
shortcut.Description = exeName + "_Ink";
//设置快捷键(如果有必要的话.)
//shortcut.Hotkey = "CTRL+ALT+D";
shortcut.Save();
return true;
}
catch (Exception) { }
return false;
}
/// <summary>
/// 删除开机自启动快捷方式。
/// </summary>
/// <param name="exeName">可执行文件名,用于定位要删除的快捷方式。</param>
/// <returns>删除成功返回true,失败返回false。</returns>
/// <remarks>
/// 操作包括:
/// 1. 在启动文件夹中删除指定名称的快捷方式
/// 2. 捕获可能的异常,确保方法不会因异常而崩溃
/// </remarks>
public static bool StartAutomaticallyDel(string exeName)
{
try
{
File.Delete(Environment.GetFolderPath(Environment.SpecialFolder.Startup) + "\\" + exeName +
".lnk");
return true;
}
catch (Exception) { }
return false;
}
}
}
+123 -354
View File
@@ -1,3 +1,4 @@
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Modern;
using Microsoft.Win32;
using System;
@@ -14,19 +15,18 @@ namespace Ink_Canvas
{
public partial class MainWindow : Window
{
/// <summary>
/// 浮动栏前景色,根据当前主题动态更新。
/// </summary>
private const string ThemeLight = "Light";
private const string ThemeDark = "Dark";
private const string LightThemePath = "Resources/Styles/Light.xaml";
private const string DarkThemePath = "Resources/Styles/Dark.xaml";
private const string DrawShapeImagePath = "Resources/DrawShapeImageDictionary.xaml";
private const string SeewoImagePath = "Resources/SeewoImageDictionary.xaml";
private const string IconImagePath = "Resources/IconImageDictionary.xaml";
private Color FloatBarForegroundColor;
/// <summary>
/// 应用并切换到指定的主题("Light" 或 "Dark"),更新主题资源并刷新相关 UI 元素以反映主题变化。
/// </summary>
/// <param name="theme">主题标识,支持 "Light" 或 "Dark"(区分大小写)。</param>
/// <param name="autoSwitchIcon">若为 true,则根据主题自动切换并保存浮动工具栏的图标设置。</param>
private void SetTheme(string theme, bool autoSwitchIcon = false)
{
// 清理现有的主题资源
var resourcesToRemove = new List<ResourceDictionary>();
foreach (var dict in Application.Current.Resources.MergedDictionaries)
{
@@ -43,195 +43,85 @@ namespace Ink_Canvas
Application.Current.Resources.MergedDictionaries.Remove(dict);
}
if (theme == "Light")
var isLightTheme = theme == ThemeLight;
var themePath = isLightTheme ? LightThemePath : DarkThemePath;
var elementTheme = isLightTheme ? ElementTheme.Light : ElementTheme.Dark;
var rd1 = new ResourceDictionary { Source = new Uri(themePath, UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd1);
_ = Task.Run(async () =>
{
// 先加载主题
var rd1 = new ResourceDictionary
await Task.Delay(100);
Dispatcher.Invoke(() =>
{
Source = new Uri("Resources/Styles/Light.xaml", UriKind.Relative)
};
Application.Current.Resources.MergedDictionaries.Add(rd1);
// 异步加载图形资源,避免阻塞启动
_ = Task.Run(async () =>
{
await Task.Delay(100);
Dispatcher.Invoke(() =>
{
var rd2 = new ResourceDictionary
{
Source = new Uri("Resources/DrawShapeImageDictionary.xaml", UriKind.Relative)
};
Application.Current.Resources.MergedDictionaries.Add(rd2);
var rd3 = new ResourceDictionary
{
Source = new Uri("Resources/SeewoImageDictionary.xaml", UriKind.Relative)
};
Application.Current.Resources.MergedDictionaries.Add(rd3);
var rd4 = new ResourceDictionary
{
Source = new Uri("Resources/IconImageDictionary.xaml", UriKind.Relative)
};
Application.Current.Resources.MergedDictionaries.Add(rd4);
});
LoadImageResourceDictionary(DrawShapeImagePath);
LoadImageResourceDictionary(SeewoImagePath);
LoadImageResourceDictionary(IconImagePath);
});
});
ThemeManager.SetRequestedTheme(window, ElementTheme.Light);
ThemeManager.SetRequestedTheme(window, elementTheme);
InitializeFloatBarForegroundColor();
InitializeFloatBarForegroundColor();
RefreshQuickPanelIcons();
RefreshStrokeSelectionIcons();
RefreshImageSelectionIcons();
RefreshGestureButtonIcon();
RefreshFloatingBarHighlightColors();
// 刷新快速面板图标
RefreshQuickPanelIcons();
// 刷新墨迹选中栏图标
RefreshStrokeSelectionIcons();
// 刷新图片选中栏图标
RefreshImageSelectionIcons();
// 刷新手势按钮图标
RefreshGestureButtonIcon();
RefreshFloatingBarHighlightColors();
if (autoSwitchIcon)
{
AutoSwitchFloatingBarIconForTheme("Light");
}
// 强制刷新UI
window.InvalidateVisual();
// 通知其他窗口刷新主题
RefreshOtherWindowsTheme();
}
else if (theme == "Dark")
if (autoSwitchIcon)
{
// 先加载主题
var rd1 = new ResourceDictionary { Source = new Uri("Resources/Styles/Dark.xaml", UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd1);
// 异步加载图形资源,避免阻塞启动
_ = Task.Run(async () =>
{
await Task.Delay(100);
Dispatcher.Invoke(() =>
{
var rd2 = new ResourceDictionary
{
Source = new Uri("Resources/DrawShapeImageDictionary.xaml", UriKind.Relative)
};
Application.Current.Resources.MergedDictionaries.Add(rd2);
var rd3 = new ResourceDictionary
{
Source = new Uri("Resources/SeewoImageDictionary.xaml", UriKind.Relative)
};
Application.Current.Resources.MergedDictionaries.Add(rd3);
var rd4 = new ResourceDictionary
{
Source = new Uri("Resources/IconImageDictionary.xaml", UriKind.Relative)
};
Application.Current.Resources.MergedDictionaries.Add(rd4);
});
});
ThemeManager.SetRequestedTheme(window, ElementTheme.Dark);
InitializeFloatBarForegroundColor();
// 刷新快速面板图标
RefreshQuickPanelIcons();
// 刷新墨迹选中栏图标
RefreshStrokeSelectionIcons();
// 刷新图片选中栏图标
RefreshImageSelectionIcons();
// 刷新手势按钮图标
RefreshGestureButtonIcon();
RefreshFloatingBarHighlightColors();
if (autoSwitchIcon)
{
AutoSwitchFloatingBarIconForTheme("Dark");
}
// 强制刷新UI
window.InvalidateVisual();
// 通知其他窗口刷新主题
RefreshOtherWindowsTheme();
AutoSwitchFloatingBarIconForTheme(theme);
}
window.InvalidateVisual();
RefreshOtherWindowsTheme();
}
void LoadImageResourceDictionary(string path)
{
var rd = new ResourceDictionary { Source = new Uri(path, UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd);
}
/// <summary>
/// 初始化FloatBarForegroundColor,从当前主题资源中加载颜色
/// </summary>
private void InitializeFloatBarForegroundColor()
{
try
{
FloatBarForegroundColor = (Color)Application.Current.FindResource("FloatBarForegroundColor");
// 强制刷新浮动工具栏按钮颜色
RefreshFloatingBarButtonColors();
}
catch (Exception)
{
// 如果无法从资源中加载,使用默认颜色
FloatBarForegroundColor = Color.FromRgb(0, 0, 0);
}
}
/// <summary>
/// 刷新快速面板图标
/// </summary>
private void RefreshQuickPanelIcons()
{
try
{
if (LeftUnFoldButtonQuickPanel != null)
{
LeftUnFoldButtonQuickPanel.InvalidateVisual();
}
if (RightUnFoldButtonQuickPanel != null)
{
RightUnFoldButtonQuickPanel.InvalidateVisual();
}
if (LeftSidePanel != null)
{
LeftSidePanel.InvalidateVisual();
}
if (RightSidePanel != null)
{
RightSidePanel.InvalidateVisual();
}
LeftUnFoldButtonQuickPanel?.InvalidateVisual();
RightUnFoldButtonQuickPanel?.InvalidateVisual();
LeftSidePanel?.InvalidateVisual();
RightSidePanel?.InvalidateVisual();
}
catch (Exception)
{
}
}
/// <summary>
/// 刷新浮动栏高光条颜色
/// </summary>
private void RefreshFloatingBarHighlightColors()
{
try
{
if (FloatingbarSelectionBG != null && FloatingbarSelectionBG.Visibility == Visibility.Visible)
{
// 根据主题设置高光颜色
bool isDarkTheme = IsCurrentThemeDark();
Color highlightBackgroundColor;
Color highlightBarColor;
bool isDarkTheme = Settings.Appearance.Theme == 1 ||
(Settings.Appearance.Theme == 2 && !IsSystemThemeLight());
if (isDarkTheme)
{
@@ -244,7 +134,6 @@ namespace Ink_Canvas
highlightBarColor = Color.FromRgb(37, 99, 235);
}
// 设置高光背景颜色
FloatingbarSelectionBG.Background = new SolidColorBrush(highlightBackgroundColor);
if (FloatingbarSelectionBG.Child is System.Windows.Controls.Canvas canvas && canvas.Children.Count > 0)
{
@@ -261,73 +150,67 @@ namespace Ink_Canvas
}
}
/// <summary>
/// 刷新浮动工具栏按钮颜色
/// </summary>
private bool IsCurrentThemeDark()
{
return Settings.Appearance.Theme == 1 ||
(Settings.Appearance.Theme == 2 && !ThemeHelper.IsSystemThemeLight());
}
private void RefreshFloatingBarButtonColors()
{
try
{
// 根据主题选择高光颜色
Color selectedColor;
bool isDarkTheme = Settings.Appearance.Theme == 1 ||
(Settings.Appearance.Theme == 2 && !IsSystemThemeLight());
SymbolIconDelete.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.DeleteIcon);
ShapeDrawFloatingBarBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.ShapesIcon);
SymbolIconUndo.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.UndoIcon);
SymbolIconRedo.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.RedoIcon);
CursorWithDelFloatingBarBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.CursorWithDelFloatingBarBtnIcon);
WhiteboardFloatingBarBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.WhiteboardFloatingBarBtnIcon);
ToolsFloatingBarBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.ToolsFloatingBarBtnIcon);
Fold_Icon.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.FoldIcon);
if (isDarkTheme)
{
selectedColor = Color.FromRgb(102, 204, 255);
}
else
{
selectedColor = Color.FromRgb(30, 58, 138);
}
TimerToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.TimerIconGeometry);
RandomDrawToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.RandomDrawIconGeometry);
SingleDrawToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.SingleDrawIconGeometry);
SaveToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.SaveIconGeometry);
OpenToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.OpenIconGeometry);
ReplayToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.ReplayIconGeometry);
ScreenshotToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.ScreenshotIconGeometry);
ManualToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.ManualIconGeometry);
SettingsToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.SettingsIconGeometry);
BoardTimerToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.TimerIconGeometry);
BoardRandomDrawToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.RandomDrawIconGeometry);
BoardSingleDrawToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.SingleDrawIconGeometry);
BoardSaveToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.SaveIconGeometry);
BoardOpenToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.OpenIconGeometry);
BoardReplayToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.ReplayIconGeometry);
BoardScreenshotToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.ScreenshotIconGeometry);
BoardManualToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.ManualIconGeometry);
BoardSettingsToolBtn.Icon.Geometry = Geometry.Parse(XamlGraphicsIconGeometries.SettingsIconGeometry);
bool isDarkTheme = IsCurrentThemeDark();
Color selectedColor = isDarkTheme ? Color.FromRgb(102, 204, 255) : Color.FromRgb(30, 58, 138);
SetAllFloatingBarButtonsToColor(FloatBarForegroundColor);
// 根据当前模式设置按钮颜色
switch (_currentToolMode)
{
case "cursor":
CursorIconGeometry.Brush = new SolidColorBrush(selectedColor);
PenIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
StrokeEraserIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
CircleEraserIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
LassoSelectIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
Cursor_Icon.Icon.Brush = new SolidColorBrush(selectedColor);
break;
case "pen":
case "color":
CursorIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
PenIconGeometry.Brush = new SolidColorBrush(selectedColor);
StrokeEraserIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
CircleEraserIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
LassoSelectIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
Pen_Icon.Icon.Brush = new SolidColorBrush(selectedColor);
break;
case "eraser":
CursorIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
PenIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
StrokeEraserIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
CircleEraserIconGeometry.Brush = new SolidColorBrush(selectedColor);
LassoSelectIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
Eraser_Icon.Icon.Brush = new SolidColorBrush(selectedColor);
break;
case "eraserByStrokes":
CursorIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
PenIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
StrokeEraserIconGeometry.Brush = new SolidColorBrush(selectedColor);
CircleEraserIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
LassoSelectIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
EraserByStrokes_Icon.Icon.Brush = new SolidColorBrush(selectedColor);
break;
case "select":
CursorIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
PenIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
StrokeEraserIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
CircleEraserIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
LassoSelectIconGeometry.Brush = new SolidColorBrush(selectedColor);
break;
default:
// 默认情况,所有按钮都使用主题颜色
CursorIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
PenIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
StrokeEraserIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
CircleEraserIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
LassoSelectIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
SymbolIconSelect.Icon.Brush = new SolidColorBrush(selectedColor);
break;
}
}
@@ -336,79 +219,44 @@ namespace Ink_Canvas
}
}
/// <summary>
/// 处理系统主题偏好变化事件,根据当前设置更新应用主题。
/// </summary>
/// <param name="sender">事件发送者。</param>
/// <param name="e">用户偏好变化事件参数。</param>
/// <remarks>
/// 操作包括:
/// 1. 根据当前主题设置(Settings.Appearance.Theme)决定使用哪种主题
/// 2. 如果设置为0(浅色主题),则设置为Light主题
/// 3. 如果设置为1(深色主题),则设置为Dark主题
/// 4. 如果设置为2(跟随系统主题),则根据系统主题设置应用相应的主题
/// </remarks>
void SetAllFloatingBarButtonsToColor(Color color)
{
var brush = new SolidColorBrush(color);
Cursor_Icon.Icon.Brush = brush;
Pen_Icon.Icon.Brush = brush;
EraserByStrokes_Icon.Icon.Brush = brush;
Eraser_Icon.Icon.Brush = brush;
SymbolIconSelect.Icon.Brush = brush;
ShapeDrawFloatingBarBtn.Icon.Brush = brush;
SymbolIconUndo.Icon.Brush = brush;
SymbolIconRedo.Icon.Brush = brush;
CursorWithDelFloatingBarBtn.Icon.Brush = brush;
WhiteboardFloatingBarBtn.Icon.Brush = brush;
ToolsFloatingBarBtn.Icon.Brush = brush;
Fold_Icon.Icon.Brush = brush;
}
private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
{
switch (Settings.Appearance.Theme)
{
case 0:
SetTheme("Light");
SetTheme(ThemeLight);
break;
case 1:
SetTheme("Dark");
SetTheme(ThemeDark);
break;
case 2:
if (IsSystemThemeLight()) SetTheme("Light");
else SetTheme("Dark");
SetTheme(ThemeHelper.IsSystemThemeLightLegacy() ? ThemeLight : ThemeDark);
break;
}
}
/// <summary>
/// 检查系统主题是否为浅色主题。
/// </summary>
/// <returns>系统主题为浅色返回true,深色返回false。</returns>
/// <remarks>
/// 操作包括:
/// 1. 从注册表中读取系统主题设置
/// 2. 检查"SystemUsesLightTheme"键的值
/// 3. 如果值为1,则表示系统使用浅色主题
/// 4. 捕获可能的异常,确保方法不会因异常而崩溃
/// </remarks>
private bool IsSystemThemeLight()
{
var light = false;
try
{
var registryKey = Registry.CurrentUser;
var themeKey =
registryKey.OpenSubKey("software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize");
var keyValue = 0;
if (themeKey != null) keyValue = (int)themeKey.GetValue("SystemUsesLightTheme");
if (keyValue == 1) light = true;
}
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
return light;
}
/// <summary>
/// 根据主题自动切换浮动栏图标
/// </summary>
private void AutoSwitchFloatingBarIconForTheme(string theme)
{
try
{
if (theme == "Light")
{
Settings.Appearance.FloatingBarImg = 0;
}
else if (theme == "Dark")
{
Settings.Appearance.FloatingBarImg = 3;
}
Settings.Appearance.FloatingBarImg = theme == ThemeLight ? 0 : 3;
UpdateFloatingBarIcon();
UpdateFloatingBarIconComboBox();
}
@@ -417,111 +265,49 @@ namespace Ink_Canvas
}
}
/// <summary>
/// 更新设置界面中的浮动栏图标选择下拉框显示
/// </summary>
private void UpdateFloatingBarIconComboBox()
{
try
{
if (ComboBoxFloatingBarImg != null)
{
ComboBoxFloatingBarImg.SelectedIndex = Settings.Appearance.FloatingBarImg;
}
}
catch (Exception)
{
}
}
/// <summary>
/// 刷新墨迹选中栏图标
/// </summary>
private void RefreshStrokeSelectionIcons()
{
try
{
if (BorderStrokeSelectionControl != null)
{
// 强制刷新墨迹选中栏的视觉状态
BorderStrokeSelectionControl.InvalidateVisual();
// 刷新墨迹选中栏内的所有图标
var viewbox = BorderStrokeSelectionControl.Child as Viewbox;
if (viewbox?.Child is ui.SimpleStackPanel stackPanel)
{
RefreshStrokeSelectionIconsRecursive(stackPanel);
RefreshIconsRecursive(stackPanel);
}
}
}
catch (Exception)
{
// 忽略异常,确保主题切换不会因为图标刷新失败而中断
}
}
/// <summary>
/// 递归刷新墨迹选中栏内的图标
/// </summary>
private void RefreshStrokeSelectionIconsRecursive(System.Windows.Controls.Panel panel)
{
try
{
foreach (var child in panel.Children)
{
if (child is Image image)
{
// 强制刷新图像
image.InvalidateVisual();
}
else if (child is System.Windows.Controls.Panel childPanel)
{
// 递归处理子面板
RefreshStrokeSelectionIconsRecursive(childPanel);
}
else if (child is Border border && border.Child is System.Windows.Controls.Panel borderPanel)
{
// 处理Border内的面板
RefreshStrokeSelectionIconsRecursive(borderPanel);
}
}
}
catch (Exception)
{
// 忽略异常
}
}
/// <summary>
/// 刷新图片选中栏图标
/// </summary>
private void RefreshImageSelectionIcons()
{
try
{
if (BorderImageSelectionControl != null)
{
// 强制刷新图片选中栏的视觉状态
BorderImageSelectionControl.InvalidateVisual();
// 刷新图片选中栏内的所有图标
var viewbox = BorderImageSelectionControl.Child as Viewbox;
if (viewbox?.Child is ui.SimpleStackPanel stackPanel)
{
RefreshImageSelectionIconsRecursive(stackPanel);
RefreshIconsRecursive(stackPanel);
}
}
}
catch (Exception)
{
// 忽略异常,确保主题切换不会因为图标刷新失败而中断
}
}
/// <summary>
/// 递归刷新图片选中栏内的图标
/// </summary>
private void RefreshImageSelectionIconsRecursive(System.Windows.Controls.Panel panel)
private void RefreshIconsRecursive(System.Windows.Controls.Panel panel)
{
try
{
@@ -529,22 +315,18 @@ namespace Ink_Canvas
{
if (child is Image image)
{
// 强制刷新图像
image.InvalidateVisual();
}
else if (child is System.Windows.Controls.Panel childPanel)
{
// 递归处理子面板
RefreshImageSelectionIconsRecursive(childPanel);
RefreshIconsRecursive(childPanel);
}
else if (child is Border border && border.Child is System.Windows.Controls.Panel borderPanel)
{
// 处理Border内的面板
RefreshImageSelectionIconsRecursive(borderPanel);
RefreshIconsRecursive(borderPanel);
}
else if (child is Grid grid)
{
// 处理Grid内的子元素
foreach (var gridChild in grid.Children)
{
if (gridChild is Image gridImage)
@@ -557,18 +339,13 @@ namespace Ink_Canvas
}
catch (Exception)
{
// 忽略异常
}
}
/// <summary>
/// 刷新手势按钮图标
/// </summary>
private void RefreshGestureButtonIcon()
{
try
{
// 调用手势按钮颜色和图标更新方法,该方法会根据当前主题和手势状态设置正确的图标
CheckEnableTwoFingerGestureBtnColorPrompt();
}
catch (Exception)
@@ -576,14 +353,10 @@ namespace Ink_Canvas
}
}
/// <summary>
/// 刷新其他窗口的主题
/// </summary>
private void RefreshOtherWindowsTheme()
{
try
{
// 刷新所有打开的窗口
foreach (Window window in Application.Current.Windows)
{
if (window is CountdownTimerWindow timerWindow)
@@ -598,22 +371,18 @@ namespace Ink_Canvas
{
operatingGuideWindow.RefreshTheme();
}
else if (window is Windows.SettingsViews.SettingsWindow settingsWindow)
{
settingsWindow.RefreshTheme();
}
}
// 刷新计时器控件
if (TimerControl != null)
{
TimerControl.RefreshTheme();
}
if (MinimizedTimerControl != null)
{
MinimizedTimerControl.RefreshTheme();
}
TimerControl?.RefreshTheme();
MinimizedTimerControl?.RefreshTheme();
}
catch (Exception)
{
}
}
}
}
}
+27 -17
View File
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
@@ -426,7 +427,10 @@ namespace Ink_Canvas
/// <remarks>
/// 该方法在切换前会取消当前选中元素(同时保留并恢复编辑模式)、调用视频呈现器的离开页前钩子、保存当前页的笔迹与元素、清空画布;切换到前一页后恢复该页内容、调用视频呈现器的页已更改钩子并刷新页面索引显示。
/// </remarks>
private void BtnWhiteBoardSwitchPrevious_Click(object sender, EventArgs e)
private void BoardBtnWhiteBoardSwitchPrevious_MouseUp(object sender, MouseButtonEventArgs e)
=> BtnWhiteBoardSwitchPrevious_Click(sender, e);
private void BtnWhiteBoardSwitchPrevious_Click(object sender, RoutedEventArgs e)
{
if (CurrentWhiteboardIndex <= 1) return;
@@ -458,7 +462,10 @@ namespace Ink_Canvas
/// </summary>
/// <param name="sender">触发事件的源对象(通常为按钮)。</param>
/// <param name="e">事件参数。</param>
private void BtnWhiteBoardSwitchNext_Click(object sender, EventArgs e)
private void BoardBtnWhiteBoardSwitchNext_MouseUp(object sender, MouseButtonEventArgs e)
=> BtnWhiteBoardSwitchNext_Click(sender, e);
private void BtnWhiteBoardSwitchNext_Click(object sender, RoutedEventArgs e)
{
if (CurrentWhiteboardIndex < WhiteboardTotalCount &&
Settings.Automation.IsAutoSaveStrokesAtClear &&
@@ -505,7 +512,10 @@ namespace Ink_Canvas
/// - 将当前页面的历史保存到时间轴并清空画布,然后在白板集合中插入一个空白页面(其历史为 null),随后恢复该页面并触发页面变更回调。
/// - 更新页码显示并在达到上限时禁用添加按钮;若侧边页列表可见,则刷新该列表。
/// </remarks>
private void BtnWhiteBoardAdd_Click(object sender, EventArgs e)
private void BoardBtnWhiteBoardAdd_MouseUp(object sender, MouseButtonEventArgs e)
=> BtnWhiteBoardAdd_Click(sender, e);
private void BtnWhiteBoardAdd_Click(object sender, RoutedEventArgs e)
{
if (WhiteboardTotalCount >= 99) return;
if (Settings.Automation.IsAutoSaveStrokesAtClear &&
@@ -652,8 +662,8 @@ namespace Ink_Canvas
bool isMaxPage = WhiteboardTotalCount >= 99;
// 设置按钮文本
BtnLeftWhiteBoardSwitchNextLabel.Text = isLastPage ? "新页面" : "下一页";
BtnRightWhiteBoardSwitchNextLabel.Text = isLastPage ? "新页面" : "下一页";
BtnLeftWhiteBoardSwitchNext.LabelTextBlockControl.Text = isLastPage ? "新页面" : "下一页";
BtnRightWhiteBoardSwitchNext.LabelTextBlockControl.Text = isLastPage ? "新页面" : "下一页";
if (isLastPage)
{
@@ -670,11 +680,11 @@ namespace Ink_Canvas
// 设置下一页按钮颜色
if (iconForegroundBrush != null)
{
BtnLeftWhiteBoardSwitchNextGeometry.Brush = iconForegroundBrush;
BtnRightWhiteBoardSwitchNextGeometry.Brush = iconForegroundBrush;
BtnLeftWhiteBoardSwitchNext.IconGeometryDrawing.Brush = iconForegroundBrush;
BtnRightWhiteBoardSwitchNext.IconGeometryDrawing.Brush = iconForegroundBrush;
}
BtnLeftWhiteBoardSwitchNextLabel.Opacity = 1;
BtnRightWhiteBoardSwitchNextLabel.Opacity = 1;
BtnLeftWhiteBoardSwitchNext.LabelTextBlockControl.Opacity = 1;
BtnRightWhiteBoardSwitchNext.LabelTextBlockControl.Opacity = 1;
BtnWhiteBoardSwitchPrevious.IsEnabled = true;
@@ -684,21 +694,21 @@ namespace Ink_Canvas
if (iconForegroundBrush != null)
{
var disabledBrush = new SolidColorBrush(Color.FromArgb(127, iconForegroundBrush.Color.R, iconForegroundBrush.Color.G, iconForegroundBrush.Color.B));
BtnLeftWhiteBoardSwitchPreviousGeometry.Brush = disabledBrush;
BtnRightWhiteBoardSwitchPreviousGeometry.Brush = disabledBrush;
BtnLeftWhiteBoardSwitchPrevious.IconGeometryDrawing.Brush = disabledBrush;
BtnRightWhiteBoardSwitchPrevious.IconGeometryDrawing.Brush = disabledBrush;
}
BtnLeftWhiteBoardSwitchPreviousLabel.Opacity = 0.5;
BtnRightWhiteBoardSwitchPreviousLabel.Opacity = 0.5;
BtnLeftWhiteBoardSwitchPrevious.LabelTextBlockControl.Opacity = 0.5;
BtnRightWhiteBoardSwitchPrevious.LabelTextBlockControl.Opacity = 0.5;
}
else
{
if (iconForegroundBrush != null)
{
BtnLeftWhiteBoardSwitchPreviousGeometry.Brush = iconForegroundBrush;
BtnRightWhiteBoardSwitchPreviousGeometry.Brush = iconForegroundBrush;
BtnLeftWhiteBoardSwitchPrevious.IconGeometryDrawing.Brush = iconForegroundBrush;
BtnRightWhiteBoardSwitchPrevious.IconGeometryDrawing.Brush = iconForegroundBrush;
}
BtnLeftWhiteBoardSwitchPreviousLabel.Opacity = 1;
BtnRightWhiteBoardSwitchPreviousLabel.Opacity = 1;
BtnLeftWhiteBoardSwitchPrevious.LabelTextBlockControl.Opacity = 1;
BtnRightWhiteBoardSwitchPrevious.LabelTextBlockControl.Opacity = 1;
}
BtnWhiteBoardDelete.IsEnabled = WhiteboardTotalCount != 1;
+127 -642
View File
@@ -3,10 +3,8 @@ using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Ink_Canvas
{
@@ -29,594 +27,159 @@ namespace Ink_Canvas
{
if (!isLoaded) return;
// 创建背景选项面板(如果不存在)
if (BackgroundPalette == null)
if (BackgroundPalette.Visibility == Visibility.Visible)
{
CreateBackgroundPalette();
}
// 显示或隐藏背景选项面板
if (BackgroundPalette != null)
{
if (BackgroundPalette.Visibility == Visibility.Visible)
{
// 如果面板已经显示,则隐藏它
AnimationsHelper.HideWithSlideAndFade(BackgroundPalette);
}
else
{
// 隐藏其他可能显示的面板
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
AnimationsHelper.HideWithSlideAndFade(BorderTools);
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
AnimationsHelper.HideWithSlideAndFade(PenPalette);
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
AnimationsHelper.HideWithSlideAndFade(BoardBorderDrawShape);
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
// 显示背景选项面板
AnimationsHelper.ShowWithSlideFromBottomAndFade(BackgroundPalette);
}
return;
}
// 原有的背景切换代码
Settings.Canvas.UsingWhiteboard = !Settings.Canvas.UsingWhiteboard;
SaveSettingsToFile();
if (Settings.Canvas.UsingWhiteboard)
{
if (inkColor == 5) lastBoardInkColor = 0;
ICCWaterMarkDark.Visibility = Visibility.Visible;
ICCWaterMarkWhite.Visibility = Visibility.Collapsed;
// 设置为白板默认背景色
Color defaultWhiteboardColor = Color.FromRgb(255, 255, 255);
if (currentMode == 1) // 白板模式
{
// 设置背景为默认白板背景色
GridBackgroundCover.Background = new SolidColorBrush(defaultWhiteboardColor);
// 更新RGB滑块的值为默认白板背景色
if (BackgroundPalette != null && BackgroundPalette.Visibility == Visibility.Visible)
{
UpdateRGBSliders(defaultWhiteboardColor);
}
// 更新自定义背景色为默认白板背景色
CustomBackgroundColor = defaultWhiteboardColor;
// 保存到设置
string colorHex = $"#{defaultWhiteboardColor.R:X2}{defaultWhiteboardColor.G:X2}{defaultWhiteboardColor.B:X2}";
Settings.Canvas.CustomBackgroundColor = colorHex;
SaveSettingsToFile();
}
// 设置墨迹颜色为黑色
CheckLastColor(0);
forceEraser = false;
AnimationsHelper.HideWithSlideAndFade(BackgroundPalette);
}
else
{
if (inkColor == 0) lastBoardInkColor = 5;
ICCWaterMarkWhite.Visibility = Visibility.Visible;
ICCWaterMarkDark.Visibility = Visibility.Collapsed;
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
AnimationsHelper.HideWithSlideAndFade(BorderTools);
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
AnimationsHelper.HideWithSlideAndFade(PenPalette);
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
AnimationsHelper.HideWithSlideAndFade(BoardBorderDrawShape);
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
// 设置为黑板默认背景色
Color defaultBlackboardColor = Color.FromRgb(22, 41, 36);
if (currentMode == 1) // 黑板模式
{
// 设置背景为默认黑板背景色
GridBackgroundCover.Background = new SolidColorBrush(defaultBlackboardColor);
// 更新RGB滑块的值为默认黑板背景色
if (BackgroundPalette != null && BackgroundPalette.Visibility == Visibility.Visible)
{
UpdateRGBSliders(defaultBlackboardColor);
}
// 更新自定义背景色为默认黑板背景色
CustomBackgroundColor = defaultBlackboardColor;
// 保存到设置
string colorHex = $"#{defaultBlackboardColor.R:X2}{defaultBlackboardColor.G:X2}{defaultBlackboardColor.B:X2}";
Settings.Canvas.CustomBackgroundColor = colorHex;
SaveSettingsToFile();
}
// 设置墨迹颜色为白色
CheckLastColor(5);
forceEraser = false;
LoadCustomBackgroundColor();
UpdateBackgroundButtonsState();
AnimationsHelper.ShowWithSlideFromBottomAndFade(BackgroundPalette);
}
CheckColorTheme(true);
}
/// <summary>
/// 创建背景颜色选项面板
/// </summary>
/// <remarks>
/// - 加载自定义背景色
/// - 创建背景选项面板UI
/// - 添加标题栏和关闭按钮
/// - 添加白板/黑板模式选择按钮
/// - 添加RGB颜色选择器
/// - 添加颜色预览和应用按钮
/// - 将面板添加到主网格
/// </remarks>
private void CreateBackgroundPalette()
private void WhiteboardModeBtn_MouseUp(object sender, MouseButtonEventArgs e)
{
// 确保加载自定义背景色
LoadCustomBackgroundColor();
Settings.Canvas.UsingWhiteboard = true;
SaveSettingsToFile();
ICCWaterMarkDark.Visibility = Visibility.Visible;
ICCWaterMarkWhite.Visibility = Visibility.Collapsed;
// 创建一个类似于PenPalette的面板
BackgroundPalette = new Border
Color defaultWhiteboardColor = Color.FromRgb(255, 255, 255);
if (currentMode == 1)
{
Name = "BackgroundPalette",
Visibility = Visibility.Collapsed,
Background = (SolidColorBrush)Application.Current.FindResource("SettingsPageBackground"),
Opacity = 1,
BorderBrush = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)),
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(8),
Width = 300,
MaxHeight = 400
};
// 确保面板显示在顶层
Panel.SetZIndex(BackgroundPalette, 1000);
// 创建面板内容
var stackPanel = new StackPanel();
// 创建标题栏
var titleBorder = new Border
{
BorderBrush = new SolidColorBrush(Color.FromRgb(0x1e, 0x3a, 0x8a)),
Height = 32,
BorderThickness = new Thickness(0, 0, 0, 1),
CornerRadius = new CornerRadius(8, 8, 0, 0),
Background = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)),
Margin = new Thickness(-1, -1, -1, 0),
Padding = new Thickness(1, 1, 1, 0)
};
var titleCanvas = new System.Windows.Controls.Canvas { Height = 24, ClipToBounds = true };
var titleText = new TextBlock
{
Text = "背景设置",
Foreground = (SolidColorBrush)Application.Current.FindResource("FloatBarForeground"),
Padding = new Thickness(0, 5, 0, 0),
FontSize = 11,
FontWeight = FontWeights.Bold,
TextAlignment = TextAlignment.Center
};
System.Windows.Controls.Canvas.SetLeft(titleText, 8);
titleCanvas.Children.Add(titleText);
// 关闭按钮
var closeImage = new Image
{
Source = new BitmapImage(new Uri("/Resources/new-icons/close-white.png", UriKind.Relative)),
Height = 16,
Width = 16
};
RenderOptions.SetBitmapScalingMode(closeImage, BitmapScalingMode.HighQuality);
closeImage.MouseUp += CloseBordertools_MouseUp;
System.Windows.Controls.Canvas.SetRight(closeImage, 8);
System.Windows.Controls.Canvas.SetTop(closeImage, 4);
titleCanvas.Children.Add(closeImage);
titleBorder.Child = titleCanvas;
stackPanel.Children.Add(titleBorder);
// 创建背景选项内容区域
var contentPanel = new StackPanel { Margin = new Thickness(8) };
// 黑板/白板选择
var modeTitle = new TextBlock
{
Text = "白板模式",
Foreground = (SolidColorBrush)Application.Current.FindResource("TextForeground"),
FontSize = 10,
FontWeight = FontWeights.Bold,
HorizontalAlignment = HorizontalAlignment.Center,
Margin = new Thickness(0, 4, 0, 8)
};
contentPanel.Children.Add(modeTitle);
var modePanel = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Center };
// 白板按钮
var whiteboardButton = new Border
{
Width = 60,
Height = 30,
Background = Settings.Canvas.UsingWhiteboard ? new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)) : new SolidColorBrush(Colors.LightGray),
CornerRadius = new CornerRadius(4),
Margin = new Thickness(0, 0, 8, 0)
};
var whiteboardText = new TextBlock
{
Text = "白板",
Foreground = Settings.Canvas.UsingWhiteboard ? new SolidColorBrush(Colors.White) : new SolidColorBrush(Colors.Black),
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
whiteboardButton.Child = whiteboardText;
whiteboardButton.MouseUp += (s, args) =>
{
Settings.Canvas.UsingWhiteboard = true;
GridBackgroundCover.Background = new SolidColorBrush(defaultWhiteboardColor);
UpdateRGBSliders(defaultWhiteboardColor);
CustomBackgroundColor = defaultWhiteboardColor;
string colorHex = $"#{defaultWhiteboardColor.R:X2}{defaultWhiteboardColor.G:X2}{defaultWhiteboardColor.B:X2}";
Settings.Canvas.CustomBackgroundColor = colorHex;
SaveSettingsToFile();
ICCWaterMarkDark.Visibility = Visibility.Visible;
ICCWaterMarkWhite.Visibility = Visibility.Collapsed;
// 设置为白板默认背景色
Color defaultWhiteboardColor = Color.FromRgb(255, 255, 255);
if (currentMode == 1) // 白板模式
{
// 设置背景为默认白板背景色
GridBackgroundCover.Background = new SolidColorBrush(defaultWhiteboardColor);
// 更新RGB滑块的值为默认白板背景色
UpdateRGBSliders(defaultWhiteboardColor);
// 更新自定义背景色为默认白板背景色
CustomBackgroundColor = defaultWhiteboardColor;
// 保存到设置
string colorHex = $"#{defaultWhiteboardColor.R:X2}{defaultWhiteboardColor.G:X2}{defaultWhiteboardColor.B:X2}";
Settings.Canvas.CustomBackgroundColor = colorHex;
SaveSettingsToFile();
}
// 设置墨迹颜色为黑色
CheckLastColor(0);
forceEraser = false;
CheckColorTheme(true);
UpdateBackgroundButtonsState();
};
modePanel.Children.Add(whiteboardButton);
// 黑板按钮
var blackboardButton = new Border
{
Width = 60,
Height = 30,
Background = !Settings.Canvas.UsingWhiteboard ? new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)) : new SolidColorBrush(Colors.LightGray),
CornerRadius = new CornerRadius(4)
};
var blackboardText = new TextBlock
{
Text = "黑板",
Foreground = !Settings.Canvas.UsingWhiteboard ? new SolidColorBrush(Colors.White) : new SolidColorBrush(Colors.Black),
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
blackboardButton.Child = blackboardText;
blackboardButton.MouseUp += (s, args) =>
{
Settings.Canvas.UsingWhiteboard = false;
SaveSettingsToFile();
ICCWaterMarkWhite.Visibility = Visibility.Visible;
ICCWaterMarkDark.Visibility = Visibility.Collapsed;
// 设置为黑板默认背景色
Color defaultBlackboardColor = Color.FromRgb(22, 41, 36);
if (currentMode == 1) // 黑板模式
{
// 设置背景为默认黑板背景色
GridBackgroundCover.Background = new SolidColorBrush(defaultBlackboardColor);
// 更新RGB滑块的值为默认黑板背景色
UpdateRGBSliders(defaultBlackboardColor);
// 更新自定义背景色为默认黑板背景色
CustomBackgroundColor = defaultBlackboardColor;
// 保存到设置
string colorHex = $"#{defaultBlackboardColor.R:X2}{defaultBlackboardColor.G:X2}{defaultBlackboardColor.B:X2}";
Settings.Canvas.CustomBackgroundColor = colorHex;
SaveSettingsToFile();
}
// 设置墨迹颜色为白色
CheckLastColor(5);
forceEraser = false;
CheckColorTheme(true);
UpdateBackgroundButtonsState();
};
modePanel.Children.Add(blackboardButton);
contentPanel.Children.Add(modePanel);
// 添加一条分隔线
var separator = new Border
{
Height = 1,
Background = (SolidColorBrush)Application.Current.FindResource("SettingsPageBorderBrush"),
Margin = new Thickness(0, 12, 0, 12)
};
contentPanel.Children.Add(separator);
// 添加RGB颜色选择器部分
var colorTitle = new TextBlock
{
Text = "背景颜色",
Foreground = (SolidColorBrush)Application.Current.FindResource("TextForeground"),
FontSize = 10,
FontWeight = FontWeights.Bold,
HorizontalAlignment = HorizontalAlignment.Center,
Margin = new Thickness(0, 4, 0, 8)
};
contentPanel.Children.Add(colorTitle);
// 创建颜色预览
Border colorPreview = new Border
{
Width = 100,
Height = 40,
BorderThickness = new Thickness(1),
BorderBrush = (SolidColorBrush)Application.Current.FindResource("SettingsPageBorderBrush"),
Background = new SolidColorBrush(Colors.White),
CornerRadius = new CornerRadius(4),
Margin = new Thickness(0, 0, 0, 10),
HorizontalAlignment = HorizontalAlignment.Center
};
contentPanel.Children.Add(colorPreview);
// 获取当前背景颜色
Color currentBackgroundColor;
if (currentMode == 1) // 白板或黑板模式
{
if (GridBackgroundCover.Background is SolidColorBrush brush)
{
currentBackgroundColor = brush.Color;
}
else
{
// 默认颜色
currentBackgroundColor = Settings.Canvas.UsingWhiteboard ?
Color.FromRgb(234, 235, 237) : // 白板默认颜色
Color.FromRgb(22, 41, 36); // 黑板默认颜色
}
}
else
{
// 默认白色
currentBackgroundColor = Colors.White;
}
// 更新颜色预览
colorPreview.Background = new SolidColorBrush(currentBackgroundColor);
CheckLastColor(0);
forceEraser = false;
CheckColorTheme(true);
UpdateBackgroundButtonsState();
}
// 先创建所有滑块控件
// R滑块和文本框
var rPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(10, 0, 10, 5) };
var rLabel = new TextBlock { Text = "R:", Width = 20, VerticalAlignment = VerticalAlignment.Center, Foreground = (SolidColorBrush)Application.Current.FindResource("TextForeground") };
var rSlider = new Slider
private void BlackboardModeBtn_MouseUp(object sender, MouseButtonEventArgs e)
{
Settings.Canvas.UsingWhiteboard = false;
SaveSettingsToFile();
ICCWaterMarkWhite.Visibility = Visibility.Visible;
ICCWaterMarkDark.Visibility = Visibility.Collapsed;
Color defaultBlackboardColor = Color.FromRgb(22, 41, 36);
if (currentMode == 1)
{
Minimum = 0,
Maximum = 255,
Value = currentBackgroundColor.R,
Width = 150,
Margin = new Thickness(5, 0, 5, 0),
VerticalAlignment = VerticalAlignment.Center
};
var rValueText = new TextBlock
GridBackgroundCover.Background = new SolidColorBrush(defaultBlackboardColor);
UpdateRGBSliders(defaultBlackboardColor);
CustomBackgroundColor = defaultBlackboardColor;
string colorHex = $"#{defaultBlackboardColor.R:X2}{defaultBlackboardColor.G:X2}{defaultBlackboardColor.B:X2}";
Settings.Canvas.CustomBackgroundColor = colorHex;
SaveSettingsToFile();
}
CheckLastColor(5);
forceEraser = false;
CheckColorTheme(true);
UpdateBackgroundButtonsState();
}
private void BackgroundRSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (BackgroundRValue != null)
{
Text = currentBackgroundColor.R.ToString(),
Width = 30,
VerticalAlignment = VerticalAlignment.Center,
TextAlignment = TextAlignment.Right,
Foreground = (SolidColorBrush)Application.Current.FindResource("TextForeground")
};
// G滑块和文本框
var gPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(10, 0, 10, 5) };
var gLabel = new TextBlock { Text = "G:", Width = 20, VerticalAlignment = VerticalAlignment.Center, Foreground = (SolidColorBrush)Application.Current.FindResource("TextForeground") };
var gSlider = new Slider
{
Minimum = 0,
Maximum = 255,
Value = currentBackgroundColor.G,
Width = 150,
Margin = new Thickness(5, 0, 5, 0),
VerticalAlignment = VerticalAlignment.Center
};
var gValueText = new TextBlock
{
Text = currentBackgroundColor.G.ToString(),
Width = 30,
VerticalAlignment = VerticalAlignment.Center,
TextAlignment = TextAlignment.Right,
Foreground = (SolidColorBrush)Application.Current.FindResource("TextForeground")
};
// B滑块和文本框
var bPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(10, 0, 10, 5) };
var bLabel = new TextBlock { Text = "B:", Width = 20, VerticalAlignment = VerticalAlignment.Center, Foreground = (SolidColorBrush)Application.Current.FindResource("TextForeground") };
var bSlider = new Slider
{
Minimum = 0,
Maximum = 255,
Value = currentBackgroundColor.B,
Width = 150,
Margin = new Thickness(5, 0, 5, 0),
VerticalAlignment = VerticalAlignment.Center
};
var bValueText = new TextBlock
{
Text = currentBackgroundColor.B.ToString(),
Width = 30,
VerticalAlignment = VerticalAlignment.Center,
TextAlignment = TextAlignment.Right,
Foreground = (SolidColorBrush)Application.Current.FindResource("TextForeground")
};
// 现在添加事件处理程序
rSlider.ValueChanged += (s, e) =>
{
int value = (int)e.NewValue;
rValueText.Text = value.ToString();
UpdateColorPreview(colorPreview, rSlider, gSlider, bSlider);
};
gSlider.ValueChanged += (s, e) =>
{
int value = (int)e.NewValue;
gValueText.Text = value.ToString();
UpdateColorPreview(colorPreview, rSlider, gSlider, bSlider);
};
bSlider.ValueChanged += (s, e) =>
{
int value = (int)e.NewValue;
bValueText.Text = value.ToString();
UpdateColorPreview(colorPreview, rSlider, gSlider, bSlider);
};
// 添加控件到面板
rPanel.Children.Add(rLabel);
rPanel.Children.Add(rSlider);
rPanel.Children.Add(rValueText);
contentPanel.Children.Add(rPanel);
gPanel.Children.Add(gLabel);
gPanel.Children.Add(gSlider);
gPanel.Children.Add(gValueText);
contentPanel.Children.Add(gPanel);
bPanel.Children.Add(bLabel);
bPanel.Children.Add(bSlider);
bPanel.Children.Add(bValueText);
contentPanel.Children.Add(bPanel);
// 应用按钮
var applyButton = new Button
{
Content = "应用颜色",
Margin = new Thickness(0, 10, 0, 0),
Padding = new Thickness(10, 5, 10, 5),
Background = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)),
Foreground = new SolidColorBrush(Colors.White),
BorderThickness = new Thickness(0),
HorizontalAlignment = HorizontalAlignment.Center
};
applyButton.Click += (s, e) =>
{
Color selectedColor = Color.FromRgb(
(byte)rSlider.Value,
(byte)gSlider.Value,
(byte)bSlider.Value
);
ApplyCustomBackgroundColor(selectedColor);
};
contentPanel.Children.Add(applyButton);
stackPanel.Children.Add(contentPanel);
// 将面板添加到父容器
BackgroundPalette.Child = stackPanel;
// 获取主窗口中的根网格,确保面板添加到顶层
Grid mainGrid = FindName("Main_Grid") as Grid;
if (mainGrid != null)
{
// 删除可能已存在的BackgroundPalette
foreach (UIElement element in mainGrid.Children)
{
if (element is Border border && border.Name == "BackgroundPalette")
{
mainGrid.Children.Remove(border);
break;
}
}
// 重新定位面板
BackgroundPalette.HorizontalAlignment = HorizontalAlignment.Center;
BackgroundPalette.VerticalAlignment = VerticalAlignment.Center;
BackgroundPalette.Margin = new Thickness(0, 0, 0, 0);
// 添加到主网格
mainGrid.Children.Add(BackgroundPalette);
// 设置面板位置
var clickElement = FindName("BoardChangeBackgroundColorBtn") as FrameworkElement;
if (clickElement != null)
{
Point position = clickElement.TranslatePoint(new Point(0, 0), mainGrid);
BackgroundPalette.Margin = new Thickness(
position.X - 150,
position.Y + clickElement.ActualHeight + 5,
0, 0);
BackgroundPalette.HorizontalAlignment = HorizontalAlignment.Left;
BackgroundPalette.VerticalAlignment = VerticalAlignment.Top;
}
BackgroundRValue.Text = ((int)e.NewValue).ToString();
UpdateColorPreviewFromSliders();
}
}
private void BackgroundGSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (BackgroundGValue != null)
{
BackgroundGValue.Text = ((int)e.NewValue).ToString();
UpdateColorPreviewFromSliders();
}
}
private void BackgroundBSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (BackgroundBValue != null)
{
BackgroundBValue.Text = ((int)e.NewValue).ToString();
UpdateColorPreviewFromSliders();
}
}
private void ApplyBackgroundColorBtn_Click(object sender, RoutedEventArgs e)
{
Color selectedColor = Color.FromRgb(
(byte)BackgroundRSlider.Value,
(byte)BackgroundGSlider.Value,
(byte)BackgroundBSlider.Value
);
ApplyCustomBackgroundColor(selectedColor);
}
private void UpdateColorPreviewFromSliders()
{
if (BackgroundColorPreview != null)
{
Color previewColor = Color.FromRgb(
(byte)BackgroundRSlider.Value,
(byte)BackgroundGSlider.Value,
(byte)BackgroundBSlider.Value
);
BackgroundColorPreview.Background = new SolidColorBrush(previewColor);
}
}
/// <summary>
/// 更新背景颜色选项面板中的按钮状态
/// </summary>
/// <remarks>
/// - 更新白板和黑板按钮的背景和前景色
/// - 根据当前使用的模式设置按钮状态
/// </remarks>
private void UpdateBackgroundButtonsState()
{
if (BackgroundPalette != null && BackgroundPalette.Child is StackPanel stackPanel)
if (WhiteboardModeBtn != null)
{
if (stackPanel.Children.Count > 1 && stackPanel.Children[1] is StackPanel contentPanel)
WhiteboardModeBtn.Background = Settings.Canvas.UsingWhiteboard ?
new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)) :
new SolidColorBrush(Colors.LightGray);
if (WhiteboardModeBtn.Child is TextBlock whiteboardText)
{
if (contentPanel.Children.Count > 1 && contentPanel.Children[1] is StackPanel modePanel)
{
if (modePanel.Children.Count > 1)
{
var whiteboardButton = modePanel.Children[0] as Border;
var blackboardButton = modePanel.Children[1] as Border;
whiteboardText.Foreground = Settings.Canvas.UsingWhiteboard ?
new SolidColorBrush(Colors.White) :
new SolidColorBrush(Colors.Black);
}
}
if (whiteboardButton != null && whiteboardButton.Child is TextBlock whiteboardText)
{
whiteboardButton.Background = Settings.Canvas.UsingWhiteboard ?
new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)) :
new SolidColorBrush(Colors.LightGray);
whiteboardText.Foreground = Settings.Canvas.UsingWhiteboard ?
new SolidColorBrush(Colors.White) :
new SolidColorBrush(Colors.Black);
}
if (blackboardButton != null && blackboardButton.Child is TextBlock blackboardText)
{
blackboardButton.Background = !Settings.Canvas.UsingWhiteboard ?
new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)) :
new SolidColorBrush(Colors.LightGray);
blackboardText.Foreground = !Settings.Canvas.UsingWhiteboard ?
new SolidColorBrush(Colors.White) :
new SolidColorBrush(Colors.Black);
}
}
}
if (BlackboardModeBtn != null)
{
BlackboardModeBtn.Background = !Settings.Canvas.UsingWhiteboard ?
new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)) :
new SolidColorBrush(Colors.LightGray);
if (BlackboardModeBtn.Child is TextBlock blackboardText)
{
blackboardText.Foreground = !Settings.Canvas.UsingWhiteboard ?
new SolidColorBrush(Colors.White) :
new SolidColorBrush(Colors.Black);
}
}
}
/// <summary>
/// 背景颜色选项面板
/// </summary>
private Border BackgroundPalette { get; set; }
/// <summary>
/// 当前自定义背景色
/// </summary>
@@ -767,41 +330,7 @@ namespace Ink_Canvas
/// - 启用橡皮擦模式
/// - 设置橡皮擦形状为圆形
/// - 设置当前工具模式为按笔画擦除
/// - 禁用形状绘制模式
/// - 重置钢笔类型和属性
/// - 触发编辑模式变更事件
/// - 取消单指拖动模式
/// - 隐藏子面板
/// </remarks>
private void BoardEraserIconByStrokes_Click(object sender, RoutedEventArgs e)
{
//if (BoardEraserByStrokes.Background.ToString() == "#FF679CF4") {
// AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardDeleteIcon);
//}
//else {
// 禁用高级橡皮擦系统
DisableEraserOverlay();
forceEraser = true;
forcePointEraser = false;
inkCanvas.EraserShape = new EllipseStylusShape(5, 5);
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.EraseByStroke);
drawingShapeMode = 0;
penType = 0;
drawingAttributes.IsHighlighter = false;
drawingAttributes.StylusTip = StylusTip.Ellipse;
inkCanvas_EditingModeChanged(inkCanvas, null);
CancelSingleFingerDragMode();
HideSubPanels("eraserByStrokes");
//}
}
/// <summary>
/// 处理删除图标点击事件,清空画布内容
/// </summary>
/// <param name="sender">事件发送者</param>
@@ -913,53 +442,9 @@ namespace Ink_Canvas
/// </summary>
private void UpdateRGBSliders(Color color)
{
if (BackgroundPalette != null && BackgroundPalette.Child is StackPanel stackPanel)
{
if (stackPanel.Children.Count > 1 && stackPanel.Children[1] is StackPanel contentPanel)
{
// 查找RGB滑块
Slider rSlider = null;
Slider gSlider = null;
Slider bSlider = null;
// 遍历面板查找RGB滑块
foreach (var child in contentPanel.Children)
{
if (child is StackPanel panel && panel.Orientation == Orientation.Horizontal)
{
foreach (var panelChild in panel.Children)
{
if (panelChild is Slider slider)
{
if (panel.Children.Count > 0 && panel.Children[0] is TextBlock label)
{
if (label.Text == "R:")
{
rSlider = slider;
}
else if (label.Text == "G:")
{
gSlider = slider;
}
else if (label.Text == "B:")
{
bSlider = slider;
}
}
}
}
}
}
// 更新滑块值
if (rSlider != null && gSlider != null && bSlider != null)
{
rSlider.Value = color.R;
gSlider.Value = color.G;
bSlider.Value = color.B;
}
}
}
if (BackgroundRSlider != null) BackgroundRSlider.Value = color.R;
if (BackgroundGSlider != null) BackgroundGSlider.Value = color.G;
if (BackgroundBSlider != null) BackgroundBSlider.Value = color.B;
}
}
}
@@ -272,21 +272,21 @@ namespace Ink_Canvas
/// - 显示通知
/// - 包含异常处理
/// </remarks>
private async Task PasteImageFromClipboard(Point? position = null)
private Task PasteImageFromClipboard(Point? position = null)
{
try
{
if (!Clipboard.ContainsImage())
{
ShowNotification("剪贴板中没有图片");
return;
return Task.CompletedTask;
}
var clipboardImage = Clipboard.GetImage();
if (clipboardImage == null)
{
ShowNotification("无法获取剪贴板图片");
return;
return Task.CompletedTask;
}
// 创建Image控件
@@ -383,6 +383,7 @@ namespace Ink_Canvas
ShowNotification($"粘贴图片失败: {ex.Message}");
LogHelper.WriteLogToFile($"粘贴图片失败: {ex.Message}", LogHelper.LogType.Error);
}
return Task.CompletedTask;
}
+116 -159
View File
@@ -1,13 +1,11 @@
using Ink_Canvas.Helpers;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
namespace Ink_Canvas
@@ -283,26 +281,26 @@ namespace Ink_Canvas
{
// 亮系
// 亮色的红色
BorderPenColorRed.Background = new SolidColorBrush(Color.FromRgb(239, 68, 68));
BoardBorderPenColorRed.Background = new SolidColorBrush(Color.FromRgb(239, 68, 68));
BorderPenColorRed.Color = Color.FromRgb(239, 68, 68);
BoardBorderPenColorRed.Color = Color.FromRgb(239, 68, 68);
// 亮色的绿色
BorderPenColorGreen.Background = new SolidColorBrush(Color.FromRgb(34, 197, 94));
BoardBorderPenColorGreen.Background = new SolidColorBrush(Color.FromRgb(34, 197, 94));
BorderPenColorGreen.Color = Color.FromRgb(34, 197, 94);
BoardBorderPenColorGreen.Color = Color.FromRgb(34, 197, 94);
// 亮色的蓝色
BorderPenColorBlue.Background = new SolidColorBrush(Color.FromRgb(59, 130, 246));
BoardBorderPenColorBlue.Background = new SolidColorBrush(Color.FromRgb(59, 130, 246));
BorderPenColorBlue.Color = Color.FromRgb(59, 130, 246);
BoardBorderPenColorBlue.Color = Color.FromRgb(59, 130, 246);
// 亮色的黄色
BorderPenColorYellow.Background = new SolidColorBrush(Color.FromRgb(250, 204, 21));
BoardBorderPenColorYellow.Background = new SolidColorBrush(Color.FromRgb(250, 204, 21));
BorderPenColorYellow.Color = Color.FromRgb(250, 204, 21);
BoardBorderPenColorYellow.Color = Color.FromRgb(250, 204, 21);
// 亮色的粉色
BorderPenColorPink.Background = new SolidColorBrush(Color.FromRgb(236, 72, 153));
BoardBorderPenColorPink.Background = new SolidColorBrush(Color.FromRgb(236, 72, 153));
BorderPenColorPink.Color = Color.FromRgb(236, 72, 153);
BoardBorderPenColorPink.Color = Color.FromRgb(236, 72, 153);
// 亮色的Teal
BorderPenColorTeal.Background = new SolidColorBrush(Color.FromRgb(20, 184, 166));
BoardBorderPenColorTeal.Background = new SolidColorBrush(Color.FromRgb(20, 184, 166));
BorderPenColorTeal.Color = Color.FromRgb(20, 184, 166);
BoardBorderPenColorTeal.Color = Color.FromRgb(20, 184, 166);
// 亮色的Orange
BorderPenColorOrange.Background = new SolidColorBrush(Color.FromRgb(249, 115, 22));
BoardBorderPenColorOrange.Background = new SolidColorBrush(Color.FromRgb(249, 115, 22));
BorderPenColorOrange.Color = Color.FromRgb(249, 115, 22);
BoardBorderPenColorOrange.Color = Color.FromRgb(249, 115, 22);
var newImageSource = new BitmapImage();
newImageSource.BeginInit();
@@ -319,26 +317,26 @@ namespace Ink_Canvas
{
// 暗系
// 暗色的红色
BorderPenColorRed.Background = new SolidColorBrush(Color.FromRgb(220, 38, 38));
BoardBorderPenColorRed.Background = new SolidColorBrush(Color.FromRgb(220, 38, 38));
BorderPenColorRed.Color = Color.FromRgb(220, 38, 38);
BoardBorderPenColorRed.Color = Color.FromRgb(220, 38, 38);
// 暗色的绿色
BorderPenColorGreen.Background = new SolidColorBrush(Color.FromRgb(22, 163, 74));
BoardBorderPenColorGreen.Background = new SolidColorBrush(Color.FromRgb(22, 163, 74));
BorderPenColorGreen.Color = Color.FromRgb(22, 163, 74);
BoardBorderPenColorGreen.Color = Color.FromRgb(22, 163, 74);
// 暗色的蓝色
BorderPenColorBlue.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235));
BoardBorderPenColorBlue.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235));
BorderPenColorBlue.Color = Color.FromRgb(37, 99, 235);
BoardBorderPenColorBlue.Color = Color.FromRgb(37, 99, 235);
// 暗色的黄色
BorderPenColorYellow.Background = new SolidColorBrush(Color.FromRgb(234, 179, 8));
BoardBorderPenColorYellow.Background = new SolidColorBrush(Color.FromRgb(234, 179, 8));
BorderPenColorYellow.Color = Color.FromRgb(234, 179, 8);
BoardBorderPenColorYellow.Color = Color.FromRgb(234, 179, 8);
// 暗色的紫色对应亮色的粉色
BorderPenColorPink.Background = new SolidColorBrush(Color.FromRgb(147, 51, 234));
BoardBorderPenColorPink.Background = new SolidColorBrush(Color.FromRgb(147, 51, 234));
BorderPenColorPink.Color = Color.FromRgb(147, 51, 234);
BoardBorderPenColorPink.Color = Color.FromRgb(147, 51, 234);
// 暗色的Teal
BorderPenColorTeal.Background = new SolidColorBrush(Color.FromRgb(13, 148, 136));
BoardBorderPenColorTeal.Background = new SolidColorBrush(Color.FromRgb(13, 148, 136));
BorderPenColorTeal.Color = Color.FromRgb(13, 148, 136);
BoardBorderPenColorTeal.Color = Color.FromRgb(13, 148, 136);
// 暗色的Orange
BorderPenColorOrange.Background = new SolidColorBrush(Color.FromRgb(234, 88, 12));
BoardBorderPenColorOrange.Background = new SolidColorBrush(Color.FromRgb(234, 88, 12));
BorderPenColorOrange.Color = Color.FromRgb(234, 88, 12);
BoardBorderPenColorOrange.Color = Color.FromRgb(234, 88, 12);
var newImageSource = new BitmapImage();
newImageSource.BeginInit();
@@ -353,127 +351,129 @@ namespace Ink_Canvas
}
// 改变选中提示
ViewboxBtnColorBlackContent.Visibility = Visibility.Collapsed;
ViewboxBtnColorBlueContent.Visibility = Visibility.Collapsed;
ViewboxBtnColorGreenContent.Visibility = Visibility.Collapsed;
ViewboxBtnColorRedContent.Visibility = Visibility.Collapsed;
ViewboxBtnColorYellowContent.Visibility = Visibility.Collapsed;
ViewboxBtnColorWhiteContent.Visibility = Visibility.Collapsed;
ViewboxBtnColorPinkContent.Visibility = Visibility.Collapsed;
ViewboxBtnColorTealContent.Visibility = Visibility.Collapsed;
ViewboxBtnColorOrangeContent.Visibility = Visibility.Collapsed;
BorderPenColorBlack.IsChecked = false;
BorderPenColorBlue.IsChecked = false;
BorderPenColorGreen.IsChecked = false;
BorderPenColorRed.IsChecked = false;
BorderPenColorYellow.IsChecked = false;
BorderPenColorWhite.IsChecked = false;
BorderPenColorPink.IsChecked = false;
BorderPenColorTeal.IsChecked = false;
BorderPenColorOrange.IsChecked = false;
BoardViewboxBtnColorBlackContent.Visibility = Visibility.Collapsed;
BoardViewboxBtnColorBlueContent.Visibility = Visibility.Collapsed;
BoardViewboxBtnColorGreenContent.Visibility = Visibility.Collapsed;
BoardViewboxBtnColorRedContent.Visibility = Visibility.Collapsed;
BoardViewboxBtnColorYellowContent.Visibility = Visibility.Collapsed;
BoardViewboxBtnColorWhiteContent.Visibility = Visibility.Collapsed;
BoardViewboxBtnColorPinkContent.Visibility = Visibility.Collapsed;
BoardViewboxBtnColorTealContent.Visibility = Visibility.Collapsed;
BoardViewboxBtnColorOrangeContent.Visibility = Visibility.Collapsed;
BoardBorderPenColorBlack.IsChecked = false;
BoardBorderPenColorBlue.IsChecked = false;
BoardBorderPenColorGreen.IsChecked = false;
BoardBorderPenColorRed.IsChecked = false;
BoardBorderPenColorYellow.IsChecked = false;
BoardBorderPenColorWhite.IsChecked = false;
BoardBorderPenColorPink.IsChecked = false;
BoardBorderPenColorTeal.IsChecked = false;
BoardBorderPenColorOrange.IsChecked = false;
HighlighterPenViewboxBtnColorBlackContent.Visibility = Visibility.Collapsed;
HighlighterPenViewboxBtnColorBlueContent.Visibility = Visibility.Collapsed;
HighlighterPenViewboxBtnColorGreenContent.Visibility = Visibility.Collapsed;
HighlighterPenViewboxBtnColorOrangeContent.Visibility = Visibility.Collapsed;
HighlighterPenViewboxBtnColorPurpleContent.Visibility = Visibility.Collapsed;
HighlighterPenViewboxBtnColorRedContent.Visibility = Visibility.Collapsed;
HighlighterPenViewboxBtnColorTealContent.Visibility = Visibility.Collapsed;
HighlighterPenViewboxBtnColorWhiteContent.Visibility = Visibility.Collapsed;
HighlighterPenViewboxBtnColorYellowContent.Visibility = Visibility.Collapsed;
HighlighterPenViewboxBtnColorZincContent.Visibility = Visibility.Collapsed;
HighlighterPenColorBlack.IsChecked = false;
HighlighterPenColorBlue.IsChecked = false;
HighlighterPenColorGreen.IsChecked = false;
HighlighterPenColorOrange.IsChecked = false;
HighlighterPenPenColorPurple.IsChecked = false;
HighlighterPenColorRed.IsChecked = false;
HighlighterPenColorTeal.IsChecked = false;
HighlighterPenColorWhite.IsChecked = false;
HighlighterPenColorYellow.IsChecked = false;
HighlighterPenColorZinc.IsChecked = false;
BoardHighlighterPenViewboxBtnColorBlackContent.Visibility = Visibility.Collapsed;
BoardHighlighterPenViewboxBtnColorBlueContent.Visibility = Visibility.Collapsed;
BoardHighlighterPenViewboxBtnColorGreenContent.Visibility = Visibility.Collapsed;
BoardHighlighterPenViewboxBtnColorOrangeContent.Visibility = Visibility.Collapsed;
BoardHighlighterPenViewboxBtnColorPurpleContent.Visibility = Visibility.Collapsed;
BoardHighlighterPenViewboxBtnColorRedContent.Visibility = Visibility.Collapsed;
BoardHighlighterPenViewboxBtnColorTealContent.Visibility = Visibility.Collapsed;
BoardHighlighterPenViewboxBtnColorWhiteContent.Visibility = Visibility.Collapsed;
BoardHighlighterPenViewboxBtnColorYellowContent.Visibility = Visibility.Collapsed;
BoardHighlighterPenViewboxBtnColorZincContent.Visibility = Visibility.Collapsed;
BoardHighlighterPenColorBlack.IsChecked = false;
BoardHighlighterPenColorBlue.IsChecked = false;
BoardHighlighterPenColorGreen.IsChecked = false;
BoardHighlighterPenColorOrange.IsChecked = false;
BoardHighlighterPenPenColorPurple.IsChecked = false;
BoardHighlighterPenColorRed.IsChecked = false;
BoardHighlighterPenColorTeal.IsChecked = false;
BoardHighlighterPenColorWhite.IsChecked = false;
BoardHighlighterPenColorYellow.IsChecked = false;
BoardHighlighterPenColorZinc.IsChecked = false;
switch (inkColor)
{
case 0:
ViewboxBtnColorBlackContent.Visibility = Visibility.Visible;
BoardViewboxBtnColorBlackContent.Visibility = Visibility.Visible;
BorderPenColorBlack.IsChecked = true;
BoardBorderPenColorBlack.IsChecked = true;
break;
case 1:
ViewboxBtnColorRedContent.Visibility = Visibility.Visible;
BoardViewboxBtnColorRedContent.Visibility = Visibility.Visible;
BorderPenColorRed.IsChecked = true;
BoardBorderPenColorRed.IsChecked = true;
break;
case 2:
ViewboxBtnColorGreenContent.Visibility = Visibility.Visible;
BoardViewboxBtnColorGreenContent.Visibility = Visibility.Visible;
BorderPenColorGreen.IsChecked = true;
BoardBorderPenColorGreen.IsChecked = true;
break;
case 3:
ViewboxBtnColorBlueContent.Visibility = Visibility.Visible;
BoardViewboxBtnColorBlueContent.Visibility = Visibility.Visible;
BorderPenColorBlue.IsChecked = true;
BoardBorderPenColorBlue.IsChecked = true;
break;
case 4:
ViewboxBtnColorYellowContent.Visibility = Visibility.Visible;
BoardViewboxBtnColorYellowContent.Visibility = Visibility.Visible;
BorderPenColorYellow.IsChecked = true;
BoardBorderPenColorYellow.IsChecked = true;
break;
case 5:
ViewboxBtnColorWhiteContent.Visibility = Visibility.Visible;
BoardViewboxBtnColorWhiteContent.Visibility = Visibility.Visible;
BorderPenColorWhite.IsChecked = true;
BoardBorderPenColorWhite.IsChecked = true;
break;
case 6:
ViewboxBtnColorPinkContent.Visibility = Visibility.Visible;
BoardViewboxBtnColorPinkContent.Visibility = Visibility.Visible;
BorderPenColorPink.IsChecked = true;
BoardBorderPenColorPink.IsChecked = true;
break;
case 7:
ViewboxBtnColorTealContent.Visibility = Visibility.Visible;
BorderPenColorTeal.IsChecked = true;
BoardBorderPenColorTeal.IsChecked = true;
break;
case 8:
ViewboxBtnColorOrangeContent.Visibility = Visibility.Visible;
BorderPenColorOrange.IsChecked = true;
BoardBorderPenColorOrange.IsChecked = true;
break;
}
switch (highlighterColor)
{
case 100:
HighlighterPenViewboxBtnColorBlackContent.Visibility = Visibility.Visible;
BoardHighlighterPenViewboxBtnColorBlackContent.Visibility = Visibility.Visible;
HighlighterPenColorBlack.IsChecked = true;
BoardHighlighterPenColorBlack.IsChecked = true;
break;
case 101:
HighlighterPenViewboxBtnColorWhiteContent.Visibility = Visibility.Visible;
BoardHighlighterPenViewboxBtnColorWhiteContent.Visibility = Visibility.Visible;
HighlighterPenColorWhite.IsChecked = true;
BoardHighlighterPenColorWhite.IsChecked = true;
break;
case 102:
HighlighterPenViewboxBtnColorRedContent.Visibility = Visibility.Visible;
BoardHighlighterPenViewboxBtnColorRedContent.Visibility = Visibility.Visible;
HighlighterPenColorRed.IsChecked = true;
BoardHighlighterPenColorRed.IsChecked = true;
break;
case 103:
HighlighterPenViewboxBtnColorYellowContent.Visibility = Visibility.Visible;
BoardHighlighterPenViewboxBtnColorYellowContent.Visibility = Visibility.Visible;
HighlighterPenColorYellow.IsChecked = true;
BoardHighlighterPenColorYellow.IsChecked = true;
break;
case 104:
HighlighterPenViewboxBtnColorGreenContent.Visibility = Visibility.Visible;
BoardHighlighterPenViewboxBtnColorGreenContent.Visibility = Visibility.Visible;
HighlighterPenColorGreen.IsChecked = true;
BoardHighlighterPenColorGreen.IsChecked = true;
break;
case 105:
HighlighterPenViewboxBtnColorZincContent.Visibility = Visibility.Visible;
BoardHighlighterPenViewboxBtnColorZincContent.Visibility = Visibility.Visible;
HighlighterPenColorZinc.IsChecked = true;
BoardHighlighterPenColorZinc.IsChecked = true;
break;
case 106:
HighlighterPenViewboxBtnColorBlueContent.Visibility = Visibility.Visible;
BoardHighlighterPenViewboxBtnColorBlueContent.Visibility = Visibility.Visible;
HighlighterPenColorBlue.IsChecked = true;
BoardHighlighterPenColorBlue.IsChecked = true;
break;
case 107:
HighlighterPenViewboxBtnColorPurpleContent.Visibility = Visibility.Visible;
BoardHighlighterPenViewboxBtnColorPurpleContent.Visibility = Visibility.Visible;
HighlighterPenPenColorPurple.IsChecked = true;
BoardHighlighterPenPenColorPurple.IsChecked = true;
break;
case 108:
HighlighterPenViewboxBtnColorTealContent.Visibility = Visibility.Visible;
BoardHighlighterPenViewboxBtnColorTealContent.Visibility = Visibility.Visible;
HighlighterPenColorTeal.IsChecked = true;
BoardHighlighterPenColorTeal.IsChecked = true;
break;
case 109:
HighlighterPenViewboxBtnColorOrangeContent.Visibility = Visibility.Visible;
BoardHighlighterPenViewboxBtnColorOrangeContent.Visibility = Visibility.Visible;
HighlighterPenColorOrange.IsChecked = true;
BoardHighlighterPenColorOrange.IsChecked = true;
break;
}
@@ -554,37 +554,15 @@ namespace Ink_Canvas
BoardHighlightPenTabButton.Background = new SolidColorBrush(Colors.Transparent);
BoardHighlightPenTabButtonIndicator.Visibility = Visibility.Collapsed;
// PenPalette.Margin = new Thickness(-160, -200, -33, 32);
// 动态计算面板位置,使其对齐笔按钮(考虑快捷调色盘等动态宽度)
await Dispatcher.InvokeAsync(() =>
{
var marginAnimation = new ThicknessAnimation
{
Duration = TimeSpan.FromSeconds(0.1),
From = PenPalette.Margin,
To = new Thickness(-160, -200, -33, 32),
EasingFunction = new CubicEase()
};
PenPalette.BeginAnimation(MarginProperty, marginAnimation);
PenPalette.BeginAnimation(MarginProperty, null);
var currentMargin = PenPalette.Margin;
// 先设置正确的Top/Bottom,保持当前Left/Right
PenPalette.Margin = new Thickness(currentMargin.Left, -200, currentMargin.Right, 32);
UpdatePenPalettePosition();
});
await Dispatcher.InvokeAsync(() =>
{
var marginAnimation = new ThicknessAnimation
{
Duration = TimeSpan.FromSeconds(0.1),
From = PenPalette.Margin,
To = new Thickness(-160, -200, -33, 50),
EasingFunction = new CubicEase()
};
BoardPenPaletteGrid.BeginAnimation(MarginProperty, marginAnimation);
});
await Task.Delay(100);
await Dispatcher.InvokeAsync(() => { PenPalette.Margin = new Thickness(-160, -200, -33, 32); });
await Dispatcher.InvokeAsync(() => { BoardPenPaletteGrid.Margin = new Thickness(-160, -200, -33, 50); });
}
else if (penType == 1)
{
@@ -622,36 +600,15 @@ namespace Ink_Canvas
BoardHighlightPenTabButton.Background = new SolidColorBrush(Color.FromArgb(72, 219, 234, 254));
BoardHighlightPenTabButtonIndicator.Visibility = Visibility.Visible;
// PenPalette.Margin = new Thickness(-160, -157, -33, 32);
// 动态计算面板位置,使其对齐笔按钮(考虑快捷调色盘等动态宽度)
await Dispatcher.InvokeAsync(() =>
{
var marginAnimation = new ThicknessAnimation
{
Duration = TimeSpan.FromSeconds(0.1),
From = PenPalette.Margin,
To = new Thickness(-160, -157, -33, 32),
EasingFunction = new CubicEase()
};
PenPalette.BeginAnimation(MarginProperty, marginAnimation);
PenPalette.BeginAnimation(MarginProperty, null);
var currentMargin = PenPalette.Margin;
// 荧光笔模式面板稍小,使用不同的Top/Bottom
PenPalette.Margin = new Thickness(currentMargin.Left, -157, currentMargin.Right, 32);
UpdatePenPalettePosition();
});
await Dispatcher.InvokeAsync(() =>
{
var marginAnimation = new ThicknessAnimation
{
Duration = TimeSpan.FromSeconds(0.1),
From = PenPalette.Margin,
To = new Thickness(-160, -154, -33, 50),
EasingFunction = new CubicEase()
};
BoardPenPaletteGrid.BeginAnimation(MarginProperty, marginAnimation);
});
await Task.Delay(100);
await Dispatcher.InvokeAsync(() => { PenPalette.Margin = new Thickness(-160, -157, -33, 32); });
await Dispatcher.InvokeAsync(() => { BoardPenPaletteGrid.Margin = new Thickness(-160, -154, -33, 50); });
}
}
File diff suppressed because it is too large Load Diff
@@ -1,7 +1,5 @@
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Modern.Controls;
using System;
using System.Linq;
using System.Windows;
namespace Ink_Canvas
@@ -10,25 +8,14 @@ namespace Ink_Canvas
{
#region
/// <summary>
/// 初始化悬浮窗拦截管理器
/// </summary>
private void InitializeFloatingWindowInterceptor()
{
try
{
_floatingWindowInterceptorManager = new FloatingWindowInterceptorManager();
// 订阅事件
_floatingWindowInterceptorManager.WindowIntercepted += OnFloatingWindowIntercepted;
_floatingWindowInterceptorManager.WindowRestored += OnFloatingWindowRestored;
// 初始化拦截器
_floatingWindowInterceptorManager.Initialize(Settings.Automation.FloatingWindowInterceptor);
// 加载UI状态
LoadFloatingWindowInterceptorUI();
LogHelper.WriteLogToFile("悬浮窗拦截管理器初始化完成", LogHelper.LogType.Event);
}
catch (Exception ex)
@@ -37,83 +24,11 @@ namespace Ink_Canvas
}
}
/// <summary>
/// 加载悬浮窗拦截UI状态
/// </summary>
private void LoadFloatingWindowInterceptorUI()
{
try
{
if (!isLoaded) return;
// 设置主开关状态
ToggleSwitchFloatingWindowInterceptorEnabled.IsOn = Settings.Automation.FloatingWindowInterceptor.IsEnabled;
// 设置各个拦截规则的状态
foreach (var kvp in Settings.Automation.FloatingWindowInterceptor.InterceptRules)
{
var toggleName = $"ToggleSwitch{kvp.Key}";
var toggle = FindName(toggleName) as ToggleSwitch;
if (toggle != null)
{
toggle.IsOn = kvp.Value;
}
}
// 更新UI可见性
UpdateFloatingWindowInterceptorUI();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载悬浮窗拦截UI状态失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 更新悬浮窗拦截UI
/// </summary>
private void UpdateFloatingWindowInterceptorUI()
{
try
{
var isEnabled = Settings.Automation.FloatingWindowInterceptor.IsEnabled;
FloatingWindowInterceptorGrid.Visibility = isEnabled ? Visibility.Visible : Visibility.Collapsed;
// 计算启用的规则数量
var enabledRulesCount = Settings.Automation.FloatingWindowInterceptor.InterceptRules.Where(kvp => kvp.Value).Count();
var totalRulesCount = Settings.Automation.FloatingWindowInterceptor.InterceptRules.Count;
// 更新状态文本
if (_floatingWindowInterceptorManager != null)
{
var stats = _floatingWindowInterceptorManager.GetStatistics();
TextBlockFloatingWindowInterceptorStatus.Text = stats.IsRunning
? $"拦截器运行中 - 已启用 {enabledRulesCount}/{totalRulesCount} 个规则"
: $"拦截器未启动 - 已启用 {enabledRulesCount}/{totalRulesCount} 个规则";
}
else
{
TextBlockFloatingWindowInterceptorStatus.Text = $"拦截器未初始化 - 已启用 {enabledRulesCount}/{totalRulesCount} 个规则";
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新悬浮窗拦截UI失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 窗口被拦截事件处理
/// </summary>
private void OnFloatingWindowIntercepted(object sender, FloatingWindowInterceptor.WindowInterceptedEventArgs e)
{
try
{
// 在UI线程中更新状态
Dispatcher.BeginInvoke(new Action(() =>
{
UpdateFloatingWindowInterceptorUI();
}));
Dispatcher.BeginInvoke(new Action(() => { }));
}
catch (Exception ex)
{
@@ -121,18 +36,11 @@ namespace Ink_Canvas
}
}
/// <summary>
/// 窗口被恢复事件处理
/// </summary>
private void OnFloatingWindowRestored(object sender, FloatingWindowInterceptor.WindowRestoredEventArgs e)
{
try
{
// 在UI线程中更新状态
Dispatcher.BeginInvoke(new Action(() =>
{
UpdateFloatingWindowInterceptorUI();
}));
Dispatcher.BeginInvoke(new Action(() => { }));
}
catch (Exception ex)
{
@@ -144,30 +52,24 @@ namespace Ink_Canvas
#region
/// <summary>
/// 主开关切换事件
/// </summary>
private void ToggleSwitchFloatingWindowInterceptorEnabled_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
try
{
Settings.Automation.FloatingWindowInterceptor.IsEnabled = ToggleSwitchFloatingWindowInterceptorEnabled.IsOn;
var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch;
if (toggle != null)
Settings.Automation.FloatingWindowInterceptor.IsEnabled = toggle.IsOn;
if (_floatingWindowInterceptorManager != null)
{
if (Settings.Automation.FloatingWindowInterceptor.IsEnabled)
{
_floatingWindowInterceptorManager.Start();
}
else
{
_floatingWindowInterceptorManager.Stop();
}
}
UpdateFloatingWindowInterceptorUI();
SaveSettingsToFile();
}
catch (Exception ex)
@@ -176,129 +78,98 @@ namespace Ink_Canvas
}
}
/// <summary>
/// 希沃白板3拦截开关
/// </summary>
private void ToggleSwitchSeewoWhiteboard3Floating_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoWhiteboard3Floating, ToggleSwitchSeewoWhiteboard3Floating.IsOn);
var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch;
if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoWhiteboard3Floating, toggle.IsOn);
}
/// <summary>
/// 希沃白板5拦截开关
/// </summary>
private void ToggleSwitchSeewoWhiteboard5Floating_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoWhiteboard5Floating, ToggleSwitchSeewoWhiteboard5Floating.IsOn);
var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch;
if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoWhiteboard5Floating, toggle.IsOn);
}
/// <summary>
/// 希沃白板5C拦截开关
/// </summary>
private void ToggleSwitchSeewoWhiteboard5CFloating_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoWhiteboard5CFloating, ToggleSwitchSeewoWhiteboard5CFloating.IsOn);
var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch;
if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoWhiteboard5CFloating, toggle.IsOn);
}
/// <summary>
/// 希沃品课侧栏拦截开关
/// </summary>
private void ToggleSwitchSeewoPincoSideBarFloating_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoPincoSideBarFloating, ToggleSwitchSeewoPincoSideBarFloating.IsOn);
var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch;
if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoPincoSideBarFloating, toggle.IsOn);
}
/// <summary>
/// 希沃品课画笔拦截开关
/// </summary>
private void ToggleSwitchSeewoPincoDrawingFloating_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoPincoDrawingFloating, ToggleSwitchSeewoPincoDrawingFloating.IsOn);
var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch;
if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoPincoDrawingFloating, toggle.IsOn);
}
/// <summary>
/// 希沃PPT小工具拦截开关
/// </summary>
private void ToggleSwitchSeewoPPTFloating_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoPPTFloating, ToggleSwitchSeewoPPTFloating.IsOn);
var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch;
if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoPPTFloating, toggle.IsOn);
}
/// <summary>
/// AiClass拦截开关
/// </summary>
private void ToggleSwitchAiClassFloating_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
SetInterceptRule(FloatingWindowInterceptor.InterceptType.AiClassFloating, ToggleSwitchAiClassFloating.IsOn);
var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch;
if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.AiClassFloating, toggle.IsOn);
}
/// <summary>
/// 鸿合屏幕书写拦截开关
/// </summary>
private void ToggleSwitchHiteAnnotationFloating_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
SetInterceptRule(FloatingWindowInterceptor.InterceptType.HiteAnnotationFloating, ToggleSwitchHiteAnnotationFloating.IsOn);
var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch;
if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.HiteAnnotationFloating, toggle.IsOn);
}
/// <summary>
/// 畅言智慧课堂拦截开关
/// </summary>
private void ToggleSwitchChangYanFloating_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
SetInterceptRule(FloatingWindowInterceptor.InterceptType.ChangYanFloating, ToggleSwitchChangYanFloating.IsOn);
var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch;
if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.ChangYanFloating, toggle.IsOn);
}
/// <summary>
/// 畅言PPT拦截开关
/// </summary>
private void ToggleSwitchChangYanPptFloating_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
SetInterceptRule(FloatingWindowInterceptor.InterceptType.ChangYanPptFloating, ToggleSwitchChangYanPptFloating.IsOn);
var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch;
if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.ChangYanPptFloating, toggle.IsOn);
}
/// <summary>
/// 天喻教育云拦截开关
/// </summary>
private void ToggleSwitchIntelligentClassFloating_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
SetInterceptRule(FloatingWindowInterceptor.InterceptType.IntelligentClassFloating, ToggleSwitchIntelligentClassFloating.IsOn);
var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch;
if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.IntelligentClassFloating, toggle.IsOn);
}
/// <summary>
/// 希沃桌面画笔拦截开关
/// </summary>
private void ToggleSwitchSeewoDesktopAnnotationFloating_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoDesktopAnnotationFloating, ToggleSwitchSeewoDesktopAnnotationFloating.IsOn);
var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch;
if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoDesktopAnnotationFloating, toggle.IsOn);
}
/// <summary>
/// 希沃桌面侧栏拦截开关
/// </summary>
private void ToggleSwitchSeewoDesktopSideBarFloating_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoDesktopSideBarFloating, ToggleSwitchSeewoDesktopSideBarFloating.IsOn);
var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch;
if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoDesktopSideBarFloating, toggle.IsOn);
}
/// <summary>
/// 设置拦截规则
/// </summary>
/// <param name="type">拦截类型</param>
/// <param name="enabled">是否启用拦截</param>
private void SetInterceptRule(FloatingWindowInterceptor.InterceptType type, bool enabled)
public void SetInterceptRule(FloatingWindowInterceptor.InterceptType type, bool enabled)
{
try
{
@@ -307,18 +178,15 @@ namespace Ink_Canvas
_floatingWindowInterceptorManager.SetInterceptRule(type, enabled);
}
// 更新设置
var ruleName = type.ToString();
if (Settings.Automation.FloatingWindowInterceptor.InterceptRules.ContainsKey(ruleName))
{
Settings.Automation.FloatingWindowInterceptor.InterceptRules[ruleName] = enabled;
}
// 获取规则信息以处理父子关系
var rule = _floatingWindowInterceptorManager?.GetInterceptRule(type);
if (rule != null)
{
// 如果是父规则,更新所有子规则的设置
if (rule.ChildTypes.Count > 0)
{
foreach (var childType in rule.ChildTypes)
@@ -330,7 +198,6 @@ namespace Ink_Canvas
}
}
}
// 如果是子规则,更新父规则的设置
else if (rule.ParentType.HasValue)
{
var parentRule = _floatingWindowInterceptorManager?.GetInterceptRule(rule.ParentType.Value);
@@ -345,9 +212,6 @@ namespace Ink_Canvas
}
}
// 更新UI显示
UpdateFloatingWindowInterceptorUI();
SaveSettingsToFile();
}
catch (Exception ex)
@@ -357,4 +221,4 @@ namespace Ink_Canvas
}
#endregion
}
}
}
+13 -7
View File
@@ -104,8 +104,12 @@ namespace Ink_Canvas
/// </summary>
/// <param name="sender">发送者</param>
/// <param name="e">执行路由事件参数</param>
private void KeyChangeToDrawTool(object sender, ExecutedRoutedEventArgs e)
private async void KeyChangeToDrawTool(object sender, ExecutedRoutedEventArgs e)
{
if (isFloatingBarFolded)
{
await UnFoldFloatingBar(new object());
}
PenIcon_Click(lastBorderMouseDownObject, null);
}
@@ -150,15 +154,17 @@ namespace Ink_Canvas
/// <param name="sender">发送者</param>
/// <param name="e">执行路由事件参数</param>
/// <remarks>仅当画布控件面板可见时生效,根据当前橡皮擦状态选择相应的橡皮擦模式</remarks>
private void KeyChangeToEraser(object sender, ExecutedRoutedEventArgs e)
private async void KeyChangeToEraser(object sender, ExecutedRoutedEventArgs e)
{
if (StackPanelCanvasControls.Visibility == Visibility.Visible)
if (isFloatingBarFolded)
{
if (Eraser_Icon.Background != null)
EraserIconByStrokes_Click(lastBorderMouseDownObject, null);
else
EraserIcon_Click(lastBorderMouseDownObject, null);
await UnFoldFloatingBar(new object());
}
if (Eraser_Icon.Background != null)
EraserIconByStrokes_Click(lastBorderMouseDownObject, null);
else
EraserIcon_Click(lastBorderMouseDownObject, null);
}
/// <summary>
+60
View File
@@ -176,5 +176,65 @@ namespace Ink_Canvas
/// <remarks>老版浮动栏中用于表示套索选择工具的图标</remarks>
public static string LegacySolidLassoSelectIcon =
"F1 M24,24z M0,0z M14.6618,10.9193C14.4981,10.784 14.2733,10.6788 14.0025,10.6788 13.9951,10.6788 13.9877,10.6789 13.9803,10.6791 13.7083,10.6872 13.4502,10.8008 13.2607,10.9961 13.0712,11.1913 12.9653,11.4526 12.9653,11.7246L12.9653,20.3314C12.9653,20.3403 12.9655,20.3491 12.9658,20.358 12.9734,20.5733 13.0468,20.7811 13.176,20.9534 13.3053,21.1258 13.4842,21.2544 13.6888,21.3219 13.765,21.3471 13.8447,21.36 13.925,21.36L14.0025,21.36C14.1661,21.36 14.3276,21.3218 14.474,21.2486 14.6204,21.1754 14.7477,21.0692 14.8459,20.9382 14.8542,20.9272 14.8622,20.9159 14.8698,20.9045L16.8582,17.9258 20.3145,17.9258C20.5287,17.9281 20.7384,17.8641 20.9149,17.7424 21.0941,17.6187 21.2299,17.4417 21.3029,17.2365 21.3759,17.0313 21.3825,16.8084 21.3217,16.5993 21.262,16.3936 21.14,16.2117 20.9729,16.0782L14.6618,10.9193z M8.14548,20.0044C7.70454,19.6737 7.34665,19.2448 7.10016,18.7519 6.94658,18.4447 6.83888,18.1179 6.7795,17.7818 7.131,17.6605 7.45404,17.4604 7.72196,17.1924 7.74959,17.1648 7.77649,17.1366 7.80267,17.1078 8.5567,17.4118 9.3392,17.6365 10.1444,17.7694 10.5548,17.8372 10.9424,17.5594 11.0101,17.149 11.0779,16.7387 10.8001,16.3511 10.3897,16.2833 9.72172,16.1731 9.06686,15.9883 8.42926,15.7362 8.44084,15.6393 8.44672,15.5413 8.44672,15.4427 8.44672,14.7865 8.18602,14.1571 7.72196,13.693 7.25791,13.229 6.62852,12.9682 5.97224,12.9682 5.65536,12.9682 5.34474,13.029 5.05598,13.1441 4.47073,12.3026 4.15196,11.303 4.14328,10.2756 4.14532,7.03688 7.49758,4.1462 11.9971,4.1462 16.4941,4.1462 19.8451,7.03371 19.8508,10.2703 19.8388,10.7807 19.7549,11.2869 19.6016,11.7739 19.4767,12.1706 19.697,12.5934 20.0938,12.7183 20.4905,12.8432 20.9134,12.6228 21.0383,12.2261 21.2351,11.6008 21.3424,10.9507 21.3568,10.2952L21.357,10.2786C21.357,5.91009 16.9982,2.64 11.9971,2.64 6.9959,2.64 2.63705,5.91009 2.63705,10.2786L2.6371,10.2845C2.6479,11.6579 3.08647,12.993 3.89074,14.1047 3.63615,14.5007 3.49777,14.9646 3.49777,15.4427 3.49777,16.099 3.75847,16.7284 4.22252,17.1924 4.51465,17.4846 4.87229,17.6961 5.26092,17.8128 5.33333,18.3726 5.49917,18.9178 5.75297,19.4255 6.10404,20.1276 6.61375,20.7383 7.24176,21.2093 7.5745,21.4589 8.04654,21.3915 8.2961,21.0587 8.54566,20.726 8.47822,20.2539 8.14548,20.0044z";
public static string FoldIcon =
"F1 M24,24z M0,0z M19.1634,15.0537C18.9999,15.0537 18.8278,15.0021 18.6902,14.8902 18.3632,14.6321 18.3116,14.1589 18.5783,13.832 19.0343,13.2642 19.4387,12.6447 19.7656,11.9909 19.172,10.8208 16.763,6.72556 11.9967,6.72556 11.6612,6.72556 11.3256,6.75137 10.9987,6.78579 10.5943,6.83741 10.2072,6.54489 10.1556,6.13193 10.1039,5.71896 10.3965,5.34041 10.8094,5.28879 11.1966,5.23717 11.5923,5.21136 11.9967,5.21136 18.4407,5.21136 21.1766,11.423 21.297,11.6897 21.383,11.8876 21.3744,12.1113 21.2884,12.3006 20.8754,13.1781 20.3592,14.0041 19.757,14.7612 19.6107,14.9418 19.387,15.0451 19.172,15.0451L19.1634,15.0537z M10.689,15.1483C11.0847,15.3118,11.5063,15.3892,11.9365,15.3892L11.9451,15.4064 12.0053,15.4064C12.4613,15.3978 12.8915,15.2946 13.3044,15.1139 13.4763,15.0387 13.6392,14.9486 13.7931,14.8455L15.4197,16.472 16.533,17.5854 20.0753,21.1277C20.2216,21.2826,20.4195,21.3514,20.6087,21.3514L20.6001,21.36C20.7894,21.36 20.9873,21.2826 21.1335,21.1363 21.4261,20.8438 21.4261,20.362 21.1335,20.0695L3.92668,2.86262C3.63416,2.5615 3.16958,2.57011 2.86846,2.86262 2.56734,3.15514 2.56734,3.62833 2.86846,3.92085L6.21371,7.2661C4.72738,8.44444 3.5311,9.95577 2.71359,11.6725 2.62756,11.8704 2.61896,12.0941 2.70499,12.292 2.81684,12.5587 5.55273,18.7704 12.0053,18.7704L12.0311,18.7704C13.6161,18.7704,15.1606,18.3654,16.533,17.5854L15.4197,16.472C14.3708,16.9906,13.2144,17.2648,12.0311,17.2648L12.0053,17.2648C7.239,17.2648 4.83004,13.1695 4.2364,11.9995 4.96897,10.5725 6.01289,9.32147 7.29011,8.3425L9.15767,10.2101C9.05523,10.3645 8.96629,10.5279 8.89086,10.7003 8.71018,11.1133 8.60694,11.5521 8.60694,11.9995 8.59834,12.4468 8.67577,12.8856 8.84784,13.3072 9.0113,13.7288 9.26081,14.0987 9.57913,14.417 9.89746,14.7354 10.2674,14.9763 10.689,15.1483z";
public static string DeleteIcon =
"F1 M24,24z M0,0z M8.53333,2.37333L7.92,4.21333 2.56,4.21333C2.4,4.21333 2.26667,4.27556 2.16,4.4 2.05333,4.50667 2,4.63111 2,4.77333L2,5.89333C2,6.03556 2.05333,6.16889 2.16,6.29333 2.26667,6.4 2.4,6.45333 2.56,6.45333L21.44,6.45333C21.6,6.45333 21.7333,6.4 21.84,6.29333 21.9467,6.16889 22,6.03556 22,5.89333L22,4.77333C22,4.63111 21.9467,4.50667 21.84,4.4 21.7333,4.27556 21.6,4.21333 21.44,4.21333L16.08,4.21333 15.4667,2.37333C15.4311,2.26667 15.36,2.17778 15.2533,2.10667 15.1644,2.03556 15.0578,2 14.9333,2L9.06667,2C8.94222,2 8.82667,2.03556 8.72,2.10667 8.63111,2.17778 8.56889,2.26667 8.53333,2.37333z M19.7867,8.10667L18.7467,20.9867C18.7111,21.2711 18.5867,21.5111 18.3733,21.7067 18.1778,21.9022 17.9378,22 17.6533,22L6.37333,22C6.08889,22 5.84,21.9022 5.62667,21.7067 5.41333,21.5111 5.28889,21.2711 5.25333,20.9867L4.21333,8.10667 19.7867,8.10667z M8.66667,12.56L8.66667,16.9867C8.66667,17.1467 8.72,17.28 8.82667,17.3867 8.93333,17.4933 9.06667,17.5467 9.22667,17.5467L9.78667,17.5467C9.92889,17.5467 10.0533,17.4933 10.16,17.3867 10.2844,17.28 10.3467,17.1467 10.3467,16.9867L10.3467,12.56C10.3467,12.4 10.2844,12.2667 10.16,12.16 10.0533,12.0533 9.92889,12 9.78667,12L9.22667,12C9.06667,12 8.93333,12.0533 8.82667,12.16 8.72,12.2667 8.66667,12.4 8.66667,12.56z M14.2133,12C14.0711,12 13.9467,12.0533 13.84,12.16 13.7333,12.2667 13.68,12.4 13.68,12.56L13.68,16.9867C13.68,17.1467 13.7333,17.28 13.84,17.3867 13.9467,17.4933 14.0711,17.5467 14.2133,17.5467L14.7733,17.5467C14.9333,17.5467 15.0667,17.4933 15.1733,17.3867 15.28,17.28 15.3333,17.1467 15.3333,16.9867L15.3333,12.56C15.3333,12.4 15.28,12.2667 15.1733,12.16 15.0667,12.0533 14.9333,12 14.7733,12L14.2133,12z";
public static string ShapesIcon =
"F1 M24,24z M0,0z M8.7269,2.9067C8.84872,2.70875 9.00852,2.57931 9.20647,2.5184 9.41966,2.44227 9.62541,2.43465 9.82337,2.49556 10.0365,2.55647 10.2039,2.67829 10.3257,2.86102L13.9118,8.13731C12.9068,8.47231 12.0084,8.99766 11.2165,9.71334 10.4247,10.4138 9.8157,11.2513 9.38933,12.2259 8.93251,13.2156 8.70413,14.2587 8.70413,15.3551L8.7269,15.9946 2.46851,15.9946C2.22487,15.9946 2.01158,15.9185 1.82885,15.7662 1.66135,15.614 1.55481,15.4236 1.50913,15.1952 1.47868,14.9668 1.5244,14.746 1.64622,14.5328L8.7269,2.9067z M16.3329,21.545C15.2061,21.545 14.1632,21.2633 13.2038,20.6999 12.275,20.1517 11.5288,19.4056 10.9654,18.4615 10.4172,17.5022 10.1431,16.4667 10.1431,15.3551 10.1431,14.2283 10.4172,13.1928 10.9654,12.2487 11.5288,11.3046 12.2825,10.5585 13.2266,10.0103 14.1707,9.44687 15.1986,9.16516 16.3102,9.16516 17.437,9.16516 18.4724,9.44687 19.4165,10.0103 20.3606,10.5585 21.1068,11.3046 21.6549,12.2487 22.2184,13.1928 22.5,14.2283 22.5,15.3551 22.5,16.4667 22.2184,17.5022 21.6549,18.4615 21.1068,19.4056 20.3683,20.1517 19.4395,20.6999 18.4801,21.2633 17.4445,21.545 16.3329,21.545z";
public static string UndoIcon =
"F1 M24,24z M0,0z M10.3344,5.02734L10.3605,8.62849C12.2431,8.87029 14.0305,9.44026 15.7232,10.3384 17.4676,11.2538 18.9187,12.4196 20.0759,13.8359 21.3367,15.3731 22.1398,17.0657 22.4852,18.9138 22.5197,19.1211 22.4938,19.3197 22.4075,19.5097 22.3211,19.6997 22.2003,19.8292 22.0448,19.8983 21.8894,19.9501 21.7423,19.9069 21.6042,19.7687 20.7579,18.8361 19.6268,17.9984 18.2105,17.2557 16.9324,16.5821 15.5591,16.0553 14.091,15.6754 12.7093,15.3126 11.4659,15.1313 10.3605,15.1313L10.3344,18.862C10.3344,19.2938 10.2395,19.6047 10.0495,19.7947 9.8941,19.9501 9.69547,20.0106 9.45367,19.976 9.21187,19.9242 8.99586,19.8119 8.80587,19.6392 8.14955,18.9829 6.90606,17.7739 5.07526,16.0121 3.46899,14.475 2.41544,13.4559 1.91456,12.9551 1.63821,12.6787 1.5,12.3678 1.5,12.0224 1.51727,11.6769 1.67272,11.3574 1.96634,11.0638 4.02168,9.00847 6.3103,6.73724 8.83197,4.25011 9.00468,4.07739 9.20331,3.99967 9.42784,4.01694 9.66965,4.01694 9.87683,4.11194 10.0495,4.30193 10.2395,4.47464 10.3344,4.71645 10.3344,5.02734z";
public static string RedoIcon =
"F1 M24,24z M0,0z M13.6656,5.02734L13.6395,8.62849C11.7569,8.87029 9.96948,9.44026 8.27685,10.3384 6.5324,11.2538 5.08134,12.4196 3.92413,13.8359 2.6633,15.3731 1.86023,17.0657 1.5148,18.9138 1.48026,19.1211 1.50619,19.3197 1.59255,19.5097 1.6789,19.6997 1.79974,19.8292 1.95518,19.8983 2.11063,19.9501 2.25766,19.9069 2.39583,19.7687 3.24215,18.8361 4.37323,17.9984 5.78951,17.2557 7.06761,16.5821 8.44089,16.0553 9.90899,15.6754 11.2907,15.3126 12.5341,15.1313 13.6395,15.1313L13.6656,18.862C13.6656,19.2938 13.7605,19.6047 13.9505,19.7947 14.1059,19.9501 14.3045,20.0106 14.5463,19.976 14.7881,19.9242 15.0041,19.8119 15.1941,19.6392 15.8505,18.9829 17.0939,17.7739 18.9247,16.0121 20.531,14.475 21.5846,13.4559 22.0854,12.9551 22.3618,12.6787 22.5,12.3678 22.5,12.0224 22.4827,11.6769 22.3273,11.3574 22.0337,11.0638 19.9783,9.00847 17.6897,6.73724 15.168,4.25011 14.9953,4.07739 14.7967,3.99967 14.5722,4.01694 14.3304,4.01694 14.1232,4.11194 13.9505,4.30193 13.7605,4.47464 13.6656,4.71645 13.6656,5.02734z";
/// <summary>
/// 鼠并清
/// </summary>
public static string CursorWithDelFloatingBarBtnIcon =
"F0 M24,24z M0,0z M11.5007,11.0214C11.5007,10.674,11.7823,10.3923,12.1298,10.3923L14.2967,10.3923 14.2967,9.90299C14.2967,9.41878 14.5325,8.98723 14.8305,8.68924 15.1285,8.39124 15.56,8.15547 16.0442,8.15547L18.281,8.15547C18.7652,8.15547 19.1968,8.39124 19.4948,8.68924 19.7928,8.98723 20.0286,9.41878 20.0286,9.90299L20.0286,10.3923 22.1955,10.3923C22.5429,10.3923 22.8246,10.674 22.8246,11.0214 22.8246,11.3688 22.5429,11.6505 22.1955,11.6505L21.7062,11.6505 21.7062,18.8503C21.7062,19.3345 21.4704,19.766 21.1724,20.064 20.8744,20.362 20.4429,20.5978 19.9587,20.5978L14.3666,20.5978C13.8824,20.5978 13.4508,20.362 13.1528,20.064 12.8549,19.766 12.6191,19.3345 12.6191,18.8503L12.6191,11.6505 12.1298,11.6505C11.7823,11.6505,11.5007,11.3688,11.5007,11.0214z M14.0425,19.1743C13.9211,19.0529,13.8773,18.9253,13.8773,18.8503L13.8773,11.6505 20.448,11.6505 20.448,18.8503C20.448,18.9253 20.4041,19.0529 20.2827,19.1743 20.1613,19.2958 20.0337,19.3396 19.9587,19.3396L14.3666,19.3396C14.2916,19.3396,14.1639,19.2958,14.0425,19.1743z M15.5549,9.90299C15.5549,9.82799 15.5987,9.70034 15.7202,9.57893 15.8416,9.45752 15.9692,9.41368 16.0442,9.41368L18.281,9.41368C18.356,9.41368 18.4837,9.45752 18.6051,9.57893 18.7265,9.70034 18.7703,9.82799 18.7703,9.90299L18.7703,10.3923 15.5549,10.3923 15.5549,9.90299z M18.9101,13.8174C18.9101,13.47 18.6285,13.1883 18.281,13.1883 17.9336,13.1883 17.6519,13.47 17.6519,13.8174L17.6519,17.1727C17.6519,17.5201 17.9336,17.8018 18.281,17.8018 18.6285,17.8018 18.9101,17.5201 18.9101,17.1727L18.9101,13.8174z M16.6733,13.8174C16.6733,13.47 16.3917,13.1883 16.0442,13.1883 15.6968,13.1883 15.4151,13.47 15.4151,13.8174L15.4151,17.1727C15.4151,17.5201 15.6968,17.8018 16.0442,17.8018 16.3917,17.8018 16.6733,17.5201 16.6733,17.1727L16.6733,13.8174z M1.32567,3.55246C1.47265,3.40549,1.69379,3.36174,1.88566,3.44167L11.8342,7.58639C12.0311,7.66845 12.1567,7.86389 12.1495,8.07716 12.1424,8.29043 12.0039,8.47698 11.8018,8.54561L8.25047,9.75183 11.4309,12.9323C11.6313,13.1326 11.6313,13.4574 11.4309,13.6577 11.2306,13.8581 10.9058,13.8581 10.7055,13.6577L7.52503,10.4773 6.31881,14.0286C6.25019,14.2307 6.06364,14.3692 5.85037,14.3763 5.6371,14.3835 5.44166,14.2579 5.3596,14.0609L1.21488,4.11245C1.13494,3.92058,1.1787,3.69944,1.32567,3.55246z";
/// <summary>
/// 白板
/// </summary>
public static string WhiteboardFloatingBarBtnIcon =
"F0 M24,24z M0,0z M5,5.875C4.70163,5.875 4.41548,5.99353 4.2045,6.2045 3.99353,6.41548 3.875,6.70163 3.875,7L3.875,17C3.875,17.2984 3.99353,17.5845 4.2045,17.7955 4.41548,18.0065 4.70163,18.125 5,18.125L8,18.125C8.48325,18.125 8.875,18.5168 8.875,19 8.875,19.4832 8.48325,19.875 8,19.875L5,19.875C4.2375,19.875 3.50623,19.5721 2.96707,19.0329 2.4279,18.4938 2.125,17.7625 2.125,17L2.125,7C2.125,6.2375 2.4279,5.50624 2.96707,4.96707 3.50624,4.4279 4.2375,4.125 5,4.125L19,4.125C19.7625,4.125 20.4938,4.4279 21.0329,4.96707 21.5721,5.50623 21.875,6.2375 21.875,7L21.875,18C21.875,18.4973 21.6775,18.9742 21.3258,19.3258 20.9742,19.6775 20.4973,19.875 20,19.875 19.5168,19.875 19.125,19.4832 19.125,19 19.125,18.5168 19.5168,18.125 20,18.125 20.0332,18.125 20.0649,18.1118 20.0884,18.0884 20.1118,18.0649 20.125,18.0332 20.125,18L20.125,7C20.125,6.70163 20.0065,6.41548 19.7955,6.2045 19.5845,5.99353 19.2984,5.875 19,5.875L5,5.875z M10.6742,15.6742C11.0258,15.3225,11.5027,15.125,12,15.125L16,15.125C16.4973,15.125 16.9742,15.3225 17.3258,15.6742 17.6775,16.0258 17.875,16.5027 17.875,17L17.875,18C17.875,18.4973 17.6775,18.9742 17.3258,19.3258 16.9742,19.6775 16.4973,19.875 16,19.875L12,19.875C11.5027,19.875 11.0258,19.6775 10.6742,19.3258 10.3225,18.9742 10.125,18.4973 10.125,18L10.125,17C10.125,16.5027,10.3225,16.0258,10.6742,15.6742z";
public static string ToolsFloatingBarBtnIcon =
"F0 M24,24z M0,0z M4.41721,4.29873C4.35178,4.29873,4.29873,4.35178,4.29873,4.41721L4.29873,9.15646C4.29873,9.22189,4.35178,9.27494,4.41721,9.27494L9.15646,9.27494C9.22189,9.27494,9.27494,9.22189,9.27494,9.15646L9.27494,4.41721C9.27494,4.35178,9.22189,4.29873,9.15646,4.29873L4.41721,4.29873z M2.64,4.41721C2.64,3.43569,3.43569,2.64,4.41721,2.64L9.15646,2.64C10.138,2.64,10.9337,3.43569,10.9337,4.41721L10.9337,9.15646C10.9337,10.138,10.138,10.9337,9.15646,10.9337L4.41721,10.9337C3.43569,10.9337,2.64,10.138,2.64,9.15646L2.64,4.41721z M14.8435,4.29873C14.7781,4.29873,14.7251,4.35178,14.7251,4.41721L14.7251,9.15646C14.7251,9.22189,14.7781,9.27494,14.8435,9.27494L19.5828,9.27494C19.6482,9.27494,19.7013,9.22189,19.7013,9.15646L19.7013,4.41721C19.7013,4.35178,19.6482,4.29873,19.5828,4.29873L14.8435,4.29873z M13.0663,4.41721C13.0663,3.43569,13.862,2.64,14.8435,2.64L19.5828,2.64C20.5643,2.64,21.36,3.43569,21.36,4.41721L21.36,9.15646C21.36,10.138,20.5643,10.9337,19.5828,10.9337L14.8435,10.9337C13.862,10.9337,13.0663,10.138,13.0663,9.15646L13.0663,4.41721z M14.8435,14.7251C14.7781,14.7251,14.7251,14.7781,14.7251,14.8435L14.7251,19.5828C14.7251,19.6482,14.7781,19.7013,14.8435,19.7013L19.5828,19.7013C19.6482,19.7013,19.7013,19.6482,19.7013,19.5828L19.7013,14.8435C19.7013,14.7781,19.6482,14.7251,19.5828,14.7251L14.8435,14.7251z M13.0663,14.8435C13.0663,13.862,13.862,13.0663,14.8435,13.0663L19.5828,13.0663C20.5643,13.0663,21.36,13.862,21.36,14.8435L21.36,19.5828C21.36,20.5643,20.5643,21.36,19.5828,21.36L14.8435,21.36C13.862,21.36,13.0663,20.5643,13.0663,19.5828L13.0663,14.8435z M4.41721,14.7251C4.35178,14.7251,4.29873,14.7781,4.29873,14.8435L4.29873,19.5828C4.29873,19.6482,4.35178,19.7013,4.41721,19.7013L9.15646,19.7013C9.22189,19.7013,9.27494,19.6482,9.27494,19.5828L9.27494,14.8435C9.27494,14.7781,9.22189,14.7251,9.15646,14.7251L4.41721,14.7251z M2.64,14.8435C2.64,13.862,3.43569,13.0663,4.41721,13.0663L9.15646,13.0663C10.138,13.0663,10.9337,13.862,10.9337,14.8435L10.9337,19.5828C10.9337,20.5643,10.138,21.36,9.15646,21.36L4.41721,21.36C3.43569,21.36,2.64,20.5643,2.64,19.5828L2.64,14.8435z";
public static string ClipGeometry24x24 = "M0,0 V24 H24 V0 H0 Z";
public static string ClearInkIconGeometry = "F0 M24,24z M0,0z M10.1573,10.0421C10.7298,10.0421,11.1938,10.5062,11.1938,11.0787L11.1938,16.6067C11.1938,17.1792,10.7298,17.6433,10.1573,17.6433,9.58485,17.6433,9.12079,17.1792,9.12079,16.6067L9.12079,11.0787C9.12079,10.5062,9.58485,10.0421,10.1573,10.0421z M13.8427,10.0421C14.4151,10.0421,14.8792,10.5062,14.8792,11.0787L14.8792,16.6067C14.8792,17.1792,14.4151,17.6433,13.8427,17.6433,13.2702,17.6433,12.8062,17.1792,12.8062,16.6067L12.8062,11.0787C12.8062,10.5062,13.2702,10.0421,13.8427,10.0421z M3.70787,5.43539C3.13541,5.43539,2.67135,5.89946,2.67135,6.47191,2.67135,7.04436,3.13541,7.50843,3.70787,7.50843L4.51405,7.50843,4.51405,19.3708C4.51405,20.1686,4.9025,20.8796,5.39348,21.3706,5.88445,21.8615,6.59548,22.25,7.39326,22.25L16.6067,22.25C17.4045,22.25,18.1155,21.8615,18.6065,21.3706,19.0975,20.8796,19.486,20.1686,19.486,19.3708L19.486,7.50843,20.2921,7.50843C20.8646,7.50843,21.3287,7.04436,21.3287,6.47191,21.3287,5.89946,20.8646,5.43539,20.2921,5.43539L16.7219,5.43539,16.7219,4.62921C16.7219,3.83143,16.3335,3.12041,15.8425,2.62943,15.3515,2.13845,14.6405,1.75,13.8427,1.75L10.1573,1.75C9.35952,1.75,8.6485,2.13845,8.15752,2.62943,7.66654,3.12041,7.27809,3.83143,7.27809,4.62921L7.27809,5.43539,3.70787,5.43539z M6.58708,19.3708C6.58708,19.4944,6.6593,19.7047,6.85933,19.9047,7.05937,20.1047,7.26969,20.177,7.39326,20.177L16.6067,20.177C16.7303,20.177,16.9406,20.1047,17.1407,19.9047,17.3407,19.7047,17.4129,19.4944,17.4129,19.3708L17.4129,7.50843,6.58708,7.50843,6.58708,19.3708z M9.62338,4.09529C9.42334,4.29532,9.35112,4.50565,9.35112,4.62921L9.35112,5.43539,14.6489,5.43539,14.6489,4.62921C14.6489,4.50565,14.5767,4.29532,14.3766,4.09529,14.1766,3.89525,13.9663,3.82303,13.8427,3.82303L10.1573,3.82303C10.0337,3.82303,9.82341,3.89525,9.62338,4.09529z";
public static string TimerIconGeometry = "F0 M24,24z M0,0z M12,3C11.7613,3,11.5324,3.09482,11.3636,3.2636,11.1948,3.43239,11.1,3.6613,11.1,3.9L11.1,6.6C11.1,7.09705,11.5029,7.5,12,7.5,12.4971,7.5,12.9,7.09705,12.9,6.6L12.9,4.85646C14.3196,5.0353,15.6613,5.63436,16.7472,6.58675,18.0606,7.73848,18.9104,9.32838,19.1384,11.0602,19.3664,12.792,18.957,14.5477,17.9865,16.0001,17.0162,17.4525,15.5508,18.5026,13.8635,18.9547,12.1762,19.4067,10.3822,19.2301,8.81552,18.4575,7.24888,17.6849,6.01653,16.3691,5.34806,14.7554,4.67961,13.1415,4.62062,11.3397,5.1821,9.68563,5.49617,8.76044,5.99235,7.91676,6.63344,7.2,6.96481,6.8295,6.93309,6.26054,6.5626,5.92917,6.19212,5.59781,5.62315,5.62952,5.29179,6.00001,4.49049,6.8959,3.87021,7.95054,3.47763,9.10704,2.77577,11.1746,2.84951,13.4269,3.68509,15.4441,4.52067,17.4614,6.0611,19.1061,8.0194,20.0718,9.97769,21.0375,12.2203,21.2585,14.3294,20.6933,16.4384,20.1282,18.2701,18.8156,19.4832,17.0001,20.6963,15.1847,21.208,12.9901,20.923,10.8252,20.638,8.66047,19.5758,6.67311,17.9341,5.23344,16.2925,3.79377,14.1835,3,12,3z M9.0364,7.7636C8.68492,7.41213,8.11508,7.41213,7.7636,7.7636,7.41213,8.11508,7.41213,8.68492,7.7636,9.0364L10.2609,11.5338C10.2212,11.6825,10.2,11.8387,10.2,12,10.2,12.9941,11.0059,13.8,12,13.8,12.9941,13.8,13.8,12.9941,13.8,12,13.8,11.0059,12.9941,10.2,12,10.2,11.8387,10.2,11.6825,10.2212,11.5338,10.2609L9.0364,7.7636z";
public static string RandomDrawIconGeometry = "F0 M24,24z M0,0z M15.9475,4.75965C15.7358,5.18305,15.9074,5.69791,16.3308,5.90961L16.9034,6.19589C14.0673,6.82039,11.7039,8.89176,10.7582,11.729,9.8511,14.4502,7.3045,16.2857,4.43608,16.2857L3.85713,16.2857C3.38376,16.2857,3,16.6694,3,17.1428,3,17.6162,3.38376,17.9999,3.85713,17.9999L4.43608,17.9999C8.04237,17.9999,11.2441,15.6923,12.3845,12.271,13.2915,9.54985,15.8381,7.71436,18.7066,7.71436L20.124,7.71436C20.1661,7.71524,20.208,7.71301,20.2494,7.70777,20.4903,7.67729,20.7143,7.54515,20.8554,7.33338,20.9116,7.24952,20.9533,7.15519,20.9771,7.05396,21.001,6.95301,21.0059,6.85024,20.9932,6.75036,20.9627,6.50938,20.8304,6.28523,20.6184,6.14418,20.5838,6.12101,20.5474,6.1003,20.5094,6.08229L17.0974,4.37632C16.674,4.16462,16.1592,4.33624,15.9475,4.75965z M20.8557,16.667C20.9116,16.7507,20.9533,16.8447,20.977,16.9456,21.001,17.0467,21.0059,17.1497,20.9932,17.2498,20.9626,17.4908,20.8303,17.7148,20.6185,17.8558,20.5838,17.8791,20.5474,17.8997,20.5094,17.9177L17.0975,19.6237C16.674,19.8354,16.1592,19.6638,15.9475,19.2404,15.7358,18.817,15.9074,18.3022,16.3308,18.0904L17.0491,17.7313C15.7728,17.4079,14.5941,16.8029,13.5952,15.9737,13.231,15.6713,13.1809,15.1308,13.4833,14.7667,13.7857,14.4025,14.3261,14.3523,14.6903,14.6547,15.9326,15.6862,17.5188,16.2857,19.2067,16.2857L20.1241,16.2857C20.1662,16.2848,20.2081,16.287,20.2495,16.2923,20.4905,16.3228,20.7146,16.455,20.8557,16.667z M3,6.85722C3,6.38384,3.38376,6.00009,3.85713,6.00009L4.36455,6.00009C6.25231,6.00009,8.03834,6.60414,9.50119,7.6588,9.88517,7.93565,9.972,8.47136,9.69519,8.85534,9.41835,9.23934,8.88264,9.32619,8.49866,9.04935,7.32072,8.2001,5.88391,7.71436,4.36455,7.71436L3.85713,7.71436C3.38376,7.71436,3,7.3306,3,6.85722z";
public static string SingleDrawIconGeometry = "F0 M24,24z M0,0z M3.9,9.3C4.39706,9.3,4.8,8.89706,4.8,8.4L4.8,4.8,8.4,4.8C8.89705,4.8,9.3,4.39706,9.3,3.9,9.3,3.40295,8.89705,3,8.4,3L3.9,3C3.6613,3,3.43239,3.09482,3.2636,3.2636,3.09482,3.43239,3,3.6613,3,3.9L3,8.4C3,8.89706,3.40295,9.3,3.9,9.3z M3.9,14.7C4.39706,14.7,4.8,15.1029,4.8,15.6L4.8,19.2,8.4,19.2C8.89705,19.2,9.3,19.6029,9.3,20.1,9.3,20.5971,8.89705,21,8.4,21L3.9,21C3.6613,21,3.43239,20.9051,3.2636,20.7364,3.09482,20.5676,3,20.3387,3,20.1L3,15.6C3,15.1029,3.40295,14.7,3.9,14.7z M20.1,9.3C19.6029,9.3,19.2,8.89706,19.2,8.4L19.2,4.8,15.6,4.8C15.1029,4.8,14.7,4.39706,14.7,3.9,14.7,3.40295,15.1029,3,15.6,3L20.1,3C20.3387,3,20.5676,3.09482,20.7364,3.2636,20.9051,3.43239,21,3.6613,21,3.9L21,8.4C21,8.89706,20.5971,9.3,20.1,9.3z M20.1,14.7C19.6029,14.7,19.2,15.1029,19.2,15.6L19.2,19.2,15.6,19.2C15.1029,19.2,14.7,19.6029,14.7,20.1,14.7,20.5971,15.1029,21,15.6,21L20.1,21C20.3387,21,20.5676,20.9051,20.7364,20.7364,20.9051,20.5676,21,20.3387,21,20.1L21,15.6C21,15.1029,20.5971,14.7,20.1,14.7z M8.4,10.2C8.4,8.21177,10.0118,6.6,12,6.6,13.9882,6.6,15.6,8.21177,15.6,10.2,15.6,12.1882,13.9882,13.8,12,13.8,10.0118,13.8,8.4,12.1882,8.4,10.2z M12,8.4C11.0059,8.4,10.2,9.20589,10.2,10.2,10.2,11.1941,11.0059,12,12,12,12.9941,12,13.8,11.1941,13.8,10.2,13.8,9.20589,12.9941,8.4,12,8.4z M7.5,16.5C7.5,16.0029,7.90295,15.6,8.4,15.6L15.6,15.6C16.0971,15.6,16.5,16.0029,16.5,16.5,16.5,16.9971,16.0971,17.4,15.6,17.4L8.4,17.4C7.90295,17.4,7.5,16.9971,7.5,16.5z";
public static string SaveIconGeometry = "F0 M24,24z M0,0z M6,5C5.73478,5,5.48043,5.10536,5.29289,5.29289,5.10536,5.48043,5,5.73478,5,6L5,18C5,18.2652,5.10536,18.5196,5.29289,18.7071,5.48043,18.8946,5.73478,19,6,19L18,19C18.2652,19,18.5196,18.8946,18.7071,18.7071,18.8946,18.5196,19,18.2652,19,18L19,8.41421,15.5858,5,15,5,15,8C15,8.55228,14.5523,9,14,9L8,9C7.44772,9,7,8.55228,7,8L7,5,6,5z M8,3L6,3C5.20435,3,4.44129,3.31607,3.87868,3.87868,3.31607,4.44129,3,5.20435,3,6L3,18C3,18.7956,3.31607,19.5587,3.87868,20.1213,4.44129,20.6839,5.20435,21,6,21L18,21C18.7957,21,19.5587,20.6839,20.1213,20.1213,20.6839,19.5587,21,18.7957,21,18L21,8C21,7.73478,20.8946,7.48043,20.7071,7.29289L16.7071,3.29289C16.5196,3.10536,16.2652,3,16,3L14,3,8,3z M9,5L9,7,13,7,13,5,9,5z M9.87868,11.8787C10.4413,11.3161,11.2043,11,12,11,12.7957,11,13.5587,11.3161,14.1213,11.8787,14.6839,12.4413,15,13.2043,15,14,15,14.7957,14.6839,15.5587,14.1213,16.1213,13.5587,16.6839,12.7957,17,12,17,11.2043,17,10.4413,16.6839,9.87868,16.1213,9.31607,15.5587,9,14.7957,9,14,9,13.2043,9.31607,12.4413,9.87868,11.8787z M12,13C11.7348,13,11.4804,13.1054,11.2929,13.2929,11.1054,13.4804,11,13.7348,11,14,11,14.2652,11.1054,14.5196,11.2929,14.7071,11.4804,14.8946,11.7348,15,12,15,12.2652,15,12.5196,14.8946,12.7071,14.7071,12.8946,14.5196,13,14.2652,13,14,13,13.7348,12.8946,13.4804,12.7071,13.2929,12.5196,13.1054,12.2652,13,12,13z";
public static string OpenIconGeometry = "F0 M24,24z M0,0z M5,5C4.73478,5,4.48043,5.10536,4.29289,5.29289,4.10536,5.48043,4,5.73478,4,6L4,17C4,17.2652,4.10536,17.5196,4.29289,17.7071,4.32236,17.7366,4.35349,17.764,4.38604,17.7893L6.82062,11.298C6.82065,11.2979,6.8206,11.2981,6.82062,11.298,6.96351,10.9169,7.21932,10.5883,7.55377,10.3564,7.88827,10.1245,8.28558,10.0002,8.69262,10L20,10,20,9C20,8.73478,19.8946,8.48043,19.7071,8.29289,19.5196,8.10536,19.2652,8,19,8L12,8C11.7348,8,11.4804,7.89464,11.2929,7.70711L8.58579,5,5,5z M20.9992,12L8.69338,12,6.44307,18,19.0257,18C19.0258,18,19.0256,18,19.0257,18,19.2583,17.9999,19.4838,17.9187,19.663,17.7705,19.8422,17.6222,19.9641,17.416,20.0077,17.1875L20.9992,12z M22,10.2682C22.1988,10.383,22.3767,10.5315,22.5256,10.7073,22.7133,10.9288,22.8504,11.1885,22.9276,11.4684,23.0048,11.7483,23.0201,12.0416,22.9725,12.328L22.9682,12.3517,21.9723,17.5625C21.8414,18.248,21.4756,18.8665,20.9379,19.3114,20.4002,19.7563,19.7242,19.9998,19.0263,20L5,20C4.20435,20,3.44129,19.6839,2.87868,19.1213,2.31607,18.5587,2,17.7956,2,17L2,6C2,5.20435,2.31607,4.44129,2.87868,3.87868,3.44129,3.31607,4.20435,3,5,3L9,3C9.26522,3,9.51957,3.10536,9.70711,3.29289L12.4142,6,19,6C19.7957,6,20.5587,6.31607,21.1213,6.87868,21.6839,7.44129,22,8.20435,22,9L22,10.2682z";
public static string ReplayIconGeometry = "F0 M24,24z M0,0z M11,4.99999C11,4.60761,10.7705,4.25149,10.4132,4.08935,10.0559,3.92722,9.63679,3.98903,9.3415,4.24741L1.3415,11.2474C1.12448,11.4373,1,11.7116,1,12,1,12.2883,1.12448,12.5627,1.3415,12.7526L9.3415,19.7526C9.63679,20.0109,10.0559,20.0728,10.4132,19.9106,10.7705,19.7485,11,19.3924,11,19L11,4.99999z M9,16.7962L3.51859,12,9,7.20375,9,16.7962z M22,4.99999C22,4.60761,21.7705,4.25149,21.4132,4.08935,21.0559,3.92722,20.6368,3.98903,20.3415,4.24741L12.3415,11.2474C12.1245,11.4373,12,11.7116,12,12,12,12.2883,12.1245,12.5627,12.3415,12.7526L20.3415,19.7526C20.6368,20.0109,21.0559,20.0728,21.4132,19.9106,21.7705,19.7485,22,19.3924,22,19L22,4.99999z M20,16.7962L14.5186,12,20,7.20375,20,16.7962z";
public static string ScreenshotIconGeometry = "F0 M24,24z M0,0z M9,3C8.46957,3,7.96086,3.21071,7.58579,3.58579,7.21071,3.96086,7,4.46957,7,5,7,5.26522,6.89464,5.51957,6.70711,5.70711,6.51957,5.89464,6.26522,6,6,6L5,6C4.20435,6,3.44129,6.31607,2.87868,6.87868,2.31607,7.44129,2,8.20435,2,9L2,18C2,18.7956,2.31607,19.5587,2.87868,20.1213,3.44129,20.6839,4.20435,21,5,21L19,21C19.7957,21,20.5587,20.6839,21.1213,20.1213,21.6839,19.5587,22,18.7957,22,18L22,9C22,8.20435,21.6839,7.44129,21.1213,6.87868,20.5587,6.31607,19.7957,6,19,6L18,6C17.7348,6,17.4804,5.89464,17.2929,5.70711,17.1054,5.51957,17,5.26522,17,5,17,4.46957,16.7893,3.96086,16.4142,3.58579,16.0391,3.21071,15.5304,3,15,3L9,3z M9,5L15,5C15,5.79565,15.3161,6.55871,15.8787,7.12132,16.4413,7.68393,17.2044,8,18,8L19,8C19.2652,8,19.5196,8.10536,19.7071,8.29289,19.8946,8.48043,20,8.73478,20,9L20,18C20,18.2652,19.8946,18.5196,19.7071,18.7071,19.5196,18.8946,19.2652,19,19,19L5,19C4.73478,19,4.48043,18.8946,4.29289,18.7071,4.10536,18.5196,4,18.2652,4,18L4,9C4,8.73478,4.10536,8.48043,4.29289,8.29289,4.48043,8.10536,4.73478,8,5,8L6,8C6.79565,8,7.55871,7.68393,8.12132,7.12132,8.68393,6.55871,9,5.79565,9,5z M12,9C10.9391,9,9.92172,9.42143,9.17157,10.1716,8.42143,10.9217,8,11.9391,8,13,8,14.0609,8.42143,15.0783,9.17157,15.8284,9.92172,16.5786,10.9391,17,12,17,13.0609,17,14.0783,16.5786,14.8284,15.8284,15.5786,15.0783,16,14.0609,16,13,16,11.9391,15.5786,10.9217,14.8284,10.1716,14.0783,9.42143,13.0609,9,12,9z M10.5858,11.5858C10.9609,11.2107,11.4696,11,12,11,12.5304,11,13.0391,11.2107,13.4142,11.5858,13.7893,11.9609,14,12.4696,14,13,14,13.5304,13.7893,14.0391,13.4142,14.4142,13.0391,14.7893,12.5304,15,12,15,11.4696,15,10.9609,14.7893,10.5858,14.4142,10.2107,14.0391,10,13.5304,10,13,10,12.4696,10.2107,11.9609,10.5858,11.5858z";
public static string ManualIconGeometry = "F0 M24,24z M0,0z M8.17317,2.7612C9.38642,2.25866 10.6868,2 12,2 13.3132,2 14.6136,2.25866 15.8268,2.7612 17.0401,3.26375 18.1425,4.00035 19.0711,4.92893 19.9997,5.85752 20.7362,6.95991 21.2388,8.17317 21.7413,9.38642 22,10.6868 22,12 22,13.3132 21.7413,14.6136 21.2388,15.8268 20.7362,17.0401 19.9997,18.1425 19.0711,19.0711 18.1425,19.9997 17.0401,20.7362 15.8268,21.2388 14.6136,21.7413 13.3132,22 12,22 10.6868,22 9.38642,21.7413 8.17317,21.2388 6.95991,20.7362 5.85752,19.9997 4.92893,19.0711 4.00035,18.1425 3.26375,17.0401 2.7612,15.8268 2.25866,14.6136 2,13.3132 2,12 2,10.6868 2.25866,9.38642 2.7612,8.17317 3.26375,6.95991 4.00035,5.85752 4.92893,4.92893 5.85752,4.00035 6.95991,3.26375 8.17317,2.7612z M12,4C10.9494,4 9.90914,4.20693 8.93853,4.60896 7.96793,5.011 7.08601,5.60028 6.34315,6.34315 5.60028,7.08601 5.011,7.96793 4.60896,8.93853 4.20693,9.90914 4,10.9494 4,12 4,13.0506 4.20693,14.0909 4.60896,15.0615 5.011,16.0321 5.60028,16.914 6.34315,17.6569 7.08601,18.3997 7.96793,18.989 8.93853,19.391 9.90914,19.7931 10.9494,20 12,20 13.0506,20 14.0909,19.7931 15.0615,19.391 16.0321,18.989 16.914,18.3997 17.6569,17.6569 18.3997,16.914 18.989,16.0321 19.391,15.0615 19.7931,14.0909 20,13.0506 20,12 20,10.9494 19.7931,9.90914 19.391,8.93853 18.989,7.96793 18.3997,7.08602 17.6569,6.34315 16.914,5.60028 16.0321,5.011 15.0615,4.60896 14.0909,4.20693 13.0506,4 12,4z M12,16C12.5523,16,13,16.4477,13,17L13,17.01C13,17.5623 12.5523,18.01 12,18.01 11.4477,18.01 11,17.5623 11,17.01L11,17C11,16.4477,11.4477,16,12,16z M12.0813,5.97153C11.5241,5.96998 10.9742,6.09779 10.4748,6.3449 9.97535,6.59201 9.54013,6.95167 9.20334,7.39557 8.86952,7.83555 8.95559,8.46284 9.39557,8.79666 9.83555,9.13047 10.4628,9.04441 10.7967,8.60443 10.9463,8.40714 11.1398,8.24729 11.3617,8.13746 11.5837,8.02764 11.8281,7.97083 12.0758,7.97152 12.3234,7.97221 12.5675,8.03037 12.7888,8.14142 13.0102,8.25248 13.2027,8.4134 13.3513,8.61151 13.4999,8.80963 13.6005,9.03953 13.6451,9.28311 13.6897,9.5267 13.6772,9.77732 13.6086,10.0152 13.5399,10.2532 13.4169,10.4719 13.2493,10.6542 13.084,10.8341 12.8798,10.9736 12.6524,11.0623 12.1479,11.2435 11.7149,11.5821 11.4175,12.0283 11.1169,12.4792 10.9709,13.0156 11.0016,13.5566 11.0329,14.108 11.5052,14.5297 12.0566,14.4984 12.608,14.4671 13.0297,13.9948 12.9984,13.4434 12.9923,13.3352 13.0214,13.2279 13.0816,13.1377 13.1417,13.0475 13.2295,12.9793 13.3317,12.9434 13.3403,12.9404 13.3487,12.9373 13.3572,12.934 13.8776,12.735 14.3448,12.4179 14.7218,12.0077 15.0989,11.5974 15.3756,11.1053 15.5301,10.5699 15.6846,10.0346 15.7128,9.47068 15.6124,8.92261 15.5119,8.37454 15.2856,7.85727 14.9513,7.41151 14.617,6.96576 14.1838,6.60369 13.6857,6.35381 13.1877,6.10393 12.6385,5.97307 12.0813,5.97153z";
public static string SettingsIconGeometry = "F0 M24,24z M0,0z M4.66591,7.13141L11.4017,3.15976C11.5957,3.0552,11.8126,3.00041,12.033,3.00041,12.2534,3.00041,12.4704,3.0552,12.6643,3.15977L19.2182,7.02415C19.2676,7.06721,19.3219,7.10586,19.3806,7.13926,19.5699,7.24687,19.727,7.40296,19.8358,7.59146,19.9447,7.77997,20.0014,7.99407,20,8.21175L20,8.21175,20,8.218,20,15.502C20,15.943,19.7585,16.3548,19.3603,16.5737,19.3424,16.5835,19.3247,16.5939,19.3074,16.6049L12.5874,20.8559C12.4062,20.9506,12.2047,21.0001,12,21.0001,11.7953,21.0001,11.5938,20.9506,11.4126,20.8559L4.69261,16.6049C4.6746,16.5935,4.65624,16.5827,4.63755,16.5725,4.44494,16.4672,4.28416,16.3122,4.172,16.1235,4.05999,15.9351,4.00059,15.72,4,15.5008L4,8.217C4,7.77653,4.24107,7.36544,4.63968,7.14635,4.6485,7.1415,4.65724,7.13652,4.66591,7.13141z M20.4159,5.40859C20.4791,5.44583,20.5369,5.4892,20.589,5.53759,20.9895,5.81003,21.3244,6.16988,21.5678,6.59125,21.8538,7.08656,22.003,7.649,22,8.22093L22,15.502C22,16.6678,21.3677,17.7387,20.353,18.31L13.6266,22.5651C13.6092,22.5761,13.5914,22.5866,13.5733,22.5966,13.0911,22.8613,12.55,23.0001,12,23.0001,11.45,23.0001,10.9089,22.8613,10.4267,22.5966,10.4086,22.5866,10.3908,22.5761,10.3734,22.5651L3.64791,18.3106C3.15439,18.0339,2.74214,17.6322,2.45282,17.1455,2.15755,16.6488,2.00116,16.0818,2,15.504L2,15.502,2,8.217C2,7.04497,2.63892,5.97063,3.6619,5.40163L10.4001,1.42859C10.4084,1.4237,10.4167,1.41894,10.4252,1.41429,10.9176,1.1428,11.4707,1.00041,12.033,1.00041,12.5953,1.00041,13.1484,1.1428,13.6408,1.41429,13.6493,1.41894,13.6576,1.4237,13.6659,1.42859L20.4159,5.40859z M12,8C10.9391,8,9.92172,8.42143,9.17157,9.17157,8.42143,9.92172,8,10.9391,8,12,8,13.0609,8.42143,14.0783,9.17157,14.8284,9.92172,15.5786,10.9391,16,12,16,13.0609,16,14.0783,15.5786,14.8284,14.8284,15.5786,14.0783,16,13.0609,16,12,16,10.9391,15.5786,9.92172,14.8284,9.17157,14.0783,8.42143,13.0609,8,12,8z M10.5858,10.5858C10.9609,10.2107,11.4696,10,12,10,12.5304,10,13.0391,10.2107,13.4142,10.5858,13.7893,10.9609,14,11.4696,14,12,14,12.5304,13.7893,13.0391,13.4142,13.4142,13.0391,13.7893,12.5304,14,12,14,11.4696,14,10.9609,13.7893,10.5858,13.4142,10.2107,13.0391,10,12.5304,10,12,10,11.4696,10.2107,10.9609,10.5858,10.5858z";
#region
public static string NavStartupIconGeometry = "F0 M24,24z M0,0z M6.63415,3.34528C6.87134,3.21274,7.16167,3.21885,7.39307,3.36126L20.3931,11.3613C20.6149,11.4978 20.75,11.7396 20.75,12 20.75,12.2604 20.6149,12.5022 20.3931,12.6387L7.39307,20.6387C7.16167,20.7811 6.87134,20.7873 6.63415,20.6547 6.39696,20.5222 6.25,20.2717 6.25,20L6.25,4C6.25,3.72829,6.39696,3.47783,6.63415,3.34528z M7.75,5.34217L7.75,18.6578 18.569,12 7.75,5.34217z";
public static string NavCanvasIconGeometry = "F0 M24,24z M0,0z M4.11612,6.11612C4.35054,5.8817,4.66848,5.75,5,5.75L19,5.75C19.3315,5.75 19.6495,5.8817 19.8839,6.11612 20.1183,6.35054 20.25,6.66848 20.25,7L20.25,18C20.25,18.0663 20.2237,18.1299 20.1768,18.1768 20.1299,18.2237 20.0663,18.25 20,18.25 19.5858,18.25 19.25,18.5858 19.25,19 19.25,19.4142 19.5858,19.75 20,19.75 20.4641,19.75 20.9092,19.5656 21.2374,19.2374 21.5656,18.9092 21.75,18.4641 21.75,18L21.75,7C21.75,6.27065 21.4603,5.57118 20.9445,5.05546 20.4288,4.53973 19.7293,4.25 19,4.25L5,4.25C4.27065,4.25 3.57118,4.53973 3.05546,5.05546 2.53973,5.57118 2.25,6.27065 2.25,7L2.25,17C2.25,17.7293 2.53973,18.4288 3.05546,18.9445 3.57118,19.4603 4.27065,19.75 5,19.75L8,19.75C8.41421,19.75 8.75,19.4142 8.75,19 8.75,18.5858 8.41421,18.25 8,18.25L5,18.25C4.66848,18.25 4.35054,18.1183 4.11612,17.8839 3.8817,17.6495 3.75,17.3315 3.75,17L3.75,7C3.75,6.66848,3.8817,6.35054,4.11612,6.11612z M11.8232,16.8232C11.8701,16.7763,11.9337,16.75,12,16.75L16,16.75C16.0663,16.75 16.1299,16.7763 16.1768,16.8232 16.2237,16.8701 16.25,16.9337 16.25,17L16.25,18C16.25,18.0663 16.2237,18.1299 16.1768,18.1768 16.1299,18.2237 16.0663,18.25 16,18.25L12,18.25C11.9337,18.25 11.8701,18.2237 11.8232,18.1768 11.7763,18.1299 11.75,18.0663 11.75,18L11.75,17C11.75,16.9337,11.7763,16.8701,11.8232,16.8232z M12,15.25C11.5359,15.25 11.0908,15.4344 10.7626,15.7626 10.4344,16.0908 10.25,16.5359 10.25,17L10.25,18C10.25,18.4641 10.4344,18.9092 10.7626,19.2374 11.0908,19.5656 11.5359,19.75 12,19.75L16,19.75C16.4641,19.75 16.9092,19.5656 17.2374,19.2374 17.5656,18.9092 17.75,18.4641 17.75,18L17.75,17C17.75,16.5359 17.5656,16.0908 17.2374,15.7626 16.9092,15.4344 16.4641,15.25 16,15.25L12,15.25z";
public static string NavInkRecognitionIconGeometry = "F0 M24,24z M0,0z M15.1306,4.19378C15.5647,4.01395 16.0301,3.92139 16.5,3.92139 16.9699,3.92139 17.4353,4.01395 17.8694,4.19378 18.3036,4.37361 18.698,4.6372 19.0303,4.96948 19.3626,5.30177 19.6262,5.69625 19.806,6.13041 19.9859,6.56457 20.0784,7.02989 20.0784,7.49981 20.0784,7.96974 19.9859,8.43506 19.806,8.86922 19.6262,9.30337 19.3626,9.69786 19.0303,10.0301L18.0403,11.0202 18.0303,11.0303 18.0202,11.0403 8.53033,20.5301C8.38968,20.6708,8.19891,20.7498,8,20.7498L4,20.7498C3.58579,20.7498,3.25,20.414,3.25,19.9998L3.25,15.9998C3.25,15.8009,3.32902,15.6101,3.46967,15.4695L13.9697,4.96948C14.302,4.6372,14.6964,4.37361,15.1306,4.19378z M17.9697,8.96948L17.4999,9.43925 14.5606,6.49991 15.0303,6.03014C15.2233,5.83714 15.4525,5.68405 15.7046,5.5796 15.9568,5.47515 16.2271,5.42139 16.5,5.42139 16.7729,5.42139 17.0432,5.47515 17.2954,5.5796 17.5475,5.68405 17.7767,5.83714 17.9697,6.03014 18.1627,6.22314 18.3158,6.45227 18.4202,6.70443 18.5247,6.9566 18.5784,7.22687 18.5784,7.49981 18.5784,7.77276 18.5247,8.04303 18.4202,8.29519 18.3158,8.54736 18.1627,8.77648 17.9697,8.96948z M4.75,16.3105L13.4999,7.56057 16.4392,10.4999 7.68934,19.2498 4.75,19.2498 4.75,16.3105z M21.5303,17.5303C21.8232,17.2374 21.8232,16.7626 21.5303,16.4697 21.2374,16.1768 20.7626,16.1768 20.4697,16.4697L17,19.9393 15.5303,18.4697C15.2374,18.1768 14.7626,18.1768 14.4697,18.4697 14.1768,18.7626 14.1768,19.2374 14.4697,19.5303L16.4697,21.5303C16.7626,21.8232,17.2374,21.8232,17.5303,21.5303L21.5303,17.5303z";
public static string NavThemeIconGeometry = "F0 M24,24z M0,0z M9.43853,3.39157C9.63411,3.53254 9.75,3.75892 9.75,4.00001 9.75,4.59674 9.98705,5.16904 10.409,5.591 10.831,6.01295 11.4033,6.25001 12,6.25001 12.5967,6.25001 13.169,6.01295 13.591,5.591 14.0129,5.16904 14.25,4.59674 14.25,4.00001 14.25,3.75892 14.3659,3.53254 14.5615,3.39157 14.757,3.25061 15.0085,3.21226 15.2372,3.28849L21.2372,5.28849C21.5434,5.39058,21.75,5.67718,21.75,6.00001L21.75,11C21.75,11.4142,21.4142,11.75,21,11.75L18.75,11.75 18.75,19C18.75,19.4641 18.5656,19.9093 18.2374,20.2374 17.9092,20.5656 17.4641,20.75 17,20.75L7,20.75C6.53587,20.75 6.09075,20.5656 5.76256,20.2374 5.43437,19.9093 5.25,19.4641 5.25,19L5.25,11.75 3,11.75C2.58579,11.75,2.25,11.4142,2.25,11L2.25,6.00001C2.25,5.67718,2.45657,5.39058,2.76283,5.28849L8.76283,3.28849C8.99154,3.21226,9.24296,3.25061,9.43853,3.39157z M3.75,6.54058L3.75,10.25 6,10.25C6.41421,10.25,6.75,10.5858,6.75,11L6.75,19C6.75,19.0663 6.77634,19.1299 6.82322,19.1768 6.87011,19.2237 6.93369,19.25 7,19.25L17,19.25C17.0663,19.25 17.1299,19.2237 17.1768,19.1768 17.2237,19.1299 17.25,19.0663 17.25,19L17.25,11C17.25,10.5858,17.5858,10.25,18,10.25L20.25,10.25 20.25,6.54058 15.6154,4.99571C15.4444,5.61643 15.1149,6.1884 14.6517,6.65166 13.9484,7.35492 12.9946,7.75001 12,7.75001 11.0054,7.75001 10.0516,7.35492 9.34835,6.65166 8.8851,6.1884 8.55557,5.61643 8.3846,4.99571L3.75,6.54058z";
public static string NavPPTIconGeometry = "F0 M24,24z M0,0z M12.6955,2.31464C12.8562,2.2432,13.037,2.23053,13.2061,2.27886L20.2061,4.27886C20.5281,4.37085,20.7501,4.66514,20.7501,5L20.7501,18C20.7501,18.3,20.5713,18.5712,20.2955,18.6894L13.2955,21.6894C13.1267,21.7617,12.9372,21.7696,12.7629,21.7115L3.7629,18.7115C3.41393,18.5952 3.20083,18.243 3.25975,17.8799 3.31867,17.5168 3.63223,17.25 4.00007,17.25L12.2501,17.25 12.2501,7.10778 8.75007,8.50778 8.75007,13C8.75007,13.2841,8.58957,13.5438,8.33548,13.6708L4.33548,15.6708C4.10299,15.7871 3.82688,15.7746 3.60577,15.638 3.38466,15.5013 3.25007,15.2599 3.25007,15L3.25007,7C3.25007,6.70361,3.42462,6.43502,3.69546,6.31464L12.6955,2.31464z M13.0558,3.79595L4.75007,7.48741 4.75007,13.7865 7.25007,12.5365 7.25007,8C7.25007,7.69332,7.43678,7.41754,7.72153,7.30364L12.7215,5.30364C12.9526,5.21122 13.2145,5.23943 13.4205,5.37895 13.6266,5.51847 13.7501,5.75113 13.7501,6L13.7501,18C13.7501,18.4142,13.4143,18.75,13.0001,18.75L8.62178,18.75 12.9667,20.1983 19.2501,17.5055 19.2501,5.56573 13.0558,3.79595z";
public static string NavAdvancedIconGeometry = "F0 M24,24z M0,0z M7.8679,3.80724L10.5302,6.46952C10.6708,6.61018,10.7499,6.80094,10.7499,6.99985L10.7499,9.99985C10.7499,10.4141,10.4141,10.7499,9.99985,10.7499L6.99985,10.7499C6.80094,10.7499,6.61018,10.6708,6.46952,10.5302L3.80724,7.8679C3.64358,8.5543 3.61907,9.27047 3.73968,9.97343 3.92327,11.0435 4.43407,12.0303 5.20176,12.798 5.96944,13.5656 6.95625,14.0764 8.02628,14.26 9.09632,14.4436 10.197,14.291 11.1766,13.8231 11.4634,13.6861 11.8054,13.7448 12.0302,13.9695L18.0302,19.9695C18.2874,20.2267 18.6362,20.3712 18.9999,20.3712 19.3636,20.3712 19.7124,20.2267 19.9695,19.9695 20.2267,19.7124 20.3712,19.3636 20.3712,18.9999 20.3712,18.6362 20.2267,18.2874 19.9695,18.0302L13.9695,12.0302C13.7448,11.8054 13.6861,11.4634 13.8231,11.1766 14.291,10.197 14.4436,9.09632 14.26,8.02628 14.0764,6.95625 13.5656,5.96944 12.798,5.20176 12.0303,4.43407 11.0435,3.92327 9.97343,3.73968 9.27047,3.61907 8.5543,3.64358 7.8679,3.80724z M6.17663,2.82308C7.43621,2.22151 8.85132,2.02523 10.2271,2.26128 11.6028,2.49732 12.8716,3.15407 13.8586,4.1411 14.8456,5.12812 15.5024,6.39687 15.7384,7.77263 15.944,8.97097 15.8216,10.1992 15.3891,11.3284L21.0302,16.9695C21.5687,17.508 21.8712,18.2383 21.8712,18.9999 21.8712,19.7614 21.5687,20.4917 21.0302,21.0302 20.4917,21.5687 19.7614,21.8712 18.9999,21.8712 18.2383,21.8712 17.508,21.5687 16.9695,21.0302L11.3284,15.3891C10.1992,15.8216 8.97097,15.944 7.77263,15.7384 6.39687,15.5024 5.12812,14.8456 4.1411,13.8586 3.15407,12.8716 2.49732,11.6028 2.26128,10.2271 2.02523,8.85132 2.22151,7.43621 2.82308,6.17663 2.92801,5.95693 3.13306,5.80183 3.37303,5.76066 3.613,5.71948 3.85802,5.79736 4.03018,5.96952L7.31051,9.24985 9.24985,9.24985 9.24985,7.31051 5.96952,4.03018C5.79736,3.85802 5.71948,3.613 5.76066,3.37303 5.80183,3.13306 5.95693,2.92801 6.17663,2.82308z";
public static string NavAutomationIconGeometry = "F0 M24,24z M0,0z M7.81828,2.27257C8.22012,2.17211,8.62732,2.41643,8.72778,2.81828L9.08572,4.25 14.9146,4.25 15.2726,2.81828C15.373,2.41643 15.7802,2.17211 16.1821,2.27257 16.5839,2.37303 16.8282,2.78023 16.7278,3.18208L16.4608,4.25 18,4.25C18.7293,4.25 19.4288,4.53973 19.9445,5.05546 20.4603,5.57118 20.75,6.27065 20.75,7L20.75,19C20.75,19.7293 20.4603,20.4288 19.9445,20.9445 19.4288,21.4603 18.7293,21.75 18,21.75L6,21.75C5.27065,21.75 4.57118,21.4603 4.05546,20.9445 3.53973,20.4288 3.25,19.7293 3.25,19L3.25,7C3.25,6.27065 3.53973,5.57118 4.05546,5.05546 4.57118,4.53973 5.27065,4.25 6,4.25L7.53955,4.25 7.27257,3.18208C7.17211,2.78023,7.41643,2.37303,7.81828,2.27257z M14.5396,5.75L14.2726,6.81828C14.1721,7.22012 14.4164,7.62732 14.8183,7.72778 15.2201,7.82825 15.6273,7.58393 15.7278,7.18208L16.0858,5.75 18,5.75C18.3315,5.75 18.6495,5.8817 18.8839,6.11612 19.1183,6.35054 19.25,6.66848 19.25,7L19.25,19C19.25,19.3315 19.1183,19.6495 18.8839,19.8839 18.6495,20.1183 18.3315,20.25 18,20.25L6,20.25C5.66848,20.25 5.35054,20.1183 5.11612,19.8839 4.8817,19.6495 4.75,19.3315 4.75,19L4.75,7C4.75,6.66848 4.8817,6.35054 5.11612,6.11612 5.35054,5.8817 5.66848,5.75 6,5.75L7.91455,5.75 8.27257,7.18208C8.37303,7.58393 8.78023,7.82825 9.18208,7.72778 9.58393,7.62732 9.82825,7.22012 9.72778,6.81828L9.46072,5.75 14.5396,5.75z M9.41625,15.3761C9.07166,15.1463 8.60598,15.2393 8.37614,15.5839 8.14629,15.9285 8.23932,16.3942 8.58391,16.624 9.6821,17.3565 10.8245,17.7501 12.0001,17.7501 13.1757,17.7501 14.3181,17.3565 15.4162,16.624 15.7608,16.3942 15.8539,15.9285 15.624,15.5839 15.3942,15.2393 14.9285,15.1463 14.5839,15.3761 13.6821,15.9776 12.8245,16.2501 12.0001,16.2501 11.1757,16.2501 10.3181,15.9776 9.41625,15.3761z M9,10.25C9.41421,10.25,9.75,10.5858,9.75,11L9.75,12C9.75,12.4142 9.41421,12.75 9,12.75 8.58579,12.75 8.25,12.4142 8.25,12L8.25,11C8.25,10.5858,8.58579,10.25,9,10.25z M15.75,11C15.75,10.5858 15.4626,10.25 15.0484,10.25 14.6342,10.25 14.25,10.5858 14.25,11L14.25,12C14.25,12.4142 14.5374,12.75 14.9516,12.75 15.3658,12.75 15.75,12.4142 15.75,12L15.75,11z";
public static string NavRandomWindowIconGeometry = "F0 M24,24z M0,0z M18.5303,3.46967C18.2374,3.17678 17.7626,3.17678 17.4697,3.46967 17.1768,3.76256 17.1768,4.23744 17.4697,4.53033L19.1893,6.25 16,6.25C14.475,6.25 13.0125,6.8558 11.9341,7.93414 11.5659,8.30239 11.2527,8.71546 11,9.16052 10.7473,8.71546 10.4341,8.30239 10.0659,7.93414 8.98753,6.8558 7.52499,6.25 6,6.25L3,6.25C2.58579,6.25 2.25,6.58579 2.25,7 2.25,7.41421 2.58579,7.75 3,7.75L6,7.75C7.12717,7.75 8.20817,8.19777 9.0052,8.9948 9.80223,9.79183 10.25,10.8728 10.25,12 10.25,13.1272 9.80223,14.2082 9.0052,15.0052 8.20817,15.8022 7.12717,16.25 6,16.25L3,16.25C2.58579,16.25 2.25,16.5858 2.25,17 2.25,17.4142 2.58579,17.75 3,17.75L6,17.75C7.52499,17.75 8.98753,17.1442 10.0659,16.0659 10.4341,15.6976 10.7473,15.2845 11,14.8395 11.2527,15.2845 11.5659,15.6976 11.9341,16.0659 13.0125,17.1442 14.475,17.75 16,17.75L19.1893,17.75 17.4697,19.4697C17.1768,19.7626 17.1768,20.2374 17.4697,20.5303 17.7626,20.8232 18.2374,20.8232 18.5303,20.5303L21.5303,17.5303C21.6022,17.4584 21.6565,17.3755 21.6931,17.2871 21.7298,17.1987 21.75,17.1017 21.75,17 21.75,16.8081 21.6768,16.6161 21.5303,16.4697L18.5303,13.4697C18.2374,13.1768 17.7626,13.1768 17.4697,13.4697 17.1768,13.7626 17.1768,14.2374 17.4697,14.5303L19.1893,16.25 16,16.25C14.8728,16.25 13.7918,15.8022 12.9948,15.0052 12.1978,14.2082 11.75,13.1272 11.75,12 11.75,10.8728 12.1978,9.79183 12.9948,8.9948 13.7918,8.19777 14.8728,7.75 16,7.75L19.1893,7.75 17.4697,9.46967C17.1768,9.76256 17.1768,10.2374 17.4697,10.5303 17.7626,10.8232 18.2374,10.8232 18.5303,10.5303L21.5303,7.53033C21.6768,7.38388 21.75,7.19194 21.75,7 21.75,6.89831 21.7298,6.80134 21.6931,6.71291 21.6565,6.62445 21.6022,6.54158 21.5303,6.46967L18.5303,3.46967z";
public static string NavAboutIconGeometry = "F0 M24,24z M0,0z M5.10571,5.10571C6.93419,3.27723 9.41414,2.25 12,2.25 14.5859,2.25 17.0658,3.27723 18.8943,5.10571 20.7228,6.93419 21.75,9.41414 21.75,12 21.75,13.2804 21.4978,14.5482 21.0078,15.7312 20.5178,16.9141 19.7997,17.9889 18.8943,18.8943 17.9889,19.7997 16.9141,20.5178 15.7312,21.0078 14.5482,21.4978 13.2804,21.75 12,21.75 10.7196,21.75 9.45176,21.4978 8.26884,21.0078 7.08591,20.5178 6.01108,19.7997 5.10571,18.8943 4.20034,17.9889 3.48216,16.9141 2.99217,15.7312 2.50219,14.5482 2.25,13.2804 2.25,12 2.25,9.41414 3.27723,6.93419 5.10571,5.10571z M12,3.75C9.81196,3.75 7.71354,4.61919 6.16637,6.16637 4.61919,7.71354 3.75,9.81196 3.75,12 3.75,13.0834 3.96339,14.1562 4.37799,15.1571 4.79259,16.1581 5.40029,17.0675 6.16637,17.8336 6.93245,18.5997 7.84193,19.2074 8.84286,19.622 9.8438,20.0366 10.9166,20.25 12,20.25 13.0834,20.25 14.1562,20.0366 15.1571,19.622 16.1581,19.2074 17.0675,18.5997 17.8336,17.8336 18.5997,17.0675 19.2074,16.1581 19.622,15.1571 20.0366,14.1562 20.25,13.0834 20.25,12 20.25,9.81196 19.3808,7.71354 17.8336,6.16637 16.2865,4.61919 14.188,3.75 12,3.75z M11.25,9C11.25,8.58579,11.5858,8.25,12,8.25L12.01,8.25C12.4242,8.25 12.76,8.58579 12.76,9 12.76,9.41421 12.4242,9.75 12.01,9.75L12,9.75C11.5858,9.75,11.25,9.41421,11.25,9z M11,11.25C10.5858,11.25 10.25,11.5858 10.25,12 10.25,12.4142 10.5858,12.75 11,12.75L11.25,12.75 11.25,16C11.25,16.4142,11.5858,16.75,12,16.75L13,16.75C13.4142,16.75 13.75,16.4142 13.75,16 13.75,15.5858 13.4142,15.25 13,15.25L12.75,15.25 12.75,12C12.75,11.5858,12.4142,11.25,12,11.25L11,11.25z";
public static string NavShortcutsIconGeometry = "F0 M24,24z M0,0z M18.5303,3.46967C18.2374,3.17678 17.7626,3.17678 17.4697,3.46967 17.1768,3.76256 17.1768,4.23744 17.4697,4.53033L19.1893,6.25 16,6.25C14.475,6.25 13.0125,6.8558 11.9341,7.93414 11.5659,8.30239 11.2527,8.71546 11,9.16052 10.7473,8.71546 10.4341,8.30239 10.0659,7.93414 8.98753,6.8558 7.52499,6.25 6,6.25L3,6.25C2.58579,6.25 2.25,6.58579 2.25,7 2.25,7.41421 2.58579,7.75 3,7.75L6,7.75C7.12717,7.75 8.20817,8.19777 9.0052,8.9948 9.80223,9.79183 10.25,10.8728 10.25,12 10.25,13.1272 9.80223,14.2082 9.0052,15.0052 8.20817,15.8022 7.12717,16.25 6,16.25L3,16.25C2.58579,16.25 2.25,16.5858 2.25,17 2.25,17.4142 2.58579,17.75 3,17.75L6,17.75C7.52499,17.75 8.98753,17.1442 10.0659,16.0659 10.4341,15.6976 10.7473,15.2845 11,14.8395 11.2527,15.2845 11.5659,15.6976 11.9341,16.0659 13.0125,17.1442 14.475,17.75 16,17.75L19.1893,17.75 17.4697,19.4697C17.1768,19.7626 17.1768,20.2374 17.4697,20.5303 17.7626,20.8232 18.2374,20.8232 18.5303,20.5303L21.5303,17.5303C21.6022,17.4584 21.6565,17.3755 21.6931,17.2871 21.7298,17.1987 21.75,17.1017 21.75,17 21.75,16.8081 21.6768,16.6161 21.5303,16.4697L18.5303,13.4697C18.2374,13.1768 17.7626,13.1768 17.4697,13.4697 17.1768,13.7626 17.1768,14.2374 17.4697,14.5303L19.1893,16.25 16,16.25C14.8728,16.25 13.7918,15.8022 12.9948,15.0052 12.1978,14.2082 11.75,13.1272 11.75,12 11.75,10.8728 12.1978,9.79183 12.9948,8.9948 13.7918,8.19777 14.8728,7.75 16,7.75L19.1893,7.75 17.4697,9.46967C17.1768,9.76256 17.1768,10.2374 17.4697,10.5303 17.7626,10.8232 18.2374,10.8232 18.5303,10.5303L21.5303,7.53033C21.6768,7.38388 21.75,7.19194 21.75,7 21.75,6.89831 21.7298,6.80134 21.6931,6.71291 21.6565,6.62445 21.6022,6.54158 21.5303,6.46967L18.5303,3.46967z";
public static string NavCollapseSidebarIconGeometry = "F0 M24,24z M0,0z M5.10571,5.10571C6.93419,3.27723 9.41414,2.25 12,2.25 14.5859,2.25 17.0658,3.27723 18.8943,5.10571 20.7228,6.93419 21.75,9.41414 21.75,12 21.75,13.2804 21.4978,14.5482 21.0078,15.7312 20.5178,16.9141 19.7997,17.9889 18.8943,18.8943 17.9889,19.7997 16.9141,20.5178 15.7312,21.0078 14.5482,21.4978 13.2804,21.75 12,21.75 10.7196,21.75 9.45176,21.4978 8.26884,21.0078 7.08591,20.5178 6.01108,19.7997 5.10571,18.8943 4.20034,17.9889 3.48216,16.9141 2.99217,15.7312 2.50219,14.5482 2.25,13.2804 2.25,12 2.25,9.41414 3.27723,6.93419 5.10571,5.10571z M12,3.75C9.81196,3.75 7.71354,4.61919 6.16637,6.16637 4.61919,7.71354 3.75,9.81196 3.75,12 3.75,13.0834 3.96339,14.1562 4.37799,15.1571 4.79259,16.1581 5.40029,17.0675 6.16637,17.8336 6.93245,18.5997 7.84193,19.2074 8.84286,19.622 9.8438,20.0366 10.9166,20.25 12,20.25 13.0834,20.25 14.1562,20.0366 15.1571,19.622 16.1581,19.2074 17.0675,18.5997 17.8336,17.8336 18.5997,17.0675 19.2074,16.1581 19.622,15.1571 20.0366,14.1562 20.25,13.0834 20.25,12 20.25,9.81196 19.3808,7.71354 17.8336,6.16637 16.2865,4.61919 14.188,3.75 12,3.75z M11.25,9C11.25,8.58579,11.5858,8.25,12,8.25L12.01,8.25C12.4242,8.25 12.76,8.58579 12.76,9 12.76,9.41421 12.4242,9.75 12.01,9.75L12,9.75C11.5858,9.75,11.25,9.41421,11.25,9z M11,11.25C10.5858,11.25 10.25,11.5858 10.25,12 10.25,12.4142 10.5858,12.75 11,12.75L11.25,12.75 11.25,16C11.25,16.4142,11.5858,16.75,12,16.75L13,16.75C13.4142,16.75 13.75,16.4142 13.75,16 13.75,15.5858 13.4142,15.25 13,15.25L12.75,15.25 12.75,12C12.75,11.5858,12.4142,11.25,12,11.25L11,11.25z";
public static string NavShowSidebarIconGeometry = "M20,5H4A2,2 0 0,0 2,7V17A2,2 0 0,0 4,19H20A2,2 0 0,0 22,17V7A2,2 0 0,0 20,5M20,17H4V7H20V17M5,8H7V10H5V8M8,8H10V10H8V8M11,8H13V10H11V8M14,8H16V10H14V8M17,8H19V10H17V8M5,11H7V13H5V11M8,11H10V13H8V11M11,11H13V13H11V11M14,11H16V13H14V11M17,11H19V13H17V11M8,14H16V16H8V14Z";
#endregion
}
}
+193 -18
View File
@@ -30,15 +30,19 @@ namespace Ink_Canvas
public Bitmap CameraImage;
public BitmapSource CameraBitmapSource;
public bool AddToWhiteboard;
public bool IncludeInk;
public BitmapSource InkOverlayBitmapSource;
public ScreenshotResult(Rectangle area, List<Point> path = null, Bitmap cameraImage = null,
BitmapSource cameraBitmapSource = null, bool addToWhiteboard = false)
BitmapSource cameraBitmapSource = null, bool addToWhiteboard = false, bool includeInk = false, BitmapSource inkOverlayBitmapSource = null)
{
Area = area;
Path = path;
CameraImage = cameraImage;
CameraBitmapSource = cameraBitmapSource;
AddToWhiteboard = addToWhiteboard;
IncludeInk = includeInk;
InkOverlayBitmapSource = inkOverlayBitmapSource;
}
}
@@ -60,6 +64,8 @@ namespace Ink_Canvas
{
try
{
var inkOverlayPreview = CreateInkOverlayPreviewBitmapSource();
// 隐藏主窗口以避免截图包含窗口本身
var originalVisibility = Visibility;
Visibility = Visibility.Hidden;
@@ -68,7 +74,7 @@ namespace Ink_Canvas
await Task.Delay(200);
// 启动区域选择截图
var screenshotResult = await ShowScreenshotSelector();
var screenshotResult = await ShowScreenshotSelector(inkOverlayPreview);
// 恢复窗口显示
Visibility = originalVisibility;
@@ -104,11 +110,33 @@ namespace Ink_Canvas
try
{
if (screenshotResult.Value.IncludeInk && screenshotResult.Value.InkOverlayBitmapSource != null)
{
var withInkBitmap = OverlayInkOnCapturedBitmap(finalBitmap, screenshotResult.Value.Area, screenshotResult.Value.InkOverlayBitmapSource);
if (withInkBitmap != null && withInkBitmap != finalBitmap)
{
if (needDisposeFinalBitmap && finalBitmap != originalBitmap)
{
finalBitmap.Dispose();
}
finalBitmap = withInkBitmap;
needDisposeFinalBitmap = true;
}
}
// 如果有路径信息,应用形状遮罩
if (screenshotResult.Value.Path != null && screenshotResult.Value.Path.Count > 0)
{
finalBitmap = ApplyShapeMask(originalBitmap, screenshotResult.Value.Path, screenshotResult.Value.Area);
needDisposeFinalBitmap = true; // 标记需要释放新创建的位图
var maskedBitmap = ApplyShapeMask(finalBitmap, screenshotResult.Value.Path, screenshotResult.Value.Area);
if (maskedBitmap != null && maskedBitmap != finalBitmap)
{
if (needDisposeFinalBitmap && finalBitmap != originalBitmap)
{
finalBitmap.Dispose();
}
finalBitmap = maskedBitmap;
needDisposeFinalBitmap = true; // 标记需要释放新创建的位图
}
}
// 将截图转换为WPF Image并插入到画布
@@ -190,16 +218,19 @@ namespace Ink_Canvas
}
/// <summary>
/// 显示截图区域选择器
/// 显示截图区域选择器并返回用户的截图结果(区域截图或摄像头截图)。
/// </summary>
/// <returns>截图结果,包含区域、路径和摄像头截图信息</returns>
/// <param name="inkOverlayPreview">当用户选择包含墨迹的区域截图时,用于作为墨迹叠加的预览 <see cref="BitmapSource"/>;可为 <c>null</c>。</param>
/// <returns>若用户确认截图则返回 <see cref="ScreenshotResult"/>,否则返回 <c>null</c>。返回的结果可能为摄像头截图或区域截图,摄像头截图会包含 <see cref="ScreenshotResult.CameraBitmapSource"/> 或 <see cref="ScreenshotResult.CameraImage"/>,区域截图会包含有效的区域与路径。</returns>
/// <remarks>
/// 该方法会:
/// 1. 显示截图选择器窗口
/// 2. 获取用户选择的区域或摄像头截图
/// 3. 返回截图结果
/// 1. 在 UI 线程(通过 <see cref="Application.Current.Dispatcher"/>)上显示截图选择器窗口 <see cref="ScreenshotSelectorWindow"/>
/// 2. 获取用户选择的区域截图或摄像头截图
/// 3. 根据用户选择构建并返回 <see cref="ScreenshotResult"/>
/// 4. 若用户取消对话框或未确认截图,返回 <c>null</c>
/// 5. 方法内部捕获异常并记录日志(不会向调用方抛出异常),如需外部处理请调整实现以重新抛出或传回错误信息。
/// </remarks>
private async Task<ScreenshotResult?> ShowScreenshotSelector()
private async Task<ScreenshotResult?> ShowScreenshotSelector(BitmapSource inkOverlayPreview = null)
{
ScreenshotResult? result = null;
@@ -207,7 +238,7 @@ namespace Ink_Canvas
{
await Application.Current.Dispatcher.InvokeAsync(() =>
{
var selectorWindow = new ScreenshotSelectorWindow();
var selectorWindow = new ScreenshotSelectorWindow(inkOverlayPreview);
if (selectorWindow.ShowDialog() == true)
{
// 检查是否是摄像头截图
@@ -218,7 +249,9 @@ namespace Ink_Canvas
null, // 摄像头截图不需要路径
null, // 不再使用Bitmap
selectorWindow.CameraBitmapSource, // 摄像头BitmapSource
selectorWindow.ShouldAddToWhiteboard
selectorWindow.ShouldAddToWhiteboard,
false,
null
);
}
else if (selectorWindow.CameraImage != null)
@@ -228,7 +261,9 @@ namespace Ink_Canvas
null, // 摄像头截图不需要路径
selectorWindow.CameraImage, // 摄像头图像
null,
selectorWindow.ShouldAddToWhiteboard
selectorWindow.ShouldAddToWhiteboard,
false,
null
);
}
else
@@ -238,7 +273,9 @@ namespace Ink_Canvas
selectorWindow.SelectedPath,
null,
null,
selectorWindow.ShouldAddToWhiteboard
selectorWindow.ShouldAddToWhiteboard,
selectorWindow.IncludeInkInScreenshot,
selectorWindow.IncludeInkInScreenshot ? inkOverlayPreview : null
);
}
}
@@ -304,6 +341,142 @@ namespace Ink_Canvas
}
}
private BitmapSource CreateInkOverlayPreviewBitmapSource()
{
try
{
if (inkCanvas == null)
{
return null;
}
if ((inkCanvas.Strokes?.Count ?? 0) == 0 && inkCanvas.Children.Count == 0)
{
return null;
}
if (inkCanvas.ActualWidth <= 0 || inkCanvas.ActualHeight <= 0)
{
return null;
}
var virtualScreen = SystemInformation.VirtualScreen;
var source = PresentationSource.FromVisual(inkCanvas);
var transformToDevice = source?.CompositionTarget?.TransformToDevice ?? System.Windows.Media.Matrix.Identity;
// PointToScreen 返回WPF坐标(DIP),统一转换为设备像素后再与 VirtualScreen 对齐
var inkTopLeftDip = inkCanvas.PointToScreen(new Point(0, 0));
var inkTopLeftPx = transformToDevice.Transform(inkTopLeftDip);
var offsetX = inkTopLeftPx.X - virtualScreen.Left;
var offsetY = inkTopLeftPx.Y - virtualScreen.Top;
var widthPx = inkCanvas.ActualWidth * transformToDevice.M11;
var heightPx = inkCanvas.ActualHeight * transformToDevice.M22;
var drawingVisual = new DrawingVisual();
using (var dc = drawingVisual.RenderOpen())
{
// 使用完整 InkCanvas 视觉树,确保包含图片等子元素
var visualBrush = new VisualBrush(inkCanvas)
{
Stretch = Stretch.Fill
};
dc.DrawRectangle(visualBrush, null, new Rect(offsetX, offsetY, widthPx, heightPx));
}
var rtb = new RenderTargetBitmap(
Math.Max(1, virtualScreen.Width),
Math.Max(1, virtualScreen.Height),
96,
96,
PixelFormats.Pbgra32);
rtb.Render(drawingVisual);
rtb.Freeze();
return rtb;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"创建截图墨迹预览失败: {ex.Message}", LogHelper.LogType.Warning);
return null;
}
}
private Bitmap OverlayInkOnCapturedBitmap(Bitmap capturedBitmap, Rectangle captureArea, BitmapSource inkOverlayBitmapSource)
{
if (capturedBitmap == null || inkOverlayBitmapSource == null)
{
return capturedBitmap;
}
try
{
var virtualScreen = SystemInformation.VirtualScreen;
var sourceRect = new Rectangle(
captureArea.X - virtualScreen.X,
captureArea.Y - virtualScreen.Y,
captureArea.Width,
captureArea.Height);
sourceRect.Intersect(new Rectangle(0, 0, inkOverlayBitmapSource.PixelWidth, inkOverlayBitmapSource.PixelHeight));
if (sourceRect.Width <= 0 || sourceRect.Height <= 0)
{
return capturedBitmap;
}
using (var inkOverlayBitmap = ConvertBitmapSourceToBitmap(inkOverlayBitmapSource))
{
if (inkOverlayBitmap == null)
{
return capturedBitmap;
}
Bitmap resultBitmap = null;
try
{
resultBitmap = new Bitmap(capturedBitmap.Width, capturedBitmap.Height, PixelFormat.Format32bppArgb);
using (var g = Graphics.FromImage(resultBitmap))
{
g.DrawImage(capturedBitmap, 0, 0, capturedBitmap.Width, capturedBitmap.Height);
var targetRect = new Rectangle(0, 0, Math.Min(sourceRect.Width, capturedBitmap.Width), Math.Min(sourceRect.Height, capturedBitmap.Height));
g.DrawImage(inkOverlayBitmap, targetRect, sourceRect, GraphicsUnit.Pixel);
}
return resultBitmap;
}
catch
{
resultBitmap?.Dispose();
throw;
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"叠加截图墨迹失败: {ex.Message}", LogHelper.LogType.Warning);
return capturedBitmap;
}
}
private Bitmap ConvertBitmapSourceToBitmap(BitmapSource bitmapSource)
{
if (bitmapSource == null)
{
return null;
}
using (var memoryStream = new MemoryStream())
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
encoder.Save(memoryStream);
memoryStream.Position = 0;
using (var tempBitmap = new Bitmap(memoryStream))
{
return new Bitmap(tempBitmap);
}
}
}
/// <summary>
/// 将截图插入到画布
/// </summary>
@@ -323,7 +496,7 @@ namespace Ink_Canvas
/// 10. 提交历史记录
/// 11. 插入图片后切换到选择模式并刷新浮动栏高光显示
/// </remarks>
private async Task InsertScreenshotToCanvas(Bitmap bitmap)
private Task InsertScreenshotToCanvas(Bitmap bitmap)
{
try
{
@@ -331,7 +504,7 @@ namespace Ink_Canvas
if (bitmap == null || bitmap.Width <= 0 || bitmap.Height <= 0)
{
ShowNotification("无效的截图");
return;
return Task.CompletedTask;
}
// 将Bitmap转换为WPF BitmapSource
@@ -340,7 +513,7 @@ namespace Ink_Canvas
if (bitmapSource == null)
{
ShowNotification("转换截图失败");
return;
return Task.CompletedTask;
}
// 创建WPF Image控件
@@ -399,6 +572,7 @@ namespace Ink_Canvas
{
bitmap?.Dispose();
}
return Task.CompletedTask;
}
/// <summary>
@@ -418,7 +592,7 @@ namespace Ink_Canvas
/// 8. 提交历史记录
/// 9. 插入图片后切换到选择模式并刷新浮动栏高光显示
/// </remarks>
private async Task InsertBitmapSourceToCanvas(BitmapSource bitmapSource, string successMessage = "截图已插入到画布", string failureMessagePrefix = "插入截图失败")
private Task InsertBitmapSourceToCanvas(BitmapSource bitmapSource, string successMessage = "截图已插入到画布", string failureMessagePrefix = "插入截图失败")
{
try
{
@@ -474,6 +648,7 @@ namespace Ink_Canvas
ShowNotification($"{failureMessagePrefix}: {ex.Message}");
LogHelper.WriteLogToFile($"插入摄像头截图失败: {ex.Message}", LogHelper.LogType.Error);
}
return Task.CompletedTask;
}
/// <summary>
+328 -304
View File
@@ -14,6 +14,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Application = System.Windows.Application;
using File = System.IO.File;
@@ -180,6 +181,7 @@ namespace Ink_Canvas
/// PowerPoint 全屏放映顶层窗口类名(与编辑态 PPTFrameClass 区分)。
/// </summary>
private const string PowerPointSlideShowWindowClassName = "screenClass";
#endregion
#region PPT Managers
@@ -205,6 +207,7 @@ namespace Ink_Canvas
/// 提供对内部PPT链接管理器的公共访问,用于外部代码与PowerPoint进行交互。
/// </remarks>
public IPPTLinkManager PPTManager => _pptManager;
public PPTUIManager PPTUIManager => _pptUIManager;
#endregion
#region PPT Manager Initialization
@@ -214,12 +217,13 @@ namespace Ink_Canvas
/// <remarks>
/// 清理并释放现有的 PPT 管理器与 COM/Interop 状态,创建并配置新的 PPT 管理器(ROT 或 COM 实现,取决于设置)、单一的 PPT 墨迹管理器及其自动保存行为,以及 PPT UI 管理器与其显示/按钮位置选项。方法内部会订阅必要的 PPT 事件并记录初始化过程中的错误或警告。同时初始化长按页翻页定时器以支持长按翻页功能。
/// </remarks>
private void InitializePPTManagers()
public void InitializePPTManagers()
{
try
{
// 初始化长按定时器
InitializeLongPressTimer();
WirePptNavBars();
// 完全清理旧模式
try
@@ -298,7 +302,7 @@ namespace Ink_Canvas
/// <remarks>
/// 只有当Settings.PowerPointSettings.PowerPointSupport为true时才会启动监控,并记录启动事件日志。
/// </remarks>
private void StartPPTMonitoring()
public void StartPPTMonitoring()
{
if (Settings.PowerPointSettings.PowerPointSupport)
{
@@ -310,7 +314,7 @@ namespace Ink_Canvas
/// <summary>
/// 停止 PowerPoint 相关的监控:停止并清除用于延迟退出 PPT 模式的定时器,并停止 PPT 管理器的监控,同时记录事件日志。
/// </summary>
private void StopPPTMonitoring()
public void StopPPTMonitoring()
{
try
{
@@ -328,13 +332,12 @@ namespace Ink_Canvas
#region PowerPoint Application Management
/// <summary>
/// 启动PowerPoint应用程序守护
/// <summary>
/// 启动对本地 PowerPoint 应用实例的守护监控并在需要时创建应用程序实例。
/// </summary>
/// <remarks>
/// 启动对本地 PowerPoint 应用实例的守护监控并在需要时创建应用程序实例。
/// 仅在 PowerPoint 增强功能已启用且未使用 ROT 链接时生效;方法将创建 PowerPoint 应用(若不存在)并启动用于定期检查应用状态的定时器。
/// </remarks>
private void StartPowerPointProcessMonitoring()
public void StartPowerPointProcessMonitoring()
{
try
{
@@ -364,7 +367,7 @@ namespace Ink_Canvas
/// <summary>
/// 停止PowerPoint应用程序守护
/// </summary>
private void StopPowerPointProcessMonitoring()
public void StopPowerPointProcessMonitoring()
{
try
{
@@ -1370,6 +1373,8 @@ namespace Ink_Canvas
{
try
{
await Application.Current.Dispatcher.InvokeAsync(() => CollapseAllPptNavBarPreviews());
if (Settings.Automation.IsAutoFoldAfterPPTSlideShow && !isFloatingBarFolded)
{
FoldFloatingBar_MouseUp(new object(), null);
@@ -1652,7 +1657,7 @@ namespace Ink_Canvas
if (int.TryParse(File.ReadAllText(positionFile), out var page) && page > 0)
{
_lastPlaybackPage = page;
new YesOrNoNotificationWindow($"上次播放到了第 {page} 页, 是否立即跳转", () =>
var yesNoWindow = new YesOrNoNotificationWindow($"上次播放到了第 {page} 页, 是否立即跳转", () =>
{
try
{
@@ -1674,7 +1679,17 @@ namespace Ink_Canvas
{
LogHelper.WriteLogToFile($"跳转到第{page}页失败: {ex}", LogHelper.LogType.Error);
}
}).ShowDialog();
});
yesNoWindow.Owner = this;
PauseTopmostMaintenance();
try
{
yesNoWindow.ShowDialog();
}
finally
{
ResumeTopmostMaintenance();
}
}
}
catch (Exception ex)
@@ -1716,7 +1731,7 @@ namespace Ink_Canvas
if (hasHiddenSlides && !IsShowingRestoreHiddenSlidesWindow)
{
IsShowingRestoreHiddenSlidesWindow = true;
new YesOrNoNotificationWindow("检测到此演示文档中包含隐藏的幻灯片,是否取消隐藏?",
var yesNoWindow = new YesOrNoNotificationWindow("检测到此演示文档中包含隐藏的幻灯片,是否取消隐藏?",
() =>
{
try
@@ -1740,7 +1755,17 @@ namespace Ink_Canvas
}
},
() => { IsShowingRestoreHiddenSlidesWindow = false; },
() => { IsShowingRestoreHiddenSlidesWindow = false; }).ShowDialog();
() => { IsShowingRestoreHiddenSlidesWindow = false; });
yesNoWindow.Owner = this;
PauseTopmostMaintenance();
try
{
yesNoWindow.ShowDialog();
}
finally
{
ResumeTopmostMaintenance();
}
}
}
catch (Exception ex)
@@ -1786,7 +1811,7 @@ namespace Ink_Canvas
if (hasSlideTimings && !IsShowingAutoplaySlidesWindow)
{
IsShowingAutoplaySlidesWindow = true;
new YesOrNoNotificationWindow("检测到此演示文档中自动播放或排练计时已经启用,可能导致幻灯片自动翻页,是否取消?",
var yesNoWindow = new YesOrNoNotificationWindow("检测到此演示文档中自动播放或排练计时已经启用,可能导致幻灯片自动翻页,是否取消?",
() =>
{
try
@@ -1806,7 +1831,17 @@ namespace Ink_Canvas
}
},
() => { IsShowingAutoplaySlidesWindow = false; },
() => { IsShowingAutoplaySlidesWindow = false; }).ShowDialog();
() => { IsShowingAutoplaySlidesWindow = false; });
yesNoWindow.Owner = this;
PauseTopmostMaintenance();
try
{
yesNoWindow.ShowDialog();
}
finally
{
ResumeTopmostMaintenance();
}
}
}
catch (Exception ex)
@@ -1991,14 +2026,14 @@ namespace Ink_Canvas
{
if (!isLoaded) return;
Settings.PowerPointSettings.EnablePowerPointEnhancement = ToggleSwitchPowerPointEnhancement.IsOn;
var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch;
if (toggle != null)
Settings.PowerPointSettings.EnablePowerPointEnhancement = toggle.IsOn;
if (Settings.PowerPointSettings.EnablePowerPointEnhancement)
{
Settings.PowerPointSettings.IsSupportWPS = false;
ToggleSwitchSupportWPS.IsOn = false;
// 更新PPT管理器的WPS支持设置
if (_pptManager != null)
{
_pptManager.IsSupportWPS = false;
@@ -2007,7 +2042,6 @@ namespace Ink_Canvas
SaveSettingsToFile();
// 启动或停止PowerPoint进程守护
if (Settings.PowerPointSettings.EnablePowerPointEnhancement)
{
StartPowerPointProcessMonitoring();
@@ -2036,16 +2070,16 @@ namespace Ink_Canvas
{
if (!isLoaded) return;
Settings.PowerPointSettings.IsSupportWPS = ToggleSwitchSupportWPS.IsOn;
var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch;
if (toggle != null)
Settings.PowerPointSettings.IsSupportWPS = toggle.IsOn;
if (Settings.PowerPointSettings.IsSupportWPS)
{
if (!Settings.PowerPointSettings.PowerPointSupport)
{
Settings.PowerPointSettings.PowerPointSupport = true;
ToggleSwitchSupportPowerPoint.IsOn = true;
// 启动PPT监控
if (_pptManager == null)
{
InitializePPTManagers();
@@ -2056,12 +2090,10 @@ namespace Ink_Canvas
if (Settings.PowerPointSettings.EnablePowerPointEnhancement)
{
Settings.PowerPointSettings.EnablePowerPointEnhancement = false;
ToggleSwitchPowerPointEnhancement.IsOn = false;
StopPowerPointProcessMonitoring();
}
}
// 更新PPT管理器的WPS支持设置与翻页跳过动画设置
if (_pptManager != null)
{
_pptManager.IsSupportWPS = Settings.PowerPointSettings.IsSupportWPS;
@@ -2075,7 +2107,9 @@ namespace Ink_Canvas
{
if (!isLoaded) return;
Settings.PowerPointSettings.SkipAnimationsWhenGoNext = ToggleSwitchSkipAnimationsWhenGoNext.IsOn;
var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch;
if (toggle != null)
Settings.PowerPointSettings.SkipAnimationsWhenGoNext = toggle.IsOn;
if (_pptManager != null)
{
@@ -2213,100 +2247,14 @@ namespace Ink_Canvas
/// 2. 检查是否启用了PPT按钮页码点击功能
/// 3. 根据按下的按钮设置相应的反馈边框透明度
/// </remarks>
private void PPTNavigationBtn_MouseDown(object sender, MouseButtonEventArgs e)
private void PPTNavigationBtn_MouseDown(object sender, MouseButtonEventArgs e) { }
private void PPTNavigationBtn_MouseLeave(object sender, MouseEventArgs e) { }
private void PPTNavigationBtn_MouseUp(object sender, MouseButtonEventArgs e) { }
/// <summary>由 PptNavBar 控件 PageClick 事件触发的页码点击逻辑。</summary>
private async Task OnPptNavBarPageClickAsync(Controls.PptNavBar bar)
{
lastBorderMouseDownObject = sender;
if (!Settings.PowerPointSettings.EnablePPTButtonPageClickable) return;
if (sender == PPTLSPageButton)
{
PPTLSPageButtonFeedbackBorder.Opacity = 0.15;
}
else if (sender == PPTRSPageButton)
{
PPTRSPageButtonFeedbackBorder.Opacity = 0.15;
}
else if (sender == PPTLBPageButton)
{
PPTLBPageButtonFeedbackBorder.Opacity = 0.15;
}
else if (sender == PPTRBPageButton)
{
PPTRBPageButtonFeedbackBorder.Opacity = 0.15;
}
}
/// <summary>
/// 处理PPT导航按钮的鼠标离开事件
/// </summary>
/// <param name="sender">事件的来源对象</param>
/// <param name="e">鼠标事件参数</param>
/// <remarks>
/// 该方法在用户鼠标离开PPT导航按钮时执行以下操作:
/// 1. 重置按下的按钮对象为null
/// 2. 根据离开的按钮设置相应的反馈边框透明度为0(隐藏反馈效果)
/// </remarks>
private void PPTNavigationBtn_MouseLeave(object sender, MouseEventArgs e)
{
lastBorderMouseDownObject = null;
if (sender == PPTLSPageButton)
{
PPTLSPageButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTRSPageButton)
{
PPTRSPageButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTLBPageButton)
{
PPTLBPageButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTRBPageButton)
{
PPTRBPageButtonFeedbackBorder.Opacity = 0;
}
}
/// <summary>
/// 处理PPT导航按钮的鼠标释放事件
/// </summary>
/// <param name="sender">事件的来源对象</param>
/// <param name="e">鼠标按钮事件参数</param>
/// <remarks>
/// 该方法在用户释放PPT导航按钮时执行以下操作:
/// 1. 检查释放的按钮是否与按下的按钮一致
/// 2. 隐藏按钮的反馈效果
/// 3. 检查是否启用了PPT按钮页码点击功能
/// 4. 检查PPT是否已连接且在放映状态
/// 5. 设置背景透明度和颜色
/// 6. 切换到光标模式
/// 7. 尝试显示PPT幻灯片导航
/// 8. 如果浮动栏未折叠,则调整其位置
/// 9. 捕获并记录可能的异常
/// </remarks>
private async void PPTNavigationBtn_MouseUp(object sender, MouseButtonEventArgs e)
{
if (lastBorderMouseDownObject != sender) return;
if (sender == PPTLSPageButton)
{
PPTLSPageButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTRSPageButton)
{
PPTRSPageButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTLBPageButton)
{
PPTLBPageButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTRBPageButton)
{
PPTRBPageButtonFeedbackBorder.Opacity = 0;
}
if (!Settings.PowerPointSettings.EnablePPTButtonPageClickable) return;
// 使用新的PPT管理器检查连接状态
if (_pptManager?.IsConnected != true || _pptManager?.IsInSlideShow != true)
{
LogHelper.WriteLogToFile("PPT未连接或未在放映状态,无法执行页码点击操作", LogHelper.LogType.Warning);
@@ -2319,22 +2267,52 @@ namespace Ink_Canvas
GridTransparencyFakeBackground.Background = new SolidColorBrush(StringToColor("#01FFFFFF"));
CursorIcon_Click(null, null);
// 使用新的PPT管理器显示导航
if (_pptManager.TryShowSlideNavigation())
if (Settings.PowerPointSettings.EnablePPTButtonEnhancedPreview && bar != null)
{
LogHelper.WriteLogToFile("成功显示PPT幻灯片导航", LogHelper.LogType.Trace);
// 若启用了“翻页时跳过PPT动画”,显示导航后把焦点拉回本窗口
if (Settings.PowerPointSettings.SkipAnimationsWhenGoNext)
if (bar.IsPreviewExpanded)
{
try { this.Activate(); } catch { }
bar.IsPreviewExpanded = false;
}
else
{
var slides = await Task.Run(BuildPptPreviewItems);
if (slides == null || slides.Count == 0)
{
LogHelper.WriteLogToFile("PPT增强预览未生成可用缩略图,改用默认导航", LogHelper.LogType.Warning);
_pptManager.TryShowSlideNavigation();
}
else
{
var items = new List<Controls.PptNavBar.PreviewItem>(slides.Count);
foreach (var s in slides)
{
items.Add(new Controls.PptNavBar.PreviewItem
{
SlideNumber = s.SlideNumber,
Thumbnail = s.Thumbnail
});
}
bar.PreviewItems = items;
bar.CurrentSlide = _pptManager?.GetCurrentSlideNumber() ?? 0;
bar.IsPreviewExpanded = true;
}
}
}
else
{
LogHelper.WriteLogToFile("显示PPT幻灯片导航失败", LogHelper.LogType.Warning);
if (_pptManager.TryShowSlideNavigation())
{
if (Settings.PowerPointSettings.SkipAnimationsWhenGoNext)
{
try { this.Activate(); } catch { }
}
}
else
{
LogHelper.WriteLogToFile("显示PPT幻灯片导航失败", LogHelper.LogType.Warning);
}
}
// 控制居中
if (!isFloatingBarFolded)
{
await Task.Delay(100);
@@ -2347,6 +2325,229 @@ namespace Ink_Canvas
}
}
private void OnPptNavBarSlideSelected(Controls.PptNavBar bar, int slideNumber)
{
try { _pptManager?.TryNavigateToSlide(slideNumber); }
catch (Exception ex) { LogHelper.WriteLogToFile($"PPT增强预览跳转异常: {ex}", LogHelper.LogType.Error); }
finally { if (bar != null) bar.IsPreviewExpanded = false; }
}
private sealed class PptEnhancedPreviewItem
{
public int SlideNumber { get; set; }
public BitmapImage Thumbnail { get; set; }
}
private void CollapseAllPptNavBarPreviews()
{
var bars = new[]
{
LeftBottomPanelForPPTNavigation,
RightBottomPanelForPPTNavigation,
LeftSidePanelForPPTNavigation,
RightSidePanelForPPTNavigation,
};
foreach (var bar in bars)
{
if (bar == null) continue;
try
{
bar.IsPreviewExpanded = false;
bar.PreviewItems = null;
}
catch { }
}
}
/// <summary>在 MainWindow 加载完成后调用,把 4 个 PptNavBar 的事件接到本类。</summary>
private void WirePptNavBars()
{
var bars = new[]
{
LeftBottomPanelForPPTNavigation,
RightBottomPanelForPPTNavigation,
LeftSidePanelForPPTNavigation,
RightSidePanelForPPTNavigation,
};
foreach (var bar in bars)
{
if (bar == null) continue;
bar.PreviousClick += (s, e) => BtnPPTSlidesUp_Click(BtnPPTSlidesUp, null);
bar.NextClick += (s, e) => BtnPPTSlidesDown_Click(BtnPPTSlidesDown, null);
bar.PreviousPressedDown += (s, e) =>
{
if (Settings.PowerPointSettings.EnablePPTButtonLongPressPageTurn)
StartLongPressDetection(s, false);
};
bar.NextPressedDown += (s, e) =>
{
if (Settings.PowerPointSettings.EnablePPTButtonLongPressPageTurn)
StartLongPressDetection(s, true);
};
bar.PressEnded += (s, e) => StopLongPressDetection();
var captured = bar;
bar.PageClick += async (s, e) => await OnPptNavBarPageClickAsync(captured);
bar.SlideSelected += (s, slideNumber) => OnPptNavBarSlideSelected(captured, slideNumber);
bar.PreviewExpandedChanged += (s, expanded) => OnPptNavBarPreviewExpandedChanged(captured, expanded);
}
}
private bool _suppressPreviewExpandedSync;
private void OnPptNavBarPreviewExpandedChanged(Controls.PptNavBar bar, bool expanded)
{
if (_suppressPreviewExpandedSync) return;
try
{
_suppressPreviewExpandedSync = true;
if (expanded)
{
// 仅允许同时一侧展开
foreach (var other in new[]
{
LeftBottomPanelForPPTNavigation,
RightBottomPanelForPPTNavigation,
LeftSidePanelForPPTNavigation,
RightSidePanelForPPTNavigation,
})
{
if (other == null || ReferenceEquals(other, bar)) continue;
if (other.IsPreviewExpanded) other.IsPreviewExpanded = false;
}
}
// 底部条展开时,隐藏同侧的中部侧边条避免遮挡;收起后还原可见性
ApplyBottomBarSideOcclusion();
}
finally
{
_suppressPreviewExpandedSync = false;
}
}
private void ApplyBottomBarSideOcclusion()
{
var leftBottomExpanded = LeftBottomPanelForPPTNavigation != null && LeftBottomPanelForPPTNavigation.IsPreviewExpanded;
var rightBottomExpanded = RightBottomPanelForPPTNavigation != null && RightBottomPanelForPPTNavigation.IsPreviewExpanded;
// 同侧的侧边条在底部条展开时隐藏
if (LeftSidePanelForPPTNavigation != null)
{
if (leftBottomExpanded)
{
LeftSidePanelForPPTNavigation.Tag = LeftSidePanelForPPTNavigation.Visibility;
LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
}
else if (LeftSidePanelForPPTNavigation.Tag is Visibility cached)
{
LeftSidePanelForPPTNavigation.Visibility = cached;
LeftSidePanelForPPTNavigation.ClearValue(TagProperty);
}
}
if (RightSidePanelForPPTNavigation != null)
{
if (rightBottomExpanded)
{
RightSidePanelForPPTNavigation.Tag = RightSidePanelForPPTNavigation.Visibility;
RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
}
else if (RightSidePanelForPPTNavigation.Tag is Visibility cached)
{
RightSidePanelForPPTNavigation.Visibility = cached;
RightSidePanelForPPTNavigation.ClearValue(TagProperty);
}
}
}
private List<PptEnhancedPreviewItem> BuildPptPreviewItems()
{
var result = new List<PptEnhancedPreviewItem>();
string tempDir = null;
Presentation activePresentation = null;
Slides slides = null;
try
{
activePresentation = _pptManager?.GetCurrentActivePresentation() as Presentation;
if (activePresentation == null)
{
return result;
}
slides = activePresentation.Slides;
if (slides == null) return result;
int count = slides.Count;
if (count <= 0) return result;
tempDir = Path.Combine(Path.GetTempPath(), "InkCanvas", "PPTPreviews", Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(tempDir);
for (int i = 1; i <= count; i++)
{
Slide slide = null;
try
{
slide = slides[i];
var imagePath = Path.Combine(tempDir, $"slide_{i:0000}.png");
slide.Export(imagePath, "PNG", 480, 270);
var image = LoadBitmapImage(imagePath);
if (image == null) continue;
result.Add(new PptEnhancedPreviewItem
{
SlideNumber = i,
Thumbnail = image
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"生成PPT第{i}页缩略图失败: {ex.Message}", LogHelper.LogType.Warning);
}
finally
{
if (slide != null)
{
try { Marshal.ReleaseComObject(slide); } catch { }
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"构建PPT增强预览列表失败: {ex}", LogHelper.LogType.Error);
}
finally
{
if (!string.IsNullOrWhiteSpace(tempDir) && Directory.Exists(tempDir))
{
try { Directory.Delete(tempDir, true); } catch { }
}
}
return result;
}
private static BitmapImage LoadBitmapImage(string path)
{
try
{
if (!File.Exists(path)) return null;
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.UriSource = new Uri(path, UriKind.Absolute);
bitmap.EndInit();
bitmap.Freeze();
return bitmap;
}
catch
{
return null;
}
}
/// <summary>
/// 处理“开始幻灯片放映”按钮的点击事件
/// </summary>
@@ -2475,6 +2676,8 @@ namespace Ink_Canvas
{
try
{
await Application.Current.Dispatcher.InvokeAsync(() => CollapseAllPptNavBarPreviews());
if (Settings.Automation.IsAutoFoldAfterPPTSlideShow && !isFloatingBarFolded)
{
FoldFloatingBar_MouseUp(new object(), null);
@@ -2499,209 +2702,30 @@ namespace Ink_Canvas
/// </remarks>
private void GridPPTControlPrevious_MouseDown(object sender, MouseButtonEventArgs e)
{
lastBorderMouseDownObject = sender;
if (sender == PPTLSPreviousButtonBorder)
{
PPTLSPreviousButtonFeedbackBorder.Opacity = 0.15;
}
else if (sender == PPTRSPreviousButtonBorder)
{
PPTRSPreviousButtonFeedbackBorder.Opacity = 0.15;
}
else if (sender == PPTLBPreviousButtonBorder)
{
PPTLBPreviousButtonFeedbackBorder.Opacity = 0.15;
}
else if (sender == PPTRBPreviousButtonBorder)
{
PPTRBPreviousButtonFeedbackBorder.Opacity = 0.15;
}
// 启动长按检测
if (Settings.PowerPointSettings.EnablePPTButtonLongPressPageTurn)
{
StartLongPressDetection(sender, false);
}
// 旧 XAML 入口已废弃,事件由 PptNavBar 控件转发;保留方法签名以兼容潜在外部引用。
}
/// <summary>
/// 处理PPT上一页控制按钮的鼠标离开事件
/// </summary>
/// <param name="sender">事件的来源对象</param>
/// <param name="e">鼠标事件参数</param>
/// <remarks>
/// 该方法在用户鼠标离开PPT上一页控制按钮时执行以下操作:
/// 1. 重置按下的按钮对象为null
/// 2. 根据离开的按钮设置相应的反馈边框透明度为0(隐藏反馈效果)
/// 3. 停止长按检测
/// </remarks>
private void GridPPTControlPrevious_MouseLeave(object sender, MouseEventArgs e)
{
lastBorderMouseDownObject = null;
if (sender == PPTLSPreviousButtonBorder)
{
PPTLSPreviousButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTRSPreviousButtonBorder)
{
PPTRSPreviousButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTLBPreviousButtonBorder)
{
PPTLBPreviousButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTRBPreviousButtonBorder)
{
PPTRBPreviousButtonFeedbackBorder.Opacity = 0;
}
// 停止长按检测
StopLongPressDetection();
}
/// <summary>
/// 处理PPT上一页控制按钮的鼠标释放事件
/// </summary>
/// <param name="sender">事件的来源对象</param>
/// <param name="e">鼠标按钮事件参数</param>
/// <remarks>
/// 该方法在用户释放PPT上一页控制按钮时执行以下操作:
/// 1. 检查释放的按钮是否与按下的按钮一致
/// 2. 根据释放的按钮设置相应的反馈边框透明度为0(隐藏反馈效果)
/// 3. 停止长按检测
/// 4. 调用上一页按钮的点击事件处理方法,实现切换到上一页的功能
/// </remarks>
private void GridPPTControlPrevious_MouseUp(object sender, MouseButtonEventArgs e)
{
if (lastBorderMouseDownObject != sender) return;
if (sender == PPTLSPreviousButtonBorder)
{
PPTLSPreviousButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTRSPreviousButtonBorder)
{
PPTRSPreviousButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTLBPreviousButtonBorder)
{
PPTLBPreviousButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTRBPreviousButtonBorder)
{
PPTRBPreviousButtonFeedbackBorder.Opacity = 0;
}
// 停止长按检测
StopLongPressDetection();
BtnPPTSlidesUp_Click(BtnPPTSlidesUp, null);
}
/// <summary>
/// 处理PPT下一页控制按钮的鼠标按下事件
/// </summary>
/// <param name="sender">事件的来源对象</param>
/// <param name="e">鼠标按钮事件参数</param>
/// <remarks>
/// 该方法在用户按下PPT下一页控制按钮时执行以下操作:
/// 1. 记录按下的按钮对象
/// 2. 根据按下的按钮设置相应的反馈边框透明度
/// 3. 如果启用了PPT按钮长按翻页功能,则启动长按检测
/// </remarks>
private void GridPPTControlNext_MouseDown(object sender, MouseButtonEventArgs e)
{
lastBorderMouseDownObject = sender;
if (sender == PPTLSNextButtonBorder)
{
PPTLSNextButtonFeedbackBorder.Opacity = 0.15;
}
else if (sender == PPTRSNextButtonBorder)
{
PPTRSNextButtonFeedbackBorder.Opacity = 0.15;
}
else if (sender == PPTLBNextButtonBorder)
{
PPTLBNextButtonFeedbackBorder.Opacity = 0.15;
}
else if (sender == PPTRBNextButtonBorder)
{
PPTRBNextButtonFeedbackBorder.Opacity = 0.15;
}
// 启动长按检测
if (Settings.PowerPointSettings.EnablePPTButtonLongPressPageTurn)
{
StartLongPressDetection(sender, true);
}
// 旧 XAML 入口已废弃,事件由 PptNavBar 控件转发;保留方法签名以兼容潜在外部引用。
}
/// <summary>
/// 处理PPT下一页控制按钮的鼠标离开事件
/// </summary>
/// <param name="sender">事件的来源对象</param>
/// <param name="e">鼠标事件参数</param>
/// <remarks>
/// 该方法在用户鼠标离开PPT下一页控制按钮时执行以下操作:
/// 1. 重置按下的按钮对象为null
/// 2. 根据离开的按钮设置相应的反馈边框透明度为0(隐藏反馈效果)
/// 3. 停止长按检测
/// </remarks>
private void GridPPTControlNext_MouseLeave(object sender, MouseEventArgs e)
{
lastBorderMouseDownObject = null;
if (sender == PPTLSNextButtonBorder)
{
PPTLSNextButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTRSNextButtonBorder)
{
PPTRSNextButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTLBNextButtonBorder)
{
PPTLBNextButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTRBNextButtonBorder)
{
PPTRBNextButtonFeedbackBorder.Opacity = 0;
}
// 停止长按检测
StopLongPressDetection();
}
/// <summary>
/// 处理PPT下一页控制按钮的鼠标释放事件
/// </summary>
/// <param name="sender">事件的来源对象</param>
/// <param name="e">鼠标按钮事件参数</param>
/// <remarks>
/// 该方法在用户释放PPT下一页控制按钮时执行以下操作:
/// 1. 检查释放的按钮是否与按下的按钮一致
/// 2. 根据释放的按钮设置相应的反馈边框透明度为0(隐藏反馈效果)
/// 3. 停止长按检测
/// 4. 调用下一页按钮的点击事件处理方法,实现切换到下一页的功能
/// </remarks>
private void GridPPTControlNext_MouseUp(object sender, MouseButtonEventArgs e)
{
if (lastBorderMouseDownObject != sender) return;
if (sender == PPTLSNextButtonBorder)
{
PPTLSNextButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTRSNextButtonBorder)
{
PPTRSNextButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTLBNextButtonBorder)
{
PPTLBNextButtonFeedbackBorder.Opacity = 0;
}
else if (sender == PPTRBNextButtonBorder)
{
PPTRBNextButtonFeedbackBorder.Opacity = 0;
}
// 停止长按检测
StopLongPressDetection();
BtnPPTSlidesDown_Click(BtnPPTSlidesDown, null);
}
@@ -132,7 +132,19 @@ namespace Ink_Canvas
+ (currentMode == 0 ? "Annotation Strokes" : "BlackBoard Strokes");
if (!Directory.Exists(savePath)) Directory.CreateDirectory(savePath);
string savePathWithName;
if (currentMode != 0) // 黑板模式下
if (Settings.Automation.IsUseCustomSaveFileName)
{
var ctx = new SaveFileNameContext
{
Mode = currentMode == 0 ? "Annotation" : "BlackBoard",
Type = saveByUser ? "User" : "Auto",
Page = currentMode != 0 ? CurrentWhiteboardIndex : (int?)null,
Count = inkCanvas.Strokes.Count
};
var fname = SaveFileNameHelper.Render(Settings.Automation.CustomSaveFileNameTemplate, ctx);
savePathWithName = savePath + @"\" + fname + ".icstk";
}
else if (currentMode != 0) // 黑板模式下
savePathWithName = savePath + @"\" + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss-fff") + " Page-" +
CurrentWhiteboardIndex + " StrokesCount-" + inkCanvas.Strokes.Count + ".icstk";
else
+85 -12
View File
@@ -43,8 +43,23 @@ namespace Ink_Canvas
var basePath = Settings.Automation.AutoSavedStrokesLocation
+ @"\Auto Saved - BlackBoard Strokes";
if (!Directory.Exists(basePath)) Directory.CreateDirectory(basePath);
strokeSavePath = Path.Combine(basePath,
$"{DateTime.Now:yyyy-MM-dd HH-mm-ss-fff} Page-{pageIndexForStrokes} StrokesCount-{strokesToSave.Count}.icstk");
string stem;
if (Settings.Automation.IsUseCustomSaveFileName)
{
stem = SaveFileNameHelper.Render(Settings.Automation.CustomSaveFileNameTemplate,
new SaveFileNameContext
{
Mode = "BlackBoard",
Type = "Auto",
Page = pageIndexForStrokes,
Count = strokesToSave.Count
});
}
else
{
stem = $"{DateTime.Now:yyyy-MM-dd HH-mm-ss-fff} Page-{pageIndexForStrokes} StrokesCount-{strokesToSave.Count}";
}
strokeSavePath = Path.Combine(basePath, stem + ".icstk");
}
}
catch (Exception ex)
@@ -149,7 +164,7 @@ namespace Ink_Canvas
{
var desktopPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),
$"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.png");
$"{GetScreenshotFileNameStem()}.png");
CaptureAndSaveScreenshot(desktopPath, false);
@@ -162,10 +177,11 @@ namespace Ink_Canvas
var originalVisibility = Visibility;
try
{
var inkOverlayPreview = CreateInkOverlayPreviewBitmapSource();
Visibility = Visibility.Hidden;
await Task.Delay(200);
var screenshotResult = await ShowScreenshotSelector();
var screenshotResult = await ShowScreenshotSelector(inkOverlayPreview);
if (!screenshotResult.HasValue)
{
@@ -187,7 +203,7 @@ namespace Ink_Canvas
var desktopPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),
$"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.png");
$"{GetScreenshotFileNameStem()}.png");
using (var originalBitmap = CaptureScreenArea(screenshotResult.Value.Area))
{
@@ -202,10 +218,32 @@ namespace Ink_Canvas
try
{
if (screenshotResult.Value.IncludeInk && screenshotResult.Value.InkOverlayBitmapSource != null)
{
var withInkBitmap = OverlayInkOnCapturedBitmap(finalBitmap, screenshotResult.Value.Area, screenshotResult.Value.InkOverlayBitmapSource);
if (withInkBitmap != null && withInkBitmap != finalBitmap)
{
if (needDisposeFinalBitmap && finalBitmap != originalBitmap)
{
finalBitmap.Dispose();
}
finalBitmap = withInkBitmap;
needDisposeFinalBitmap = true;
}
}
if (screenshotResult.Value.Path != null && screenshotResult.Value.Path.Count > 0)
{
finalBitmap = ApplyShapeMask(originalBitmap, screenshotResult.Value.Path, screenshotResult.Value.Area);
needDisposeFinalBitmap = true;
var maskedBitmap = ApplyShapeMask(finalBitmap, screenshotResult.Value.Path, screenshotResult.Value.Area);
if (maskedBitmap != null && maskedBitmap != finalBitmap)
{
if (needDisposeFinalBitmap && finalBitmap != originalBitmap)
{
finalBitmap.Dispose();
}
finalBitmap = maskedBitmap;
needDisposeFinalBitmap = true;
}
}
var directory = Path.GetDirectoryName(desktopPath);
@@ -275,10 +313,32 @@ namespace Ink_Canvas
try
{
if (screenshotResult.IncludeInk && screenshotResult.InkOverlayBitmapSource != null)
{
var withInkBitmap = OverlayInkOnCapturedBitmap(finalBitmap, screenshotResult.Area, screenshotResult.InkOverlayBitmapSource);
if (withInkBitmap != null && withInkBitmap != finalBitmap)
{
if (needDisposeFinalBitmap && finalBitmap != originalBitmap)
{
finalBitmap.Dispose();
}
finalBitmap = withInkBitmap;
needDisposeFinalBitmap = true;
}
}
if (screenshotResult.Path != null && screenshotResult.Path.Count > 0)
{
finalBitmap = ApplyShapeMask(originalBitmap, screenshotResult.Path, screenshotResult.Area);
needDisposeFinalBitmap = true;
var maskedBitmap = ApplyShapeMask(finalBitmap, screenshotResult.Path, screenshotResult.Area);
if (maskedBitmap != null && maskedBitmap != finalBitmap)
{
if (needDisposeFinalBitmap && finalBitmap != originalBitmap)
{
finalBitmap.Dispose();
}
finalBitmap = maskedBitmap;
needDisposeFinalBitmap = true;
}
}
bitmapSourceForClipboard = ConvertBitmapToBitmapSource(finalBitmap);
@@ -308,7 +368,7 @@ namespace Ink_Canvas
await Task.Delay(150);
}
BtnWhiteBoardAdd_Click(null, EventArgs.Empty);
BtnWhiteBoardAdd_Click(null, new RoutedEventArgs());
await InsertBitmapSourceToCanvas(bitmapSourceForClipboard);
}
@@ -392,7 +452,10 @@ namespace Ink_Canvas
{
if (string.IsNullOrWhiteSpace(fileName))
{
fileName = DateTime.Now.ToString("HH-mm-ss");
fileName = Settings.Automation.IsUseCustomSaveFileName
? SaveFileNameHelper.Render(Settings.Automation.CustomSaveFileNameTemplate,
new SaveFileNameContext { Mode = "Screenshot", Type = "Auto", Count = inkCanvas?.Strokes?.Count })
: DateTime.Now.ToString("HH-mm-ss");
}
var basePath = Settings.Automation.AutoSavedStrokesLocation;
@@ -428,7 +491,17 @@ namespace Ink_Canvas
return Path.Combine(
screenshotsFolder,
$"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.png");
$"{GetScreenshotFileNameStem()}.png");
}
private string GetScreenshotFileNameStem()
{
if (Settings.Automation.IsUseCustomSaveFileName)
{
return SaveFileNameHelper.Render(Settings.Automation.CustomSaveFileNameTemplate,
new SaveFileNameContext { Mode = "Screenshot", Type = "Auto", Count = inkCanvas?.Strokes?.Count });
}
return DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
}
}
}
@@ -1,6 +1,5 @@
using Ink_Canvas.Controls;
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -29,16 +28,16 @@ namespace Ink_Canvas
/// <param name="sender">事件发送者</param>
/// <param name="e">鼠标按钮事件参数</param>
/// <remarks>
/// 如果发送者是 RandomDrawPanel 或 SingleDrawPanel,且它们被隐藏,则不处理事件
/// 如果发送者是 BoardRandomDrawToolBtn 或 BoardSingleDrawToolBtn,且它们被隐藏,则不处理事件
/// 否则存储当前鼠标按下的对象
/// </remarks>
private void Border_MouseDown(object sender, MouseButtonEventArgs e)
{
// 如果发送者是 RandomDrawPanel 或 SingleDrawPanel,且它们被隐藏,则不处理事件
if (sender is SimpleStackPanel panel)
// 如果发送者是 BoardRandomDrawToolBtn 或 BoardSingleDrawToolBtn,且它们被隐藏,则不处理事件
if (sender is FrameworkElement element)
{
if ((panel == RandomDrawPanel || panel == SingleDrawPanel) &&
panel.Visibility != Visibility.Visible)
if ((element == BoardRandomDrawToolBtn || element == BoardSingleDrawToolBtn) &&
element.Visibility != Visibility.Visible)
{
return;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+60 -9
View File
@@ -9,7 +9,6 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
@@ -33,14 +32,8 @@ namespace Ink_Canvas
/// 3. 如果形状绘制面板可见,则隐藏它
/// 4. 如果形状绘制面板不可见,则显示它
/// </remarks>
private void ImageDrawShape_MouseUp(object sender, MouseButtonEventArgs e)
internal void ImageDrawShape_MouseUp(object sender, MouseButtonEventArgs e)
{
if (lastBorderMouseDownObject != null && lastBorderMouseDownObject is Panel)
((Panel)lastBorderMouseDownObject).Background = new SolidColorBrush(Colors.Transparent);
if (sender == ShapeDrawFloatingBarBtn && lastBorderMouseDownObject != ShapeDrawFloatingBarBtn) return;
// FloatingBarIcons_MouseUp_New(sender);
if (BorderDrawShape.Visibility == Visibility.Visible)
{
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
@@ -49,6 +42,7 @@ namespace Ink_Canvas
else
{
HideSubPanels();
UpdateBorderDrawShapePosition();
AnimationsHelper.ShowWithSlideFromBottomAndFade(BorderDrawShape);
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardBorderDrawShape);
}
@@ -2494,6 +2488,7 @@ namespace Ink_Canvas
/// 用于标识鼠标是否处于按下状态,在绘制过程中使用
/// </remarks>
private bool isMouseDown;
private bool _isMouseRealtimeInking;
/// <summary>
/// 触摸按下状态标志
@@ -2517,6 +2512,17 @@ namespace Ink_Canvas
/// </remarks>
private void inkCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left && ShouldUseRealtimeVelocityBrushTipForMouse() && drawingShapeMode == 0)
{
_isMouseRealtimeInking = true;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
var p = e.GetPosition(inkCanvas);
InitializeRealtimeBrushTipStateFromPoint(MouseRealtimeStrokeId, p);
var sv = GetStrokeVisual(MouseRealtimeStrokeId);
TryAppendRealtimeVelocityBrushTipPoint(sv, MouseRealtimeStrokeId, p);
sv.ForceRedraw();
}
inkCanvas.CaptureMouse();
ViewboxFloatingBar.IsHitTestVisible = false;
BlackboardUIGridForInkReplay.IsHitTestVisible = false;
@@ -2537,7 +2543,21 @@ namespace Ink_Canvas
/// </remarks>
private void inkCanvas_MouseMove(object sender, MouseEventArgs e)
{
if (isMouseDown) MouseTouchMove(e.GetPosition(inkCanvas));
if (_isMouseRealtimeInking && isMouseDown)
{
var sv = GetStrokeVisual(MouseRealtimeStrokeId);
if (TryAppendRealtimeVelocityBrushTipPoint(sv, MouseRealtimeStrokeId, e.GetPosition(inkCanvas)))
sv.ForceRedraw();
else
{
_isMouseRealtimeInking = false;
MouseTouchMove(e.GetPosition(inkCanvas));
}
}
else if (isMouseDown)
{
MouseTouchMove(e.GetPosition(inkCanvas));
}
if (Settings.Canvas.IsShowCursor)
{
@@ -2565,6 +2585,37 @@ namespace Ink_Canvas
/// </remarks>
private void inkCanvas_MouseUp(object sender, MouseButtonEventArgs e)
{
if (_isMouseRealtimeInking)
{
try
{
var sv = GetStrokeVisual(MouseRealtimeStrokeId);
sv?.ForceRedraw();
var stroke = sv?.Stroke;
if (stroke != null)
{
if (!stroke.ContainsPropertyData(RealtimeVelocityBrushTipAppliedGuid))
stroke.AddPropertyData(RealtimeVelocityBrushTipAppliedGuid, true);
inkCanvas.Strokes.Add(stroke);
inkCanvas_StrokeCollected(inkCanvas, new InkCanvasStrokeCollectedEventArgs(stroke));
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
finally
{
if (VisualCanvasList.TryGetValue(MouseRealtimeStrokeId, out var vc) && inkCanvas.Children.Contains(vc))
inkCanvas.Children.Remove(vc);
StrokeVisualList.Remove(MouseRealtimeStrokeId);
VisualCanvasList.Remove(MouseRealtimeStrokeId);
TouchDownPointsList.Remove(MouseRealtimeStrokeId);
CleanupRealtimeBrushTipState(MouseRealtimeStrokeId);
_isMouseRealtimeInking = false;
}
}
HandleEraserOperationEnded();
inkCanvas.ReleaseMouseCapture();
ViewboxFloatingBar.IsHitTestVisible = true;
@@ -30,6 +30,10 @@ namespace Ink_Canvas
/// </remarks>
public partial class MainWindow : Window
{
private Helpers.ModernInkAnalyzer _modernInkAnalyzer;
private Helpers.ModernInkAnalyzer ModernInkAnalyzer =>
_modernInkAnalyzer ??= new Helpers.ModernInkAnalyzer();
/// <summary>
/// 存储新的笔画集合,用于形状识别
/// </summary>
@@ -520,6 +524,7 @@ namespace Ink_Canvas
&& penType != 1
&& e.Stroke?.DrawingAttributes != null
&& !e.Stroke.DrawingAttributes.IsHighlighter
&& !e.Stroke.ContainsPropertyData(RealtimeVelocityBrushTipAppliedGuid)
&& e.Stroke.StylusPoints.Count >= 3)
{
ApplyVelocityBrushTipFromSpeed(e.Stroke);
@@ -563,13 +568,10 @@ namespace Ink_Canvas
{
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
};
straightStroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
// Replace the original stroke with the straightened one
SetNewBackupOfStroke();
_currentCommitType = CommitReason.ShapeRecognition;
inkCanvas.Strokes.Remove(e.Stroke);
inkCanvas.Strokes.Add(straightStroke);
_currentCommitType = CommitReason.UserInput;
ReplaceStrokesSafely(null, straightStroke, e.Stroke);
straightStrokeForHandwritingKey = straightStroke;
@@ -616,17 +618,26 @@ namespace Ink_Canvas
ProcessRectangleGuideLines(e.Stroke);
var shapeMode = ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine);
var strokeReco = new StrokeCollection();
var result = await InkRecognizeHelper.RecognizeShapeUnifiedAsync(newStrokes, shapeMode);
for (var i = newStrokes.Count - 1; i >= 0; i--)
InkShapeRecognitionResult result = InkShapeRecognitionResult.Empty;
if (ShapeRecognitionRouter.ResolveUseWinRt(shapeMode) && Helpers.WinRtInkShapeRecognizer.IsApiAvailable)
{
strokeReco.Add(newStrokes[i]);
var newResult = await InkRecognizeHelper.RecognizeShapeUnifiedAsync(strokeReco, shapeMode);
if (newResult.IsSuccess &&
(newResult.ShapeName == "Circle" || newResult.ShapeName == "Ellipse"))
result = await ModernInkAnalyzer.AnalyzeAsync(newStrokes);
}
else
{
var strokeReco = new StrokeCollection();
result = await InkRecognizeHelper.RecognizeShapeUnifiedAsync(newStrokes, shapeMode);
for (var i = newStrokes.Count - 1; i >= 0; i--)
{
result = newResult;
break;
strokeReco.Add(newStrokes[i]);
var newResult = await InkRecognizeHelper.RecognizeShapeUnifiedAsync(strokeReco, shapeMode);
if (newResult.IsSuccess &&
(newResult.ShapeName == "Circle" || newResult.ShapeName == "Ellipse"))
{
result = newResult;
break;
}
}
}
@@ -686,12 +697,9 @@ namespace Ink_Canvas
{
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
};
stroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
circles.Add(new Circle(result.Centroid, result.ShapeWidth / 2.0, stroke));
SetNewBackupOfStroke();
_currentCommitType = CommitReason.ShapeRecognition;
inkCanvas.Strokes.Remove(result.StrokesToRemove);
inkCanvas.Strokes.Add(stroke);
_currentCommitType = CommitReason.UserInput;
ReplaceStrokesSafely(result.StrokesToRemove, stroke, e.Stroke);
newStrokes = new StrokeCollection();
}
}
@@ -718,6 +726,18 @@ namespace Ink_Canvas
var endP = new Point(result.Centroid.X + result.ShapeWidth / 2,
result.Centroid.Y + result.ShapeHeight / 2);
// WinRT 返回的热点顺序/方向不稳定时,用点集反推 IACore 风格椭圆参数(中心/长短轴/方向/四个端点)
var hasEllipseParams = TryEstimateEllipseParamsFromStrokes(
result.StrokesToRemove,
out var ellipseCentroid,
out var ellipseA,
out var ellipseB,
out var ellipseThetaRad,
out var ellipseMajor0,
out var ellipseMajor1,
out var ellipseMinor0,
out var ellipseMinor1);
foreach (var circle in circles)
//判断是否画同心椭圆
if (Math.Abs(result.Centroid.X - circle.Centroid.X) / a < 0.2 &&
@@ -766,9 +786,6 @@ namespace Ink_Canvas
var topB = endP.Y - iniP.Y;
SetNewBackupOfStroke();
_currentCommitType = CommitReason.ShapeRecognition;
inkCanvas.Strokes.Remove(result.StrokesToRemove);
newStrokes = new StrokeCollection();
var _pointList = GenerateEllipseGeometry(iniP, endP, false);
@@ -777,14 +794,14 @@ namespace Ink_Canvas
{
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
};
_stroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
var _dashedLineStroke =
GenerateDashedLineEllipseStrokeCollection(iniP, endP, true, false);
var strokes = new StrokeCollection {
_stroke,
_dashedLineStroke
};
inkCanvas.Strokes.Add(strokes);
_currentCommitType = CommitReason.UserInput;
ReplaceStrokesSafely(result.StrokesToRemove, strokes, e.Stroke);
return;
}
}
@@ -805,13 +822,17 @@ namespace Ink_Canvas
}
}
//纠正垂直与水平关系
var newPoints = FixPointsDirection(p[0], p[2]);
p[0] = newPoints[0];
p[2] = newPoints[1];
newPoints = FixPointsDirection(p[1], p[3]);
p[1] = newPoints[0];
p[3] = newPoints[1];
// 用反推参数替换中心与长短轴(比 WinRT 的包围盒更接近 IACore,且不会竖横翻转)
if (hasEllipseParams)
{
result.Centroid = ellipseCentroid;
a = ellipseA;
b = ellipseB;
iniP = new Point(result.Centroid.X - a, result.Centroid.Y - b);
endP = new Point(result.Centroid.X + a, result.Centroid.Y + b);
// 用端点重写热点,保证后续回退分支也一致
p = new PointCollection { ellipseMajor0, ellipseMinor0, ellipseMajor1, ellipseMinor1 };
}
var pointList = GenerateEllipseGeometry(iniP, endP);
var point = new StylusPointCollection(pointList);
@@ -819,22 +840,18 @@ namespace Ink_Canvas
{
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
};
stroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
if (needRotation)
{
var m = new Matrix();
var fe = e.Source as FrameworkElement;
var tanTheta = (p[2].Y - p[0].Y) / (p[2].X - p[0].X);
var theta = Math.Atan(tanTheta);
// 优先使用反推参数角度;否则用端点向量角度(使用 Atan2 避免斜率无穷)
var theta = hasEllipseParams ? ellipseThetaRad : Math.Atan2(p[2].Y - p[0].Y, p[2].X - p[0].X);
m.RotateAt(theta * 180.0 / Math.PI, result.Centroid.X, result.Centroid.Y);
stroke.Transform(m, false);
}
SetNewBackupOfStroke();
_currentCommitType = CommitReason.ShapeRecognition;
inkCanvas.Strokes.Remove(result.StrokesToRemove);
inkCanvas.Strokes.Add(stroke);
_currentCommitType = CommitReason.UserInput;
ReplaceStrokesSafely(result.StrokesToRemove, stroke, e.Stroke);
GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
newStrokes = new StrokeCollection();
}
@@ -867,11 +884,8 @@ namespace Ink_Canvas
{
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
};
SetNewBackupOfStroke();
_currentCommitType = CommitReason.ShapeRecognition;
inkCanvas.Strokes.Remove(result.StrokesToRemove);
inkCanvas.Strokes.Add(stroke);
_currentCommitType = CommitReason.UserInput;
stroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
ReplaceStrokesSafely(result.StrokesToRemove, stroke, e.Stroke);
GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
newStrokes = new StrokeCollection();
}
@@ -912,11 +926,8 @@ namespace Ink_Canvas
{
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
};
SetNewBackupOfStroke();
_currentCommitType = CommitReason.ShapeRecognition;
inkCanvas.Strokes.Remove(result.StrokesToRemove);
inkCanvas.Strokes.Add(stroke);
_currentCommitType = CommitReason.UserInput;
stroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
ReplaceStrokesSafely(result.StrokesToRemove, stroke, e.Stroke);
GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
newStrokes = new StrokeCollection();
}
@@ -2189,6 +2200,49 @@ namespace Ink_Canvas
return true;
}
private void ReplaceStrokesSafely(StrokeCollection strokesToRemove, Stroke replacementStroke, Stroke fallbackOriginalStroke = null)
{
if (replacementStroke == null) return;
try
{
SetNewBackupOfStroke();
_currentCommitType = CommitReason.ShapeRecognition;
if (strokesToRemove != null && strokesToRemove.Count > 0)
inkCanvas.Strokes.Remove(strokesToRemove);
if (fallbackOriginalStroke != null && inkCanvas.Strokes.Contains(fallbackOriginalStroke))
inkCanvas.Strokes.Remove(fallbackOriginalStroke);
if (!inkCanvas.Strokes.Contains(replacementStroke))
inkCanvas.Strokes.Add(replacementStroke);
}
finally
{
_currentCommitType = CommitReason.UserInput;
}
}
private void ReplaceStrokesSafely(StrokeCollection strokesToRemove, StrokeCollection replacementStrokes, Stroke fallbackOriginalStroke = null)
{
if (replacementStrokes == null || replacementStrokes.Count == 0) return;
try
{
SetNewBackupOfStroke();
_currentCommitType = CommitReason.ShapeRecognition;
if (strokesToRemove != null && strokesToRemove.Count > 0)
inkCanvas.Strokes.Remove(strokesToRemove);
if (fallbackOriginalStroke != null && inkCanvas.Strokes.Contains(fallbackOriginalStroke))
inkCanvas.Strokes.Remove(fallbackOriginalStroke);
inkCanvas.Strokes.Add(replacementStrokes);
}
finally
{
_currentCommitType = CommitReason.UserInput;
}
}
/// <summary>
/// 将沿线速度映射为压感并与硬件压感混合,快写略细、慢写略粗;在落笔时(及手写笔移动时由调用方)统一施加。
/// 无压感设备上系统可能将 <see cref="DrawingAttributes.IgnorePressure"/> 置为 true,此处强制关闭以便粗细随合成压感变化。
@@ -2282,6 +2336,124 @@ namespace Ink_Canvas
return new Point[2] { p1, p2 };
}
/// <summary>
/// 用点集拟合出 IACore 风格椭圆参数(中心/长短半轴/方向/四个端点)。
/// 解决 WinRT 返回热点顺序不稳定导致椭圆纠正角度翻转的问题。
/// </summary>
private static bool TryEstimateEllipseParamsFromStrokes(
StrokeCollection strokes,
out Point centroid,
out double a,
out double b,
out double thetaRad,
out Point major0,
out Point major1,
out Point minor0,
out Point minor1)
{
centroid = default;
a = b = 0;
thetaRad = 0;
major0 = major1 = minor0 = minor1 = default;
if (strokes == null || strokes.Count == 0) return false;
var pts = new List<Point>(256);
foreach (var s in strokes)
{
if (s?.StylusPoints == null) continue;
foreach (var sp in s.StylusPoints)
pts.Add(sp.ToPoint());
}
if (pts.Count < 12) return false;
double mx = 0, my = 0;
for (int i = 0; i < pts.Count; i++)
{
mx += pts[i].X;
my += pts[i].Y;
}
mx /= pts.Count;
my /= pts.Count;
centroid = new Point(mx, my);
double sxx = 0, syy = 0, sxy = 0;
for (int i = 0; i < pts.Count; i++)
{
var dx = pts[i].X - mx;
var dy = pts[i].Y - my;
sxx += dx * dx;
syy += dy * dy;
sxy += dx * dy;
}
if (sxx + syy < 1e-6) return false;
thetaRad = 0.5 * Math.Atan2(2.0 * sxy, sxx - syy);
if (double.IsNaN(thetaRad) || double.IsInfinity(thetaRad)) return false;
// 主轴单位向量 v1=(cos,sin),次轴 v2=(-sin,cos)
var cos = Math.Cos(thetaRad);
var sin = Math.Sin(thetaRad);
// 投影收集,用分位数抑制离群点
var us = new double[pts.Count];
var vs = new double[pts.Count];
double maxU = double.MinValue, minU = double.MaxValue;
double maxV = double.MinValue, minV = double.MaxValue;
for (int i = 0; i < pts.Count; i++)
{
var dx = pts[i].X - mx;
var dy = pts[i].Y - my;
var u = dx * cos + dy * sin;
var v = -dx * sin + dy * cos;
us[i] = u;
vs[i] = v;
if (u > maxU) maxU = u;
if (u < minU) minU = u;
if (v > maxV) maxV = v;
if (v < minV) minV = v;
}
Array.Sort(us);
Array.Sort(vs);
int hi = (int)Math.Round((pts.Count - 1) * 0.98);
int lo = (int)Math.Round((pts.Count - 1) * 0.02);
hi = Math.Max(0, Math.Min(pts.Count - 1, hi));
lo = Math.Max(0, Math.Min(pts.Count - 1, lo));
var uHi = us[hi];
var uLo = us[lo];
var vHi = vs[hi];
var vLo = vs[lo];
var aCandidate = Math.Max(Math.Abs(uHi), Math.Abs(uLo));
var bCandidate = Math.Max(Math.Abs(vHi), Math.Abs(vLo));
if (aCandidate < 1e-3) aCandidate = Math.Max(Math.Abs(maxU), Math.Abs(minU));
if (bCandidate < 1e-3) bCandidate = Math.Max(Math.Abs(maxV), Math.Abs(minV));
a = aCandidate;
b = bCandidate;
// 保证 a 为长半轴
if (b > a)
{
var t = a; a = b; b = t;
thetaRad += Math.PI / 2;
cos = Math.Cos(thetaRad);
sin = Math.Sin(thetaRad);
}
major0 = new Point(mx - a * cos, my - a * sin);
major1 = new Point(mx + a * cos, my + a * sin);
minor0 = new Point(mx + b * sin, my - b * cos);
minor1 = new Point(mx - b * sin, my + b * cos);
return a > 1e-2 && b > 1e-2;
}
public StylusPointCollection GenerateFakePressureTriangle(StylusPointCollection points)
{
var newPoint = new StylusPointCollection();
@@ -2701,6 +2873,7 @@ namespace Ink_Canvas
{
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
};
rectangleStroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
// 移除原有的四条直线
SetNewBackupOfStroke();
+24 -12
View File
@@ -134,23 +134,35 @@ namespace Ink_Canvas
{
if (item.StrokeHasBeenCleared)
{
foreach (var strokes in item.CurrentStroke)
if (canvas.Strokes.Contains(strokes))
canvas.Strokes.Remove(strokes);
if (item.CurrentStroke != null)
{
foreach (var strokes in item.CurrentStroke)
if (canvas.Strokes.Contains(strokes))
canvas.Strokes.Remove(strokes);
}
foreach (var strokes in item.ReplacedStroke)
if (!canvas.Strokes.Contains(strokes))
canvas.Strokes.Add(strokes);
if (item.ReplacedStroke != null)
{
foreach (var strokes in item.ReplacedStroke)
if (!canvas.Strokes.Contains(strokes))
canvas.Strokes.Add(strokes);
}
}
else
{
foreach (var strokes in item.CurrentStroke)
if (!canvas.Strokes.Contains(strokes))
canvas.Strokes.Add(strokes);
if (item.CurrentStroke != null)
{
foreach (var strokes in item.CurrentStroke)
if (!canvas.Strokes.Contains(strokes))
canvas.Strokes.Add(strokes);
}
foreach (var strokes in item.ReplacedStroke)
if (canvas.Strokes.Contains(strokes))
canvas.Strokes.Remove(strokes);
if (item.ReplacedStroke != null)
{
foreach (var strokes in item.ReplacedStroke)
if (canvas.Strokes.Contains(strokes))
canvas.Strokes.Remove(strokes);
}
}
}
else if (item.CommitType == TimeMachineHistoryType.Manipulation)
+198 -128
View File
@@ -82,6 +82,12 @@ namespace Ink_Canvas
/// 进程终止定时器
/// </summary>
private Timer timerKillProcess = new Timer();
public void UpdateAutoKillProcessTimer(bool shouldRun)
{
if (shouldRun) timerKillProcess.Start();
else timerKillProcess.Stop();
}
/// <summary>
/// 统一的主窗口定时器
/// </summary>
@@ -89,7 +95,11 @@ namespace Ink_Canvas
/// <summary>
/// 可用的最新版本号
/// </summary>
private string AvailableLatestVersion;
internal string AvailableLatestVersion;
/// <summary>
/// 最近一次自动检查得到的更新说明(Markdown)
/// </summary>
internal string AvailableLatestReleaseNotes;
/// <summary>
/// 静默更新检查定时器
/// </summary>
@@ -294,7 +304,7 @@ namespace Ink_Canvas
/// 如果启用,则根据Settings.Automation.AutoSaveStrokesIntervalMinutes设置定时器间隔
/// 最小间隔为1分钟
/// </remarks>
private void UpdateAutoSaveStrokesTimer()
public void UpdateAutoSaveStrokesTimer()
{
if (autoSaveStrokesTimer == null) return;
@@ -582,7 +592,7 @@ namespace Ink_Canvas
{
// 先展开浮动栏,然后进入批注状态
// UnFoldFloatingBar 方法内部会根据设置自动进入批注模式
UnFoldFloatingBar(null);
_ = UnFoldFloatingBar(null);
}
else
{
@@ -703,10 +713,11 @@ namespace Ink_Canvas
var fullScreenWindows = _windowOverviewModel.GetFullScreenWindows();
if (fullScreenWindows == null || fullScreenWindows.Count == 0) return false;
var foregroundHandle = ForegroundWindowInfo.GetForegroundWindowHandle();
foreach (var window in fullScreenWindows)
{
var windowProcessName = window.ProcessName;
var windowRect = window.Rect;
if (windowProcessName == "EasiNote")
{
@@ -718,15 +729,18 @@ namespace Ink_Canvas
string version = versionInfo.FileVersion;
string prodName = versionInfo.ProductName;
if (version.StartsWith("5.") && Settings.Automation.IsAutoFoldInEasiNote)
if (version.StartsWith("5.") && Settings.Automation.IsAutoFoldInEasiNote &&
window.Handle == foregroundHandle)
{
return true;
}
else if (version.StartsWith("3.") && Settings.Automation.IsAutoFoldInEasiNote3)
else if (version.StartsWith("3.") && Settings.Automation.IsAutoFoldInEasiNote3 &&
window.Handle == foregroundHandle)
{
return true;
}
else if (prodName.Contains("3C") && Settings.Automation.IsAutoFoldInEasiNote3C)
else if (prodName.Contains("3C") && Settings.Automation.IsAutoFoldInEasiNote3C &&
window.Handle == foregroundHandle)
{
return true;
}
@@ -834,19 +848,15 @@ namespace Ink_Canvas
try
{
// 从窗口预览模型中获取窗口列表(已按ZOrder排序,最上层在前)
var windows = _windowOverviewModel.Windows;
if (windows == null || windows.Count == 0) return false;
// 获取前台窗口(ZOrder最小的窗口,即最上层)
var foregroundWindow = windows.FirstOrDefault();
var foregroundHandle = ForegroundWindowInfo.GetForegroundWindowHandle();
var foregroundWindow = windows.FirstOrDefault(w => w.Handle == foregroundHandle);
if (foregroundWindow == null) return false;
var windowProcessName = foregroundWindow.ProcessName;
var windowTitle = foregroundWindow.Title;
var windowRect = foregroundWindow.Rect;
// 检查EasiNote
if (windowProcessName == "EasiNote")
{
if (foregroundWindow.ProcessPath != "Unknown")
@@ -857,25 +867,18 @@ namespace Ink_Canvas
string version = versionInfo.FileVersion;
string prodName = versionInfo.ProductName;
if (version.StartsWith("5.") && Settings.Automation.IsAutoFoldInEasiNote)
if (version.StartsWith("5.") && Settings.Automation.IsAutoFoldInEasiNote &&
foregroundWindow.IsFullScreen)
{
bool isAnnotationWindow = windowTitle.Length == 0 && windowRect.Height < 500;
if (Settings.Automation.IsAutoFoldInEasiNoteIgnoreDesktopAnno && isAnnotationWindow)
{
return true;
}
else if (!isAnnotationWindow)
{
return true;
}
return true;
}
else if (version.StartsWith("3.") && Settings.Automation.IsAutoFoldInEasiNote3)
else if (version.StartsWith("3.") && Settings.Automation.IsAutoFoldInEasiNote3 &&
foregroundWindow.IsFullScreen)
{
return true;
}
else if (prodName.Contains("3C") && Settings.Automation.IsAutoFoldInEasiNote3C &&
windowRect.Height >= SystemParameters.WorkArea.Height - 16 &&
windowRect.Width >= SystemParameters.WorkArea.Width - 16)
foregroundWindow.IsFullScreen)
{
return true;
}
@@ -883,100 +886,78 @@ namespace Ink_Canvas
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
}
}
// 检查EasiCamera
else if (Settings.Automation.IsAutoFoldInEasiCamera && windowProcessName == "EasiCamera" &&
windowRect.Height >= SystemParameters.WorkArea.Height - 16 &&
windowRect.Width >= SystemParameters.WorkArea.Width - 16)
foregroundWindow.IsFullScreen)
{
return true;
}
// 检查EasiNote5C
else if (Settings.Automation.IsAutoFoldInEasiNote5C && windowProcessName == "EasiNote5C" &&
windowRect.Height >= SystemParameters.WorkArea.Height - 16 &&
windowRect.Width >= SystemParameters.WorkArea.Width - 16)
foregroundWindow.IsFullScreen)
{
return true;
}
// 检查SeewoPinco
else if (Settings.Automation.IsAutoFoldInSeewoPincoTeacher &&
(windowProcessName == "BoardService" || windowProcessName == "seewoPincoTeacher"))
(windowProcessName == "BoardService" || windowProcessName == "seewoPincoTeacher") &&
foregroundWindow.IsFullScreen)
{
return true;
}
// 检查HiteCamera
else if (Settings.Automation.IsAutoFoldInHiteCamera && windowProcessName == "HiteCamera" &&
windowRect.Height >= SystemParameters.WorkArea.Height - 16 &&
windowRect.Width >= SystemParameters.WorkArea.Width - 16)
foregroundWindow.IsFullScreen)
{
return true;
}
// 检查HiteTouchPro
else if (Settings.Automation.IsAutoFoldInHiteTouchPro && windowProcessName == "HiteTouchPro" &&
windowRect.Height >= SystemParameters.WorkArea.Height - 16 &&
windowRect.Width >= SystemParameters.WorkArea.Width - 16)
foregroundWindow.IsFullScreen)
{
return true;
}
// 检查WxBoardMain
else if (Settings.Automation.IsAutoFoldInWxBoardMain && windowProcessName == "WxBoardMain" &&
windowRect.Height >= SystemParameters.WorkArea.Height - 16 &&
windowRect.Width >= SystemParameters.WorkArea.Width - 16)
foregroundWindow.IsFullScreen)
{
return true;
}
// 检查MSWhiteboard
else if (Settings.Automation.IsAutoFoldInMSWhiteboard &&
(windowProcessName == "MicrosoftWhiteboard" || windowProcessName == "msedgewebview2"))
(windowProcessName == "MicrosoftWhiteboard" || windowProcessName == "msedgewebview2") &&
foregroundWindow.IsFullScreen)
{
return true;
}
// 检查OldZyBoard
else if (Settings.Automation.IsAutoFoldInOldZyBoard &&
(WinTabWindowsChecker.IsWindowExisted("WhiteBoard - DrawingWindow") ||
WinTabWindowsChecker.IsWindowExisted("InstantAnnotationWindow")))
WinTabWindowsChecker.IsWindowExisted("InstantAnnotationWindow")) &&
foregroundWindow.IsFullScreen)
{
return true;
}
// 检查HiteLightBoard
else if (Settings.Automation.IsAutoFoldInHiteLightBoard && windowProcessName == "HiteLightBoard" &&
windowRect.Height >= SystemParameters.WorkArea.Height - 16 &&
windowRect.Width >= SystemParameters.WorkArea.Width - 16)
foregroundWindow.IsFullScreen)
{
return true;
}
// 检查AdmoxWhiteboard
else if (Settings.Automation.IsAutoFoldInAdmoxWhiteboard && windowProcessName == "Amdox.WhiteBoard" &&
windowRect.Height >= SystemParameters.WorkArea.Height - 16 &&
windowRect.Width >= SystemParameters.WorkArea.Width - 16)
foregroundWindow.IsFullScreen)
{
return true;
}
// 检查AdmoxBooth
else if (Settings.Automation.IsAutoFoldInAdmoxBooth && windowProcessName == "Amdox.Booth" &&
windowRect.Height >= SystemParameters.WorkArea.Height - 16 &&
windowRect.Width >= SystemParameters.WorkArea.Width - 16)
foregroundWindow.IsFullScreen)
{
return true;
}
// 检查QPoint
else if (Settings.Automation.IsAutoFoldInQPoint && windowProcessName == "QPoint" &&
windowRect.Height >= SystemParameters.WorkArea.Height - 16 &&
windowRect.Width >= SystemParameters.WorkArea.Width - 16)
foregroundWindow.IsFullScreen)
{
return true;
}
// 检查YiYunVisualPresenter
else if (Settings.Automation.IsAutoFoldInYiYunVisualPresenter && windowProcessName == "YiYunVisualPresenter" &&
windowRect.Height >= SystemParameters.WorkArea.Height - 16 &&
windowRect.Width >= SystemParameters.WorkArea.Width - 16)
foregroundWindow.IsFullScreen)
{
return true;
}
// 检查MaxHubWhiteboard
else if (Settings.Automation.IsAutoFoldInMaxHubWhiteboard && windowProcessName == "WhiteBoard" &&
WinTabWindowsChecker.IsWindowExisted("白板书写") &&
windowRect.Height >= SystemParameters.WorkArea.Height - 16 &&
windowRect.Width >= SystemParameters.WorkArea.Width - 16)
foregroundWindow.IsFullScreen)
{
if (foregroundWindow.ProcessPath != "Unknown")
{
@@ -1017,19 +998,23 @@ namespace Ink_Canvas
/// </remarks>
private void timerCheckAutoFold_Elapsed(object sender, ElapsedEventArgs e)
{
if (Dispatcher.HasShutdownStarted || Dispatcher.HasShutdownFinished) return;
if (isFloatingBarChangingHideMode) return;
try
{
bool hasFullScreen = HasFullScreenWindowOfAutoFoldApps();
bool shouldAutoFold = CheckShouldAutoFoldByWindowPreview();
var windowProcessName = ForegroundWindowInfo.ProcessName();
var windowTitle = ForegroundWindowInfo.WindowTitle();
Thickness currentMargin = new Thickness();
Dispatcher.Invoke(() =>
try
{
currentMargin = ViewboxFloatingBar.Margin;
});
if (Dispatcher.HasShutdownStarted || Dispatcher.HasShutdownFinished) return;
Dispatcher.Invoke(() => { currentMargin = ViewboxFloatingBar.Margin; });
}
catch (Exception)
{
return;
}
if (hasFullScreen)
{
@@ -1046,38 +1031,7 @@ namespace Ink_Canvas
if (shouldAutoFold)
{
if (windowProcessName == "EasiNote")
{
if (ForegroundWindowInfo.ProcessPath() != "Unknown")
{
var versionInfo = FileVersionInfo.GetVersionInfo(ForegroundWindowInfo.ProcessPath());
string version = versionInfo.FileVersion;
if (version.StartsWith("5.") && Settings.Automation.IsAutoFoldInEasiNote)
{
bool isAnnotationWindow = windowTitle.Length == 0 && ForegroundWindowInfo.WindowRect().Height < 500;
if (Settings.Automation.IsAutoFoldInEasiNoteIgnoreDesktopAnno && isAnnotationWindow)
{
if (!isFloatingBarFolded)
{
FoldFloatingBar_MouseUp(null, null);
}
}
else if (!isAnnotationWindow)
{
if (!unfoldFloatingBarByUser && !isFloatingBarFolded)
{
FoldFloatingBar_MouseUp(null, null);
}
else if (unfoldFloatingBarByUser)
{
}
}
}
}
}
// 处理其他目标软件
else if (!unfoldFloatingBarByUser && !isFloatingBarFolded)
if (!unfoldFloatingBarByUser && !isFloatingBarFolded)
{
FoldFloatingBar_MouseUp(null, null);
}
@@ -1095,6 +1049,8 @@ namespace Ink_Canvas
}
else
{
// schedule unfold if dispatcher still running
if (Dispatcher.HasShutdownStarted || Dispatcher.HasShutdownFinished) return;
UnFoldFloatingBar_MouseUp(new object(), null);
unfoldFloatingBarByUser = false;
}
@@ -1215,35 +1171,44 @@ namespace Ink_Canvas
// 空闲状态的判定为不处于批注模式和画板模式
bool canSafelyUpdate = false;
Dispatcher.Invoke(() =>
try
{
try
if (Dispatcher.HasShutdownStarted || Dispatcher.HasShutdownFinished) return;
Dispatcher.Invoke(() =>
{
// 判断是否处于批注模式(inkCanvas.EditingMode == InkCanvasEditingMode.Ink
// 判断是否处于画板模式(!Topmost)
if (inkCanvas.EditingMode != InkCanvasEditingMode.Ink && Topmost)
try
{
// 检查是否有未保存的内容或正在进行的操作
if (!isHidingSubPanelsWhenInking)
// 判断是否处于批注模式(inkCanvas.EditingMode == InkCanvasEditingMode.Ink
// 判断是否处于画板模式(!Topmost)
if (inkCanvas.EditingMode != InkCanvasEditingMode.Ink && Topmost)
{
canSafelyUpdate = true;
LogHelper.WriteLogToFile("AutoUpdate | Application is in a safe state for update - not in ink or board mode");
// 检查是否有未保存的内容或正在进行的操作
if (!isHidingSubPanelsWhenInking)
{
canSafelyUpdate = true;
LogHelper.WriteLogToFile("AutoUpdate | Application is in a safe state for update - not in ink or board mode");
}
else
{
LogHelper.WriteLogToFile("AutoUpdate | Application is currently performing operations");
}
}
else
{
LogHelper.WriteLogToFile("AutoUpdate | Application is currently performing operations");
LogHelper.WriteLogToFile("AutoUpdate | Application is in ink or board mode, cannot update now");
}
}
else
catch (Exception ex)
{
LogHelper.WriteLogToFile("AutoUpdate | Application is in ink or board mode, cannot update now");
LogHelper.WriteLogToFile($"AutoUpdate | Error checking application state: {ex.Message}", LogHelper.LogType.Error);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | Error checking application state: {ex.Message}", LogHelper.LogType.Error);
}
});
});
}
catch (Exception)
{
// Dispatcher not available
return;
}
if (canSafelyUpdate)
{
@@ -1256,10 +1221,12 @@ namespace Ink_Canvas
AutoUpdateHelper.InstallNewVersionApp(AvailableLatestVersion, true);
// 关闭应用程序
Dispatcher.Invoke(() =>
try
{
Application.Current.Shutdown();
});
if (Dispatcher.HasShutdownStarted || Dispatcher.HasShutdownFinished) return;
Dispatcher.Invoke(() => { Application.Current.Shutdown(); });
}
catch (Exception) { }
}
else
{
@@ -1388,6 +1355,11 @@ namespace Ink_Canvas
}
}
public void StartSilentUpdateTimer()
{
timerCheckAutoUpdateWithSilence.Start();
}
/// <summary>
/// 初始化橡皮擦自动切换回批注模式计时器
/// </summary>
@@ -1400,6 +1372,98 @@ namespace Ink_Canvas
}
}
/// <summary>
/// 在窗口关闭时停止并释放所有定时器与事件,防止在 Dispatcher 关闭期间还有后台线程调用 UI
/// </summary>
private void StopAllTimersAndHandlers()
{
try
{
// Stop and detach System.Timers.Timer
if (_unifiedMainWindowTimer != null)
{
_unifiedMainWindowTimer.Stop();
_unifiedMainWindowTimer.Elapsed -= OnUnifiedMainWindowTimerElapsed;
_unifiedMainWindowTimer.Dispose();
_unifiedMainWindowTimer = null;
}
if (timerKillProcess != null)
{
timerKillProcess.Stop();
timerKillProcess.Elapsed -= TimerKillProcess_Elapsed;
timerKillProcess.Dispose();
timerKillProcess = null;
}
if (timerCheckAutoUpdateWithSilence != null)
{
timerCheckAutoUpdateWithSilence.Stop();
timerCheckAutoUpdateWithSilence.Elapsed -= timerCheckAutoUpdateWithSilence_Elapsed;
timerCheckAutoUpdateWithSilence.Dispose();
timerCheckAutoUpdateWithSilence = null;
}
if (timerCheckAutoUpdateRetry != null)
{
timerCheckAutoUpdateRetry.Stop();
timerCheckAutoUpdateRetry.Elapsed -= timerCheckAutoUpdateRetry_Elapsed;
timerCheckAutoUpdateRetry.Dispose();
timerCheckAutoUpdateRetry = null;
}
if (timerDisplayTime != null)
{
timerDisplayTime.Stop();
timerDisplayTime.Elapsed -= TimerDisplayTime_Elapsed;
timerDisplayTime.Dispose();
timerDisplayTime = null;
}
if (timerDisplayDate != null)
{
timerDisplayDate.Stop();
timerDisplayDate.Elapsed -= TimerDisplayDate_Elapsed;
timerDisplayDate.Dispose();
timerDisplayDate = null;
}
if (timerNtpSync != null)
{
timerNtpSync.Stop();
timerNtpSync.Elapsed -= async (s, e) => await TimerNtpSync_ElapsedAsync();
timerNtpSync.Dispose();
timerNtpSync = null;
}
// DispatcherTimers run on UI thread
if (autoSaveStrokesTimer != null)
{
autoSaveStrokesTimer.Stop();
autoSaveStrokesTimer.Tick -= AutoSaveStrokesTimer_Tick;
autoSaveStrokesTimer = null;
}
if (_eraserAutoSwitchBackTimer != null)
{
_eraserAutoSwitchBackTimer.Stop();
_eraserAutoSwitchBackTimer.Tick -= EraserAutoSwitchBackTimer_Tick;
_eraserAutoSwitchBackTimer = null;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"StopAllTimers failed: {ex.Message}", LogHelper.LogType.Error);
}
}
protected override void OnClosing(CancelEventArgs e)
{
// Stop timers and handlers to avoid background callbacks invoking Dispatcher after shutdown
StopAllTimersAndHandlers();
base.OnClosing(e);
}
/// <summary>
/// 启动橡皮擦自动切换回批注模式计时器
/// </summary>
@@ -1452,6 +1516,7 @@ namespace Ink_Canvas
/// </summary>
private void EraserAutoSwitchBackTimer_Tick(object sender, EventArgs e)
{
if (Dispatcher.HasShutdownStarted || Dispatcher.HasShutdownFinished) return;
try
{
// 检查是否仍然在橡皮擦模式
@@ -1470,12 +1535,17 @@ namespace Ink_Canvas
}
// 切换到批注模式
Dispatcher.Invoke(() =>
try
{
PenIcon_Click(null, null);
StopEraserAutoSwitchBackTimer();
LogHelper.WriteLogToFile("橡皮擦自动切换回批注模式", LogHelper.LogType.Event);
});
if (Dispatcher.HasShutdownStarted || Dispatcher.HasShutdownFinished) return;
Dispatcher.Invoke(() =>
{
PenIcon_Click(null, null);
StopEraserAutoSwitchBackTimer();
LogHelper.WriteLogToFile("橡皮擦自动切换回批注模式", LogHelper.LogType.Event);
});
}
catch (Exception) { }
}
catch (Exception ex)
{
+56
View File
@@ -0,0 +1,56 @@
using Ink_Canvas.Controls;
using Ink_Canvas.Controls.Toolbar;
using System.Collections.Generic;
using System.Windows.Controls;
namespace Ink_Canvas
{
public partial class MainWindow
{
// 这批属性替代了 XAML 中原有的 x:Name 自动生成字段;外部代码继续按原名访问。
// 由对应 Toolbar Item 的 AfterBuild 回填,Populate 发生在 Window_Loaded 早期。
internal ToolbarImageButton SymbolIconDelete { get; private set; }
internal ToolbarImageButton Eraser_Icon { get; private set; }
internal ToolbarImageButton EraserByStrokes_Icon { get; private set; }
internal ToolbarImageButton SymbolIconSelect { get; private set; }
internal ToolbarImageButton ShapeDrawFloatingBarBtn { get; private set; }
internal ToolbarImageButton SymbolIconUndo { get; private set; }
internal ToolbarImageButton SymbolIconRedo { get; private set; }
internal ToolbarImageButton CursorWithDelFloatingBarBtn { get; private set; }
internal ToolbarImageButton WhiteboardFloatingBarBtn { get; private set; }
internal ToolbarImageButton ToolsFloatingBarBtn { get; private set; }
internal ToolbarImageButton Fold_Icon { get; private set; }
internal void AttachCursorIconView(ToolbarImageButton btn) => Cursor_Icon = btn;
internal void AttachPenIconView(ToolbarImageButton btn) => Pen_Icon = btn;
internal void AttachSymbolIconDelete(ToolbarImageButton btn) => SymbolIconDelete = btn;
internal void AttachEraserIcon(ToolbarImageButton btn) => Eraser_Icon = btn;
internal void AttachEraserByStrokesIcon(ToolbarImageButton btn) => EraserByStrokes_Icon = btn;
internal void AttachSymbolIconSelect(ToolbarImageButton btn) => SymbolIconSelect = btn;
internal void AttachShapeDrawBtn(ToolbarImageButton btn) => ShapeDrawFloatingBarBtn = btn;
internal void AttachSymbolIconUndo(ToolbarImageButton btn) => SymbolIconUndo = btn;
internal void AttachSymbolIconRedo(ToolbarImageButton btn) => SymbolIconRedo = btn;
internal void AttachCursorWithDelBtn(ToolbarImageButton btn) => CursorWithDelFloatingBarBtn = btn;
internal void AttachWhiteboardBtn(ToolbarImageButton btn) => WhiteboardFloatingBarBtn = btn;
internal void AttachToolsBtn(ToolbarImageButton btn) => ToolsFloatingBarBtn = btn;
internal void AttachFoldIcon(ToolbarImageButton btn) => Fold_Icon = btn;
/// <summary>
/// 在 Window_Loaded 早期调用:按 Settings.Toolbar 配置把插件化按钮填充到对应容器。
/// 必须在 LoadSettings 之前,因为 LoadSettings 会访问 Cursor_Icon/Pen_Icon/Eraser_Icon 等。
/// </summary>
internal void InitializeToolbarPlugins()
{
ToolbarHost = new ToolbarHost(this);
var slots = new Dictionary<ToolbarSlot, Panel>
{
{ ToolbarSlot.FloatingBarMain, StackPanelFloatingBar },
{ ToolbarSlot.FloatingBarCanvasControls, StackPanelCanvasControls },
{ ToolbarSlot.FloatingBarEnd, StackPanelFloatingBarEnd },
{ ToolbarSlot.BlackboardLeft, BlackboardLeftSide },
{ ToolbarSlot.BlackboardRight, BlackboardRightSide }
};
ToolbarRegistry.Populate(ToolbarHost, slots, Settings?.Toolbar);
}
}
}
+535 -23
View File
@@ -47,6 +47,372 @@ namespace Ink_Canvas
private bool isMultiTouchTimerActive;
private bool isPalmEraserActive;
private bool palmEraserWasEnabledBeforeMultiTouch;
private InkCanvasEditingMode palmEraserPreviousEditingMode = InkCanvasEditingMode.Ink;
private readonly Dictionary<int, RealtimeBrushTipState> _realtimeBrushTipStates = new Dictionary<int, RealtimeBrushTipState>();
private readonly Guid RealtimeVelocityBrushTipAppliedGuid = new Guid("74E57D95-945F-4A8C-B52A-7D3EF2D4FD5B");
internal const int MouseRealtimeStrokeId = -100001;
private readonly HashSet<int> _activeRealtimeTouchStrokeIds = new HashSet<int>();
private readonly HashSet<int> _activeTouchStrokeIds = new HashSet<int>();
private sealed class OneEuroFilter
{
private readonly float _minCutoff;
private readonly float _beta;
private readonly float _dCutoff;
private bool _initialized;
private float _xPrev;
private float _dxPrev;
public OneEuroFilter(float minCutoff, float beta, float dCutoff)
{
_minCutoff = minCutoff;
_beta = beta;
_dCutoff = dCutoff;
}
public float Filter(float value, float dt, float speed)
{
if (!_initialized)
{
_initialized = true;
_xPrev = value;
_dxPrev = 0f;
return value;
}
var dx = (value - _xPrev) / Math.Max(1e-6f, dt);
var aD = Alpha(_dCutoff, dt);
var dxHat = Lerp(_dxPrev, dx, aD);
var a = Alpha(_minCutoff + _beta * speed, dt);
var xHat = Lerp(_xPrev, value, a);
_xPrev = xHat;
_dxPrev = dxHat;
return xHat;
}
private static float Alpha(float cutoff, float dt)
{
var tau = 1f / (2f * (float)Math.PI * Math.Max(1e-3f, cutoff));
return 1f / (1f + tau / Math.Max(1e-6f, dt));
}
private static float Lerp(float a, float b, float t) => a + (b - a) * t;
}
private sealed class RealtimeBrushTipState
{
public float LastRawX { get; set; }
public float LastRawY { get; set; }
public long LastTimestampMs { get; set; }
public float SmoothedSampleRateHz { get; set; } = 120f;
public bool SawPressureVariation { get; set; }
public bool HasSeed { get; set; }
public float LastSmoothX { get; set; }
public float LastSmoothY { get; set; }
public float LastSmoothPressure { get; set; } = 0.5f;
public OneEuroFilter FilterX { get; } = new OneEuroFilter(1.2f, 0.015f, 1f);
public OneEuroFilter FilterY { get; } = new OneEuroFilter(1.2f, 0.015f, 1f);
public OneEuroFilter FilterPressure { get; } = new OneEuroFilter(1f, 0.02f, 1f);
}
private static long RealtimeNowMs() => Environment.TickCount & 0x7FFFFFFF;
private static float RealtimeClamp(float x, float min, float max)
{
if (x < min) return min;
if (x > max) return max;
return x;
}
private static float WidthToPressure(float width, float baseWidth)
{
if (baseWidth <= 1e-4f) return 0.5f;
var scale = width / baseWidth;
return RealtimeClamp((scale - 0.42f) / 1.16f, 0.08f, 1f);
}
private bool ShouldUseRealtimeVelocityBrushTip()
{
return Settings.Canvas.InkStyle == 3
&& Settings.Canvas.VelocityBrushTipMix > 0
&& !Settings.Canvas.DisablePressure;
}
private bool ShouldUseRealtimeVelocityBrushTipForTouch()
{
return Settings.Canvas.InkStyle == 3
&& Settings.Canvas.VelocityBrushTipMix > 0
&& !Settings.Canvas.DisablePressure
&& drawingShapeMode == 0
&& !isPalmEraserActive;
}
internal bool ShouldUseRealtimeVelocityBrushTipForMouse()
{
return ShouldUseRealtimeVelocityBrushTip()
&& drawingShapeMode == 0
&& !isPalmEraserActive;
}
private static bool IsTouchStylusDevice(StylusDevice stylusDevice)
{
return stylusDevice?.TabletDevice?.Type == TabletDeviceType.Touch;
}
internal void EnsureRealtimeStylusPipelineBinding()
{
if (inkCanvas == null) return;
inkCanvas.StylusDown -= MainWindow_StylusDown;
inkCanvas.StylusMove -= MainWindow_StylusMove;
inkCanvas.StylusUp -= MainWindow_StylusUp;
inkCanvas.StylusDown += MainWindow_StylusDown;
inkCanvas.StylusMove += MainWindow_StylusMove;
inkCanvas.StylusUp += MainWindow_StylusUp;
if (ShouldUseRealtimeVelocityBrushTip()
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke
&& inkCanvas.EditingMode != InkCanvasEditingMode.Select)
{
inkCanvas.EditingMode = InkCanvasEditingMode.None;
}
else if (!ShouldUseRealtimeVelocityBrushTip()
&& inkCanvas.EditingMode == InkCanvasEditingMode.None)
{
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
}
private void InitializeRealtimeBrushTipState(int stylusId, StylusDownEventArgs e)
{
if (!ShouldUseRealtimeVelocityBrushTip())
{
_realtimeBrushTipStates.Remove(stylusId);
return;
}
var startPoint = e.GetPosition(this);
_realtimeBrushTipStates[stylusId] = new RealtimeBrushTipState
{
LastRawX = (float)startPoint.X,
LastRawY = (float)startPoint.Y,
LastTimestampMs = RealtimeNowMs()
};
}
private void InitializeRealtimeBrushTipStateFromPoint(int strokeId, Point startPoint)
{
if (!ShouldUseRealtimeVelocityBrushTipForTouch() && strokeId != MouseRealtimeStrokeId)
{
_realtimeBrushTipStates.Remove(strokeId);
return;
}
if (!ShouldUseRealtimeVelocityBrushTipForMouse() && strokeId == MouseRealtimeStrokeId)
{
_realtimeBrushTipStates.Remove(strokeId);
return;
}
_realtimeBrushTipStates[strokeId] = new RealtimeBrushTipState
{
LastRawX = (float)startPoint.X,
LastRawY = (float)startPoint.Y,
LastTimestampMs = RealtimeNowMs()
};
}
private void CleanupRealtimeBrushTipState(int stylusId)
{
_realtimeBrushTipStates.Remove(stylusId);
}
private bool TryAppendRealtimeVelocityBrushTipPoints(StrokeVisual strokeVisual, StylusEventArgs e)
{
if (!ShouldUseRealtimeVelocityBrushTip() || strokeVisual == null || e?.StylusDevice == null)
return false;
if (!_realtimeBrushTipStates.TryGetValue(e.StylusDevice.Id, out var state))
return false;
var stylusPointCollection = e.GetStylusPoints(this);
if (stylusPointCollection == null || stylusPointCollection.Count == 0)
return true;
var mix = RealtimeClamp((float)Settings.Canvas.VelocityBrushTipMix, 0f, 1f);
var appended = false;
var baseWidth = (float)Math.Max(0.35,
strokeVisual.Stroke?.DrawingAttributes?.Width ?? inkCanvas.DefaultDrawingAttributes.Width);
foreach (StylusPoint rawPoint in stylusPointCollection)
{
var nowMs = RealtimeNowMs();
var dtMs = Math.Max(1L, nowMs - state.LastTimestampMs);
var dt = dtMs / 1000f;
var sampleRate = 1f / Math.Max(1e-4f, dt);
state.SmoothedSampleRateHz = state.SmoothedSampleRateHz * 0.85f + sampleRate * 0.15f;
var rawX = (float)rawPoint.X;
var rawY = (float)rawPoint.Y;
var dx = rawX - state.LastRawX;
var dy = rawY - state.LastRawY;
var dist = (float)Math.Sqrt(dx * dx + dy * dy);
var speed = dist / dt;
var filteredX = state.FilterX.Filter(rawX, dt, speed);
var filteredY = state.FilterY.Filter(rawY, dt, speed);
var hwPressure = RealtimeClamp((float)rawPoint.PressureFactor, 0f, 1f);
if (Math.Abs(hwPressure - 0.5f) > 0.02f)
state.SawPressureVariation = true;
var usePressure = state.SawPressureVariation && hwPressure > 0f;
var width = baseWidth;
if (usePressure)
width *= 0.25f + 0.75f * hwPressure;
var speedNormalization = 1800f + state.SmoothedSampleRateHz * 3.5f;
width *= RealtimeClamp(1.15f - (speed / speedNormalization), 0.45f, 1.25f);
var speedPressure = WidthToPressure(width, baseWidth);
var pressure = usePressure
? ((1f - mix) * hwPressure + mix * speedPressure)
: speedPressure;
pressure = RealtimeClamp(pressure, 0.08f, 1f);
pressure = state.FilterPressure.Filter(pressure, dt, speed);
// 高频采样时做最小距离门限,避免点爆炸导致实时重绘卡顿
var minDist = state.SmoothedSampleRateHz > 160f ? 0.55f
: state.SmoothedSampleRateHz > 90f ? 0.4f
: 0.25f;
if (dist < minDist && state.HasSeed)
{
state.LastRawX = rawX;
state.LastRawY = rawY;
state.LastTimestampMs = nowMs;
continue;
}
if (!state.HasSeed)
{
state.HasSeed = true;
state.LastSmoothX = filteredX;
state.LastSmoothY = filteredY;
state.LastSmoothPressure = pressure;
strokeVisual.Add(new StylusPoint(filteredX, filteredY, pressure));
}
else
{
// 采用中点链减抖:保持实时笔锋同时降低折线锯齿
var midX = (state.LastSmoothX + filteredX) * 0.5f;
var midY = (state.LastSmoothY + filteredY) * 0.5f;
var midPressure = (state.LastSmoothPressure + pressure) * 0.5f;
strokeVisual.Add(new StylusPoint(midX, midY, midPressure));
state.LastSmoothX = filteredX;
state.LastSmoothY = filteredY;
state.LastSmoothPressure = pressure;
}
state.LastRawX = rawX;
state.LastRawY = rawY;
state.LastTimestampMs = nowMs;
appended = true;
}
var committedStroke = strokeVisual.Stroke;
if (appended && committedStroke != null)
{
if (committedStroke.DrawingAttributes != null)
committedStroke.DrawingAttributes.IgnorePressure = false;
if (!committedStroke.ContainsPropertyData(RealtimeVelocityBrushTipAppliedGuid))
committedStroke.AddPropertyData(RealtimeVelocityBrushTipAppliedGuid, true);
}
return true;
}
private bool TryAppendRealtimeVelocityBrushTipPoint(StrokeVisual strokeVisual, int strokeId, Point point, float rawPressure = 0.5f)
{
var allow = strokeId == MouseRealtimeStrokeId
? ShouldUseRealtimeVelocityBrushTipForMouse()
: ShouldUseRealtimeVelocityBrushTipForTouch();
if (!allow || strokeVisual == null)
return false;
if (!_realtimeBrushTipStates.TryGetValue(strokeId, out var state))
return false;
var mix = RealtimeClamp((float)Settings.Canvas.VelocityBrushTipMix, 0f, 1f);
var nowMs = RealtimeNowMs();
var dtMs = Math.Max(1L, nowMs - state.LastTimestampMs);
var dt = dtMs / 1000f;
var sampleRate = 1f / Math.Max(1e-4f, dt);
state.SmoothedSampleRateHz = state.SmoothedSampleRateHz * 0.85f + sampleRate * 0.15f;
var baseWidth = (float)Math.Max(0.35,
strokeVisual.Stroke?.DrawingAttributes?.Width ?? inkCanvas.DefaultDrawingAttributes.Width);
var rawX = (float)point.X;
var rawY = (float)point.Y;
var dx = rawX - state.LastRawX;
var dy = rawY - state.LastRawY;
var dist = (float)Math.Sqrt(dx * dx + dy * dy);
var speed = dist / dt;
var filteredX = state.FilterX.Filter(rawX, dt, speed);
var filteredY = state.FilterY.Filter(rawY, dt, speed);
rawPressure = RealtimeClamp(rawPressure, 0f, 1f);
if (Math.Abs(rawPressure - 0.5f) > 0.02f)
state.SawPressureVariation = true;
var usePressure = state.SawPressureVariation && rawPressure > 0f;
var width = baseWidth;
if (usePressure)
width *= 0.25f + 0.75f * rawPressure;
var speedNormalization = 1800f + state.SmoothedSampleRateHz * 3.5f;
width *= RealtimeClamp(1.15f - (speed / speedNormalization), 0.45f, 1.25f);
var speedPressure = WidthToPressure(width, baseWidth);
var pressure = usePressure
? ((1f - mix) * rawPressure + mix * speedPressure)
: speedPressure;
pressure = RealtimeClamp(pressure, 0.08f, 1f);
pressure = state.FilterPressure.Filter(pressure, dt, speed);
var minDist = state.SmoothedSampleRateHz > 160f ? 0.55f
: state.SmoothedSampleRateHz > 90f ? 0.4f
: 0.25f;
if (dist < minDist && state.HasSeed)
{
state.LastRawX = rawX;
state.LastRawY = rawY;
state.LastTimestampMs = nowMs;
return true;
}
if (!state.HasSeed)
{
state.HasSeed = true;
state.LastSmoothX = filteredX;
state.LastSmoothY = filteredY;
state.LastSmoothPressure = pressure;
strokeVisual.Add(new StylusPoint(filteredX, filteredY, pressure));
}
else
{
var midX = (state.LastSmoothX + filteredX) * 0.5f;
var midY = (state.LastSmoothY + filteredY) * 0.5f;
var midPressure = (state.LastSmoothPressure + pressure) * 0.5f;
strokeVisual.Add(new StylusPoint(midX, midY, midPressure));
state.LastSmoothX = filteredX;
state.LastSmoothY = filteredY;
state.LastSmoothPressure = pressure;
}
state.LastRawX = rawX;
state.LastRawY = rawY;
state.LastTimestampMs = nowMs;
return true;
}
/// <summary>
/// 保存画布上的非笔画元素(如图片、媒体元素等)
@@ -233,8 +599,7 @@ namespace Ink_Canvas
if (palmEraserWasEnabledBeforeMultiTouch)
{
Settings.Canvas.EnablePalmEraser = true;
if (ToggleSwitchEnablePalmEraser != null)
ToggleSwitchEnablePalmEraser.IsOn = true;
SaveSettingsToFile();
}
}
else
@@ -259,8 +624,7 @@ namespace Ink_Canvas
palmEraserWasEnabledBeforeMultiTouch = Settings.Canvas.EnablePalmEraser;
Settings.Canvas.EnablePalmEraser = false;
if (ToggleSwitchEnablePalmEraser != null)
ToggleSwitchEnablePalmEraser.IsOn = false;
SaveSettingsToFile();
}
}
@@ -340,6 +704,9 @@ namespace Ink_Canvas
/// </remarks>
private void MainWindow_StylusDown(object sender, StylusDownEventArgs e)
{
if (IsTouchStylusDevice(e.StylusDevice))
return;
// 检查手写笔点击是否发生在浮动栏区域,如果是则允许事件传播到浮动栏按钮
var stylusPoint = e.GetPosition(this);
var floatingBarBounds = ViewboxFloatingBar.TransformToAncestor(this).TransformBounds(
@@ -375,7 +742,9 @@ namespace Ink_Canvas
}
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke)
{
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
inkCanvas.EditingMode = ShouldUseRealtimeVelocityBrushTip()
? InkCanvasEditingMode.None
: InkCanvasEditingMode.Ink;
}
else
{
@@ -392,6 +761,7 @@ namespace Ink_Canvas
|| inkCanvas.EditingMode == InkCanvasEditingMode.EraseByStroke
|| inkCanvas.EditingMode == InkCanvasEditingMode.Select) return;
InitializeRealtimeBrushTipState(e.StylusDevice.Id, e);
TouchDownPointsList[e.StylusDevice.Id] = InkCanvasEditingMode.None;
}
@@ -421,6 +791,9 @@ namespace Ink_Canvas
/// </remarks>
private async void MainWindow_StylusUp(object sender, StylusEventArgs e)
{
if (IsTouchStylusDevice(e.StylusDevice))
return;
if (drawingShapeMode != 0)
{
// 重置触摸状态
@@ -491,6 +864,7 @@ namespace Ink_Canvas
StrokeVisualList.Remove(e.StylusDevice.Id);
VisualCanvasList.Remove(e.StylusDevice.Id);
TouchDownPointsList.Remove(e.StylusDevice.Id);
CleanupRealtimeBrushTipState(e.StylusDevice.Id);
if (StrokeVisualList.Count == 0 || VisualCanvasList.Count == 0 || TouchDownPointsList.Count == 0)
{
// 只清除手写笔预览相关的Canvas,不清除所有子元素
@@ -534,6 +908,9 @@ namespace Ink_Canvas
{
try
{
if (IsTouchStylusDevice(e.StylusDevice))
return;
if (drawingShapeMode != 0)
{
if (isTouchDown)
@@ -553,28 +930,18 @@ namespace Ink_Canvas
var strokeVisual = GetStrokeVisual(e.StylusDevice.Id);
var stylusPointCollection = e.GetStylusPoints(this);
foreach (var stylusPoint in stylusPointCollection)
strokeVisual.Add(new StylusPoint(stylusPoint.X, stylusPoint.Y, stylusPoint.PressureFactor));
var isHandledByRealtime = TryAppendRealtimeVelocityBrushTipPoints(strokeVisual, e);
if (!isHandledByRealtime)
{
var stylusPointCollection = e.GetStylusPoints(this);
foreach (var stylusPoint in stylusPointCollection)
strokeVisual.Add(new StylusPoint(stylusPoint.X, stylusPoint.Y, stylusPoint.PressureFactor));
}
// 实时笔锋:混合度 > 0 时在绘制过程中更新压感并整笔重绘预览;混合为 0 时与普通过程一致用增量 Redraw,避免每点 ForceRedraw 整笔清空(长笔画卡顿)。
var committedStroke = strokeVisual.Stroke;
if (committedStroke != null
&& Settings.Canvas.InkStyle == 3
&& Settings.Canvas.VelocityBrushTipMix > 0
&& !Settings.Canvas.DisablePressure
&& penType == 0
&& committedStroke.DrawingAttributes != null
&& !committedStroke.DrawingAttributes.IsHighlighter
&& committedStroke.StylusPoints.Count >= 3)
{
ApplyVelocityBrushTipFromSpeed(committedStroke);
if (isHandledByRealtime)
strokeVisual.ForceRedraw();
}
else
{
strokeVisual.Redraw();
}
}
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
}
@@ -813,6 +1180,51 @@ namespace Ink_Canvas
lastTouchDownTime = DateTime.Now;
dec.Add(e.TouchDevice.Id);
if (ShouldUseRealtimeVelocityBrushTipForTouch()
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke
&& inkCanvas.EditingMode != InkCanvasEditingMode.Select)
{
try
{
inkCanvas.EditingMode = InkCanvasEditingMode.None;
var touchId = e.TouchDevice.Id;
var p = e.GetTouchPoint(inkCanvas).Position;
_activeRealtimeTouchStrokeIds.Add(touchId);
InitializeRealtimeBrushTipStateFromPoint(touchId, p);
var sv = GetStrokeVisual(touchId);
TryAppendRealtimeVelocityBrushTipPoint(sv, touchId, p);
sv.ForceRedraw();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
return;
}
if ((isInMultiTouchMode || Settings.Gesture.IsEnableMultiTouchMode)
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke
&& inkCanvas.EditingMode != InkCanvasEditingMode.Select)
{
try
{
inkCanvas.EditingMode = InkCanvasEditingMode.None;
var touchId = e.TouchDevice.Id;
var p = e.GetTouchPoint(inkCanvas).Position;
_activeTouchStrokeIds.Add(touchId);
var sv = GetStrokeVisual(touchId);
sv.Add(new StylusPoint(p.X, p.Y, 0.5f));
sv.Redraw();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
return;
}
if (Settings.Canvas.EnablePalmEraser && !isPalmEraserActive && drawingShapeMode == 0)
{
var touchPoint = e.GetTouchPoint(inkCanvas);
@@ -848,6 +1260,7 @@ namespace Ink_Canvas
if (Settings.Advanced.IsSpecialScreen)
boundWidth *= Settings.Advanced.TouchMultiplier;
palmEraserPreviousEditingMode = inkCanvas.EditingMode;
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
isPalmEraserActive = true;
@@ -923,6 +1336,40 @@ namespace Ink_Canvas
var touchPoint = e.GetTouchPoint(inkCanvas);
EraserOverlay_PointerMove(sender, touchPoint.Position);
}
var touchId = e.TouchDevice.Id;
if (ShouldUseRealtimeVelocityBrushTipForTouch())
{
if (!_activeRealtimeTouchStrokeIds.Contains(touchId))
return;
try
{
var p = e.GetTouchPoint(inkCanvas).Position;
var sv = GetStrokeVisual(touchId);
if (TryAppendRealtimeVelocityBrushTipPoint(sv, touchId, p))
sv.ForceRedraw();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
return;
}
if (_activeTouchStrokeIds.Contains(touchId))
{
try
{
var p = e.GetTouchPoint(inkCanvas).Position;
var sv = GetStrokeVisual(touchId);
sv.Add(new StylusPoint(p.X, p.Y, 0.5f));
sv.Redraw();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
}
}
/// <summary>
@@ -949,6 +1396,66 @@ namespace Ink_Canvas
/// </remarks>
private void InkCanvas_PreviewTouchUp(object sender, TouchEventArgs e)
{
var touchId = e.TouchDevice.Id;
if (_activeRealtimeTouchStrokeIds.Contains(touchId))
{
try
{
var sv = GetStrokeVisual(touchId);
sv?.ForceRedraw();
var stroke = sv?.Stroke;
if (stroke != null)
{
if (!stroke.ContainsPropertyData(RealtimeVelocityBrushTipAppliedGuid))
stroke.AddPropertyData(RealtimeVelocityBrushTipAppliedGuid, true);
inkCanvas.Strokes.Add(stroke);
inkCanvas_StrokeCollected(inkCanvas, new InkCanvasStrokeCollectedEventArgs(stroke));
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
finally
{
if (VisualCanvasList.TryGetValue(touchId, out var visualCanvas) && inkCanvas.Children.Contains(visualCanvas))
inkCanvas.Children.Remove(visualCanvas);
StrokeVisualList.Remove(touchId);
VisualCanvasList.Remove(touchId);
TouchDownPointsList.Remove(touchId);
CleanupRealtimeBrushTipState(touchId);
_activeRealtimeTouchStrokeIds.Remove(touchId);
}
}
else if (_activeTouchStrokeIds.Contains(touchId))
{
try
{
var sv = GetStrokeVisual(touchId);
sv?.Redraw();
var stroke = sv?.Stroke;
if (stroke != null)
{
inkCanvas.Strokes.Add(stroke);
inkCanvas_StrokeCollected(inkCanvas, new InkCanvasStrokeCollectedEventArgs(stroke));
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
finally
{
if (VisualCanvasList.TryGetValue(touchId, out var visualCanvas) && inkCanvas.Children.Contains(visualCanvas))
inkCanvas.Children.Remove(visualCanvas);
StrokeVisualList.Remove(touchId);
VisualCanvasList.Remove(touchId);
TouchDownPointsList.Remove(touchId);
CleanupRealtimeBrushTipState(touchId);
_activeTouchStrokeIds.Remove(touchId);
}
}
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint && !isPalmEraserActive)
{
return;
@@ -1021,6 +1528,11 @@ namespace Ink_Canvas
{
isPalmEraserActive = false;
DisableEraserOverlay();
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
inkCanvas.EditingMode = palmEraserPreviousEditingMode;
SetCursorBasedOnEditingMode(inkCanvas);
}
}
}
}
-19
View File
@@ -123,20 +123,6 @@ namespace Ink_Canvas
return true;
}
private bool IsLegacySettingsVisible(MainWindow mainWin)
{
try
{
var borderSettingsField = typeof(MainWindow).GetField("BorderSettings", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var borderSettings = borderSettingsField?.GetValue(mainWin) as FrameworkElement;
return borderSettings?.Visibility == Visibility.Visible;
}
catch
{
return false;
}
}
private void TempShowMainWindowTrayIconMenuItem_Clicked(object sender, RoutedEventArgs e)
{
var mainWin = Current.MainWindow as MainWindow;
@@ -224,11 +210,6 @@ namespace Ink_Canvas
return;
}
if (IsLegacySettingsVisible(mainWin))
{
return;
}
try
{
var method = typeof(MainWindow).GetMethod("BtnSettings_Click", BindingFlags.NonPublic | BindingFlags.Instance);
@@ -232,7 +232,6 @@ namespace Ink_Canvas
}
_lastAppliedProfileName = profileName.Trim();
ReloadSettingsFromFile();
RefreshConfigProfileList();
File.WriteAllText(resultPath, "ok", System.Text.Encoding.UTF8);
ShowNotification($"已通过 URI 切换至方案「{profileName}」");
LogHelper.WriteLogToFile($"URI 已切换配置方案: {profileName}", LogHelper.LogType.Event);