Merge branch 'beta' into net462

Till commit 057fb35d00
This commit is contained in:
doudou0720
2026-04-06 09:47:27 +08:00
139 changed files with 9475 additions and 26713 deletions
+1
View File
@@ -1,3 +1,4 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33530.505
+25 -4
View File
@@ -1,8 +1,9 @@
<Application x:Class="Ink_Canvas.App"
<Application x:Class="Ink_Canvas.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Ink_Canvas"
xmlns:tb="http://www.hardcodet.net/taskbar"
xmlns:props="clr-namespace:Ink_Canvas.Properties"
xmlns:tb="clr-namespace:H.NotifyIcon;assembly=H.NotifyIcon.Wpf"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
>
@@ -32,10 +33,30 @@
</Image>
</MenuItem.Icon>
</MenuItem>
<MenuItem Name="TempShowMainWindowTrayIconMenuItem" Click="TempShowMainWindowTrayIconMenuItem_Clicked">
<MenuItem.Header>
<ikw:SimpleStackPanel Orientation="Horizontal" Margin="-4,0,0,0">
<TextBlock FontSize="14" VerticalAlignment="Center" Foreground="#18181b" Text="{x:Static props:Strings.Tray_TempShowMainWindow}" />
</ikw:SimpleStackPanel>
</MenuItem.Header>
<MenuItem.Icon>
<Image Width="28" Height="28" Margin="-2">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
<GeometryDrawing Brush="#27272a" Geometry="F0 M24,24z M0,0z M5,6C4.73478,6 4.48043,6.10536 4.29289,6.29289 4.10536,6.48043 4,6.73478 4,7L4,17C4,17.2652 4.10536,17.5196 4.29289,17.7071 4.48043,17.8946 4.73478,18 5,18L19,18C19.2652,18 19.5196,17.8946 19.7071,17.7071 19.8946,17.5196 20,17.2652 20,17L20,7C20,6.73478 19.8946,6.48043 19.7071,6.29289 19.5196,6.10536 19.2652,6 19,6L5,6z M2.87868,4.87868C3.44129,4.31607,4.20435,4,5,4L19,4C19.7957,4 20.5587,4.31607 21.1213,4.87868 21.6839,5.44129 22,6.20435 22,7L22,17C22,17.7957 21.6839,18.5587 21.1213,19.1213 20.5587,19.6839 19.7957,20 19,20L5,20C4.20435,20 3.44129,19.6839 2.87868,19.1213 2.31607,18.5587 2,17.7956 2,17L2,7C2,6.20435,2.31607,5.44129,2.87868,4.87868z M5,8C5,7.44772,5.44772,7,6,7L6.01,7C6.56228,7 7.01,7.44772 7.01,8 7.01,8.55228 6.56228,9 6.01,9L6,9C5.44772,9,5,8.55228,5,8z M9,7C8.44772,7 8,7.44772 8,8 8,8.55228 8.44772,9 9,9L9.01,9C9.56228,9 10.01,8.55228 10.01,8 10.01,7.44772 9.56228,7 9.01,7L9,7z" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</MenuItem.Icon>
</MenuItem>
<MenuItem Name="OpenSettingsTrayIconMenuItem" Click="OpenSettingsTrayIconMenuItem_Clicked">
<MenuItem.Header>
<ikw:SimpleStackPanel Orientation="Horizontal" Margin="-4,0,0,0">
<TextBlock FontSize="14" VerticalAlignment="Center" Foreground="#18181b" Text="打开设置" />
<TextBlock FontSize="14" VerticalAlignment="Center" Foreground="#18181b" Text="{x:Static props:Strings.Tray_OpenSettings}" />
</ikw:SimpleStackPanel>
</MenuItem.Header>
<MenuItem.Icon>
@@ -242,7 +263,7 @@
ToolTipText="InkCanvasForClass"
ContextMenu="{StaticResource SysTrayMenu}"
IconSource="/Resources/icc.ico"/>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary.MergedDictionaries>
<ui:ThemeResources/>
<ui:XamlControlsResources />
<ResourceDictionary Source="Resources/SeewoImageDictionary.xaml"/>
+2 -1
View File
@@ -1,4 +1,4 @@
using Hardcodet.Wpf.TaskbarNotification;
using H.NotifyIcon;
using Ink_Canvas.Helpers;
using Ink_Canvas.Properties;
using iNKORE.UI.WPF.Modern.Controls;
@@ -1061,6 +1061,7 @@ namespace Ink_Canvas
}
_taskbar = (TaskbarIcon)FindResource("TaskbarTrayIcon");
_taskbar.ForceCreate();
StartArgs = e.Args;
+24
View File
@@ -0,0 +1,24 @@
<UserControl x:Class="Ink_Canvas.Controls.CopyButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern">
<Button x:Name="CopyButtonControl" Padding="6" Click="CopyButton_Click"
ToolTipService.ToolTip="Copy">
<Grid>
<ui:FontIcon x:Name="FontIcon_Copy" FontSize="16"
Icon="{x:Static ui:SegoeFluentIcons.Copy}" RenderTransformOrigin="0.5 0.5">
<FrameworkElement.RenderTransform>
<ScaleTransform x:Name="ScaleTransform_Copy"
ScaleX="1" ScaleY="{Binding ScaleX, RelativeSource={RelativeSource Self}}"/>
</FrameworkElement.RenderTransform>
</ui:FontIcon>
<ui:FontIcon x:Name="FontIcon_Success" FontSize="16"
Icon="{x:Static ui:SegoeFluentIcons.CheckMark}" RenderTransformOrigin="0.5 0.5">
<FrameworkElement.RenderTransform>
<ScaleTransform x:Name="ScaleTransform_Success"
ScaleX="0" ScaleY="{Binding ScaleX, RelativeSource={RelativeSource Self}}"/>
</FrameworkElement.RenderTransform>
</ui:FontIcon>
</Grid>
</Button>
</UserControl>
+115
View File
@@ -0,0 +1,115 @@
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace Ink_Canvas.Controls
{
public partial class CopyButton : UserControl
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
nameof(Text), typeof(string), typeof(CopyButton), new PropertyMetadata(string.Empty));
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public event EventHandler Click;
public CopyButton()
{
InitializeComponent();
}
private void CopyButton_Click(object sender, RoutedEventArgs e)
{
try
{
if (!string.IsNullOrEmpty(Text))
{
Clipboard.SetText(Text);
}
ShowSuccessAnimation();
Click?.Invoke(this, EventArgs.Empty);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Unable to Perform Copy", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private async void ShowSuccessAnimation()
{
var copyScaleAnim = new DoubleAnimation
{
To = 0,
Duration = TimeSpan.FromMilliseconds(150)
};
ScaleTransform_Copy.BeginAnimation(ScaleTransform.ScaleXProperty, copyScaleAnim);
var copyOpacityAnim = new DoubleAnimation
{
To = 0,
BeginTime = TimeSpan.FromMilliseconds(100),
Duration = TimeSpan.FromMilliseconds(10)
};
FontIcon_Copy.BeginAnimation(UIElement.OpacityProperty, copyOpacityAnim);
await Task.Delay(150);
var successScaleAnim = new DoubleAnimation
{
To = 1,
Duration = TimeSpan.FromMilliseconds(150),
EasingFunction = new BackEase { EasingMode = EasingMode.EaseOut, Amplitude = 0.2 }
};
ScaleTransform_Success.BeginAnimation(ScaleTransform.ScaleXProperty, successScaleAnim);
var successOpacityAnim = new DoubleAnimation
{
To = 1,
Duration = TimeSpan.FromMilliseconds(15)
};
FontIcon_Success.BeginAnimation(UIElement.OpacityProperty, successOpacityAnim);
await Task.Delay(1000);
ShowCopyAnimation();
}
private async void ShowCopyAnimation()
{
var successOpacityAnim = new DoubleAnimation
{
To = 0,
Duration = TimeSpan.FromMilliseconds(150)
};
FontIcon_Success.BeginAnimation(UIElement.OpacityProperty, successOpacityAnim);
await Task.Delay(150);
var copyScaleAnim = new DoubleAnimation
{
To = 1,
Duration = TimeSpan.Zero
};
ScaleTransform_Copy.BeginAnimation(ScaleTransform.ScaleXProperty, copyScaleAnim);
var copyOpacityAnim = new DoubleAnimation
{
To = 1,
Duration = TimeSpan.FromMilliseconds(150)
};
FontIcon_Copy.BeginAnimation(UIElement.OpacityProperty, copyOpacityAnim);
var successScaleAnim = new DoubleAnimation
{
To = 0,
Duration = TimeSpan.Zero
};
ScaleTransform_Success.BeginAnimation(ScaleTransform.ScaleXProperty, successScaleAnim);
}
}
}
+145
View File
@@ -0,0 +1,145 @@
using Ink_Canvas.Helpers;
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Ink_Canvas.Controls
{
/// <summary>
/// 画布上的多页 PDF:仅显示当前页;翻页与页码由主窗口 PDF 侧栏控制(无 XAML 文件)。
/// </summary>
public class PdfEmbeddedView : UserControl
{
private readonly Image _pageImage;
private string _pdfPath;
private uint _pageCount;
private uint _currentIndex;
private bool _compressLargePictures;
private bool _isPagingBusy;
private bool _layoutSizeCommitted;
/// <summary>页码或可翻页状态变化(用于更新侧栏)。</summary>
public event EventHandler PageNavigationStateChanged;
public PdfEmbeddedView()
{
MinWidth = 80;
MinHeight = 60;
var grid = new Grid { ClipToBounds = true };
_pageImage = new Image
{
Stretch = Stretch.Uniform,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
grid.Children.Add(_pageImage);
Content = grid;
}
/// <summary>
/// 初始化并显示指定页;由 MainWindow 在 UI 线程创建后调用。
/// </summary>
/// <param name="initialPageIndex">从 0 开始的页码,超出范围时夹紧到合法区间。</param>
public async Task InitializeAsync(string pdfFilePath, uint pageCount, bool compressLargePictures, uint initialPageIndex = 0)
{
_pdfPath = pdfFilePath ?? throw new ArgumentNullException(nameof(pdfFilePath));
_pageCount = pageCount;
_compressLargePictures = compressLargePictures;
if (_pageCount == 0)
_currentIndex = 0;
else
_currentIndex = initialPageIndex >= _pageCount ? _pageCount - 1 : initialPageIndex;
await ShowPageAsync(_currentIndex);
}
public string PdfPath => _pdfPath;
public uint PageCount => _pageCount;
public uint CurrentPageIndex => _currentIndex;
public string PageLabelText => _pageCount == 0 ? "" : $"{_currentIndex + 1} / {_pageCount}";
public bool CanGoPrevious => !_isPagingBusy && _pageCount > 1 && _currentIndex > 0;
public bool CanGoNext => !_isPagingBusy && _pageCount > 1 && _currentIndex < _pageCount - 1;
public async Task GoToPreviousPageAsync()
{
await GoRelativeAsync(-1);
}
public async Task GoToNextPageAsync()
{
await GoRelativeAsync(1);
}
private void NotifyPageNavigationStateChanged()
{
PageNavigationStateChanged?.Invoke(this, EventArgs.Empty);
}
private async Task GoRelativeAsync(int delta)
{
if (_isPagingBusy || _pageCount <= 1)
return;
int next = (int)_currentIndex + delta;
if (next < 0 || next >= _pageCount)
return;
_currentIndex = (uint)next;
await ShowPageAsync(_currentIndex);
}
private async Task ShowPageAsync(uint pageIndex)
{
_isPagingBusy = true;
NotifyPageNavigationStateChanged();
try
{
BitmapSource raw = await PdfWinRtHelper.RenderPageToBitmapSourceAsync(_pdfPath, pageIndex);
if (raw == null)
return;
BitmapSource display = ApplyCompressionIfNeeded(raw);
_pageImage.Source = display;
if (!_layoutSizeCommitted)
{
bool callerSized = !double.IsNaN(Width) && Width > 0 && !double.IsNaN(Height) && Height > 0;
if (!callerSized)
{
Width = display.PixelWidth;
Height = display.PixelHeight;
}
_layoutSizeCommitted = true;
}
}
finally
{
_isPagingBusy = false;
NotifyPageNavigationStateChanged();
}
}
private BitmapSource ApplyCompressionIfNeeded(BitmapSource rendered)
{
int width = rendered.PixelWidth;
int height = rendered.PixelHeight;
if (_compressLargePictures && (width > 1920 || height > 1080))
{
double scaleX = 1920.0 / width;
double scaleY = 1080.0 / height;
double scale = Math.Min(scaleX, scaleY);
return new TransformedBitmap(rendered, new ScaleTransform(scale, scale));
}
return rendered;
}
}
}
+12
View File
@@ -285,6 +285,7 @@ namespace Ink_Canvas.Helpers
// 功能快捷键
RegisterHotkey("DrawLine", Key.L, ModifierKeys.Alt, () => _mainWindow.BtnDrawLine_Click(null, null));
RegisterHotkey("Screenshot", Key.C, ModifierKeys.Alt, () => _mainWindow.SaveScreenShotToDesktop());
RegisterHotkey("QuickDraw", Key.K, ModifierKeys.Alt, () => _mainWindow.OpenQuickDrawFromHotkey());
RegisterHotkey("Hide", Key.V, ModifierKeys.Alt, () => _mainWindow.SymbolIconEmoji_MouseUp(null, null));
// 退出快捷键
@@ -1033,6 +1034,7 @@ namespace Ink_Canvas.Helpers
new HotkeyConfigItem { Name = "Pen5", Key = Key.D5, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "DrawLine", Key = Key.L, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "Screenshot", Key = Key.C, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "QuickDraw", Key = Key.K, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "Hide", Key = Key.V, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "Exit", Key = Key.Escape, Modifiers = ModifierKeys.None }
});
@@ -1111,6 +1113,14 @@ namespace Ink_Canvas.Helpers
}
}
// 旧版 HotkeyConfig.json 无「快抽」项时补注册默认组合,避免升级后无快捷键
if (successCount > 0 && !IsHotkeyRegistered("QuickDraw"))
{
var quickDrawAction = GetActionByName("QuickDraw");
if (quickDrawAction != null && RegisterHotkey("QuickDraw", Key.K, ModifierKeys.Alt, quickDrawAction))
successCount++;
}
if (successCount > 0)
{
_hotkeysShouldBeRegistered = true;
@@ -1221,6 +1231,8 @@ namespace Ink_Canvas.Helpers
return () => _mainWindow.BtnDrawLine_Click(null, null);
case "Screenshot":
return () => _mainWindow.SaveScreenShotToDesktop();
case "QuickDraw":
return () => _mainWindow.OpenQuickDrawFromHotkey();
case "Hide":
return () => _mainWindow.SymbolIconEmoji_MouseUp(null, null);
case "Exit":
+6 -3
View File
@@ -1,4 +1,3 @@
using Ink_Canvas;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
@@ -49,13 +48,17 @@ namespace Ink_Canvas.Helpers
break;
}
if (alternates.Count > 0)
analysisAlternate = alternates[0];
{
var altFinal = alternates[0];
if (altFinal?.AlternateNodes != null && altFinal.AlternateNodes.Count > 0)
analysisAlternate = altFinal;
}
}
}
analyzer.Dispose();
if (analysisAlternate != null && analysisAlternate.AlternateNodes.Count > 0)
if (analysisAlternate != null && analysisAlternate.AlternateNodes != null && analysisAlternate.AlternateNodes.Count > 0)
{
var node = analysisAlternate.AlternateNodes[0] as InkDrawingNode;
if (node == null)
+9 -5
View File
@@ -390,11 +390,15 @@ namespace Ink_Canvas.Helpers
{
try
{
PPTApplication.PresentationOpen -= OnPresentationOpen;
PPTApplication.PresentationClose -= OnPresentationClose;
PPTApplication.SlideShowBegin -= OnSlideShowBegin;
PPTApplication.SlideShowNextSlide -= OnSlideShowNextSlide;
PPTApplication.SlideShowEnd -= OnSlideShowEnd;
// 再次检查PPTApplication是否为null,因为可能在异步操作期间被修改
if (PPTApplication != null && Marshal.IsComObject(PPTApplication))
{
PPTApplication.PresentationOpen -= OnPresentationOpen;
PPTApplication.PresentationClose -= OnPresentationClose;
PPTApplication.SlideShowBegin -= OnSlideShowBegin;
PPTApplication.SlideShowNextSlide -= OnSlideShowNextSlide;
PPTApplication.SlideShowEnd -= OnSlideShowEnd;
}
}
catch (COMException comEx)
{
+79
View File
@@ -0,0 +1,79 @@
using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
using Windows.Data.Pdf;
using Windows.Storage;
using Windows.Storage.Streams;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 使用 Windows.Data.PdfWinRT)将 PDF 页渲染为 WPF 可用的位图。
/// </summary>
internal static class PdfWinRtHelper
{
public static async Task<uint> GetPageCountAsync(string pdfPath)
{
if (string.IsNullOrWhiteSpace(pdfPath) || !File.Exists(pdfPath))
return 0;
var file = await StorageFile.GetFileFromPathAsync(pdfPath).AsTask();
var doc = await PdfDocument.LoadFromFileAsync(file).AsTask();
if (doc.IsPasswordProtected)
return 0;
return doc.PageCount;
}
public static async Task<BitmapSource> RenderPageToBitmapSourceAsync(string pdfPath, uint pageIndex)
{
if (string.IsNullOrWhiteSpace(pdfPath) || !File.Exists(pdfPath))
return null;
var file = await StorageFile.GetFileFromPathAsync(pdfPath).AsTask();
var doc = await PdfDocument.LoadFromFileAsync(file).AsTask();
if (doc.IsPasswordProtected)
return null;
if (pageIndex >= doc.PageCount)
return null;
var page = doc.GetPage(pageIndex);
try
{
using (var ras = new InMemoryRandomAccessStream())
{
await page.RenderToStreamAsync(ras).AsTask();
ras.Seek(0);
var ms = new MemoryStream();
using (var netStream = ras.AsStreamForRead())
netStream.CopyTo(ms);
ms.Position = 0;
try
{
return await Application.Current.Dispatcher.InvokeAsync(() =>
{
var bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = ms;
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.EndInit();
bi.Freeze();
return (BitmapSource)bi;
});
}
finally
{
ms.Dispose();
}
}
}
finally
{
(page as IDisposable)?.Dispose();
}
}
}
}
@@ -1,274 +0,0 @@
using iNKORE.UI.WPF.Controls;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
/// <summary>
/// 启动台按钮控件
/// </summary>
public class LauncherButton
{
/// <summary>
/// 父插件
/// </summary>
private readonly SuperLauncherPlugin _plugin;
/// <summary>
/// 实际按钮控件
/// </summary>
private readonly SimpleStackPanel _panel;
/// <summary>
/// 获取按钮UI元素
/// </summary>
public UIElement Element => _panel;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="plugin">父插件</param>
public LauncherButton(SuperLauncherPlugin plugin)
{
try
{
_plugin = plugin;
LogHelper.WriteLogToFile("开始创建启动台按钮");
// 创建SimpleStackPanel
_panel = new SimpleStackPanel
{
Name = "Launcher_Icon",
Orientation = Orientation.Vertical,
HorizontalAlignment = HorizontalAlignment.Center,
Width = 28,
Margin = new Thickness(0, -2, 0, 0),
Background = Brushes.Transparent
};
LogHelper.WriteLogToFile("创建SimpleStackPanel完成");
// 添加图标
var image = CreateIconImage();
_panel.Children.Add(image);
// 添加文本
TextBlock textBlock = new TextBlock
{
Text = "启动台",
Foreground = Brushes.Black,
FontSize = 8,
Margin = new Thickness(0, 1, 0, 0),
TextAlignment = TextAlignment.Center
};
_panel.Children.Add(textBlock);
// 设置鼠标事件
_panel.MouseDown += Panel_MouseDown;
_panel.MouseUp += Panel_MouseUp;
_panel.MouseLeave += Panel_MouseLeave;
// 右键菜单支持
_panel.ContextMenu = CreateContextMenu();
// 设置工具提示
_panel.ToolTip = "启动台";
LogHelper.WriteLogToFile("启动台按钮创建完成");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"创建启动台按钮时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
}
}
/// <summary>
/// 创建右键菜单
/// </summary>
private ContextMenu CreateContextMenu()
{
try
{
// 创建菜单
ContextMenu menu = new ContextMenu();
// 创建位置切换菜单项
MenuItem positionMenuItem = new MenuItem();
positionMenuItem.Header = _plugin.Config.ButtonPosition == LauncherButtonPosition.Left ?
"移至右侧" : "移至左侧";
positionMenuItem.Click += (s, e) =>
{
// 切换位置
_plugin.Config.ButtonPosition = _plugin.Config.ButtonPosition == LauncherButtonPosition.Left ?
LauncherButtonPosition.Right : LauncherButtonPosition.Left;
// 更新按钮位置
_plugin.UpdateButtonPosition();
// 保存配置
_plugin.SaveConfig();
LogHelper.WriteLogToFile($"通过右键菜单切换启动台按钮位置为: {_plugin.Config.ButtonPosition}");
};
menu.Items.Add(positionMenuItem);
// 添加设置菜单项
MenuItem settingsMenuItem = new MenuItem();
settingsMenuItem.Header = "打开设置";
settingsMenuItem.Click += (s, e) =>
{
// 打开插件设置窗口
var mainWindow = Application.Current.MainWindow;
if (mainWindow != null)
{
try
{
// 使用反射调用主窗口的ShowPluginSettings方法
var method = mainWindow.GetType().GetMethod("ShowPluginSettings");
if (method != null)
{
method.Invoke(mainWindow, null);
LogHelper.WriteLogToFile("已打开插件设置窗口");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"打开插件设置窗口失败: {ex.Message}", LogHelper.LogType.Error);
}
}
};
menu.Items.Add(settingsMenuItem);
return menu;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"创建右键菜单时出错: {ex.Message}", LogHelper.LogType.Error);
return null;
}
}
/// <summary>
/// 获取实际的UI元素
/// </summary>
[Obsolete("使用Element属性代替")]
public UIElement GetUIElement()
{
return _panel;
}
/// <summary>
/// 创建图标图像
/// </summary>
private Image CreateIconImage()
{
try
{
// 创建图像
Image image = new Image
{
Height = 17,
Margin = new Thickness(0, 3, 0, 0)
};
// 设置位图缩放模式
RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.HighQuality);
// 创建绘图图像
DrawingImage drawingImage = new DrawingImage();
DrawingGroup drawingGroup = new DrawingGroup();
drawingGroup.ClipGeometry = Geometry.Parse("M0,0 V24 H24 V0 H0 Z");
// 使用提供的应用网格图标
GeometryDrawing geometryDrawing = new GeometryDrawing
{
Brush = new SolidColorBrush(Color.FromRgb(0x1B, 0x1B, 0x1B)),
Geometry = Geometry.Parse("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")
};
drawingGroup.Children.Add(geometryDrawing);
// 设置图像源
drawingImage.Drawing = drawingGroup;
image.Source = drawingImage;
return image;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"创建图标图像时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
// 返回一个空图像
return new Image();
}
}
/// <summary>
/// 鼠标按下事件
/// </summary>
private void Panel_MouseDown(object sender, MouseButtonEventArgs e)
{
try
{
// 提供反馈
_panel.Background = new SolidColorBrush(Color.FromArgb(40, 0, 0, 0));
LogHelper.WriteLogToFile("启动台按钮鼠标按下");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动台按钮鼠标按下事件出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 鼠标抬起事件
/// </summary>
private void Panel_MouseUp(object sender, MouseButtonEventArgs e)
{
try
{
// 只有左键点击才显示启动台窗口
if (e.ChangedButton != MouseButton.Left)
{
return;
}
// 恢复背景
_panel.Background = Brushes.Transparent;
LogHelper.WriteLogToFile("启动台按钮鼠标抬起,准备显示启动台窗口");
// 获取按钮在屏幕上的位置
Point buttonPosition = _panel.PointToScreen(new Point(_panel.ActualWidth / 2, 0));
// 显示启动台窗口
_plugin.ShowLauncherWindow(buttonPosition);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动台按钮鼠标抬起事件出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
}
}
/// <summary>
/// 鼠标离开事件
/// </summary>
private void Panel_MouseLeave(object sender, MouseEventArgs e)
{
try
{
// 恢复背景
_panel.Background = Brushes.Transparent;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动台按钮鼠标离开事件出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
}
@@ -1,332 +0,0 @@
using Microsoft.Win32;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
/// <summary>
/// 启动台按钮位置
/// </summary>
public enum LauncherButtonPosition
{
/// <summary>
/// 左侧
/// </summary>
Left,
/// <summary>
/// 右侧
/// </summary>
Right
}
/// <summary>
/// 启动台配置
/// </summary>
public class LauncherConfig
{
/// <summary>
/// 启动台按钮位置
/// </summary>
public LauncherButtonPosition ButtonPosition { get; set; } = LauncherButtonPosition.Right;
/// <summary>
/// 启动台应用程序列表
/// </summary>
public List<LauncherItem> Items { get; set; } = new List<LauncherItem>();
}
/// <summary>
/// 启动台应用项
/// </summary>
public class LauncherItem
{
/// <summary>
/// 应用程序名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 应用程序路径
/// </summary>
public string Path { get; set; }
/// <summary>
/// 是否可见
/// </summary>
public bool IsVisible { get; set; } = true;
/// <summary>
/// 在启动台中的位置(0-39
/// </summary>
public int Position { get; set; } = -1;
/// <summary>
/// 是否已固定位置
/// </summary>
public bool IsPositionFixed { get; set; } = false;
/// <summary>
/// 图标缓存
/// </summary>
[JsonIgnore]
private ImageSource _iconCache;
/// <summary>
/// 获取应用程序图标
/// </summary>
[JsonIgnore]
public ImageSource Icon
{
get
{
if (_iconCache != null)
{
return _iconCache;
}
try
{
if (File.Exists(Path))
{
// 从文件中获取图标
Icon icon = System.Drawing.Icon.ExtractAssociatedIcon(Path);
if (icon != null)
{
_iconCache = Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
icon.Dispose();
return _iconCache;
}
}
else
{
// 从注册表中获取文件类型关联图标
string extension = System.IO.Path.GetExtension(Path);
if (!string.IsNullOrEmpty(extension))
{
string fileType = Registry.ClassesRoot.OpenSubKey(extension)?.GetValue(string.Empty) as string;
if (!string.IsNullOrEmpty(fileType))
{
string iconPath = Registry.ClassesRoot.OpenSubKey(fileType + "\\DefaultIcon")?.GetValue(string.Empty) as string;
if (!string.IsNullOrEmpty(iconPath))
{
string[] parts = iconPath.Split(',');
string iconFile = parts[0].Trim('"');
int iconIndex = parts.Length > 1 ? Convert.ToInt32(parts[1]) : 0;
if (File.Exists(iconFile))
{
Icon icon = IconExtractor.Extract(iconFile, iconIndex, true);
if (icon != null)
{
_iconCache = Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
icon.Dispose();
return _iconCache;
}
}
}
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取应用图标时出错: {ex.Message}", LogHelper.LogType.Error);
}
// 返回默认图标
return GetDefaultIcon();
}
}
/// <summary>
/// 获取默认图标
/// </summary>
private ImageSource GetDefaultIcon()
{
try
{
// 对于资源管理器,使用特定图标
if (Path.EndsWith("explorer.exe", StringComparison.OrdinalIgnoreCase))
{
try
{
// 直接从C:\Windows\explorer.exe获取图标
string explorerPath = @"C:\Windows\explorer.exe";
if (File.Exists(explorerPath))
{
Icon icon = System.Drawing.Icon.ExtractAssociatedIcon(explorerPath);
if (icon != null)
{
_iconCache = Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
icon.Dispose();
return _iconCache;
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取资源管理器图标时出错: {ex.Message}", LogHelper.LogType.Warning);
// 如果获取Windows图标失败,回退到默认图标
}
// 回退到备用图标
string explorerIconPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "Icons-Fluent", "ic_fluent_folder_24_regular.png");
if (File.Exists(explorerIconPath))
{
Uri uri = new Uri(explorerIconPath);
BitmapImage image = new BitmapImage(uri);
_iconCache = image;
return _iconCache;
}
}
// 返回一个简单的默认图标
string iconPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "Icons-png", "icc.png");
if (File.Exists(iconPath))
{
Uri uri = new Uri(iconPath);
BitmapImage image = new BitmapImage(uri);
_iconCache = image;
return _iconCache;
}
// 如果还是没有找到,尝试使用应用程序图标
string appIconPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "Icons-Fluent", "ic_fluent_apps_24_regular.png");
if (File.Exists(appIconPath))
{
Uri uri = new Uri(appIconPath);
BitmapImage image = new BitmapImage(uri);
_iconCache = image;
return _iconCache;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取默认图标时出错: {ex.Message}", LogHelper.LogType.Error);
}
return null;
}
/// <summary>
/// 启动应用程序
/// </summary>
public void Launch()
{
try
{
if (string.IsNullOrEmpty(Path))
{
LogHelper.WriteLogToFile("无法启动应用程序:路径为空", LogHelper.LogType.Error);
return;
}
// 检查文件是否存在
if (!File.Exists(Path) && !Path.Contains(":\\"))
{
// 可能是系统命令,如explorer.exe
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = Path,
UseShellExecute = true
};
Process.Start(psi);
}
else
{
// 使用Process.Start启动应用程序
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = Path,
UseShellExecute = true
};
Process.Start(psi);
}
LogHelper.WriteLogToFile($"已启动应用程序: {Path}");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动应用程序时出错: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"启动应用程序时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
/// <summary>
/// 图标提取工具类
/// </summary>
public static class IconExtractor
{
/// <summary>
/// 从文件中提取图标
/// </summary>
/// <param name="file">文件路径</param>
/// <param name="index">图标索引</param>
/// <param name="largeIcon">是否提取大图标</param>
/// <returns>提取的图标</returns>
public static Icon Extract(string file, int index, bool largeIcon)
{
try
{
IntPtr large;
IntPtr small;
ExtractIconEx(file, index, out large, out small, 1);
try
{
return Icon.FromHandle(largeIcon ? large : small);
}
catch
{
return null;
}
finally
{
if (large != IntPtr.Zero)
DestroyIcon(large);
if (small != IntPtr.Zero)
DestroyIcon(small);
}
}
catch
{
return null;
}
}
[DllImport("Shell32.dll", EntryPoint = "ExtractIconEx")]
private static extern int ExtractIconEx(
[MarshalAs(UnmanagedType.LPStr)] string lpszFile,
int nIconIndex,
out IntPtr phiconLarge,
out IntPtr phiconSmall,
int nIcons);
[DllImport("User32.dll")]
private static extern int DestroyIcon(IntPtr hIcon);
}
}
@@ -1,143 +0,0 @@
<UserControl x:Class="Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher.LauncherSettingsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher"
mc:Ignorable="d"
d:DesignHeight="500" d:DesignWidth="600">
<UserControl.Resources>
<!-- 自定义按钮样式 -->
<Style x:Key="DefaultButtonStyle" TargetType="Button">
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
TextElement.Foreground="{TemplateBinding Foreground}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Opacity" Value="0.8"/>
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="Black" Direction="270" ShadowDepth="2" Opacity="0.3" BlurRadius="4"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="border" Property="Opacity" Value="0.6"/>
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX="0.95" ScaleY="0.95"/>
</Setter.Value>
</Setter>
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="border" Property="Opacity" Value="0.4"/>
<Setter Property="Cursor" Value="Arrow"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 标题 -->
<TextBlock Grid.Row="0" Text="超级启动台设置" FontSize="16" FontWeight="Bold" Margin="0,0,0,15" Foreground="Black"/>
<!-- 基本设置 -->
<StackPanel Grid.Row="1" Margin="0,0,0,15">
<TextBlock Text="基本设置" FontSize="14" FontWeight="SemiBold" Margin="0,0,0,10" Foreground="Black"/>
<Grid Margin="10,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 按钮位置 -->
<TextBlock Grid.Row="0" Grid.Column="0" Text="按钮位置:" VerticalAlignment="Center" Foreground="Black"/>
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" Margin="0,5">
<RadioButton x:Name="RbtnLeft" Content="浮动栏左侧" Margin="0,0,20,0" Checked="RbtnPosition_Checked" Foreground="Black"/>
<RadioButton x:Name="RbtnRight" Content="浮动栏右侧" IsChecked="True" Checked="RbtnPosition_Checked" Foreground="Black"/>
</StackPanel>
</Grid>
</StackPanel>
<!-- 应用管理 -->
<Grid Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="应用管理" FontSize="14" FontWeight="SemiBold" Margin="0,0,0,10" Foreground="Black"/>
<Border Grid.Row="1" BorderThickness="1" BorderBrush="#CCCCCC" CornerRadius="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 应用列表 -->
<DataGrid Grid.Row="0" x:Name="DgApps" AutoGenerateColumns="False" Margin="5"
CanUserAddRows="False" CanUserDeleteRows="False"
HeadersVisibility="Column" SelectionMode="Single"
SelectionChanged="DgApps_SelectionChanged">
<DataGrid.Columns>
<DataGridCheckBoxColumn Header="显示" Binding="{Binding IsVisible}" Width="50"/>
<DataGridTextColumn Header="名称" Binding="{Binding Name}" Width="150"/>
<DataGridTextColumn Header="路径" Binding="{Binding Path}" Width="*"/>
<DataGridTextColumn Header="位置" Binding="{Binding Position}" Width="50"/>
</DataGrid.Columns>
</DataGrid>
<!-- 操作按钮 -->
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="5">
<Button x:Name="BtnAdd" Content="添加" Padding="10,5" Margin="0,5,5,5" Click="BtnAdd_Click"
Background="#FF007ACC" Foreground="White" BorderBrush="#FF005A9B" BorderThickness="1"
Style="{StaticResource DefaultButtonStyle}"/>
<Button x:Name="BtnEdit" Content="编辑" Padding="10,5" Margin="5" Click="BtnEdit_Click"
Background="#FF6C757D" Foreground="White" BorderBrush="#FF5A6268" BorderThickness="1"
Style="{StaticResource DefaultButtonStyle}"/>
<Button x:Name="BtnDelete" Content="删除" Padding="10,5" Margin="5" Click="BtnDelete_Click"
Background="#FFDC3545" Foreground="White" BorderBrush="#FFBD2130" BorderThickness="1"
Style="{StaticResource DefaultButtonStyle}"/>
</StackPanel>
</Grid>
</Border>
</Grid>
<!-- 底部按钮 -->
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,15,0,0">
<Button x:Name="BtnSave" Content="保存设置" Padding="15,5" Click="BtnSave_Click"
Background="#FF28A745" Foreground="White" BorderBrush="#FF1E7E34" BorderThickness="1"
Style="{StaticResource DefaultButtonStyle}"/>
</StackPanel>
</Grid>
</UserControl>
@@ -1,396 +0,0 @@
using Ink_Canvas.Windows;
using Microsoft.Win32;
using System;
using System.ComponentModel;
using System.IO;
using System.Windows;
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
/// <summary>
/// LauncherSettingsControl.xaml 的交互逻辑
/// </summary>
public partial class LauncherSettingsControl : UserControl
{
/// <summary>
/// 父插件
/// </summary>
private readonly SuperLauncherPlugin _plugin;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="plugin">父插件</param>
public LauncherSettingsControl(SuperLauncherPlugin plugin)
{
InitializeComponent();
_plugin = plugin;
// 设置按钮位置
RbtnLeft.IsChecked = _plugin.Config.ButtonPosition == LauncherButtonPosition.Left;
RbtnRight.IsChecked = _plugin.Config.ButtonPosition == LauncherButtonPosition.Right;
// 绑定应用列表
DgApps.ItemsSource = _plugin.LauncherItems;
// 初始化按钮状态
UpdateButtonStates();
}
/// <summary>
/// 更新按钮状态
/// </summary>
private void UpdateButtonStates()
{
bool hasSelection = DgApps.SelectedItem != null;
BtnEdit.IsEnabled = hasSelection;
BtnDelete.IsEnabled = hasSelection;
}
/// <summary>
/// 位置单选按钮选择事件
/// </summary>
private void RbtnPosition_Checked(object sender, RoutedEventArgs e)
{
if (!IsLoaded) return;
LauncherButtonPosition oldPosition = _plugin.Config.ButtonPosition;
if (sender == RbtnLeft)
{
_plugin.Config.ButtonPosition = LauncherButtonPosition.Left;
}
else if (sender == RbtnRight)
{
_plugin.Config.ButtonPosition = LauncherButtonPosition.Right;
}
// 如果位置发生变化,更新按钮位置
if (oldPosition != _plugin.Config.ButtonPosition)
{
try
{
// 更新按钮位置
_plugin.UpdateButtonPosition();
// 保存配置
_plugin.SaveConfig();
LogHelper.WriteLogToFile($"启动台按钮位置已更改为: {_plugin.Config.ButtonPosition}");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新启动台按钮位置时出错: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"更新启动台按钮位置时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
/// <summary>
/// 添加按钮点击事件
/// </summary>
private void BtnAdd_Click(object sender, RoutedEventArgs e)
{
try
{
// 创建新的启动项
LauncherItem item = new LauncherItem
{
Name = "",
Path = "",
IsVisible = true,
Position = -1 // 让插件管理器分配位置
};
// 直接显示编辑对话框
EditLauncherItem(item, true);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"添加启动项时出错: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"添加启动项时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 编辑应用按钮点击事件
/// </summary>
private void BtnEdit_Click(object sender, RoutedEventArgs e)
{
if (DgApps.SelectedItem is LauncherItem item)
{
EditLauncherItem(item, false);
}
}
/// <summary>
/// 删除应用按钮点击事件
/// </summary>
private void BtnDelete_Click(object sender, RoutedEventArgs e)
{
if (DgApps.SelectedItem is LauncherItem item)
{
// 确认删除
MessageBoxResult result = MessageBox.Show(
$"确定要删除 {item.Name} 吗?",
"删除确认",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
// 从集合中移除
_plugin.LauncherItems.Remove(item);
// 保存配置
_plugin.SaveConfig();
}
}
}
/// <summary>
/// 保存设置按钮点击事件
/// </summary>
private void BtnSave_Click(object sender, RoutedEventArgs e)
{
try
{
// 保存配置
_plugin.SaveConfig();
// 如果插件已启用,重新加载启动台按钮
if (_plugin.IsEnabled)
{
_plugin.Disable();
_plugin.Enable();
}
else
{
// 如果插件未启用,则启用它
_plugin.Enable();
// 通知PluginSettingsWindow刷新插件列表
var window = Window.GetWindow(this);
if (window is PluginSettingsWindow pluginSettingsWindow)
{
// 触发刷新
pluginSettingsWindow.RefreshPluginList();
}
}
MessageBox.Show("设置已保存并应用!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存设置时出错: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"保存设置时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 应用项选择变更事件
/// </summary>
private void DgApps_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
UpdateButtonStates();
}
/// <summary>
/// 编辑启动项
/// </summary>
/// <param name="item">启动项</param>
/// <param name="isNew">是否为新建</param>
private void EditLauncherItem(LauncherItem item, bool isNew)
{
// 创建简单的编辑窗口
Window editWindow = new Window
{
Title = isNew ? "添加" : "编辑应用",
Width = 400,
Height = 200,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
ResizeMode = ResizeMode.NoResize
};
// 创建编辑表单
Grid grid = new Grid
{
Margin = new Thickness(20)
};
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(80) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
// 名称输入框
TextBlock nameLabel = new TextBlock
{
Text = "名称:",
VerticalAlignment = VerticalAlignment.Center
};
TextBox nameTextBox = new TextBox
{
Text = item.Name,
Margin = new Thickness(0, 5, 0, 5)
};
Grid.SetRow(nameLabel, 0);
Grid.SetColumn(nameLabel, 0);
Grid.SetRow(nameTextBox, 0);
Grid.SetColumn(nameTextBox, 1);
grid.Children.Add(nameLabel);
grid.Children.Add(nameTextBox);
// 路径输入框
TextBlock pathLabel = new TextBlock
{
Text = "路径:",
VerticalAlignment = VerticalAlignment.Center
};
Grid pathGrid = new Grid();
pathGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
pathGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength() });
TextBox pathTextBox = new TextBox
{
Text = item.Path,
Margin = new Thickness(0, 5, 5, 5)
};
Button browseButton = new Button
{
Content = "浏览",
Padding = new Thickness(5, 0, 5, 0),
Margin = new Thickness(0, 5, 0, 5)
};
browseButton.Click += (s, e) =>
{
OpenFileDialog dialog = new OpenFileDialog
{
Title = "选择应用程序",
Filter = "应用程序 (*.exe)|*.exe|所有文件 (*.*)|*.*",
Multiselect = false,
FileName = pathTextBox.Text
};
if (dialog.ShowDialog() == true)
{
pathTextBox.Text = dialog.FileName;
// 如果选择的是.exe文件,自动获取文件名填入名称字段
if (Path.GetExtension(dialog.FileName).ToLower() == ".exe")
{
string fileName = Path.GetFileNameWithoutExtension(dialog.FileName);
// 只有在名称字段为空或者是新建项目时才自动填入
if (string.IsNullOrWhiteSpace(nameTextBox.Text) || isNew)
{
nameTextBox.Text = fileName;
}
}
}
};
Grid.SetColumn(pathTextBox, 0);
Grid.SetColumn(browseButton, 1);
pathGrid.Children.Add(pathTextBox);
pathGrid.Children.Add(browseButton);
Grid.SetRow(pathLabel, 1);
Grid.SetColumn(pathLabel, 0);
Grid.SetRow(pathGrid, 1);
Grid.SetColumn(pathGrid, 1);
grid.Children.Add(pathLabel);
grid.Children.Add(pathGrid);
// 确认和取消按钮
StackPanel buttonPanel = new StackPanel
{
Orientation = Orientation.Horizontal,
HorizontalAlignment = HorizontalAlignment.Right,
Margin = new Thickness(0, 10, 0, 0)
};
Button okButton = new Button
{
Content = "确定",
Padding = new Thickness(15, 5, 15, 5),
Margin = new Thickness(0, 0, 10, 0),
IsDefault = true
};
Button cancelButton = new Button
{
Content = "取消",
Padding = new Thickness(15, 5, 15, 5),
IsCancel = true
};
okButton.Click += (s, e) =>
{
// 验证输入
if (string.IsNullOrWhiteSpace(nameTextBox.Text))
{
MessageBox.Show("请输入应用名称!", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
if (string.IsNullOrWhiteSpace(pathTextBox.Text))
{
MessageBox.Show("请输入应用路径!", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
// 更新项目
item.Name = nameTextBox.Text;
item.Path = pathTextBox.Text;
// 如果是新建,添加到集合
if (isNew)
{
_plugin.AddLauncherItem(item);
}
else
{
// 触发属性变更通知,刷新DataGrid
if (DgApps.ItemsSource is ICollectionView view)
{
view.Refresh();
}
// 保存配置
_plugin.SaveConfig();
}
editWindow.DialogResult = true;
editWindow.Close();
};
cancelButton.Click += (s, e) =>
{
editWindow.DialogResult = false;
editWindow.Close();
};
buttonPanel.Children.Add(okButton);
buttonPanel.Children.Add(cancelButton);
Grid.SetRow(buttonPanel, 2);
Grid.SetColumnSpan(buttonPanel, 2);
grid.Children.Add(buttonPanel);
// 设置窗口内容
editWindow.Content = grid;
// 显示窗口
editWindow.ShowDialog();
}
}
}
@@ -1,91 +0,0 @@
<Window x:Class="Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher.LauncherWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher"
mc:Ignorable="d"
Title="启动台"
Width="400"
Height="300"
WindowStyle="None"
AllowsTransparency="True"
Background="#80000000"
ResizeMode="NoResize"
Topmost="True"
Deactivated="Window_Deactivated"
ShowInTaskbar="False">
<Window.Resources>
<!-- 应用项样式 -->
<Style x:Key="LauncherItemStyle" TargetType="Button">
<Setter Property="Width" Value="80"/>
<Setter Property="Height" Value="80"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="Background" Value="#40FFFFFF"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="border" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="8">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Image Grid.Row="0" Source="{Binding Icon}" Width="32" Height="32" Margin="0,10,0,5"/>
<TextBlock Grid.Row="1" Text="{Binding Name}" TextWrapping="Wrap" TextAlignment="Center"
Margin="2,0,2,8" FontSize="11" Foreground="White"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#80FFFFFF"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#C0FFFFFF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Border CornerRadius="15" Background="#80000000" BorderThickness="1" BorderBrush="#40FFFFFF">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- 标题栏 -->
<Grid Grid.Row="0" Height="40">
<TextBlock Text="启动台" Foreground="White" FontSize="18" FontWeight="Bold"
VerticalAlignment="Center" Margin="15,0,0,0"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,10,0">
<Button x:Name="BtnFixMode" Click="BtnFixMode_Click" Width="30" Height="30"
Margin="5,0" Background="Transparent" BorderThickness="0"
ToolTip="切换固定模式">
<Path x:Name="FixModeIcon" Data="M7,2V13H10V22L17,10H13L17,2H7Z" Fill="White" Stretch="Uniform" Width="16" Height="16"/>
</Button>
<Button x:Name="BtnClose" Click="BtnClose_Click" Width="30" Height="30"
Background="Transparent" BorderThickness="0"
ToolTip="关闭">
<Path Data="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
Fill="White" Stretch="Uniform" Width="16" Height="16"/>
</Button>
</StackPanel>
</Grid>
<!-- 应用网格 -->
<ScrollViewer Grid.Row="1" Margin="10" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<WrapPanel x:Name="AppPanel" Orientation="Horizontal" HorizontalAlignment="Center"/>
</ScrollViewer>
</Grid>
</Border>
</Window>
@@ -1,466 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
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.Threading;
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
/// <summary>
/// LauncherWindow.xaml 的交互逻辑
/// </summary>
public partial class LauncherWindow : Window
{
/// <summary>
/// 父插件
/// </summary>
private readonly SuperLauncherPlugin _plugin;
/// <summary>
/// 是否处于固定模式
/// </summary>
private bool _isFixMode;
/// <summary>
/// 应用项按钮列表
/// </summary>
private readonly Dictionary<Button, LauncherItem> _appButtons = new Dictionary<Button, LauncherItem>();
/// <summary>
/// 拖拽中的按钮
/// </summary>
private Button _draggingButton;
/// <summary>
/// 拖拽开始位置
/// </summary>
private Point _dragStartPoint;
/// <summary>
/// 构造函数
/// </summary>
public LauncherWindow(SuperLauncherPlugin plugin)
{
InitializeComponent();
_plugin = plugin;
// 加载应用项
LoadLauncherItems();
// 添加鼠标按下事件(用于拖动窗口)
MouseDown += (s, e) =>
{
if (e.ChangedButton == MouseButton.Left && e.ButtonState == MouseButtonState.Pressed)
{
DragMove();
}
};
// 根据应用数量调整窗口大小
AdjustWindowSize();
}
/// <summary>
/// 加载启动台应用项
/// </summary>
private void LoadLauncherItems()
{
// 清空现有应用项
AppPanel.Children.Clear();
_appButtons.Clear();
// 获取显示的应用项
var visibleItems = _plugin.LauncherItems
.Where(item => item.IsVisible)
.OrderBy(item => item.Position)
.ToList();
foreach (var item in visibleItems)
{
// 创建应用按钮
Button appButton = new Button
{
Style = (Style)FindResource("LauncherItemStyle"),
DataContext = item,
Tag = item.Position
};
// 添加点击事件
appButton.Click += AppButton_Click;
// 在固定模式下,添加拖拽事件
appButton.PreviewMouseDown += AppButton_PreviewMouseDown;
appButton.PreviewMouseMove += AppButton_PreviewMouseMove;
appButton.PreviewMouseUp += AppButton_PreviewMouseUp;
// 记录按钮和项目的对应关系
_appButtons.Add(appButton, item);
// 添加到面板
AppPanel.Children.Add(appButton);
}
}
/// <summary>
/// 根据应用数量调整窗口大小
/// </summary>
private void AdjustWindowSize()
{
try
{
// 每行最多显示4个应用
const int appsPerRow = 4;
// 计算行数
int visibleCount = _appButtons.Count;
int rowCount = (int)Math.Ceiling(visibleCount / (double)appsPerRow);
// 设置窗口宽度(每个应用90像素宽 = 80 + 5*2
Width = Math.Min(appsPerRow * 90 + 40, 400); // 最大宽度400
// 设置窗口高度(每个应用90像素高 = 80 + 5*2
Height = Math.Min(rowCount * 90 + 60, 600); // 最大高度600,标题栏40 + 边距20
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"调整启动台窗口大小时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 应用按钮点击事件
/// </summary>
private void AppButton_Click(object sender, RoutedEventArgs e)
{
try
{
if (_isFixMode) return; // 在固定模式下,不响应点击事件
if (sender is Button button && _appButtons.TryGetValue(button, out LauncherItem item))
{
// 获取应用路径和名称,用于后续启动
string appPath = item.Path;
string appName = item.Name;
LogHelper.WriteLogToFile($"点击启动应用: {appName}, 路径: {appPath}");
// 首先标记窗口正在关闭
IsClosing = true;
// 创建一个应用启动任务
var launchTask = new Task(() =>
{
try
{
// 等待一段时间,确保窗口关闭流程已经开始
Thread.Sleep(200);
// 使用UI线程启动应用
Application.Current.Dispatcher.Invoke(() =>
{
try
{
// 检查应用路径是否存在
if (File.Exists(appPath) || !appPath.Contains(":\\"))
{
// 创建进程启动信息
var psi = new ProcessStartInfo
{
FileName = appPath,
UseShellExecute = true,
};
// 启动应用程序
var process = Process.Start(psi);
LogHelper.WriteLogToFile($"应用程序 {appName} 已启动");
}
else
{
LogHelper.WriteLogToFile($"应用路径不存在: {appPath}", LogHelper.LogType.Error);
MessageBox.Show($"找不到应用程序: {appPath}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动应用程序失败: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"启动应用程序失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"应用启动任务出错: {ex.Message}", LogHelper.LogType.Error);
}
});
// 关闭窗口
try
{
Dispatcher.BeginInvoke(new Action(() =>
{
try { Close(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
// 启动应用程序任务
launchTask.Start();
}), DispatcherPriority.Background);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"关闭窗口或启动任务时出错: {ex.Message}", LogHelper.LogType.Error);
// 如果无法通过UI关闭窗口,直接启动任务
launchTask.Start();
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"应用按钮点击事件出错: {ex.Message}", LogHelper.LogType.Error);
try { IsClosing = true; Close(); } catch (Exception innerEx) { System.Diagnostics.Debug.WriteLine(innerEx); }
}
}
#region
/// <summary>
/// 应用按钮鼠标按下事件
/// </summary>
private void AppButton_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (!_isFixMode) return;
if (e.ChangedButton == MouseButton.Left && sender is Button button)
{
_draggingButton = button;
_dragStartPoint = e.GetPosition(AppPanel);
button.CaptureMouse();
button.Opacity = 0.7;
// 阻止事件冒泡,以避免触发按钮点击
e.Handled = true;
}
}
/// <summary>
/// 应用按钮鼠标移动事件
/// </summary>
private void AppButton_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (!_isFixMode || _draggingButton == null) return;
if (e.LeftButton == MouseButtonState.Pressed)
{
Point currentPosition = e.GetPosition(AppPanel);
// 移动按钮
System.Windows.Controls.Canvas.SetLeft(_draggingButton, currentPosition.X - _draggingButton.ActualWidth / 2);
System.Windows.Controls.Canvas.SetTop(_draggingButton, currentPosition.Y - _draggingButton.ActualHeight / 2);
// 将按钮移到最上层
Panel.SetZIndex(_draggingButton, 100);
// 阻止事件冒泡
e.Handled = true;
}
}
/// <summary>
/// 应用按钮鼠标释放事件
/// </summary>
private void AppButton_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (!_isFixMode || _draggingButton == null) return;
// 释放鼠标捕获
_draggingButton.ReleaseMouseCapture();
// 计算新位置
Point releasePoint = e.GetPosition(AppPanel);
int newPosition = CalculateGridPosition(releasePoint);
// 获取当前项目
LauncherItem currentItem = _appButtons[_draggingButton];
// 重新排序
ReorderItems(currentItem, newPosition);
// 重新加载应用项
LoadLauncherItems();
// 保存配置
_plugin.SaveConfig();
// 清除拖拽状态
_draggingButton.Opacity = 1;
Panel.SetZIndex(_draggingButton, 0);
_draggingButton = null;
// 阻止事件冒泡
e.Handled = true;
}
/// <summary>
/// 计算网格位置
/// </summary>
private int CalculateGridPosition(Point point)
{
// 计算行和列
int columnCount = 4; // 每行最多4个应用
int columnWidth = 90; // 应用宽度(包括边距)
int rowHeight = 90; // 应用高度(包括边距)
int column = (int)(point.X / columnWidth);
int row = (int)(point.Y / rowHeight);
// 确保在有效范围内
column = Math.Max(0, Math.Min(column, columnCount - 1));
row = Math.Max(0, row);
// 计算位置索引
return row * columnCount + column;
}
/// <summary>
/// 重新排序应用项
/// </summary>
private void ReorderItems(LauncherItem item, int newPosition)
{
try
{
// 设置项目为固定位置
item.IsPositionFixed = true;
// 如果位置相同,无需调整
if (item.Position == newPosition)
{
return;
}
// 获取所有可见项目
var visibleItems = _plugin.LauncherItems
.Where(i => i.IsVisible)
.OrderBy(i => i.Position)
.ToList();
// 移除当前项目
visibleItems.Remove(item);
// 查找插入位置
int insertIndex = 0;
for (int i = 0; i < visibleItems.Count; i++)
{
if (visibleItems[i].Position >= newPosition)
{
insertIndex = i;
break;
}
insertIndex = i + 1;
}
// 插入项目
visibleItems.Insert(insertIndex, item);
// 重新分配位置
for (int i = 0; i < visibleItems.Count; i++)
{
visibleItems[i].Position = i;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"重新排序应用项时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
#region
/// <summary>
/// 窗口失去焦点事件
/// </summary>
private void Window_Deactivated(object sender, EventArgs e)
{
try
{
// 只有在非固定模式、窗口已加载、未处于关闭状态且IsLoaded=true时关闭窗口
if (!_isFixMode && IsLoaded && !IsClosing)
{
// 标记为正在关闭
IsClosing = true;
// 使用Dispatcher.BeginInvoke而不是直接调用Close,避免冲突
Dispatcher.BeginInvoke(new Action(() =>
{
try
{
// 再次检查窗口状态
if (IsLoaded && !IsClosing)
{
Close();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"延迟关闭窗口时出错: {ex.Message}", LogHelper.LogType.Error);
}
}), DispatcherPriority.Background);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"窗口失去焦点关闭时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 窗口是否正在关闭
/// </summary>
private bool IsClosing { get; set; }
/// <summary>
/// 重写OnClosing方法,标记窗口正在关闭
/// </summary>
protected override void OnClosing(CancelEventArgs e)
{
IsClosing = true;
base.OnClosing(e);
}
/// <summary>
/// 关闭按钮点击事件
/// </summary>
private void BtnClose_Click(object sender, RoutedEventArgs e)
{
Close();
}
/// <summary>
/// 固定模式按钮点击事件
/// </summary>
private void BtnFixMode_Click(object sender, RoutedEventArgs e)
{
// 切换固定模式
_isFixMode = !_isFixMode;
// 更新固定模式按钮图标颜色
FixModeIcon.Fill = _isFixMode ? Brushes.Yellow : Brushes.White;
// 显示提示
if (_isFixMode)
{
MessageBox.Show("已进入固定模式,您可以拖动应用图标调整位置。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
#endregion
}
}
@@ -1,589 +0,0 @@
using Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Ink_Canvas.Helpers.Plugins.BuiltIn
{
/// <summary>
/// 超级启动台插件
/// </summary>
public class SuperLauncherPlugin : PluginBase
{
#region
public override string Name => "超级启动台";
public override string Description => "在浮动栏添加一个启动台按钮,可快速启动常用应用程序。";
public override Version Version => new Version(1, 0, 1);
public override string Author => "ICC CE 团队";
public override bool IsBuiltIn => true;
#endregion
#region
/// <summary>
/// 启动台配置
/// </summary>
public LauncherConfig Config { get; private set; }
/// <summary>
/// 启动台应用程序列表
/// </summary>
public ObservableCollection<LauncherItem> LauncherItems { get; private set; }
/// <summary>
/// 启动台按钮
/// </summary>
private LauncherButton _launcherButton;
/// <summary>
/// 启动台窗口
/// </summary>
private LauncherWindow _launcherWindow;
/// <summary>
/// 配置文件路径
/// </summary>
private readonly string _configPath = Path.Combine(App.RootPath, "PluginConfigs", "SuperLauncher.json");
/// <summary>
/// 标记是否已添加到浮动栏
/// </summary>
private bool _isAddedToFloatingBar;
#endregion
#region
public override void Initialize()
{
try
{
base.Initialize();
// 创建配置目录
string configDir = Path.Combine(App.RootPath, "PluginConfigs");
if (!Directory.Exists(configDir))
{
Directory.CreateDirectory(configDir);
}
// 加载配置
LoadConfig();
LogHelper.WriteLogToFile("超级启动台插件已初始化");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"初始化超级启动台插件时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
}
}
public override void Enable()
{
try
{
if (IsEnabled) return; // 防止重复启用
// 创建启动台按钮
if (_launcherButton == null)
{
_launcherButton = new LauncherButton(this);
LogHelper.WriteLogToFile("超级启动台按钮已创建");
}
// 添加启动台按钮到浮动栏
AddLauncherButtonToFloatingBar();
// 设置启用状态
base.Enable();
// 保存插件配置
SavePluginSettings();
LogHelper.WriteLogToFile("超级启动台插件已启用");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启用超级启动台插件时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
}
}
public override void Disable()
{
try
{
if (!IsEnabled) return; // 防止重复禁用
// 从浮动栏移除启动台按钮
RemoveLauncherButtonFromFloatingBar();
// 如果启动台窗口打开,则关闭
if (_launcherWindow != null && _launcherWindow.IsVisible)
{
_launcherWindow.Close();
_launcherWindow = null;
}
// 设置禁用状态
base.Disable();
// 保存插件配置
SavePluginSettings();
LogHelper.WriteLogToFile("超级启动台插件已禁用");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"禁用超级启动台插件时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
}
}
public override UserControl GetSettingsView()
{
return new LauncherSettingsControl(this);
}
public override void Cleanup()
{
// 保存配置
SaveConfig();
// 从浮动栏移除启动台按钮
RemoveLauncherButtonFromFloatingBar();
// 如果启动台窗口打开,则关闭
if (_launcherWindow != null && _launcherWindow.IsVisible)
{
_launcherWindow.Close();
_launcherWindow = null;
}
base.Cleanup();
}
/// <summary>
/// 保存插件设置
/// </summary>
public override void SavePluginSettings()
{
try
{
// 确保配置已加载
if (Config == null)
{
LoadConfig();
}
// 更新其他设置,但不更改插件启用状态
// 保存配置
SaveConfig();
LogHelper.WriteLogToFile("超级启动台插件设置已保存");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存超级启动台插件设置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
#region
/// <summary>
/// 加载配置
/// </summary>
private void LoadConfig()
{
try
{
if (File.Exists(_configPath))
{
string json = File.ReadAllText(_configPath);
Config = JsonConvert.DeserializeObject<LauncherConfig>(json) ?? CreateDefaultConfig();
LauncherItems = new ObservableCollection<LauncherItem>(Config.Items ?? new List<LauncherItem>());
// 注意:不再根据配置更改插件启用状态
// 插件状态由PluginManager统一管理
}
else
{
Config = CreateDefaultConfig();
LauncherItems = new ObservableCollection<LauncherItem>(Config.Items);
SaveConfig();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载超级启动台配置时出错: {ex.Message}", LogHelper.LogType.Error);
Config = CreateDefaultConfig();
LauncherItems = new ObservableCollection<LauncherItem>(Config.Items);
}
}
/// <summary>
/// 保存配置
/// </summary>
public void SaveConfig()
{
try
{
// 同步LauncherItems到Config
Config.Items = new List<LauncherItem>(LauncherItems);
// 序列化并保存配置
string json = JsonConvert.SerializeObject(Config, Formatting.Indented);
File.WriteAllText(_configPath, json);
LogHelper.WriteLogToFile("超级启动台配置已保存");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存超级启动台配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 创建默认配置
/// </summary>
private LauncherConfig CreateDefaultConfig()
{
var config = new LauncherConfig
{
ButtonPosition = LauncherButtonPosition.Right,
// 不再使用IsEnabled,插件状态由PluginManager管理
Items = new List<LauncherItem>
{
new LauncherItem
{
Name = "资源管理器",
Path = @"C:\Windows\explorer.exe",
IsVisible = true,
Position = 0
}
}
};
return config;
}
#endregion
#region
/// <summary>
/// 将启动台按钮添加到浮动栏
/// </summary>
private void AddLauncherButtonToFloatingBar()
{
try
{
// 如果已经添加,先移除
if (_isAddedToFloatingBar)
{
RemoveLauncherButtonFromFloatingBar();
_isAddedToFloatingBar = false;
}
// 获取主窗口实例
var mainWindow = Application.Current.MainWindow;
if (mainWindow == null)
{
LogHelper.WriteLogToFile("未找到主窗口实例,无法添加启动台按钮", LogHelper.LogType.Error);
return;
}
// 创建启动台按钮
_launcherButton = new LauncherButton(this);
var buttonElement = _launcherButton.Element;
// 查找浮动栏
var floatingBar = mainWindow.FindName("StackPanelFloatingBar") as Panel;
if (floatingBar == null)
{
// 如果直接查找失败,则尝试遍历可视树查找
Panel floatingBarPanelFromTree = null;
FindStackPanelFloatingBar(mainWindow, ref floatingBarPanelFromTree);
floatingBar = floatingBarPanelFromTree;
}
if (floatingBar == null)
{
LogHelper.WriteLogToFile("未找到浮动栏,无法添加启动台按钮", LogHelper.LogType.Error);
return;
}
// 添加启动台按钮到浮动栏
if (Config.ButtonPosition == LauncherButtonPosition.Left)
{
floatingBar.Children.Insert(0, buttonElement);
LogHelper.WriteLogToFile("启动台按钮已添加到浮动栏左侧");
}
else
{
floatingBar.Children.Add(buttonElement);
LogHelper.WriteLogToFile("启动台按钮已添加到浮动栏右侧");
}
_isAddedToFloatingBar = true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"添加启动台按钮到浮动栏时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
}
}
/// <summary>
/// 递归查找StackPanelFloatingBar
/// </summary>
private void FindStackPanelFloatingBar(DependencyObject parent, ref Panel result)
{
if (parent == null || result != null) return;
try
{
// 检查当前对象是否为我们要找的面板
if (parent is Panel panel && panel.Name == "StackPanelFloatingBar")
{
result = panel;
return;
}
// 获取子元素数量
int childCount = VisualTreeHelper.GetChildrenCount(parent);
// 遍历所有子元素
for (int i = 0; i < childCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
FindStackPanelFloatingBar(child, ref result);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"查找StackPanelFloatingBar时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 从浮动栏移除启动台按钮
/// </summary>
private void RemoveLauncherButtonFromFloatingBar()
{
try
{
if (!_isAddedToFloatingBar || _launcherButton == null)
{
return;
}
// 获取主窗口实例
var mainWindow = Application.Current.MainWindow;
if (mainWindow == null)
{
LogHelper.WriteLogToFile("未找到主窗口实例,无法移除启动台按钮", LogHelper.LogType.Error);
return;
}
// 获取按钮元素
var buttonElement = _launcherButton.Element;
// 查找浮动栏
var floatingBar = mainWindow.FindName("StackPanelFloatingBar") as Panel;
if (floatingBar == null)
{
// 如果直接查找失败,则尝试遍历可视树查找
Panel floatingBarPanelFromTree = null;
FindStackPanelFloatingBar(mainWindow, ref floatingBarPanelFromTree);
floatingBar = floatingBarPanelFromTree;
}
if (floatingBar == null)
{
LogHelper.WriteLogToFile("未找到浮动栏,无法移除启动台按钮", LogHelper.LogType.Error);
return;
}
// 从浮动栏移除启动台按钮
if (floatingBar.Children.Contains(buttonElement))
{
floatingBar.Children.Remove(buttonElement);
LogHelper.WriteLogToFile("启动台按钮已从浮动栏移除");
}
_isAddedToFloatingBar = false;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"移除启动台按钮时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
}
}
/// <summary>
/// 更新启动台按钮位置
/// </summary>
public void UpdateButtonPosition()
{
try
{
// 如果按钮已添加到浮动栏,重新添加以更新位置
if (_isAddedToFloatingBar)
{
RemoveLauncherButtonFromFloatingBar();
AddLauncherButtonToFloatingBar();
LogHelper.WriteLogToFile($"启动台按钮位置已更新为: {Config.ButtonPosition}");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新启动台按钮位置时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
}
}
#endregion
#region
/// <summary>
/// 显示启动台窗口
/// </summary>
/// <param name="buttonPosition">按钮在屏幕上的位置</param>
public void ShowLauncherWindow(Point buttonPosition)
{
try
{
// 如果窗口已存在,关闭它
if (_launcherWindow != null && _launcherWindow.IsVisible)
{
_launcherWindow.Close();
_launcherWindow = null;
return;
}
// 创建新的启动台窗口
_launcherWindow = new LauncherWindow(this);
// 计算窗口位置,使其位于按钮上方
PositionLauncherWindow(_launcherWindow, buttonPosition);
// 显示窗口
_launcherWindow.Show();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"显示启动台窗口时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 设置启动台窗口位置
/// </summary>
/// <param name="window">启动台窗口</param>
/// <param name="buttonPosition">按钮在屏幕上的位置</param>
private void PositionLauncherWindow(LauncherWindow window, Point buttonPosition)
{
// 确保窗口已加载
if (window.ActualWidth == 0 || window.ActualHeight == 0)
{
window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
// 设置窗口加载完成后的位置
window.Loaded += (s, e) =>
{
// 窗口位于按钮上方居中
double left = buttonPosition.X - (window.ActualWidth / 2);
double top = buttonPosition.Y - window.ActualHeight - 10; // 在按钮上方留出一些间距
// 确保窗口在屏幕内
left = Math.Max(0, Math.Min(left, SystemParameters.WorkArea.Width - window.ActualWidth));
top = Math.Max(0, Math.Min(top, SystemParameters.WorkArea.Height - window.ActualHeight));
window.Left = left;
window.Top = top;
};
}
else
{
// 窗口位于按钮上方居中
double left = buttonPosition.X - (window.ActualWidth / 2);
double top = buttonPosition.Y - window.ActualHeight - 10; // 在按钮上方留出一些间距
// 确保窗口在屏幕内
left = Math.Max(0, Math.Min(left, SystemParameters.WorkArea.Width - window.ActualWidth));
top = Math.Max(0, Math.Min(top, SystemParameters.WorkArea.Height - window.ActualHeight));
window.Left = left;
window.Top = top;
}
}
/// <summary>
/// 添加应用到启动台
/// </summary>
/// <param name="item">启动台项</param>
public void AddLauncherItem(LauncherItem item)
{
// 如果项目数量已达上限,则不添加
if (LauncherItems.Count >= 40)
{
MessageBox.Show("启动台项目数量已达上限(40个)!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// 寻找合适的位置
if (item.Position < 0)
{
item.Position = FindNextAvailablePosition();
}
// 添加项目并保存配置
LauncherItems.Add(item);
SaveConfig();
}
/// <summary>
/// 查找下一个可用位置
/// </summary>
private int FindNextAvailablePosition()
{
// 获取已使用的位置列表
var usedPositions = new HashSet<int>();
foreach (var item in LauncherItems)
{
usedPositions.Add(item.Position);
}
// 查找第一个可用位置
for (int i = 0; i < 40; i++)
{
if (!usedPositions.Contains(i))
{
return i;
}
}
// 如果所有位置都已使用,则返回0
return 0;
}
#endregion
}
}
@@ -1,92 +0,0 @@
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 增强的插件基类,提供对插件服务的访问和基本实现
/// </summary>
public abstract class EnhancedPluginBase : PluginBase, IEnhancedPlugin
{
/// <summary>
/// 插件服务实例
/// </summary>
public IPluginService PluginService { get; private set; }
/// <summary>
/// 构造函数
/// </summary>
protected EnhancedPluginBase()
{
PluginService = PluginServiceManager.Instance;
}
/// <summary>
/// 插件启动时调用,在Initialize之后
/// </summary>
public virtual void OnStartup()
{
LogHelper.WriteLogToFile($"插件 {Name} 已启动");
}
/// <summary>
/// 插件关闭时调用,在Cleanup之前
/// </summary>
public virtual void OnShutdown()
{
LogHelper.WriteLogToFile($"插件 {Name} 正在关闭");
}
/// <summary>
/// 获取插件的菜单项
/// </summary>
/// <returns>菜单项集合</returns>
public virtual MenuItem[] GetMenuItems()
{
return new MenuItem[0];
}
/// <summary>
/// 获取插件的工具栏按钮
/// </summary>
/// <returns>工具栏按钮集合</returns>
public virtual Button[] GetToolbarButtons()
{
return new Button[0];
}
/// <summary>
/// 获取插件的状态栏信息
/// </summary>
/// <returns>状态栏信息</returns>
public virtual string GetStatusBarInfo()
{
return $"{Name} v{Version} - {(IsEnabled ? "" : "")}";
}
/// <summary>
/// 插件配置变更时调用
/// </summary>
public virtual void OnConfigurationChanged()
{
LogHelper.WriteLogToFile($"插件 {Name} 配置已变更");
}
/// <summary>
/// 重写初始化方法,调用OnStartup
/// </summary>
public override void Initialize()
{
base.Initialize();
OnStartup();
}
/// <summary>
/// 重写清理方法,调用OnShutdown
/// </summary>
public override void Cleanup()
{
OnShutdown();
base.Cleanup();
}
}
}
@@ -1,241 +0,0 @@
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 增强的插件基类 V2,提供对三个专门服务接口的访问
/// 插件开发者可以根据需要选择性地使用这些服务
/// </summary>
public abstract class EnhancedPluginBaseV2 : PluginBase, IEnhancedPlugin
{
/// <summary>
/// 获取服务实例
/// </summary>
public IGetService GetService { get; private set; }
/// <summary>
/// 窗口服务实例
/// </summary>
public IWindowService WindowService { get; private set; }
/// <summary>
/// 操作服务实例
/// </summary>
public IActionService ActionService { get; private set; }
/// <summary>
/// 插件服务实例(兼容性)
/// </summary>
public IPluginService PluginService { get; private set; }
/// <summary>
/// 构造函数
/// </summary>
protected EnhancedPluginBaseV2()
{
// 初始化所有服务实例
PluginService = PluginServiceManager.Instance;
GetService = PluginServiceManager.Instance;
WindowService = PluginServiceManager.Instance;
ActionService = PluginServiceManager.Instance;
}
/// <summary>
/// 插件启动时调用,在Initialize之后
/// </summary>
public virtual void OnStartup()
{
LogHelper.WriteLogToFile($"插件 {Name} 已启动");
}
/// <summary>
/// 插件关闭时调用,在Cleanup之前
/// </summary>
public virtual void OnShutdown()
{
LogHelper.WriteLogToFile($"插件 {Name} 正在关闭");
}
/// <summary>
/// 获取插件的菜单项
/// </summary>
/// <returns>菜单项集合</returns>
public virtual MenuItem[] GetMenuItems()
{
return new MenuItem[0];
}
/// <summary>
/// 获取插件的工具栏按钮
/// </summary>
/// <returns>工具栏按钮集合</returns>
public virtual Button[] GetToolbarButtons()
{
return new Button[0];
}
/// <summary>
/// 获取插件的状态栏信息
/// </summary>
/// <returns>状态栏信息</returns>
public virtual string GetStatusBarInfo()
{
return $"{Name} v{Version} - {(IsEnabled ? "" : "")}";
}
/// <summary>
/// 插件配置变更时调用
/// </summary>
public virtual void OnConfigurationChanged()
{
LogHelper.WriteLogToFile($"插件 {Name} 配置已变更");
}
#region 便
/// <summary>
/// 显示通知消息
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="type">消息类型</param>
protected void ShowNotification(string message, NotificationType type = NotificationType.Info)
{
WindowService.ShowNotification(message, type);
}
/// <summary>
/// 显示确认对话框
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="title">标题</param>
/// <returns>用户选择结果</returns>
protected bool ShowConfirmDialog(string message, string title = "确认")
{
return WindowService.ShowConfirmDialog(message, title);
}
/// <summary>
/// 显示输入对话框
/// </summary>
/// <param name="message">提示消息</param>
/// <param name="title">标题</param>
/// <param name="defaultValue">默认值</param>
/// <returns>用户输入内容</returns>
protected string ShowInputDialog(string message, string title = "输入", string defaultValue = "")
{
return WindowService.ShowInputDialog(message, title, defaultValue);
}
/// <summary>
/// 获取系统设置
/// </summary>
/// <typeparam name="T">设置类型</typeparam>
/// <param name="key">设置键</param>
/// <param name="defaultValue">默认值</param>
/// <returns>设置值</returns>
protected T GetSetting<T>(string key, T defaultValue = default(T))
{
return GetService.GetSetting(key, defaultValue);
}
/// <summary>
/// 设置系统设置
/// </summary>
/// <typeparam name="T">设置类型</typeparam>
/// <param name="key">设置键</param>
/// <param name="value">设置值</param>
protected void SetSetting<T>(string key, T value)
{
ActionService.SetSetting(key, value);
}
/// <summary>
/// 保存设置
/// </summary>
protected void SaveSettings()
{
ActionService.SaveSettings();
}
/// <summary>
/// 清除当前画布
/// </summary>
protected void ClearCanvas()
{
ActionService.ClearCanvas();
}
/// <summary>
/// 撤销操作
/// </summary>
protected void Undo()
{
ActionService.Undo();
}
/// <summary>
/// 重做操作
/// </summary>
protected void Redo()
{
ActionService.Redo();
}
/// <summary>
/// 检查是否可以撤销
/// </summary>
protected bool CanUndo => GetService.CanUndo;
/// <summary>
/// 检查是否可以重做
/// </summary>
protected bool CanRedo => GetService.CanRedo;
/// <summary>
/// 获取当前绘制模式
/// </summary>
protected int CurrentDrawingMode => GetService.CurrentDrawingMode;
/// <summary>
/// 设置绘制模式
/// </summary>
/// <param name="mode">绘制模式</param>
protected void SetDrawingMode(int mode)
{
ActionService.SetDrawingMode(mode);
}
/// <summary>
/// 注册事件处理器
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="handler">事件处理器</param>
protected void RegisterEventHandler(string eventName, System.EventHandler handler)
{
ActionService.RegisterEventHandler(eventName, handler);
}
/// <summary>
/// 注销事件处理器
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="handler">事件处理器</param>
protected void UnregisterEventHandler(string eventName, System.EventHandler handler)
{
ActionService.UnregisterEventHandler(eventName, handler);
}
/// <summary>
/// 触发事件
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="sender">事件发送者</param>
/// <param name="args">事件参数</param>
protected void TriggerEvent(string eventName, object sender, System.EventArgs args)
{
ActionService.TriggerEvent(eventName, sender, args);
}
#endregion
}
}
@@ -1,296 +0,0 @@
using System;
using System.Windows.Media;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 操作服务接口,统一所有执行操作相关的方法
/// </summary>
public interface IActionService
{
#region
/// <summary>
/// 清除当前画布
/// </summary>
void ClearCanvas();
/// <summary>
/// 清除所有画布
/// </summary>
void ClearAllCanvases();
/// <summary>
/// 添加新页面
/// </summary>
void AddNewPage();
/// <summary>
/// 删除当前页面
/// </summary>
void DeleteCurrentPage();
/// <summary>
/// 切换到指定页面
/// </summary>
/// <param name="pageIndex">页面索引</param>
void SwitchToPage(int pageIndex);
/// <summary>
/// 切换到下一页
/// </summary>
void NextPage();
/// <summary>
/// 切换到上一页
/// </summary>
void PreviousPage();
#endregion
#region
/// <summary>
/// 设置绘制模式
/// </summary>
/// <param name="mode">绘制模式</param>
void SetDrawingMode(int mode);
/// <summary>
/// 设置笔触宽度
/// </summary>
/// <param name="width">宽度</param>
void SetInkWidth(double width);
/// <summary>
/// 设置笔触颜色
/// </summary>
/// <param name="color">颜色</param>
void SetInkColor(Color color);
/// <summary>
/// 设置高亮笔宽度
/// </summary>
/// <param name="width">宽度</param>
void SetHighlighterWidth(double width);
/// <summary>
/// 设置橡皮擦大小
/// </summary>
/// <param name="size">大小</param>
void SetEraserSize(int size);
/// <summary>
/// 设置橡皮擦类型
/// </summary>
/// <param name="type">类型</param>
void SetEraserType(int type);
/// <summary>
/// 设置橡皮擦形状
/// </summary>
/// <param name="shape">形状</param>
void SetEraserShape(int shape);
/// <summary>
/// 设置笔触透明度
/// </summary>
/// <param name="alpha">透明度</param>
void SetInkAlpha(double alpha);
/// <summary>
/// 设置笔触样式
/// </summary>
/// <param name="style">样式</param>
void SetInkStyle(int style);
/// <summary>
/// 设置背景颜色
/// </summary>
/// <param name="color">颜色</param>
void SetBackgroundColor(string color);
#endregion
#region
/// <summary>
/// 保存画布内容
/// </summary>
/// <param name="filePath">文件路径</param>
void SaveCanvas(string filePath);
/// <summary>
/// 加载画布内容
/// </summary>
/// <param name="filePath">文件路径</param>
void LoadCanvas(string filePath);
/// <summary>
/// 导出为图片
/// </summary>
/// <param name="filePath">文件路径</param>
/// <param name="format">图片格式</param>
void ExportAsImage(string filePath, string format);
/// <summary>
/// 导出为PDF
/// </summary>
/// <param name="filePath">文件路径</param>
void ExportAsPDF(string filePath);
#endregion
#region
/// <summary>
/// 撤销操作
/// </summary>
void Undo();
/// <summary>
/// 重做操作
/// </summary>
void Redo();
#endregion
#region
/// <summary>
/// 全选
/// </summary>
void SelectAll();
/// <summary>
/// 取消选择
/// </summary>
void DeselectAll();
/// <summary>
/// 删除选中内容
/// </summary>
void DeleteSelected();
/// <summary>
/// 复制选中内容
/// </summary>
void CopySelected();
/// <summary>
/// 剪切选中内容
/// </summary>
void CutSelected();
/// <summary>
/// 粘贴内容
/// </summary>
void Paste();
#endregion
#region
/// <summary>
/// 设置系统设置
/// </summary>
/// <typeparam name="T">设置类型</typeparam>
/// <param name="key">设置键</param>
/// <param name="value">设置值</param>
void SetSetting<T>(string key, T value);
/// <summary>
/// 保存设置到文件
/// </summary>
void SaveSettings();
/// <summary>
/// 从文件加载设置
/// </summary>
void LoadSettings();
/// <summary>
/// 重置设置为默认值
/// </summary>
void ResetSettings();
#endregion
#region
/// <summary>
/// 启用插件
/// </summary>
/// <param name="pluginName">插件名称</param>
void EnablePlugin(string pluginName);
/// <summary>
/// 禁用插件
/// </summary>
/// <param name="pluginName">插件名称</param>
void DisablePlugin(string pluginName);
/// <summary>
/// 卸载插件
/// </summary>
/// <param name="pluginName">插件名称</param>
void UnloadPlugin(string pluginName);
#endregion
#region
/// <summary>
/// 注册事件处理器
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="handler">事件处理器</param>
void RegisterEventHandler(string eventName, EventHandler handler);
/// <summary>
/// 注销事件处理器
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="handler">事件处理器</param>
void UnregisterEventHandler(string eventName, EventHandler handler);
/// <summary>
/// 触发事件
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="sender">事件发送者</param>
/// <param name="args">事件参数</param>
void TriggerEvent(string eventName, object sender, EventArgs args);
#endregion
#region
/// <summary>
/// 重启应用程序
/// </summary>
void RestartApplication();
/// <summary>
/// 退出应用程序
/// </summary>
void ExitApplication();
/// <summary>
/// 检查更新
/// </summary>
void CheckForUpdates();
/// <summary>
/// 打开帮助文档
/// </summary>
void OpenHelpDocument();
/// <summary>
/// 打开关于页面
/// </summary>
void OpenAboutPage();
#endregion
}
}
@@ -1,178 +0,0 @@
using System;
using System.IO;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// ICCPP 插件适配器,用于加载和管理 .iccpp 格式的插件
/// </summary>
public class ICCPPPluginAdapter : PluginBase
{
private readonly byte[] _pluginData;
private readonly string _pluginPath;
private readonly string _pluginName;
private readonly Version _pluginVersion;
private bool _isInitialized;
/// <summary>
/// 创建 ICCPP 插件适配器
/// </summary>
/// <param name="pluginPath">插件文件路径</param>
/// <param name="pluginData">插件文件数据</param>
public ICCPPPluginAdapter(string pluginPath, byte[] pluginData)
{
_pluginPath = pluginPath;
_pluginData = pluginData;
PluginPath = pluginPath;
// 从文件名获取插件名称
_pluginName = Path.GetFileNameWithoutExtension(pluginPath);
_pluginVersion = new Version(1, 0, 0); // 默认版本
// 尝试从插件数据中读取更多信息
TryReadPluginMetadata();
}
public ICCPPPluginAdapter()
{
_pluginPath = string.Empty;
_pluginData = new byte[0];
PluginPath = string.Empty;
_pluginName = "ICCPPPlugin";
_pluginVersion = new Version(1, 0, 0);
// 可选:初始化其他字段
}
/// <summary>
/// 尝试从插件数据中读取元数据
/// </summary>
private void TryReadPluginMetadata()
{
try
{
// 这里可以根据 .iccpp 文件的实际格式解析元数据
// 例如,如果文件有特定的头部结构,可以在这里解析
// 示例:如果前100字节包含元数据
if (_pluginData.Length > 100)
{
// 解析元数据的代码...
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"解析插件 {_pluginName} 元数据时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#region IPlugin
/// <summary>
/// 插件名称
/// </summary>
public override string Name => _pluginName;
/// <summary>
/// 插件描述
/// </summary>
public override string Description => $"{_pluginName} (ICCPP 格式插件)";
/// <summary>
/// 插件版本
/// </summary>
public override Version Version => _pluginVersion;
/// <summary>
/// 插件作者
/// </summary>
public override string Author => "未知";
/// <summary>
/// 是否为内置插件
/// </summary>
public override bool IsBuiltIn => false;
/// <summary>
/// 初始化插件
/// </summary>
public override void Initialize()
{
if (_isInitialized) return;
try
{
// 这里可以添加 .iccpp 插件的初始化逻辑
// 例如,根据文件格式加载特定资源
LogHelper.WriteLogToFile($"ICCPP 插件 {Name} 已初始化");
_isInitialized = true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"初始化 ICCPP 插件 {Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 启用插件
/// </summary>
public override void Enable()
{
if (IsEnabled) return;
try
{
// 这里可以添加 .iccpp 插件的启用逻辑
// 例如,加载动态库、注册事件等
base.Enable(); // 设置启用状态并触发事件
LogHelper.WriteLogToFile($"ICCPP 插件 {Name} 已启用");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启用 ICCPP 插件 {Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 禁用插件
/// </summary>
public override void Disable()
{
if (!IsEnabled) return;
try
{
// 这里可以添加 .iccpp 插件的禁用逻辑
// 例如,卸载动态库、注销事件等
base.Disable(); // 设置禁用状态并触发事件
LogHelper.WriteLogToFile($"ICCPP 插件 {Name} 已禁用");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"禁用 ICCPP 插件 {Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 清理插件资源
/// </summary>
public override void Cleanup()
{
try
{
// 这里可以添加 .iccpp 插件的清理逻辑
// 例如,释放资源等
LogHelper.WriteLogToFile($"ICCPP 插件 {Name} 已清理资源");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理 ICCPP 插件 {Name} 资源时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
}
}
@@ -1,48 +0,0 @@
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 增强的插件接口,提供对插件服务的访问
/// </summary>
public interface IEnhancedPlugin : IPlugin
{
/// <summary>
/// 获取插件服务实例
/// </summary>
IPluginService PluginService { get; }
/// <summary>
/// 插件启动时调用,在Initialize之后
/// </summary>
void OnStartup();
/// <summary>
/// 插件关闭时调用,在Cleanup之前
/// </summary>
void OnShutdown();
/// <summary>
/// 获取插件的菜单项
/// </summary>
/// <returns>菜单项集合</returns>
MenuItem[] GetMenuItems();
/// <summary>
/// 获取插件的工具栏按钮
/// </summary>
/// <returns>工具栏按钮集合</returns>
Button[] GetToolbarButtons();
/// <summary>
/// 获取插件的状态栏信息
/// </summary>
/// <returns>状态栏信息</returns>
string GetStatusBarInfo();
/// <summary>
/// 插件配置变更时调用
/// </summary>
void OnConfigurationChanged();
}
}
-214
View File
@@ -1,214 +0,0 @@
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 获取服务接口,统一所有获取类的方法
/// </summary>
public interface IGetService
{
#region UI获取
/// <summary>
/// 获取主窗口引用
/// </summary>
Window MainWindow { get; }
/// <summary>
/// 获取当前画布
/// </summary>
InkCanvas CurrentCanvas { get; }
/// <summary>
/// 获取所有画布页面
/// </summary>
List<Canvas> AllCanvasPages { get; }
/// <summary>
/// 获取当前页面索引
/// </summary>
int CurrentPageIndex { get; }
/// <summary>
/// 获取当前页面数量
/// </summary>
int TotalPageCount { get; }
/// <summary>
/// 获取浮动工具栏
/// </summary>
FrameworkElement FloatingToolBar { get; }
/// <summary>
/// 获取左侧面板
/// </summary>
FrameworkElement LeftPanel { get; }
/// <summary>
/// 获取右侧面板
/// </summary>
FrameworkElement RightPanel { get; }
/// <summary>
/// 获取顶部面板
/// </summary>
FrameworkElement TopPanel { get; }
/// <summary>
/// 获取底部面板
/// </summary>
FrameworkElement BottomPanel { get; }
#endregion
#region
/// <summary>
/// 获取当前绘制模式
/// </summary>
int CurrentDrawingMode { get; }
/// <summary>
/// 获取当前笔触宽度
/// </summary>
double CurrentInkWidth { get; }
/// <summary>
/// 获取当前笔触颜色
/// </summary>
Color CurrentInkColor { get; }
/// <summary>
/// 获取当前高亮笔宽度
/// </summary>
double CurrentHighlighterWidth { get; }
/// <summary>
/// 获取当前橡皮擦大小
/// </summary>
int CurrentEraserSize { get; }
/// <summary>
/// 获取当前橡皮擦类型
/// </summary>
int CurrentEraserType { get; }
/// <summary>
/// 获取当前橡皮擦形状
/// </summary>
int CurrentEraserShape { get; }
/// <summary>
/// 获取当前笔触透明度
/// </summary>
double CurrentInkAlpha { get; }
/// <summary>
/// 获取当前笔触样式
/// </summary>
int CurrentInkStyle { get; }
/// <summary>
/// 获取当前背景颜色
/// </summary>
string CurrentBackgroundColor { get; }
#endregion
#region
/// <summary>
/// 获取当前主题模式
/// </summary>
bool IsDarkTheme { get; }
/// <summary>
/// 获取当前是否为白板模式
/// </summary>
bool IsWhiteboardMode { get; }
/// <summary>
/// 获取当前是否为PPT模式
/// </summary>
bool IsPPTMode { get; }
/// <summary>
/// 获取当前是否为全屏模式
/// </summary>
bool IsFullScreenMode { get; }
/// <summary>
/// 获取当前是否为画板模式
/// </summary>
bool IsCanvasMode { get; }
/// <summary>
/// 获取当前是否为选择模式
/// </summary>
bool IsSelectionMode { get; }
/// <summary>
/// 获取当前是否为擦除模式
/// </summary>
bool IsEraserMode { get; }
/// <summary>
/// 获取当前是否为形状绘制模式
/// </summary>
bool IsShapeDrawingMode { get; }
/// <summary>
/// 获取当前是否为高亮模式
/// </summary>
bool IsHighlighterMode { get; }
#endregion
#region
/// <summary>
/// 获取是否可以撤销
/// </summary>
bool CanUndo { get; }
/// <summary>
/// 获取是否可以重做
/// </summary>
bool CanRedo { get; }
#endregion
#region
/// <summary>
/// 获取系统设置
/// </summary>
/// <typeparam name="T">设置类型</typeparam>
/// <param name="key">设置键</param>
/// <param name="defaultValue">默认值</param>
/// <returns>设置值</returns>
T GetSetting<T>(string key, T defaultValue = default(T));
#endregion
#region
/// <summary>
/// 获取所有已加载的插件
/// </summary>
/// <returns>插件列表</returns>
List<IPlugin> GetAllPlugins();
/// <summary>
/// 获取指定插件
/// </summary>
/// <param name="pluginName">插件名称</param>
/// <returns>插件实例</returns>
IPlugin GetPlugin(string pluginName);
#endregion
}
}
-67
View File
@@ -1,67 +0,0 @@
using System;
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 定义插件的基本接口
/// </summary>
public interface IPlugin
{
/// <summary>
/// 插件名称
/// </summary>
string Name { get; }
/// <summary>
/// 插件描述
/// </summary>
string Description { get; }
/// <summary>
/// 插件版本
/// </summary>
Version Version { get; }
/// <summary>
/// 插件作者
/// </summary>
string Author { get; }
/// <summary>
/// 是否为内置插件
/// </summary>
bool IsBuiltIn { get; }
/// <summary>
/// 初始化插件
/// 此方法在插件加载时被调用,用于执行一些初始化工作
/// </summary>
void Initialize();
/// <summary>
/// 启用插件
/// 此方法在插件被用户或系统启用时调用,激活插件功能
/// </summary>
void Enable();
/// <summary>
/// 禁用插件
/// 此方法在插件被用户或系统禁用时调用,停用插件功能
/// </summary>
void Disable();
/// <summary>
/// 获取插件设置界面
/// 此方法返回插件的设置界面控件,用于展示在设置窗口
/// </summary>
/// <returns>插件设置界面</returns>
UserControl GetSettingsView();
/// <summary>
/// 插件卸载时的清理工作
/// 此方法在插件被卸载前调用,用于释放资源和执行清理
/// </summary>
void Cleanup();
}
}
@@ -1,38 +0,0 @@
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 插件服务接口,提供对软件内部功能的访问
/// 继承自三个专门的服务接口:获取服务、窗口服务、操作服务
/// </summary>
public interface IPluginService : IGetService, IWindowService, IActionService
{
// 这个接口现在继承自三个专门的服务接口
// 所有方法都在子接口中定义,这里不需要重复定义
}
/// <summary>
/// 通知类型枚举
/// </summary>
public enum NotificationType
{
/// <summary>
/// 信息
/// </summary>
Info,
/// <summary>
/// 成功
/// </summary>
Success,
/// <summary>
/// 警告
/// </summary>
Warning,
/// <summary>
/// 错误
/// </summary>
Error
}
}
@@ -1,152 +0,0 @@
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 窗口服务接口,统一所有窗口操作相关的方法
/// </summary>
public interface IWindowService
{
#region
/// <summary>
/// 显示设置窗口
/// </summary>
void ShowSettingsWindow();
/// <summary>
/// 隐藏设置窗口
/// </summary>
void HideSettingsWindow();
/// <summary>
/// 显示插件设置窗口
/// </summary>
void ShowPluginSettingsWindow();
/// <summary>
/// 隐藏插件设置窗口
/// </summary>
void HidePluginSettingsWindow();
/// <summary>
/// 显示帮助窗口
/// </summary>
void ShowHelpWindow();
/// <summary>
/// 隐藏帮助窗口
/// </summary>
void HideHelpWindow();
/// <summary>
/// 显示关于窗口
/// </summary>
void ShowAboutWindow();
/// <summary>
/// 隐藏关于窗口
/// </summary>
void HideAboutWindow();
#endregion
#region
/// <summary>
/// 显示通知消息
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="type">消息类型</param>
void ShowNotification(string message, NotificationType type = NotificationType.Info);
/// <summary>
/// 显示确认对话框
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="title">标题</param>
/// <returns>用户选择结果</returns>
bool ShowConfirmDialog(string message, string title = "确认");
/// <summary>
/// 显示输入对话框
/// </summary>
/// <param name="message">提示消息</param>
/// <param name="title">标题</param>
/// <param name="defaultValue">默认值</param>
/// <returns>用户输入内容</returns>
string ShowInputDialog(string message, string title = "输入", string defaultValue = "");
#endregion
#region
/// <summary>
/// 设置窗口全屏状态
/// </summary>
/// <param name="isFullScreen">是否全屏</param>
void SetFullScreen(bool isFullScreen);
/// <summary>
/// 设置窗口置顶状态
/// </summary>
/// <param name="isTopMost">是否置顶</param>
void SetTopMost(bool isTopMost);
/// <summary>
/// 设置窗口可见性
/// </summary>
/// <param name="isVisible">是否可见</param>
void SetWindowVisibility(bool isVisible);
/// <summary>
/// 最小化窗口
/// </summary>
void MinimizeWindow();
/// <summary>
/// 最大化窗口
/// </summary>
void MaximizeWindow();
/// <summary>
/// 恢复窗口
/// </summary>
void RestoreWindow();
/// <summary>
/// 关闭窗口
/// </summary>
void CloseWindow();
#endregion
#region
/// <summary>
/// 设置窗口位置
/// </summary>
/// <param name="x">X坐标</param>
/// <param name="y">Y坐标</param>
void SetWindowPosition(double x, double y);
/// <summary>
/// 设置窗口大小
/// </summary>
/// <param name="width">宽度</param>
/// <param name="height">高度</param>
void SetWindowSize(double width, double height);
/// <summary>
/// 获取窗口位置
/// </summary>
/// <returns>窗口位置</returns>
(double x, double y) GetWindowPosition();
/// <summary>
/// 获取窗口大小
/// </summary>
/// <returns>窗口大小</returns>
(double width, double height) GetWindowSize();
#endregion
}
}
-161
View File
@@ -1,161 +0,0 @@
using System;
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 插件基类,提供基本实现
/// </summary>
public abstract class PluginBase : IPlugin
{
/// <summary>
/// 插件状态(私有字段)
/// </summary>
private bool _isEnabled;
/// <summary>
/// 插件状态(公共属性)
/// </summary>
public bool IsEnabled
{
get => _isEnabled;
protected set
{
if (_isEnabled != value)
{
_isEnabled = value;
OnEnabledStateChanged(value);
}
}
}
/// <summary>
/// 插件ID
/// </summary>
public string Id { get; protected set; }
/// <summary>
/// 插件路径
/// </summary>
public string PluginPath { get; set; }
/// <summary>
/// 插件名称
/// </summary>
public abstract string Name { get; }
/// <summary>
/// 插件描述
/// </summary>
public abstract string Description { get; }
/// <summary>
/// 插件版本
/// </summary>
public abstract Version Version { get; }
/// <summary>
/// 插件作者
/// </summary>
public abstract string Author { get; }
/// <summary>
/// 是否为内置插件
/// </summary>
public virtual bool IsBuiltIn => false;
/// <summary>
/// 状态变更事件
/// </summary>
public event EventHandler<bool> EnabledStateChanged;
/// <summary>
/// 初始化插件
/// </summary>
public virtual void Initialize()
{
Id = GetType().FullName;
// 添加日志,记录插件名称
try
{
string name = Name;
LogHelper.WriteLogToFile($"初始化插件: ID={Id}, 名称={name ?? ""}");
if (string.IsNullOrEmpty(name))
{
LogHelper.WriteLogToFile($"警告: 插件 {Id} 的名称为空", LogHelper.LogType.Warning);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取插件名称时出错: {ex.Message}", LogHelper.LogType.Error);
}
LogHelper.WriteLogToFile($"插件 {Name} 已初始化");
}
/// <summary>
/// 启用插件
/// </summary>
public virtual void Enable()
{
if (!IsEnabled)
{
IsEnabled = true;
LogHelper.WriteLogToFile($"插件 {Name} 已启用");
}
}
/// <summary>
/// 禁用插件
/// </summary>
public virtual void Disable()
{
if (IsEnabled)
{
IsEnabled = false;
LogHelper.WriteLogToFile($"插件 {Name} 已禁用");
}
}
/// <summary>
/// 获取插件设置界面
/// </summary>
/// <returns>插件设置界面</returns>
public virtual UserControl GetSettingsView()
{
// 默认返回空设置页面
return new UserControl();
}
/// <summary>
/// 插件卸载时的清理工作
/// </summary>
public virtual void Cleanup()
{
LogHelper.WriteLogToFile($"插件 {Name} 已卸载");
}
/// <summary>
/// 保存插件自身的设置
/// 注意:此方法仅用于保存插件的特定设置,不应影响插件启用/禁用状态
/// 插件启用状态由PluginManager统一管理
/// </summary>
public virtual void SavePluginSettings()
{
// 默认实现不做任何事情
// 子类可以重写此方法,将自身设置保存到配置文件中
LogHelper.WriteLogToFile($"插件 {Name} 设置已保存", LogHelper.LogType.Event);
}
/// <summary>
/// 触发状态变更事件
/// </summary>
/// <param name="isEnabled">是否启用</param>
protected virtual void OnEnabledStateChanged(bool isEnabled)
{
EnabledStateChanged?.Invoke(this, isEnabled);
}
}
}
@@ -1,273 +0,0 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 插件配置管理器,允许插件管理自己的配置
/// </summary>
public class PluginConfigurationManager
{
private static readonly string PluginConfigDirectory = Path.Combine(App.RootPath, "PluginConfigs");
private static readonly Dictionary<string, Dictionary<string, object>> _pluginConfigs = new Dictionary<string, Dictionary<string, object>>();
private static readonly object _lockObject = new object();
static PluginConfigurationManager()
{
// 确保配置目录存在
if (!Directory.Exists(PluginConfigDirectory))
{
Directory.CreateDirectory(PluginConfigDirectory);
}
}
/// <summary>
/// 获取插件配置值
/// </summary>
/// <typeparam name="T">配置值类型</typeparam>
/// <param name="pluginName">插件名称</param>
/// <param name="key">配置键</param>
/// <param name="defaultValue">默认值</param>
/// <returns>配置值</returns>
public static T GetConfiguration<T>(string pluginName, string key, T defaultValue = default(T))
{
lock (_lockObject)
{
try
{
if (_pluginConfigs.TryGetValue(pluginName, out var pluginConfig))
{
if (pluginConfig.TryGetValue(key, out var value))
{
if (value is T typedValue)
{
return typedValue;
}
// 尝试类型转换
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch
{
return defaultValue;
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取插件 {pluginName} 配置 {key} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
return defaultValue;
}
}
/// <summary>
/// 设置插件配置值
/// </summary>
/// <typeparam name="T">配置值类型</typeparam>
/// <param name="pluginName">插件名称</param>
/// <param name="key">配置键</param>
/// <param name="value">配置值</param>
public static void SetConfiguration<T>(string pluginName, string key, T value)
{
lock (_lockObject)
{
try
{
if (!_pluginConfigs.ContainsKey(pluginName))
{
_pluginConfigs[pluginName] = new Dictionary<string, object>();
}
_pluginConfigs[pluginName][key] = value;
// 异步保存配置
Task.Run(() => SavePluginConfiguration(pluginName));
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"设置插件 {pluginName} 配置 {key} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 删除插件配置
/// </summary>
/// <param name="pluginName">插件名称</param>
/// <param name="key">配置键</param>
public static void RemoveConfiguration(string pluginName, string key)
{
lock (_lockObject)
{
try
{
if (_pluginConfigs.TryGetValue(pluginName, out var pluginConfig))
{
if (pluginConfig.Remove(key))
{
// 异步保存配置
Task.Run(() => SavePluginConfiguration(pluginName));
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"删除插件 {pluginName} 配置 {key} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 获取插件的所有配置
/// </summary>
/// <param name="pluginName">插件名称</param>
/// <returns>配置字典</returns>
public static Dictionary<string, object> GetAllConfigurations(string pluginName)
{
lock (_lockObject)
{
if (_pluginConfigs.TryGetValue(pluginName, out var pluginConfig))
{
return new Dictionary<string, object>(pluginConfig);
}
return new Dictionary<string, object>();
}
}
/// <summary>
/// 清除插件的所有配置
/// </summary>
/// <param name="pluginName">插件名称</param>
public static void ClearAllConfigurations(string pluginName)
{
lock (_lockObject)
{
try
{
if (_pluginConfigs.Remove(pluginName))
{
// 删除配置文件
string configFile = Path.Combine(PluginConfigDirectory, $"{pluginName}.json");
if (File.Exists(configFile))
{
File.Delete(configFile);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清除插件 {pluginName} 所有配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 加载插件配置
/// </summary>
/// <param name="pluginName">插件名称</param>
public static void LoadPluginConfiguration(string pluginName)
{
try
{
string configFile = Path.Combine(PluginConfigDirectory, $"{pluginName}.json");
if (File.Exists(configFile))
{
string json = File.ReadAllText(configFile);
var config = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
lock (_lockObject)
{
_pluginConfigs[pluginName] = config ?? new Dictionary<string, object>();
}
LogHelper.WriteLogToFile($"已加载插件 {pluginName} 的配置");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载插件 {pluginName} 配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 保存插件配置
/// </summary>
/// <param name="pluginName">插件名称</param>
private static void SavePluginConfiguration(string pluginName)
{
try
{
Dictionary<string, object> pluginConfig;
lock (_lockObject)
{
if (!_pluginConfigs.TryGetValue(pluginName, out pluginConfig))
{
return;
}
}
string configFile = Path.Combine(PluginConfigDirectory, $"{pluginName}.json");
string json = JsonConvert.SerializeObject(pluginConfig, Formatting.Indented);
File.WriteAllText(configFile, json);
LogHelper.WriteLogToFile($"已保存插件 {pluginName} 的配置");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存插件 {pluginName} 配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 加载所有插件的配置
/// </summary>
public static void LoadAllPluginConfigurations()
{
try
{
if (Directory.Exists(PluginConfigDirectory))
{
string[] configFiles = Directory.GetFiles(PluginConfigDirectory, "*.json");
foreach (string configFile in configFiles)
{
string pluginName = Path.GetFileNameWithoutExtension(configFile);
LoadPluginConfiguration(pluginName);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载所有插件配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 保存所有插件的配置
/// </summary>
public static void SaveAllPluginConfigurations()
{
try
{
lock (_lockObject)
{
foreach (string pluginName in _pluginConfigs.Keys)
{
SavePluginConfiguration(pluginName);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存所有插件配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -1,509 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 插件服务管理器,实现IPluginService接口,提供对软件内部功能的访问
/// </summary>
public class PluginServiceManager : IPluginService
{
private static PluginServiceManager _instance;
private MainWindow _mainWindow;
private Dictionary<string, EventHandler> _eventHandlers;
/// <summary>
/// 单例实例
/// </summary>
public static PluginServiceManager Instance
{
get
{
if (_instance == null)
{
_instance = new PluginServiceManager();
}
return _instance;
}
}
private PluginServiceManager()
{
_eventHandlers = new Dictionary<string, EventHandler>();
}
/// <summary>
/// 设置主窗口引用
/// </summary>
/// <param name="mainWindow">主窗口实例</param>
public void SetMainWindow(MainWindow mainWindow)
{
_mainWindow = mainWindow;
}
#region UI访问
public Window MainWindow => _mainWindow;
public InkCanvas CurrentCanvas => null; // 暂时返回null,避免访问权限问题
public List<Canvas> AllCanvasPages => new List<Canvas>(); // 暂时返回空列表
public int CurrentPageIndex => 0; // 暂时返回0
public int TotalPageCount => 0; // 暂时返回0
public FrameworkElement FloatingToolBar => _mainWindow?.ViewboxFloatingBar;
public FrameworkElement LeftPanel => _mainWindow?.BlackboardLeftSide;
public FrameworkElement RightPanel => _mainWindow?.BlackboardRightSide;
public FrameworkElement TopPanel => _mainWindow?.BorderTools;
public FrameworkElement BottomPanel => _mainWindow?.BorderSettings;
#endregion
#region
public int CurrentDrawingMode => 0; // 暂时返回0
public double CurrentInkWidth => 2.5; // 暂时返回默认值
public Color CurrentInkColor => Colors.Black; // 暂时返回默认值
public double CurrentHighlighterWidth => 20.0; // 暂时返回默认值
public int CurrentEraserSize => 2; // 暂时返回默认值
public int CurrentEraserType => 0; // 暂时返回默认值
public int CurrentEraserShape => 0; // 暂时返回默认值
public double CurrentInkAlpha => 255.0; // 暂时返回默认值
public int CurrentInkStyle => 0; // 暂时返回默认值
public string CurrentBackgroundColor => "#162924"; // 暂时返回默认值
#endregion
#region
public bool IsDarkTheme => false; // 暂时返回默认值
public bool IsWhiteboardMode => false; // 暂时返回默认值
public bool IsPPTMode => false; // 暂时返回默认值
public bool IsFullScreenMode => false; // 暂时返回默认值
public bool IsCanvasMode => true; // 暂时返回默认值
public bool IsSelectionMode => false; // 暂时返回默认值
public bool IsEraserMode => false; // 暂时返回默认值
public bool IsShapeDrawingMode => false; // 暂时返回默认值
public bool IsHighlighterMode => false; // 暂时返回默认值
#endregion
#region IGetService
public bool CanUndo => false; // 暂时返回默认值
public bool CanRedo => false; // 暂时返回默认值
public T GetSetting<T>(string key, T defaultValue = default(T))
{
// 暂时不实现,避免访问权限问题
return defaultValue;
}
public List<IPlugin> GetAllPlugins()
{
return new List<IPlugin>(PluginManager.Instance.Plugins);
}
public IPlugin GetPlugin(string pluginName)
{
return PluginManager.Instance.Plugins.FirstOrDefault(p => p.Name == pluginName);
}
#endregion
#region IWindowService
public void ShowSettingsWindow()
{
// 暂时不实现,避免访问权限问题
}
public void HideSettingsWindow()
{
// 暂时不实现,避免访问权限问题
}
public void ShowPluginSettingsWindow()
{
// 暂时不实现,避免访问权限问题
}
public void HidePluginSettingsWindow()
{
// 暂时不实现,避免访问权限问题
}
public void ShowHelpWindow()
{
// 暂时不实现,避免访问权限问题
}
public void HideHelpWindow()
{
// 暂时不实现,避免访问权限问题
}
public void ShowAboutWindow()
{
// 暂时不实现,避免访问权限问题
}
public void HideAboutWindow()
{
// 暂时不实现,避免访问权限问题
}
public void ShowNotification(string message, NotificationType type = NotificationType.Info)
{
// 暂时不实现,避免访问权限问题
}
public bool ShowConfirmDialog(string message, string title = "确认")
{
// 暂时不实现,避免访问权限问题
return false;
}
public string ShowInputDialog(string message, string title = "输入", string defaultValue = "")
{
// 暂时不实现,避免访问权限问题
return defaultValue;
}
public void SetFullScreen(bool isFullScreen)
{
// 暂时不实现,避免访问权限问题
}
public void SetTopMost(bool isTopMost)
{
// 暂时不实现,避免访问权限问题
}
public void SetWindowVisibility(bool isVisible)
{
// 暂时不实现,避免访问权限问题
}
public void MinimizeWindow()
{
// 暂时不实现,避免访问权限问题
}
public void MaximizeWindow()
{
// 暂时不实现,避免访问权限问题
}
public void RestoreWindow()
{
// 暂时不实现,避免访问权限问题
}
public void CloseWindow()
{
// 暂时不实现,避免访问权限问题
}
public void SetWindowPosition(double x, double y)
{
// 暂时不实现,避免访问权限问题
}
public void SetWindowSize(double width, double height)
{
// 暂时不实现,避免访问权限问题
}
public (double x, double y) GetWindowPosition()
{
// 暂时不实现,避免访问权限问题
return (0, 0);
}
public (double width, double height) GetWindowSize()
{
// 暂时不实现,避免访问权限问题
return (800, 600);
}
#endregion
#region IActionService
public void ClearCanvas()
{
// 暂时不实现,避免访问权限问题
}
public void ClearAllCanvases()
{
// 暂时不实现,避免访问权限问题
}
public void AddNewPage()
{
// 暂时不实现,避免访问权限问题
}
public void DeleteCurrentPage()
{
// 暂时不实现,避免访问权限问题
}
public void SwitchToPage(int pageIndex)
{
// 暂时不实现,避免访问权限问题
}
public void NextPage()
{
// 暂时不实现,避免访问权限问题
}
public void PreviousPage()
{
// 暂时不实现,避免访问权限问题
}
public void SetDrawingMode(int mode)
{
// 暂时不实现,避免访问权限问题
}
public void SetInkWidth(double width)
{
// 暂时不实现,避免访问权限问题
}
public void SetInkColor(Color color)
{
// 暂时不实现,避免访问权限问题
}
public void SetHighlighterWidth(double width)
{
// 暂时不实现,避免访问权限问题
}
public void SetEraserSize(int size)
{
// 暂时不实现,避免访问权限问题
}
public void SetEraserType(int type)
{
// 暂时不实现,避免访问权限问题
}
public void SetEraserShape(int shape)
{
// 暂时不实现,避免访问权限问题
}
public void SetInkAlpha(double alpha)
{
// 暂时不实现,避免访问权限问题
}
public void SetInkStyle(int style)
{
// 暂时不实现,避免访问权限问题
}
public void SetBackgroundColor(string color)
{
// 暂时不实现,避免访问权限问题
}
public void SaveCanvas(string filePath)
{
// 暂时不实现,避免访问权限问题
}
public void LoadCanvas(string filePath)
{
// 暂时不实现,避免访问权限问题
}
public void ExportAsImage(string filePath, string format)
{
// 暂时不实现,避免访问权限问题
}
public void ExportAsPDF(string filePath)
{
// 暂时不实现,避免访问权限问题
}
public void Undo()
{
// 暂时不实现,避免访问权限问题
}
public void Redo()
{
// 暂时不实现,避免访问权限问题
}
public void SelectAll()
{
// 暂时不实现,避免访问权限问题
}
public void DeselectAll()
{
// 暂时不实现,避免访问权限问题
}
public void DeleteSelected()
{
// 暂时不实现,避免访问权限问题
}
public void CopySelected()
{
// 暂时不实现,避免访问权限问题
}
public void CutSelected()
{
// 暂时不实现,避免访问权限问题
}
public void Paste()
{
// 暂时不实现,避免访问权限问题
}
public void SetSetting<T>(string key, T value)
{
// 暂时不实现,避免访问权限问题
}
public void SaveSettings()
{
// 暂时不实现,避免访问权限问题
}
public void LoadSettings()
{
// 暂时不实现,避免访问权限问题
}
public void ResetSettings()
{
// 暂时不实现,避免访问权限问题
}
public void EnablePlugin(string pluginName)
{
var plugin = GetPlugin(pluginName);
if (plugin != null)
{
PluginManager.Instance.TogglePlugin(plugin, true);
}
}
public void DisablePlugin(string pluginName)
{
var plugin = GetPlugin(pluginName);
if (plugin != null)
{
PluginManager.Instance.TogglePlugin(plugin, false);
}
}
public void UnloadPlugin(string pluginName)
{
var plugin = GetPlugin(pluginName);
if (plugin != null)
{
PluginManager.Instance.UnloadPlugin(plugin);
}
}
public void RegisterEventHandler(string eventName, EventHandler handler)
{
if (!_eventHandlers.ContainsKey(eventName))
{
_eventHandlers[eventName] = handler;
}
else
{
_eventHandlers[eventName] += handler;
}
}
public void UnregisterEventHandler(string eventName, EventHandler handler)
{
if (_eventHandlers.ContainsKey(eventName))
{
_eventHandlers[eventName] -= handler;
}
}
public void TriggerEvent(string eventName, object sender, EventArgs args)
{
if (_eventHandlers.ContainsKey(eventName))
{
_eventHandlers[eventName]?.Invoke(sender, args);
}
}
public void RestartApplication()
{
// 暂时不实现,避免访问权限问题
}
public void ExitApplication()
{
// 暂时不实现,避免访问权限问题
}
public void CheckForUpdates()
{
// 暂时不实现,避免访问权限问题
}
public void OpenHelpDocument()
{
// 暂时不实现,避免访问权限问题
}
public void OpenAboutPage()
{
// 暂时不实现,避免访问权限问题
}
#endregion
}
}
@@ -1,276 +0,0 @@
using System;
using System.Windows;
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 插件模板,用于开发者参考
/// 注意:实际开发时,请将此类移到单独的程序集中
/// </summary>
public class PluginTemplate : PluginBase
{
#region
/// <summary>
/// 插件名称
/// </summary>
public override string Name => "插件模板";
/// <summary>
/// 插件描述
/// </summary>
public override string Description => "这是一个插件开发模板,用于开发者参考。";
/// <summary>
/// 插件版本
/// </summary>
public override Version Version => new Version(1, 0, 0);
/// <summary>
/// 插件作者
/// </summary>
public override string Author => "Your Name";
/// <summary>
/// 是否为内置插件(外部插件请返回false)
/// </summary>
public override bool IsBuiltIn => false;
#endregion
#region
/// <summary>
/// 插件初始化
/// 在这里进行插件的初始化工作,如加载配置、注册事件等
/// </summary>
public override void Initialize()
{
// 先调用基类方法,这样会设置插件ID和记录日志
base.Initialize();
// TODO: 在这里进行插件初始化工作
// 示例:记录初始化信息
LogHelper.WriteLogToFile($"插件 {Name} 开始初始化");
// 示例:加载配置
LoadConfig();
// 示例:注册自定义事件
// MainWindow.Instance.SomeEvent += OnSomeEvent;
LogHelper.WriteLogToFile($"插件 {Name} 初始化完成");
}
/// <summary>
/// 启用插件
/// 在这里激活插件功能
/// </summary>
public override void Enable()
{
// 先调用基类方法,这样会设置插件状态和记录日志
base.Enable();
// TODO: 在这里启用插件功能
LogHelper.WriteLogToFile($"插件 {Name} 已启用");
}
/// <summary>
/// 禁用插件
/// 在这里停用插件功能
/// </summary>
public override void Disable()
{
// 先调用基类方法,这样会设置插件状态和记录日志
base.Disable();
// TODO: 在这里禁用插件功能
LogHelper.WriteLogToFile($"插件 {Name} 已禁用");
}
/// <summary>
/// 清理资源
/// 在插件卸载时调用,清理资源
/// </summary>
public override void Cleanup()
{
// TODO: 在这里清理插件资源
// 示例:取消注册事件
// MainWindow.Instance.SomeEvent -= OnSomeEvent;
// 示例:保存配置
SaveConfig();
// 最后调用基类方法
base.Cleanup();
}
#endregion
#region
/// <summary>
/// 加载插件配置
/// </summary>
private void LoadConfig()
{
try
{
// TODO: 从文件或其他位置加载配置
// 示例:
// string configPath = Path.Combine(App.RootPath, "PluginConfigs", "YourPluginName.json");
// if (File.Exists(configPath))
// {
// string json = File.ReadAllText(configPath);
// YourConfig = Newtonsoft.Json.JsonConvert.DeserializeObject<YourConfigClass>(json);
// }
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载插件配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 保存插件配置
/// </summary>
private void SaveConfig()
{
try
{
// TODO: 保存配置到文件或其他位置
// 示例:
// string configDir = Path.Combine(App.RootPath, "PluginConfigs");
// if (!Directory.Exists(configDir))
// {
// Directory.CreateDirectory(configDir);
// }
// string configPath = Path.Combine(configDir, "YourPluginName.json");
// string json = Newtonsoft.Json.JsonConvert.SerializeObject(YourConfig, Newtonsoft.Json.Formatting.Indented);
// File.WriteAllText(configPath, json);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存插件配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
#region
/// <summary>
/// 获取插件设置界面
/// </summary>
/// <returns>插件设置界面</returns>
public override UserControl GetSettingsView()
{
// 创建插件设置界面
return new PluginTemplateSettingsControl();
}
#endregion
#region
// TODO: 在这里添加插件的具体功能方法
/// <summary>
/// 示例方法:执行一些功能
/// </summary>
public void DoSomething()
{
if (!IsEnabled) return;
try
{
// TODO: 实现你的功能
MessageBox.Show("插件功能执行示例", "插件模板", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"执行插件功能时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
}
/// <summary>
/// 插件设置控件
/// </summary>
public class PluginTemplateSettingsControl : UserControl
{
public PluginTemplateSettingsControl()
{
// 创建设置界面布局
var panel = new StackPanel
{
Margin = new Thickness(10)
};
// 添加标题
panel.Children.Add(new TextBlock
{
Text = "插件模板设置",
FontSize = 16,
FontWeight = FontWeights.Bold,
Margin = new Thickness(0, 0, 0, 10)
});
// 添加说明文字
panel.Children.Add(new TextBlock
{
Text = "这是一个示例设置界面,你可以在这里添加自己的设置控件。",
TextWrapping = TextWrapping.Wrap,
Margin = new Thickness(0, 0, 0, 15)
});
// 添加示例设置选项
var checkBox = new CheckBox
{
Content = "启用某项功能",
Margin = new Thickness(0, 0, 0, 10)
};
panel.Children.Add(checkBox);
// 添加文本输入框
panel.Children.Add(new TextBlock
{
Text = "设置项:",
Margin = new Thickness(0, 5, 0, 5)
});
panel.Children.Add(new TextBox
{
Margin = new Thickness(0, 0, 0, 10),
Width = 200,
HorizontalAlignment = HorizontalAlignment.Left
});
// 添加按钮
var button = new Button
{
Content = "保存设置",
Padding = new Thickness(10, 5, 10, 5),
Margin = new Thickness(0, 10, 0, 0),
HorizontalAlignment = HorizontalAlignment.Left
};
button.Click += (sender, e) =>
{
MessageBox.Show("设置已保存!", "插件模板", MessageBoxButton.OK, MessageBoxImage.Information);
};
panel.Children.Add(button);
// 设置控件内容
Content = panel;
}
}
}
+6 -9
View File
@@ -49,16 +49,13 @@ namespace Ink_Canvas.Helpers
if (string.IsNullOrWhiteSpace(softwareName))
return null;
// 64 位进程默认只枚举 64 位注册表视图;32 位希沃常写在 WOW6432Node 下,需一并扫描。
string[] uninstallRoots =
// 须用 OpenBaseKey + RegistryView 显式指定视图:Registry.LocalMachine.OpenSubKey 跟随进程位数,
// 32 位进程下无法靠拼接 WOW6432Node 路径进入 64 位视图,会找不到 64 位安装的展台。
const string uninstallSubKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
foreach (RegistryView view in new[] { RegistryView.Registry64, RegistryView.Registry32 })
{
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall",
};
foreach (string root in uninstallRoots)
{
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(root))
using (RegistryKey baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view))
using (RegistryKey key = baseKey.OpenSubKey(uninstallSubKey))
{
if (key == null) continue;
string found = FindInUninstallKey(key, softwareName);
+11 -5
View File
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Ink;
@@ -100,8 +100,8 @@ namespace Ink_Canvas.Helpers
var item = _currentStrokeHistory[_currentIndex];
item.StrokeHasBeenCleared = !item.StrokeHasBeenCleared;
_currentIndex--;
OnUndoStateChanged?.Invoke(_currentIndex > -1);
OnRedoStateChanged?.Invoke(_currentStrokeHistory.Count - _currentIndex - 1 > 0);
OnUndoStateChanged?.Invoke(CanUndo);
OnRedoStateChanged?.Invoke(CanRedo);
return item;
}
@@ -137,9 +137,15 @@ namespace Ink_Canvas.Helpers
}
private void NotifyUndoRedoState()
{
OnUndoStateChanged?.Invoke(_currentIndex > -1);
OnRedoStateChanged?.Invoke(_currentStrokeHistory.Count - _currentIndex - 1 > 0);
OnUndoStateChanged?.Invoke(CanUndo);
OnRedoStateChanged?.Invoke(CanRedo);
}
/// <summary>当前历史是否允许撤销。</summary>
public bool CanUndo => _currentIndex > -1;
/// <summary>当前历史是否允许重做。</summary>
public bool CanRedo => _currentStrokeHistory.Count > 0 && _currentStrokeHistory.Count - _currentIndex - 1 > 0;
}
public class TimeMachineHistory
@@ -3,7 +3,6 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Ink;
@@ -19,6 +18,9 @@ namespace Ink_Canvas.Helpers
/// </summary>
internal static class WinRtHandwritingRecognizer
{
private static WinRtInk.InkRecognizer _preferredHandwritingRecognizer;
private static bool _preferredHandwritingRecognizerResolved;
private static void LogHandwriting(string message, LogHelper.LogType logType = LogHelper.LogType.Info)
{
LogHelper.WriteLogToFile("[手写体] " + message, logType);
@@ -38,7 +40,9 @@ namespace Ink_Canvas.Helpers
{
try
{
await RecognizeHandwritingAsync(new StrokeCollection()).ConfigureAwait(true);
await RecognizeHandwritingAsync(
WinRtInkShapeRecognizer.CreateMinimalWarmupStrokeCollection(),
verboseTrace: false).ConfigureAwait(true);
}
catch
{
@@ -57,15 +61,21 @@ namespace Ink_Canvas.Helpers
/// 再对每一分词用 <see cref="WinRtInk.InkRecognizerContainer"/> 取 <c>GetTextCandidates</c>(与当前 SDK 中部分版本的
/// <see cref="WinRtInk.InkRecognitionResult"/> 未暴露笔画映射的局限兼容)。
/// </summary>
public static async Task<HandwritingRecognitionResult> RecognizeHandwritingAsync(StrokeCollection strokes)
/// <param name="verboseTrace">为 false 时跳过详细识别日志(用于 <see cref="Warmup"/> 等)。</param>
public static async Task<HandwritingRecognitionResult> RecognizeHandwritingAsync(
StrokeCollection strokes,
bool verboseTrace = true)
{
if (!IsApiAvailable || strokes == null || strokes.Count == 0)
return HandwritingRecognitionResult.Empty;
var traceRecognition = strokes.Count > 0;
var traceRecognition = verboseTrace;
try
{
var recognizer = new WinRtInk.InkRecognizerContainer();
TryApplyPreferredHandwritingRecognizer(recognizer, traceRecognition);
var analyzer = new WinAnalysis.InkAnalyzer();
var idToWpf = new Dictionary<uint, Stroke>();
@@ -92,19 +102,21 @@ namespace Ink_Canvas.Helpers
LogHandwriting(
"识别:AnalyzeAsync 未得到 UpdatedStatus=" +
(analysisResult == null ? "null" : analysisResult.Status.ToString()) +
",有效笔画数=" + idToWpf.Count);
return HandwritingRecognitionResult.Empty;
",有效笔画数=" + idToWpf.Count +
",尝试整批 RecognizeAsync 回退。");
return await RecognizeHandwritingWholeInkAsync(strokes, traceRecognition).ConfigureAwait(true);
}
var wordNodes = analyzer.AnalysisRoot?.FindNodes(WinAnalysis.InkAnalysisNodeKind.InkWord);
if (wordNodes == null || wordNodes.Count == 0)
{
if (traceRecognition)
LogHandwriting("识别:未找到 InkWord 节点(可能被判为绘图或非书写),有效笔画数=" + idToWpf.Count);
return HandwritingRecognitionResult.Empty;
LogHandwriting(
"识别:未找到 InkWord 节点(墨迹分析常将非横平笔划判为绘图),有效笔画数=" + idToWpf.Count +
",改用整批 RecognizeAsync 回退。");
return await RecognizeHandwritingWholeInkAsync(strokes, traceRecognition).ConfigureAwait(true);
}
var recognizer = new WinRtInk.InkRecognizerContainer();
var segments = new List<HandwritingWordSegment>();
foreach (var node in wordNodes)
@@ -232,6 +244,243 @@ namespace Ink_Canvas.Helpers
}
}
private static void TryApplyPreferredHandwritingRecognizer(
WinRtInk.InkRecognizerContainer container,
bool logDetail)
{
if (container == null)
return;
try
{
if (!_preferredHandwritingRecognizerResolved)
{
_preferredHandwritingRecognizerResolved = true;
var all = container.GetRecognizers();
_preferredHandwritingRecognizer = SelectBestInkRecognizer(all);
if (logDetail)
{
if (_preferredHandwritingRecognizer != null)
LogHandwriting("识别器:已选用 \"" + _preferredHandwritingRecognizer.Name + "\"。");
else if (all != null && all.Count > 0)
LogHandwriting("识别器:未匹配到与 UI/区域语言对应的引擎,使用系统默认(共 " + all.Count + " 个)。");
}
}
if (_preferredHandwritingRecognizer != null)
container.SetDefaultRecognizer(_preferredHandwritingRecognizer);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile("[手写体] 设置默认手写识别器失败: " + ex.Message, LogHelper.LogType.Warning);
}
}
private static WinRtInk.InkRecognizer SelectBestInkRecognizer(
IReadOnlyList<WinRtInk.InkRecognizer> list)
{
if (list == null || list.Count == 0)
return null;
var culture = PrimaryHandwritingCulture();
var lang = (culture?.TwoLetterISOLanguageName ?? string.Empty).ToLowerInvariant();
var name = culture?.Name ?? string.Empty;
bool wantZhHans = lang == "zh" &&
(name.IndexOf("hans", StringComparison.OrdinalIgnoreCase) >= 0 ||
name.Equals("zh-cn", StringComparison.OrdinalIgnoreCase) ||
name.Equals("zh-sg", StringComparison.OrdinalIgnoreCase) ||
(name.IndexOf("hant", StringComparison.OrdinalIgnoreCase) < 0 &&
!name.Equals("zh-tw", StringComparison.OrdinalIgnoreCase) &&
!name.Equals("zh-hk", StringComparison.OrdinalIgnoreCase) &&
!name.Equals("zh-mo", StringComparison.OrdinalIgnoreCase)));
bool wantZhHant = lang == "zh" &&
(name.IndexOf("hant", StringComparison.OrdinalIgnoreCase) >= 0 ||
name.Equals("zh-tw", StringComparison.OrdinalIgnoreCase) ||
name.Equals("zh-hk", StringComparison.OrdinalIgnoreCase) ||
name.Equals("zh-mo", StringComparison.OrdinalIgnoreCase));
WinRtInk.InkRecognizer Pick(Func<string, bool> match)
{
foreach (var r in list)
{
var n = r?.Name;
if (string.IsNullOrEmpty(n))
continue;
if (match(n))
return r;
}
return null;
}
if (wantZhHans)
{
var r = Pick(n =>
n.IndexOf("简体", StringComparison.OrdinalIgnoreCase) >= 0 ||
n.IndexOf("簡體", StringComparison.OrdinalIgnoreCase) >= 0 ||
(n.IndexOf("中文", StringComparison.OrdinalIgnoreCase) >= 0 &&
(n.IndexOf("简体", StringComparison.OrdinalIgnoreCase) >= 0 ||
n.IndexOf("簡體", StringComparison.OrdinalIgnoreCase) >= 0)) ||
(n.IndexOf("Chinese", StringComparison.OrdinalIgnoreCase) >= 0 &&
(n.IndexOf("Simplified", StringComparison.OrdinalIgnoreCase) >= 0 ||
n.IndexOf("Hans", StringComparison.OrdinalIgnoreCase) >= 0 ||
n.IndexOf("PRC", StringComparison.OrdinalIgnoreCase) >= 0)));
if (r != null)
return r;
r = Pick(n =>
n.IndexOf("中文", StringComparison.OrdinalIgnoreCase) >= 0 ||
n.IndexOf("Chinese", StringComparison.OrdinalIgnoreCase) >= 0);
if (r != null)
return r;
}
else if (wantZhHant)
{
var r = Pick(n =>
n.IndexOf("繁体", StringComparison.OrdinalIgnoreCase) >= 0 ||
n.IndexOf("繁體", StringComparison.OrdinalIgnoreCase) >= 0 ||
(n.IndexOf("中文", StringComparison.OrdinalIgnoreCase) >= 0 &&
(n.IndexOf("繁体", StringComparison.OrdinalIgnoreCase) >= 0 ||
n.IndexOf("繁體", StringComparison.OrdinalIgnoreCase) >= 0)) ||
(n.IndexOf("Chinese", StringComparison.OrdinalIgnoreCase) >= 0 &&
(n.IndexOf("Traditional", StringComparison.OrdinalIgnoreCase) >= 0 ||
n.IndexOf("Hant", StringComparison.OrdinalIgnoreCase) >= 0 ||
n.IndexOf("Taiwan", StringComparison.OrdinalIgnoreCase) >= 0 ||
n.IndexOf("Hong Kong", StringComparison.OrdinalIgnoreCase) >= 0)));
if (r != null)
return r;
r = Pick(n =>
n.IndexOf("中文", StringComparison.OrdinalIgnoreCase) >= 0 ||
n.IndexOf("Chinese", StringComparison.OrdinalIgnoreCase) >= 0);
if (r != null)
return r;
}
else if (lang == "ja")
{
var r = Pick(n =>
n.IndexOf("Japanese", StringComparison.OrdinalIgnoreCase) >= 0 ||
n.IndexOf("日本語", StringComparison.OrdinalIgnoreCase) >= 0 ||
n.IndexOf("日语", StringComparison.OrdinalIgnoreCase) >= 0);
if (r != null)
return r;
}
else if (lang == "en")
{
var r = Pick(n => n.IndexOf("English", StringComparison.OrdinalIgnoreCase) >= 0);
if (r != null)
return r;
}
if (lang == "zh")
{
var r = Pick(n =>
n.IndexOf("中文", StringComparison.OrdinalIgnoreCase) >= 0 ||
n.IndexOf("Chinese", StringComparison.OrdinalIgnoreCase) >= 0);
if (r != null)
return r;
}
return null;
}
private static CultureInfo PrimaryHandwritingCulture()
{
var ui = CultureInfo.CurrentUICulture;
var ct = CultureInfo.CurrentCulture;
if (string.Equals(ui.TwoLetterISOLanguageName, "zh", StringComparison.OrdinalIgnoreCase))
return ui;
if (string.Equals(ct.TwoLetterISOLanguageName, "zh", StringComparison.OrdinalIgnoreCase))
return ct;
return ui;
}
private static async Task<HandwritingRecognitionResult> RecognizeHandwritingWholeInkAsync(
StrokeCollection strokes,
bool traceRecognition)
{
if (strokes == null || strokes.Count == 0)
return HandwritingRecognitionResult.Empty;
var container = new WinRtInk.InkStrokeContainer();
foreach (Stroke s in strokes)
{
var ink = WinRtInkShapeRecognizer.CreateInkStrokeFromWpf(s);
if (ink != null)
container.AddStroke(ink);
}
var winStrokes = container.GetStrokes();
if (winStrokes == null || winStrokes.Count == 0)
{
if (traceRecognition)
LogHandwriting("整批回退:无有效 WinRT 笔画。");
return HandwritingRecognitionResult.Empty;
}
var reco = new WinRtInk.InkRecognizerContainer();
TryApplyPreferredHandwritingRecognizer(reco, false);
IReadOnlyList<WinRtInk.InkRecognitionResult> rr;
try
{
rr = await reco
.RecognizeAsync(container, WinRtInk.InkRecognitionTarget.All)
.AsTask()
.ConfigureAwait(true);
}
catch (Exception ex)
{
if (traceRecognition)
LogHandwriting("整批回退:RecognizeAsync 异常:" + ex.Message);
return HandwritingRecognitionResult.Empty;
}
if (rr == null || rr.Count == 0 || rr[0] == null)
{
if (traceRecognition)
LogHandwriting("整批回退:RecognizeAsync 无结果。");
return HandwritingRecognitionResult.Empty;
}
var cands = rr[0].GetTextCandidates();
var primary = (cands != null && cands.Count > 0) ? cands[0] : string.Empty;
if (string.IsNullOrWhiteSpace(primary))
{
if (traceRecognition)
LogHandwriting("整批回退:候选文本为空。");
return HandwritingRecognitionResult.Empty;
}
var merged = new List<string>();
if (cands != null)
{
foreach (var c in cands)
{
if (!string.IsNullOrEmpty(c) && !merged.Contains(c))
merged.Add(c);
}
}
var bounds = UnionStrokeBounds(strokes);
var group = new List<Stroke>();
foreach (Stroke s in strokes)
group.Add(s);
var seg = new HandwritingWordSegment(primary, merged, bounds, group);
return new HandwritingRecognitionResult(new List<HandwritingWordSegment> { seg });
}
private static Rect UnionStrokeBounds(StrokeCollection strokes)
{
if (strokes == null || strokes.Count == 0)
return Rect.Empty;
var r = strokes[0].GetBounds();
for (var i = 1; i < strokes.Count; i++)
r = Rect.Union(r, strokes[i].GetBounds());
return r;
}
private const string DefaultHandwritingFontFamilyList = "Ink Free,KaiTi,Segoe Script";
/// <summary>
+19 -1
View File
@@ -28,7 +28,8 @@ namespace Ink_Canvas.Helpers
{
try
{
await RecognizeShapeAsync(new StrokeCollection());
// 空 StrokeCollection 在 RecognizeShapeAsync 入口会直接返回,无法预热 WinRT InkAnalyzer。
await RecognizeShapeAsync(CreateMinimalWarmupStrokeCollection()).ConfigureAwait(true);
}
catch
{
@@ -99,6 +100,23 @@ namespace Ink_Canvas.Helpers
}
}
/// <summary>
/// 极短合成笔画,供 <see cref="Warmup"/> 等场景走完整 WinRT 转换与分析管线(空集合在入口处会被直接返回)。
/// </summary>
internal static StrokeCollection CreateMinimalWarmupStrokeCollection()
{
var da = new DrawingAttributes { Color = Colors.Black, Width = 2, Height = 2 };
var pts = new StylusPointCollection
{
new StylusPoint(8, 8),
new StylusPoint(14, 10),
new StylusPoint(20, 8),
};
var col = new StrokeCollection();
col.Add(new Stroke(pts, da));
return col;
}
/// <summary>供 WinRT 手写等模块复用:将 WPF <see cref="Stroke"/> 转为 WinRT <see cref="global::Windows.UI.Input.Inking.InkStroke"/>。</summary>
internal static global::Windows.UI.Input.Inking.InkStroke CreateInkStrokeFromWpf(Stroke stroke)
{
@@ -1,4 +1,4 @@
using Hardcodet.Wpf.TaskbarNotification;
using H.NotifyIcon;
using Microsoft.Toolkit.Uwp.Notifications;
using System;
using System.Windows;
@@ -40,10 +40,9 @@ namespace Ink_Canvas.Helpers
taskbar.Visibility = Visibility.Visible;
taskbar.ShowBalloonTip(
taskbar.ShowNotification(
"InkCanvasForClass CE",
$"发现新版本!:{version}",
BalloonIcon.Info);
$"发现新版本!:{version}");
}
catch
{
+4 -9
View File
@@ -114,6 +114,7 @@
<Reference Include="Microsoft.VisualBasic" />
<Reference Include="netstandard" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Management" />
@@ -135,12 +136,13 @@
<PackageReference Include="Costura.Fody" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="2.0.1" />
<PackageReference Include="H.NotifyIcon.Wpf" Version="2.0.131" />
<PackageReference Include="iNKORE.UI.WPF.Modern" Version="0.10.2.1" />
<PackageReference Include="iNKORE.UI.WPF" Version="1.2.8" />
<PackageReference Include="MdXaml" Version="1.27.0" />
<PackageReference Include="Microsoft.Office.Interop.PowerPoint" Version="15.0.4420.1018" />
<PackageReference Include="Microsoft.Windows.SDK.Contracts" Version="10.0.19041.2" />
<PackageReference Include="System.Runtime.WindowsRuntime" Version="4.7.0" />
<PackageReference Include="MicrosoftOfficeCore" Version="15.0.0" />
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
<PackageReference Include="Microsoft.International.Converters.PinYinConverter" Version="1.0.0" />
@@ -563,14 +565,7 @@
<ItemGroup>
<Compile Remove="AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Remove="Properties\Strings.en-US.resx" />
<EmbeddedResource Remove="**\Strings.en-US.resx" />
<None Include="Properties\Strings.en-US.resx" />
<EmbeddedResource Include="Properties\Strings.enUS.xml">
<LogicalName>Ink_Canvas.Properties.Strings.enUS.xml</LogicalName>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Remove="MainWindow.xaml~RF6c3144.TMP" />
<None Remove="Resources\Cursors\Cursor.cur" />
+104 -31
View File
@@ -247,21 +247,6 @@
<!-- 主要导航按钮 -->
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel>
<!-- Plugins -->
<Button Width="40" Height="40" Margin="0,5,0,0" Style="{StaticResource NavButton}"
Click="NavPlugins_Click" Tag="plugins" ToolTip="{x:Static props:Strings.Nav_Plugins}">
<Image Width="20" Height="20">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<GeometryDrawing Brush="White"
Geometry="M342.826667 213.333333a149.333333 149.333333 0 0 1 295.68 0H682.666667a128 128 0 0 1 128 128v44.16a149.333333 149.333333 0 0 1 0 295.68V725.333333a128 128 0 0 1-128 128h-128v-21.333333a64 64 0 0 0-128 0v21.333333H298.666667a128 128 0 0 1-128-128v-128h21.333333a64 64 0 0 0 0-128H170.666667V341.333333a128 128 0 0 1 128-128h44.16zM426.666667 234.666667V298.666667H298.666667q-17.664 0-30.165334 12.501333T256 341.333333v56.576q22.528 10.752 41.6 29.866667Q341.333333 471.466667 341.333333 533.333333q0 61.866667-43.733333 105.6-19.072 19.072-41.6 29.824V725.333333q0 17.664 12.501333 30.165334T298.666667 768h56.576q10.752-22.528 29.866666-41.6Q428.8 682.666667 490.666667 682.666667q61.866667 0 105.6 43.733333 19.072 19.072 29.824 41.6H682.666667q17.664 0 30.165333-12.501333T725.333333 725.333333v-128h64a64 64 0 0 0 0-128H725.333333V341.333333q0-17.664-12.501333-30.165333T682.666667 298.666667h-128V234.666667a64 64 0 1 0-128 0z"/>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Button>
<!-- Startup -->
<Button Width="40" Height="40" Margin="0,10,0,0" Style="{StaticResource NavButton}"
Click="NavStartup_Click" Tag="startup" ToolTip="{x:Static props:Strings.Nav_Startup}">
@@ -658,28 +643,15 @@
</ikw:SimpleStackPanel>
</GroupBox>
<GroupBox Name="GroupBoxNewSettings">
<GroupBox Name="GroupBoxSettings">
<GroupBox.Header>
<TextBlock Margin="0,12,0,0" Text="{i18n:I18n Key=Settings_NewWindow}" FontWeight="Bold" Foreground="#fafafa"
FontSize="26" />
</GroupBox.Header>
<ikw:SimpleStackPanel Spacing="6" Margin="0,10,0,0">
<TextBlock TextWrapping="Wrap" Margin="0,0,0,10" Foreground="#fafafa" Text="{i18n:I18n Key=Settings_NewWindowDesc}"/>
<Button x:Name="BtnOpenNewSettings" Content="{i18n:I18n Key=Btn_OpenNewSettings}"
HorizontalAlignment="Left" Click="BtnOpenNewSettings_Click"
Padding="15,5" Margin="0,10,0,0"/>
</ikw:SimpleStackPanel>
</GroupBox>
<GroupBox Name="GroupBoxPlugins">
<GroupBox.Header>
<TextBlock Margin="0,12,0,0" Text="{i18n:I18n Key=Settings_Plugins}" FontWeight="Bold" Foreground="#fafafa"
FontSize="26" />
</GroupBox.Header>
<ikw:SimpleStackPanel Spacing="6" Margin="0,10,0,0">
<TextBlock TextWrapping="Wrap" Margin="0,0,0,10" Foreground="#fafafa" Text="{i18n:I18n Key=Settings_PluginsDesc}"/>
<Button x:Name="BtnOpenPluginManager" Content="{i18n:I18n Key=Btn_OpenPluginManager}"
HorizontalAlignment="Left" Click="BtnOpenPluginManager_Click"
<Button x:Name="BtnOpenSettings" Content="{i18n:I18n Key=Btn_OpenNewSettings}"
HorizontalAlignment="Left" Click="BtnOpenNewNewSettings_Click"
Padding="15,5" Margin="0,10,0,0"/>
</ikw:SimpleStackPanel>
</GroupBox>
@@ -4976,6 +4948,107 @@
</Viewbox>
</Border>
<Border Name="BorderPdfPageSidebar"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="0,0,0,0"
Width="64"
CornerRadius="8"
Background="{DynamicResource FloatBarBackground}"
BorderBrush="#33FFFFFF"
BorderThickness="1"
SnapsToDevicePixels="True"
Visibility="Collapsed"
Panel.ZIndex="1001"
ui:ThemeManager.RequestedTheme="Dark">
<Border.Effect>
<DropShadowEffect BlurRadius="24"
ShadowDepth="3"
Direction="270"
Opacity="0.45"
Color="#000000" />
</Border.Effect>
<Border.Resources>
<Style x:Key="PdfSidebarNavButtonStyle" TargetType="Border">
<Setter Property="Width" Value="40" />
<Setter Property="Height" Value="36" />
<Setter Property="CornerRadius" Value="4" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Background" Value="Transparent" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="#FFFFFF" Opacity="0.08" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Border.Resources>
<Grid>
<ikw:SimpleStackPanel VerticalAlignment="Center"
HorizontalAlignment="Center"
Spacing="8"
Margin="0,10,0,10">
<TextBlock Text="PDF"
FontSize="10"
FontWeight="SemiBold"
HorizontalAlignment="Center"
Foreground="{DynamicResource FloatBarForeground}"
Opacity="0.78" />
<Border CornerRadius="4"
Padding="8,6"
BorderThickness="0"
SnapsToDevicePixels="True"
Background="#14FFFFFF">
<TextBlock Name="TextBlockPdfSidebarPageLabel"
Text="— / —"
FontSize="12"
FontWeight="SemiBold"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextAlignment="Center"
Foreground="{DynamicResource FloatBarForeground}"
Opacity="0.55" />
</Border>
<Border Name="BorderPdfSidebarPagePrev"
Style="{StaticResource PdfSidebarNavButtonStyle}"
Opacity="0.35"
IsHitTestVisible="False"
ToolTip="上一页"
MouseDown="Border_MouseDown"
MouseUp="BorderPdfSidebarPagePrev_MouseUp">
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.ChevronLeft}"
FontSize="14"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="{DynamicResource FloatBarForeground}" />
</Border>
<Border Name="BorderPdfSidebarPageNext"
Style="{StaticResource PdfSidebarNavButtonStyle}"
Opacity="0.35"
IsHitTestVisible="False"
ToolTip="下一页"
MouseDown="Border_MouseDown"
MouseUp="BorderPdfSidebarPageNext_MouseUp">
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.ChevronRight}"
FontSize="14"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="{DynamicResource FloatBarForeground}" />
</Border>
<TextBlock Text="翻页"
FontSize="9"
HorizontalAlignment="Center"
Foreground="{DynamicResource FloatBarForeground}"
Opacity="0.55" />
</ikw:SimpleStackPanel>
</Grid>
</Border>
<!-- 图片缩放选择点 -->
<Canvas Name="ImageResizeHandlesCanvas"
Visibility="Collapsed"
+36 -94
View File
@@ -1,5 +1,4 @@
using Ink_Canvas.Helpers;
using Ink_Canvas.Helpers.Plugins;
using Ink_Canvas.Windows;
using iNKORE.UI.WPF.Modern;
using iNKORE.UI.WPF.Modern.Controls;
@@ -83,6 +82,8 @@ namespace Ink_Canvas
private static Cursor _cachedPenCursor = null;
private static readonly object _cursorLock = new object();
internal static DateTime? TrayTemporaryShowUntilUtc;
#region Window Initialization
/// <summary>
@@ -1366,8 +1367,6 @@ namespace Ink_Canvas
}), DispatcherPriority.Loaded);
}
// 初始化插件系统
InitializePluginSystem();
// 确保开关和设置同步
ToggleSwitchNoFocusMode.IsOn = Settings.Advanced.IsNoFocusMode;
ApplyNoFocusMode();
@@ -1410,6 +1409,7 @@ namespace Ink_Canvas
// 检查模式设置并应用
CheckMainWindowVisibility();
EnsurePptOnlyVisibilityProbeTimer();
// 检查是否通过--board参数启动,如果是则自动切换到白板模式
if (App.StartWithBoardMode)
@@ -2578,9 +2578,6 @@ namespace Ink_Canvas
case "about":
targetGroupBox = GroupBoxAbout;
break;
case "plugins":
targetGroupBox = GroupBoxPlugins;
break;
default:
// 默认滚动到顶部
SettingsPanelScrollViewer.ScrollToTop();
@@ -2728,9 +2725,6 @@ namespace Ink_Canvas
case "about":
SetNavButtonTag("about");
break;
case "plugins":
SetNavButtonTag("plugins");
break;
}
}
@@ -2817,97 +2811,18 @@ namespace Ink_Canvas
#endregion Navigation Sidebar Methods
#region ???
// 添加插件系统初始化方法
private void InitializePluginSystem()
{
try
{
// 初始化插件管理器
PluginManager.Instance.Initialize();
LogHelper.WriteLogToFile("插件系统已初始化");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"初始化插件系统时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
// 添加插件管理导航点击事件处理
private void NavPlugins_Click(object sender, RoutedEventArgs e)
{
ShowSettingsSection("plugins");
}
// 添加打开插件管理器按钮点击事件
private void BtnOpenPluginManager_Click(object sender, RoutedEventArgs e)
{
try
{
// 暂时隐藏设置面板
BorderSettings.Visibility = Visibility.Hidden;
BorderSettingsMask.Visibility = Visibility.Hidden;
// 创建并显示插件设置窗口
PluginSettingsWindow pluginSettingsWindow = new PluginSettingsWindow();
// 设置窗口关闭事件,用于在插件管理窗口关闭后恢复设置面板
pluginSettingsWindow.Closed += (s, args) =>
{
// 恢复设置面板显示
BorderSettings.Visibility = Visibility.Visible;
BorderSettingsMask.Visibility = Visibility.Visible;
};
// 显示插件设置窗口
pluginSettingsWindow.ShowDialog();
}
catch (Exception ex)
{
// 确保在发生错误时也恢复设置面板显示
BorderSettings.Visibility = Visibility.Visible;
BorderSettingsMask.Visibility = Visibility.Visible;
LogHelper.WriteLogToFile($"打开插件管理器时出错: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"打开插件管理器时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
#endregion ???
#region
/// <summary>
/// 在隐藏子面板后打开新的设置窗口;若需要则先提示并验证安全密码,并在正在打开或隐藏设置面板时不执行任何操作。
/// </summary>
/// <remarks>
/// 在验证密码失败或发生异常时会中止操作。成功通过验证后以模式窗口方式显示设置窗口并将当前窗口设为其所有者。
/// </remarks>
private async void BtnOpenNewSettings_Click(object sender, RoutedEventArgs e)
private async void BtnOpenNewNewSettings_Click(object sender, RoutedEventArgs e)
{
if (isOpeningOrHidingSettingsPane) return;
HideSubPanels();
{
try
{
if (SecurityManager.IsPasswordRequiredForEnterSettings(Settings))
{
bool ok = await SecurityManager.PromptAndVerifyAsync(Settings, this, "进入设置", "请输入安全密码以进入设置。");
if (!ok) return;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"安全密码校验失败: {ex}", LogHelper.LogType.Error);
return;
}
var settingsWindow = new SettingsWindow();
var settingsWindow = new Windows.SettingsViews.SettingsWindow();
settingsWindow.Owner = this;
settingsWindow.ShowDialog();
}
}
#endregion
// 在MainWindow类中添加:
@@ -4444,9 +4359,11 @@ namespace Ink_Canvas
{
Hide();
LogHelper.WriteLogToFile("已切换到仅PPT模式,主窗口已隐藏", LogHelper.LogType.Event);
EnsurePptOnlyVisibilityProbeTimer();
}
else
{
StopPptOnlyVisibilityProbeTimer();
// 如果切换到正常模式,显示主窗口
Show();
LogHelper.WriteLogToFile("已切换到正常模式,主窗口已显示", LogHelper.LogType.Event);
@@ -4462,14 +4379,23 @@ namespace Ink_Canvas
/// <summary>
/// 检查是否应该显示主窗口(基于PPT模式和PPT放映状态)
/// </summary>
private void CheckMainWindowVisibility()
internal void CheckMainWindowVisibility()
{
try
{
if (Settings.ModeSettings.IsPPTOnlyMode)
{
// 仅PPT模式下,只有在PPT放映时才显示
bool isInSlideShow = BtnPPTSlideShowEnd.Visibility == Visibility.Visible;
if (TrayTemporaryShowUntilUtc.HasValue && DateTime.UtcNow < TrayTemporaryShowUntilUtc.Value)
{
if (!IsVisible)
Show();
return;
}
// 仅PPT模式:以 COM/UI 状态为主,Win32 检测全屏放映窗口(screenClass)作兜底,避免 COM 异常时无法唤出
bool comUiSlideShow = BtnPPTSlideShowEnd.Visibility == Visibility.Visible;
bool win32SlideShow = IsPowerPointSlideshowSurfacePresentWin32();
bool isInSlideShow = comUiSlideShow || win32SlideShow;
if (isInSlideShow && !IsVisible)
{
Show();
@@ -4785,6 +4711,22 @@ namespace Ink_Canvas
}
}
internal void OpenQuickDrawFromHotkey()
{
try
{
if (Settings?.RandSettings?.EnableQuickDraw != true)
return;
var quickDrawWindow = new QuickDrawWindow();
quickDrawWindow.ShowDialog();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"打开快抽窗口失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 显示快抽悬浮按钮
/// </summary>
+20 -2
View File
@@ -1,3 +1,4 @@
using Ink_Canvas.Controls;
using Ink_Canvas.Helpers;
using System;
using System.Collections.Generic;
@@ -81,7 +82,7 @@ namespace Ink_Canvas
var missingElements = 0;
foreach (UIElement child in inkCanvas.Children)
{
if (child is Image || child is MediaElement)
if (child is Image || child is MediaElement || child is PdfEmbeddedView)
{
if (child is Image img && img.Tag is string tag && tag == VideoPresenterLiveFrameTag)
{
@@ -225,6 +226,7 @@ namespace Ink_Canvas
if (TimeMachineHistories[targetIndex] == null)
{
timeMachine.ClearStrokeHistory();
SyncPdfPageSidebarWithCanvas();
return;
}
@@ -281,7 +283,11 @@ namespace Ink_Canvas
/// </summary>
private void ProcessElementsAfterRestore(List<UIElement> elements)
{
if (elements == null || elements.Count == 0) return;
if (elements == null || elements.Count == 0)
{
SyncPdfPageSidebarWithCanvas();
return;
}
// 使用低优先级异步处理,让 UI 先响应,图片位置和事件绑定稍后完成
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() =>
@@ -310,7 +316,19 @@ namespace Ink_Canvas
}
BindElementEvents(media);
}
else if (element is PdfEmbeddedView pdf)
{
double left = InkCanvas.GetLeft(pdf);
double top = InkCanvas.GetTop(pdf);
if (double.IsNaN(left) || double.IsNaN(top))
{
CenterAndScaleElement(pdf);
}
BindElementEvents(pdf);
}
}
SyncPdfPageSidebarWithCanvas();
}));
}
+1
View File
@@ -52,6 +52,7 @@ namespace Ink_Canvas
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
EnableTwoFingerGestureBorder.Visibility = Visibility.Collapsed;
SyncPdfPageSidebarWithCanvas();
}
BtnHideInkCanvas_Click(BtnHideInkCanvas, null);
+490 -46
View File
@@ -1,3 +1,4 @@
using Ink_Canvas.Controls;
using Ink_Canvas.Helpers;
using Microsoft.Win32;
using System;
@@ -34,6 +35,14 @@ namespace Ink_Canvas
/// </summary>
private Point dragStartPoint;
/// <summary>页码侧栏当前订阅 <see cref="PdfEmbeddedView.PageNavigationStateChanged"/> 的 PDF 视图。</summary>
private PdfEmbeddedView _pdfPageSidebarEventSource;
private bool _pdfSidebarPositionRefreshPending;
/// <summary>为 true 时,下一次成功算出 PDF 边界后的侧栏定位使用宿主 Visual 变换(仅用于刚插入/恢复 PDF 的首帧对齐)。</summary>
private bool _pdfSidebarNextPositionUseHostTransform;
#region Image
/// <summary>
/// 处理图片插入按钮点击事件
@@ -56,48 +65,52 @@ namespace Ink_Canvas
private async void BtnImageInsert_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "Image files (*.jpg; *.jpeg; *.png; *.bmp)|*.jpg;*.jpeg;*.png;*.bmp";
openFileDialog.Filter = "图片与 PDF|*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.pdf|图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif|PDF|*.pdf";
if (openFileDialog.ShowDialog() == true)
{
string filePath = openFileDialog.FileName;
Image image = await CreateAndCompressImageAsync(filePath);
FrameworkElement element = await CreateAndCompressImageAsync(filePath);
if (image != null)
if (element != null)
{
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
image.Name = timestamp;
element.Name = timestamp;
// 设置图片属性,避免被InkCanvas选择系统处理
image.IsHitTestVisible = true;
image.Focusable = false;
element.IsHitTestVisible = true;
element.Focusable = false;
// 初始化InkCanvas选择设置
InitializeInkCanvasSelectionSettings();
// 先添加到画布
inkCanvas.Children.Add(image);
inkCanvas.Children.Add(element);
// 等待图片加载完成后再进行后续处理
image.Loaded += (s, args) =>
element.Loaded += (s, args) =>
{
Dispatcher.BeginInvoke(new Action(() =>
{
// 初始化TransformGroup
InitializeElementTransform(image);
InitializeElementTransform(element);
// 居中缩放
CenterAndScaleElement(image);
CenterAndScaleElement(element);
// 最后绑定事件处理器
BindElementEvents(image);
BindElementEvents(element);
LogHelper.WriteLogToFile($"图片插入完成: {image.Name}");
if (element is PdfEmbeddedView)
_pdfSidebarNextPositionUseHostTransform = true;
SyncPdfPageSidebarWithCanvas();
LogHelper.WriteLogToFile($"图片插入完成: {element.Name}");
}), DispatcherPriority.Loaded);
};
timeMachine.CommitElementInsertHistory(image);
timeMachine.CommitElementInsertHistory(element);
// 插入图片后切换到选择模式并刷新浮动栏高光显示
SetCurrentToolMode(InkCanvasEditingMode.Select);
@@ -270,13 +283,13 @@ namespace Ink_Canvas
ApplyMouseDragTransform(element, currentPoint, dragStartPoint);
// 如果是图片元素,更新工具栏位置
if (element is Image && BorderImageSelectionControl?.Visibility == Visibility.Visible)
if (IsBitmapLikeCanvasElement(element) && BorderImageSelectionControl?.Visibility == Visibility.Visible)
{
UpdateImageSelectionToolbarPosition(element);
}
// 如果是图片元素,更新选择点位置
if (element is Image && ImageResizeHandlesCanvas?.Visibility == Visibility.Visible)
if (IsBitmapLikeCanvasElement(element) && ImageResizeHandlesCanvas?.Visibility == Visibility.Visible)
{
UpdateImageResizeHandlesPosition(GetElementActualBounds(element));
}
@@ -306,13 +319,13 @@ namespace Ink_Canvas
ApplyWheelScaleTransform(element, e);
// 如果是图片元素,更新工具栏位置
if (element is Image && BorderImageSelectionControl?.Visibility == Visibility.Visible)
if (IsBitmapLikeCanvasElement(element) && BorderImageSelectionControl?.Visibility == Visibility.Visible)
{
UpdateImageSelectionToolbarPosition(element);
}
// 如果是图片元素,更新选择点位置
if (element is Image && ImageResizeHandlesCanvas?.Visibility == Visibility.Visible)
if (IsBitmapLikeCanvasElement(element) && ImageResizeHandlesCanvas?.Visibility == Visibility.Visible)
{
UpdateImageResizeHandlesPosition(GetElementActualBounds(element));
}
@@ -396,13 +409,13 @@ namespace Ink_Canvas
ApplyTouchManipulationTransform(element, e);
// 如果是图片元素,更新工具栏位置
if (element is Image && BorderImageSelectionControl?.Visibility == Visibility.Visible)
if (IsBitmapLikeCanvasElement(element) && BorderImageSelectionControl?.Visibility == Visibility.Visible)
{
UpdateImageSelectionToolbarPosition(element);
}
// 如果是图片元素,更新选择点位置
if (element is Image && ImageResizeHandlesCanvas?.Visibility == Visibility.Visible)
if (IsBitmapLikeCanvasElement(element) && ImageResizeHandlesCanvas?.Visibility == Visibility.Visible)
{
UpdateImageResizeHandlesPosition(GetElementActualBounds(element));
}
@@ -523,12 +536,12 @@ namespace Ink_Canvas
currentSelectedElement = element;
// 根据元素类型显示不同的选择工具栏
if (element is Image)
if (IsBitmapLikeCanvasElement(element))
{
// 显示图片选择工具栏并设置位置
if (BorderImageSelectionControl != null)
{
// 计算工具栏位置
// 计算工具栏位置(内部会同步 PDF 右侧栏位置)
UpdateImageSelectionToolbarPosition(element);
BorderImageSelectionControl.Visibility = Visibility.Visible;
}
@@ -568,6 +581,8 @@ namespace Ink_Canvas
// 保持选择模式,这样用户可以直接点击墨迹来选择
inkCanvas.EditingMode = InkCanvasEditingMode.Select;
}
SyncPdfPageSidebarWithCanvas();
}
/// <summary>
@@ -607,6 +622,8 @@ namespace Ink_Canvas
{
inkCanvas.EditingMode = InkCanvasEditingMode.Select;
}
SyncPdfPageSidebarWithCanvas();
}
/// <summary>
@@ -914,15 +931,24 @@ namespace Ink_Canvas
/// - 否则使用原始尺寸
/// - 返回创建的Image对象
/// </remarks>
private async Task<Image> CreateAndCompressImageAsync(string filePath)
/// <summary>与图片选择工具栏、缩放控制点联动的画布位图类元素(普通图片或多页 PDF 嵌入)。</summary>
private static bool IsBitmapLikeCanvasElement(FrameworkElement fe)
{
return fe is Image || fe is PdfEmbeddedView;
}
private async Task<FrameworkElement> CreateAndCompressImageAsync(string filePath)
{
string fileExtension = Path.GetExtension(filePath);
if (string.Equals(fileExtension, ".pdf", StringComparison.OrdinalIgnoreCase))
return await CreateAndCompressImageFromPdfAsync(filePath);
string savePath = Path.Combine(Settings.Automation.AutoSavedStrokesLocation, "File Dependency");
if (!Directory.Exists(savePath))
{
Directory.CreateDirectory(savePath);
}
string fileExtension = Path.GetExtension(filePath);
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
string newFilePath = Path.Combine(savePath, timestamp + fileExtension);
@@ -965,6 +991,84 @@ namespace Ink_Canvas
return image;
});
}
/// <summary>
/// 插入完整 PDF:嵌入控件内可翻页,右下角显示页码(类似希沃白板交互)。
/// </summary>
private async Task<PdfEmbeddedView> CreateAndCompressImageFromPdfAsync(string filePath)
{
try
{
string savePath = Path.Combine(Settings.Automation.AutoSavedStrokesLocation, "File Dependency");
if (!Directory.Exists(savePath))
Directory.CreateDirectory(savePath);
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
string newFilePath = Path.Combine(savePath, timestamp + ".pdf");
await Task.Run(() => File.Copy(filePath, newFilePath, true));
uint pageCount = await PdfWinRtHelper.GetPageCountAsync(newFilePath);
if (pageCount == 0)
{
ShowNotification("无法打开 PDF(可能已加密、损坏或不支持)。");
return null;
}
bool compress = isLoaded && Settings.Canvas.IsCompressPicturesUploaded;
var view = new PdfEmbeddedView();
await view.InitializeAsync(newFilePath, pageCount, compress);
view.Tag = filePath;
return view;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"插入 PDF 失败: {ex.Message}", LogHelper.LogType.Error);
ShowNotification($"插入 PDF 失败: {ex.Message}");
return null;
}
}
/// <summary>从保存的 <see cref="CanvasElementInfo"/> 恢复 PDF(与打开墨迹时的图片恢复流程一致,不单独写入时间轴)。</summary>
private async Task RestorePdfFromElementInfoAsync(CanvasElementInfo info)
{
if (info == null || inkCanvas == null) return;
if (!string.Equals(info.Type, "Pdf", StringComparison.OrdinalIgnoreCase)) return;
if (string.IsNullOrEmpty(info.SourcePath) || !File.Exists(info.SourcePath)) return;
try
{
uint pageCount = await PdfWinRtHelper.GetPageCountAsync(info.SourcePath);
if (pageCount == 0) return;
bool compress = isLoaded && Settings.Canvas.IsCompressPicturesUploaded;
uint initial = 0;
if (info.PdfCurrentPage.HasValue)
initial = (uint)Math.Max(0, Math.Min(info.PdfCurrentPage.Value, (int)pageCount - 1));
var view = new PdfEmbeddedView
{
Name = "pdf_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff")
};
await view.InitializeAsync(info.SourcePath, pageCount, compress, initial);
if (info.Width > 0) view.Width = info.Width;
if (info.Height > 0) view.Height = info.Height;
InkCanvas.SetLeft(view, info.Left);
InkCanvas.SetTop(view, info.Top);
InitializeElementTransform(view);
BindElementEvents(view);
inkCanvas.Children.Add(view);
_pdfSidebarNextPositionUseHostTransform = true;
SyncPdfPageSidebarWithCanvas();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"从 .elements.json 恢复 PDF 失败: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
#region Media
@@ -1453,6 +1557,10 @@ namespace Ink_Canvas
// 设置工具栏位置
BorderImageSelectionControl.Margin = new Thickness(toolbarLeft, toolbarTop, 0, 0);
var pdfTarget = GetPdfSidebarTargetElement();
if (pdfTarget != null && BorderPdfPageSidebar != null && BorderPdfPageSidebar.Visibility == Visibility.Visible)
UpdatePdfPageSidebarPosition(pdfTarget);
}
catch (Exception ex)
{
@@ -1460,6 +1568,245 @@ namespace Ink_Canvas
}
}
private const double PdfPageSidebarGap = 10;
/// <summary>
/// 侧栏绑定的 PDF:若当前选中的是 PDF 则用该项;否则用画布上最后一个 PdfEmbeddedView。
/// </summary>
private PdfEmbeddedView GetPdfSidebarTargetElement()
{
if (inkCanvas == null) return null;
var pdfs = inkCanvas.Children.OfType<PdfEmbeddedView>().ToList();
if (pdfs.Count == 0) return null;
if (currentSelectedElement is PdfEmbeddedView sel && pdfs.Contains(sel))
return sel;
return pdfs[pdfs.Count - 1];
}
private void AttachPdfPageSidebarEvents(PdfEmbeddedView pdf)
{
if (pdf == null || _pdfPageSidebarEventSource == pdf) return;
DetachPdfPageSidebarEvents();
_pdfPageSidebarEventSource = pdf;
_pdfPageSidebarEventSource.PageNavigationStateChanged += SelectedPdf_PageNavigationStateChanged;
_pdfPageSidebarEventSource.LayoutUpdated += OnPdfSidebarTargetLayoutUpdated;
}
private void DetachPdfPageSidebarEvents()
{
if (_pdfPageSidebarEventSource != null)
{
_pdfPageSidebarEventSource.PageNavigationStateChanged -= SelectedPdf_PageNavigationStateChanged;
_pdfPageSidebarEventSource.LayoutUpdated -= OnPdfSidebarTargetLayoutUpdated;
_pdfPageSidebarEventSource = null;
}
}
private void OnPdfSidebarTargetLayoutUpdated(object sender, EventArgs e)
{
if (BorderPdfPageSidebar?.Visibility != Visibility.Visible || inkCanvas == null) return;
if (!(sender is PdfEmbeddedView p) || !ReferenceEquals(_pdfPageSidebarEventSource, p)) return;
if (!inkCanvas.Children.Contains(p))
SyncPdfPageSidebarWithCanvas();
else
RequestPdfSidebarPositionRefresh();
}
/// <summary>在下一帧合并更新侧栏位置(初始布局、翻页改尺寸后 ActualWidth 仍为 0 时尤其需要)。</summary>
private void RequestPdfSidebarPositionRefresh()
{
if (_pdfSidebarPositionRefreshPending) return;
_pdfSidebarPositionRefreshPending = true;
Dispatcher.BeginInvoke(new Action(() =>
{
_pdfSidebarPositionRefreshPending = false;
try
{
var t = GetPdfSidebarTargetElement();
if (t == null || BorderPdfPageSidebar?.Visibility != Visibility.Visible)
{
SyncPdfPageSidebarWithCanvas();
return;
}
if (!inkCanvas.Children.Contains(t))
{
SyncPdfPageSidebarWithCanvas();
return;
}
t.UpdateLayout();
inkCanvas.UpdateLayout();
UpdatePdfPageSidebarPosition(t);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"PDF 侧栏延迟定位失败: {ex.Message}", LogHelper.LogType.Warning);
}
}), DispatcherPriority.Render);
}
/// <summary>
/// 画布上存在 PDF 时始终显示右侧页码栏并跟随目标 PDF;无任何 PDF 时隐藏。
/// </summary>
private void SyncPdfPageSidebarWithCanvas()
{
if (BorderPdfPageSidebar == null || inkCanvas == null) return;
// 屏幕模式(已退出白板/黑板)下不显示侧栏,避免画布仍含 PDF 时栏残留在桌面上
if (currentMode == 0)
{
DetachPdfPageSidebarEvents();
BorderPdfPageSidebar.Visibility = Visibility.Collapsed;
ResetPdfSidebarToIdle();
return;
}
var pdf = GetPdfSidebarTargetElement();
if (pdf == null)
{
DetachPdfPageSidebarEvents();
BorderPdfPageSidebar.Visibility = Visibility.Collapsed;
ResetPdfSidebarToIdle();
return;
}
AttachPdfPageSidebarEvents(pdf);
BorderPdfPageSidebar.Visibility = Visibility.Visible;
UpdatePdfSidebarFromPdf(pdf);
pdf.UpdateLayout();
inkCanvas.UpdateLayout();
UpdatePdfPageSidebarPosition(pdf);
RequestPdfSidebarPositionRefresh();
Dispatcher.BeginInvoke(new Action(() =>
{
var t = GetPdfSidebarTargetElement();
if (t != null && BorderPdfPageSidebar?.Visibility == Visibility.Visible && inkCanvas.Children.Contains(t))
UpdatePdfPageSidebarPosition(t);
}), DispatcherPriority.ContextIdle);
}
/// <summary>
/// 将 PDF 专用页码栏贴在当前 PDF 右侧。常态与早期实现一致:画布坐标 + <c>Measure(Width, ∞)</c>;仅在 <see cref="_pdfSidebarNextPositionUseHostTransform"/> 为 true 时用宿主 Visual 变换对齐首帧。
/// </summary>
private void UpdatePdfPageSidebarPosition(FrameworkElement element)
{
try
{
if (BorderPdfPageSidebar == null || inkCanvas == null || !(element is PdfEmbeddedView pdfEl))
return;
if (!inkCanvas.Children.Contains(pdfEl))
{
SyncPdfPageSidebarWithCanvas();
return;
}
bool wantHostOnce = _pdfSidebarNextPositionUseHostTransform;
// 插入首帧:先布局再取界,便于 Transform 与墨迹一致;常态与最初 PDF 侧栏实现一致,不在此强制 UpdateLayout。
if (wantHostOnce)
pdfEl.UpdateLayout();
Rect b = GetElementActualBounds(pdfEl);
if (b.Width <= 0 || b.Height <= 0 || double.IsNaN(b.Width) || double.IsNaN(b.Height))
{
Dispatcher.BeginInvoke(new Action(() =>
{
var t = GetPdfSidebarTargetElement();
if (t is PdfEmbeddedView pe && inkCanvas.Children.Contains(pe) && BorderPdfPageSidebar?.Visibility == Visibility.Visible)
UpdatePdfPageSidebarPosition(pe);
}), DispatcherPriority.Loaded);
return;
}
Visual sidebarHost = VisualTreeHelper.GetParent(BorderPdfPageSidebar) as Visual;
double left = 0, top = 0, maxLeft = 0, maxTop = 0;
bool hostOk = false;
if (wantHostOnce && sidebarHost != null)
{
BorderPdfPageSidebar.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
double sidebarW = BorderPdfPageSidebar.DesiredSize.Width;
double sidebarH = BorderPdfPageSidebar.DesiredSize.Height;
if (sidebarW <= 0)
sidebarW = BorderPdfPageSidebar.Width;
if (sidebarH <= 0)
sidebarH = BorderPdfPageSidebar.ActualHeight;
if (sidebarH <= 0)
sidebarH = 220;
try
{
GeneralTransform inkToHost = inkCanvas.TransformToVisual(sidebarHost);
Point tl = inkToHost.Transform(new Point(b.Left, b.Top));
Point br = inkToHost.Transform(new Point(b.Right, b.Bottom));
double rightX = Math.Max(tl.X, br.X);
double midY = (tl.Y + br.Y) * 0.5;
left = rightX + PdfPageSidebarGap;
top = midY - sidebarH * 0.5;
var feHost = sidebarHost as FrameworkElement;
double hostW = feHost != null && feHost.ActualWidth > 0 ? feHost.ActualWidth : inkCanvas.ActualWidth;
double hostH = feHost != null && feHost.ActualHeight > 0 ? feHost.ActualHeight : inkCanvas.ActualHeight;
maxLeft = Math.Max(0, hostW - sidebarW);
maxTop = Math.Max(0, hostH - sidebarH);
if (left > maxLeft)
{
double leftEdge = Math.Min(tl.X, br.X);
double leftAlt = leftEdge - PdfPageSidebarGap - sidebarW;
if (leftAlt >= 0)
left = leftAlt;
}
hostOk = true;
}
catch
{
hostOk = false;
}
}
if (!hostOk)
{
// 与 ea74592「PDF 侧栏」初版一致:固定宽度测量竖向所需高度,再按墨迹边界与 inkCanvas 尺寸夹紧。
BorderPdfPageSidebar.Measure(new Size(BorderPdfPageSidebar.Width, double.PositiveInfinity));
double sidebarW = BorderPdfPageSidebar.DesiredSize.Width;
double sidebarH = BorderPdfPageSidebar.DesiredSize.Height;
if (sidebarW <= 0)
sidebarW = BorderPdfPageSidebar.Width;
if (sidebarH <= 0)
sidebarH = BorderPdfPageSidebar.ActualHeight;
if (sidebarH <= 0)
sidebarH = 220;
left = b.Right + PdfPageSidebarGap;
top = b.Top + (b.Height * 0.5) - (sidebarH * 0.5);
maxLeft = Math.Max(0, inkCanvas.ActualWidth - sidebarW);
maxTop = Math.Max(0, inkCanvas.ActualHeight - sidebarH);
if (left > maxLeft)
{
double leftAlt = b.Left - PdfPageSidebarGap - sidebarW;
if (leftAlt >= 0)
left = leftAlt;
}
}
if (wantHostOnce)
_pdfSidebarNextPositionUseHostTransform = false;
left = Math.Max(0, Math.Min(left, maxLeft));
top = Math.Max(0, Math.Min(top, maxTop));
BorderPdfPageSidebar.Margin = new Thickness(left, top, 0, 0);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新 PDF 右侧页码栏位置失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 获取元素的实际边界(考虑变换)
/// </summary>
@@ -1644,7 +1991,7 @@ namespace Ink_Canvas
ApplyRotateTransform(currentSelectedElement, -45);
// 更新工具栏位置
if (currentSelectedElement is Image && BorderImageSelectionControl?.Visibility == Visibility.Visible)
if (IsBitmapLikeCanvasElement(currentSelectedElement) && BorderImageSelectionControl?.Visibility == Visibility.Visible)
{
UpdateImageSelectionToolbarPosition(currentSelectedElement);
}
@@ -1678,7 +2025,7 @@ namespace Ink_Canvas
ApplyRotateTransform(currentSelectedElement, 45);
// 更新工具栏位置
if (currentSelectedElement is Image && BorderImageSelectionControl?.Visibility == Visibility.Visible)
if (IsBitmapLikeCanvasElement(currentSelectedElement) && BorderImageSelectionControl?.Visibility == Visibility.Visible)
{
UpdateImageSelectionToolbarPosition(currentSelectedElement);
}
@@ -1714,7 +2061,7 @@ namespace Ink_Canvas
ApplyScaleTransform(currentSelectedElement, 0.9, elementCenter);
// 更新工具栏位置
if (currentSelectedElement is Image && BorderImageSelectionControl?.Visibility == Visibility.Visible)
if (IsBitmapLikeCanvasElement(currentSelectedElement) && BorderImageSelectionControl?.Visibility == Visibility.Visible)
{
UpdateImageSelectionToolbarPosition(currentSelectedElement);
}
@@ -1750,7 +2097,7 @@ namespace Ink_Canvas
ApplyScaleTransform(currentSelectedElement, 1.1, elementCenter);
// 更新工具栏位置
if (currentSelectedElement is Image && BorderImageSelectionControl?.Visibility == Visibility.Visible)
if (IsBitmapLikeCanvasElement(currentSelectedElement) && BorderImageSelectionControl?.Visibility == Visibility.Visible)
{
UpdateImageSelectionToolbarPosition(currentSelectedElement);
}
@@ -1764,19 +2111,110 @@ namespace Ink_Canvas
}
}
private void ResetPdfSidebarToIdle()
{
if (TextBlockPdfSidebarPageLabel != null)
{
TextBlockPdfSidebarPageLabel.Text = "— / —";
TextBlockPdfSidebarPageLabel.Opacity = 0.55;
}
if (BorderPdfSidebarPagePrev != null)
{
BorderPdfSidebarPagePrev.Opacity = 0.35;
BorderPdfSidebarPagePrev.IsHitTestVisible = false;
}
if (BorderPdfSidebarPageNext != null)
{
BorderPdfSidebarPageNext.Opacity = 0.35;
BorderPdfSidebarPageNext.IsHitTestVisible = false;
}
}
private void UpdatePdfSidebarFromPdf(PdfEmbeddedView pdf)
{
if (pdf == null) return;
if (TextBlockPdfSidebarPageLabel != null)
{
TextBlockPdfSidebarPageLabel.Text = pdf.PageLabelText;
TextBlockPdfSidebarPageLabel.Opacity = 1.0;
}
bool prevOk = pdf.CanGoPrevious;
bool nextOk = pdf.CanGoNext;
if (BorderPdfSidebarPagePrev != null)
{
BorderPdfSidebarPagePrev.Opacity = prevOk ? 1.0 : 0.35;
BorderPdfSidebarPagePrev.IsHitTestVisible = prevOk;
}
if (BorderPdfSidebarPageNext != null)
{
BorderPdfSidebarPageNext.Opacity = nextOk ? 1.0 : 0.35;
BorderPdfSidebarPageNext.IsHitTestVisible = nextOk;
}
}
private void SelectedPdf_PageNavigationStateChanged(object sender, EventArgs e)
{
Dispatcher.BeginInvoke(new Action(() =>
{
if (sender is PdfEmbeddedView pdf)
{
if (!inkCanvas.Children.Contains(pdf))
{
SyncPdfPageSidebarWithCanvas();
return;
}
UpdatePdfSidebarFromPdf(pdf);
UpdatePdfPageSidebarPosition(pdf);
RequestPdfSidebarPositionRefresh();
}
if (currentSelectedElement != null && IsBitmapLikeCanvasElement(currentSelectedElement))
{
UpdateImageSelectionToolbarPosition(currentSelectedElement);
if (ImageResizeHandlesCanvas?.Visibility == Visibility.Visible)
UpdateImageResizeHandlesPosition(GetElementActualBounds(currentSelectedElement));
}
}), DispatcherPriority.Loaded);
}
private async void BorderPdfSidebarPagePrev_MouseUp(object sender, MouseButtonEventArgs e)
{
try
{
var pdf = GetPdfSidebarTargetElement();
if (pdf != null && pdf.CanGoPrevious)
await pdf.GoToPreviousPageAsync();
SyncPdfPageSidebarWithCanvas();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"PDF 上一页失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private async void BorderPdfSidebarPageNext_MouseUp(object sender, MouseButtonEventArgs e)
{
try
{
var pdf = GetPdfSidebarTargetElement();
if (pdf != null && pdf.CanGoNext)
await pdf.GoToNextPageAsync();
SyncPdfPageSidebarWithCanvas();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"PDF 下一页失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 处理图片删除功能
/// </summary>
/// <param name="sender">事件发送者</param>
/// <param name="e">事件参数</param>
/// <remarks>
/// - 检查当前是否有选中元素
/// - 保存删除前的编辑模式
/// - 记录删除历史
/// - 从画布中移除
/// - 清除选中状态
/// - 包含异常处理
/// </remarks>
private void BorderImageDelete_MouseUp(object sender, MouseButtonEventArgs e)
{
try
@@ -1789,16 +2227,19 @@ namespace Ink_Canvas
// 记录删除历史
timeMachine.CommitElementRemoveHistory(currentSelectedElement);
var toRemove = currentSelectedElement;
// 从画布中移除
inkCanvas.Children.Remove(currentSelectedElement);
inkCanvas.Children.Remove(toRemove);
// 清除选中状态
UnselectElement(currentSelectedElement);
UnselectElement(toRemove);
currentSelectedElement = null;
// 恢复到删除前的编辑模式
inkCanvas.EditingMode = previousEditingMode;
SyncPdfPageSidebarWithCanvas();
LogHelper.WriteLogToFile($"图片删除完成,已恢复到编辑模式: {previousEditingMode}");
}
}
@@ -2029,7 +2470,7 @@ namespace Ink_Canvas
{
try
{
if (currentSelectedElement is Image image && sender is Ellipse ellipse)
if (IsBitmapLikeCanvasElement(currentSelectedElement) && sender is Ellipse ellipse)
{
isResizingImage = true;
imageResizeStartPoint = e.GetPosition(inkCanvas);
@@ -2072,10 +2513,10 @@ namespace Ink_Canvas
{
try
{
if (isResizingImage && currentSelectedElement is Image image && sender is Ellipse ellipse)
if (isResizingImage && IsBitmapLikeCanvasElement(currentSelectedElement) && sender is Ellipse ellipse)
{
var currentPoint = e.GetPosition(inkCanvas);
ResizeImageByHandle(image, imageResizeStartPoint, currentPoint, activeResizeHandle);
ResizeImageByHandle(currentSelectedElement, imageResizeStartPoint, currentPoint, activeResizeHandle);
imageResizeStartPoint = currentPoint;
e.Handled = true;
}
@@ -2087,11 +2528,11 @@ namespace Ink_Canvas
}
// 根据控制点缩放图片
private void ResizeImageByHandle(Image image, Point startPoint, Point currentPoint, string handleName)
private void ResizeImageByHandle(FrameworkElement element, Point startPoint, Point currentPoint, string handleName)
{
try
{
if (image.RenderTransform is TransformGroup transformGroup)
if (element.RenderTransform is TransformGroup transformGroup)
{
var scaleTransform = transformGroup.Children.OfType<ScaleTransform>().FirstOrDefault();
var translateTransform = transformGroup.Children.OfType<TranslateTransform>().FirstOrDefault();
@@ -2099,7 +2540,7 @@ namespace Ink_Canvas
if (scaleTransform == null || translateTransform == null) return;
// 获取图片的当前边界
Rect currentBounds = GetElementActualBounds(image);
Rect currentBounds = GetElementActualBounds(element);
double deltaX = currentPoint.X - startPoint.X;
double deltaY = currentPoint.Y - startPoint.Y;
@@ -2160,7 +2601,10 @@ namespace Ink_Canvas
translateTransform.Y += translateY;
// 更新选择点位置
UpdateImageResizeHandlesPosition(GetElementActualBounds(image));
UpdateImageResizeHandlesPosition(GetElementActualBounds(element));
if (BorderImageSelectionControl?.Visibility == Visibility.Visible)
UpdateImageSelectionToolbarPosition(element);
}
}
catch (Exception ex)
+95 -102
View File
@@ -1,3 +1,4 @@
using Ink_Canvas.Controls;
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Modern;
using System;
@@ -18,7 +19,6 @@ using Application = System.Windows.Application;
using Button = System.Windows.Controls.Button;
using Cursors = System.Windows.Input.Cursors;
using HorizontalAlignment = System.Windows.HorizontalAlignment;
using Image = System.Windows.Controls.Image;
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
using OpenFileDialog = Microsoft.Win32.OpenFileDialog;
@@ -370,7 +370,7 @@ namespace Ink_Canvas
/// <param name="autoAlignCenter">
/// 是否自動居中浮動工具欄
/// </param>
private async void HideSubPanels(string mode = null, bool autoAlignCenter = false)
internal async void HideSubPanels(string mode = null, bool autoAlignCenter = false)
{
AnimationsHelper.HideWithSlideAndFade(BorderTools);
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
@@ -3177,7 +3177,7 @@ namespace Ink_Canvas
/// </summary>
/// <param name="sender">发送者</param>
/// <param name="e">路由事件参数</param>
private async void BtnSettings_Click(object sender, RoutedEventArgs e)
internal async void BtnSettings_Click(object sender, RoutedEventArgs e)
{
if (BorderSettings.Visibility == Visibility.Visible)
{
@@ -3298,6 +3298,9 @@ namespace Ink_Canvas
// 清空触摸点计数器
dec.Clear();
if (isPalmEraserActive)
isPalmEraserActive = false;
// 确保触摸事件能正常响应
inkCanvas.IsHitTestVisible = true;
inkCanvas.IsManipulationEnabled = true;
@@ -3567,6 +3570,8 @@ namespace Ink_Canvas
break;
}
}
SyncPdfPageSidebarWithCanvas();
}
private int BoundsWidth = 5;
@@ -3774,32 +3779,29 @@ namespace Ink_Canvas
// Open file dialog to select image
var dialog = new OpenFileDialog
{
Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif"
Filter = "图片与 PDF|*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.pdf|图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif|PDF|*.pdf"
};
if (dialog.ShowDialog() == true)
{
string filePath = dialog.FileName;
Image image = await CreateAndCompressImageAsync(filePath);
if (image != null)
FrameworkElement element = await CreateAndCompressImageAsync(filePath);
if (element != null)
{
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
image.Name = timestamp;
element.Name = timestamp;
// 初始化TransformGroup
if (image is FrameworkElement element)
{
var transformGroup = new TransformGroup();
transformGroup.Children.Add(new ScaleTransform(1, 1));
transformGroup.Children.Add(new TranslateTransform(0, 0));
transformGroup.Children.Add(new RotateTransform(0));
element.RenderTransform = transformGroup;
}
var transformGroup = new TransformGroup();
transformGroup.Children.Add(new ScaleTransform(1, 1));
transformGroup.Children.Add(new TranslateTransform(0, 0));
transformGroup.Children.Add(new RotateTransform(0));
element.RenderTransform = transformGroup;
CenterAndScaleElement(image);
CenterAndScaleElement(element);
// 设置图片属性,避免被InkCanvas选择系统处理
image.IsHitTestVisible = true;
image.Focusable = false;
element.IsHitTestVisible = true;
element.Focusable = false;
// 初始化InkCanvas选择设置
if (inkCanvas != null)
@@ -3810,34 +3812,33 @@ namespace Ink_Canvas
inkCanvas.EditingMode = InkCanvasEditingMode.None;
}
inkCanvas.Children.Add(image);
inkCanvas.Children.Add(element);
// 绑定事件处理器
if (image is FrameworkElement elementForEvents)
{
// 鼠标事件
elementForEvents.MouseLeftButtonDown += Element_MouseLeftButtonDown;
elementForEvents.MouseLeftButtonUp += Element_MouseLeftButtonUp;
elementForEvents.MouseMove += Element_MouseMove;
elementForEvents.MouseWheel += Element_MouseWheel;
element.MouseLeftButtonDown += Element_MouseLeftButtonDown;
element.MouseLeftButtonUp += Element_MouseLeftButtonUp;
element.MouseMove += Element_MouseMove;
element.MouseWheel += Element_MouseWheel;
// 触摸事件
elementForEvents.TouchDown += Element_TouchDown;
elementForEvents.TouchUp += Element_TouchUp;
elementForEvents.IsManipulationEnabled = true;
elementForEvents.ManipulationDelta += Element_ManipulationDelta;
elementForEvents.ManipulationCompleted += Element_ManipulationCompleted;
// 触摸事件
element.TouchDown += Element_TouchDown;
element.TouchUp += Element_TouchUp;
element.IsManipulationEnabled = true;
element.ManipulationDelta += Element_ManipulationDelta;
element.ManipulationCompleted += Element_ManipulationCompleted;
// 设置光标
elementForEvents.Cursor = Cursors.Hand;
}
// 设置光标
element.Cursor = Cursors.Hand;
timeMachine.CommitElementInsertHistory(image);
timeMachine.CommitElementInsertHistory(element);
// 插入图片后切换到选择模式并刷新浮动栏高光显示
SetCurrentToolMode(InkCanvasEditingMode.Select);
UpdateCurrentToolMode("select");
HideSubPanels("select");
if (element is PdfEmbeddedView)
_pdfSidebarNextPositionUseHostTransform = true;
SyncPdfPageSidebarWithCanvas();
}
}
}
@@ -3847,32 +3848,29 @@ namespace Ink_Canvas
{
var dialog = new OpenFileDialog
{
Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif"
Filter = "图片与 PDF|*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.pdf|图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif|PDF|*.pdf"
};
if (dialog.ShowDialog() == true)
{
string filePath = dialog.FileName;
Image image = await CreateAndCompressImageAsync(filePath);
if (image != null)
FrameworkElement element = await CreateAndCompressImageAsync(filePath);
if (element != null)
{
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
image.Name = timestamp;
element.Name = timestamp;
// 初始化TransformGroup
if (image is FrameworkElement element)
{
var transformGroup = new TransformGroup();
transformGroup.Children.Add(new ScaleTransform(1, 1));
transformGroup.Children.Add(new TranslateTransform(0, 0));
transformGroup.Children.Add(new RotateTransform(0));
element.RenderTransform = transformGroup;
}
var transformGroup = new TransformGroup();
transformGroup.Children.Add(new ScaleTransform(1, 1));
transformGroup.Children.Add(new TranslateTransform(0, 0));
transformGroup.Children.Add(new RotateTransform(0));
element.RenderTransform = transformGroup;
CenterAndScaleElement(image);
CenterAndScaleElement(element);
// 设置图片属性,避免被InkCanvas选择系统处理
image.IsHitTestVisible = true;
image.Focusable = false;
element.IsHitTestVisible = true;
element.Focusable = false;
// 初始化InkCanvas选择设置
if (inkCanvas != null)
@@ -3883,34 +3881,33 @@ namespace Ink_Canvas
inkCanvas.EditingMode = InkCanvasEditingMode.None;
}
inkCanvas.Children.Add(image);
inkCanvas.Children.Add(element);
// 绑定事件处理器
if (image is FrameworkElement elementForEvents)
{
// 鼠标事件
elementForEvents.MouseLeftButtonDown += Element_MouseLeftButtonDown;
elementForEvents.MouseLeftButtonUp += Element_MouseLeftButtonUp;
elementForEvents.MouseMove += Element_MouseMove;
elementForEvents.MouseWheel += Element_MouseWheel;
element.MouseLeftButtonDown += Element_MouseLeftButtonDown;
element.MouseLeftButtonUp += Element_MouseLeftButtonUp;
element.MouseMove += Element_MouseMove;
element.MouseWheel += Element_MouseWheel;
// 触摸事件
elementForEvents.TouchDown += Element_TouchDown;
elementForEvents.TouchUp += Element_TouchUp;
elementForEvents.IsManipulationEnabled = true;
elementForEvents.ManipulationDelta += Element_ManipulationDelta;
elementForEvents.ManipulationCompleted += Element_ManipulationCompleted;
// 触摸事件
element.TouchDown += Element_TouchDown;
element.TouchUp += Element_TouchUp;
element.IsManipulationEnabled = true;
element.ManipulationDelta += Element_ManipulationDelta;
element.ManipulationCompleted += Element_ManipulationCompleted;
// 设置光标
elementForEvents.Cursor = Cursors.Hand;
}
// 设置光标
element.Cursor = Cursors.Hand;
timeMachine.CommitElementInsertHistory(image);
timeMachine.CommitElementInsertHistory(element);
// 插入图片后切换到选择模式并刷新浮动栏高光显示
SetCurrentToolMode(InkCanvasEditingMode.Select);
UpdateCurrentToolMode("select");
HideSubPanels("select");
if (element is PdfEmbeddedView)
_pdfSidebarNextPositionUseHostTransform = true;
SyncPdfPageSidebarWithCanvas();
}
}
}
@@ -3920,32 +3917,29 @@ namespace Ink_Canvas
{
var dialog = new OpenFileDialog
{
Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif"
Filter = "图片与 PDF|*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.pdf|图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif|PDF|*.pdf"
};
if (dialog.ShowDialog() == true)
{
string filePath = dialog.FileName;
Image image = await CreateAndCompressImageAsync(filePath); // 补充image定义
if (image != null)
FrameworkElement element = await CreateAndCompressImageAsync(filePath);
if (element != null)
{
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
image.Name = timestamp;
element.Name = timestamp;
// 初始化TransformGroup
if (image is FrameworkElement element)
{
var transformGroup = new TransformGroup();
transformGroup.Children.Add(new ScaleTransform(1, 1));
transformGroup.Children.Add(new TranslateTransform(0, 0));
transformGroup.Children.Add(new RotateTransform(0));
element.RenderTransform = transformGroup;
}
var transformGroup = new TransformGroup();
transformGroup.Children.Add(new ScaleTransform(1, 1));
transformGroup.Children.Add(new TranslateTransform(0, 0));
transformGroup.Children.Add(new RotateTransform(0));
element.RenderTransform = transformGroup;
CenterAndScaleElement(image);
CenterAndScaleElement(element);
// 设置图片属性,避免被InkCanvas选择系统处理
image.IsHitTestVisible = true;
image.Focusable = false;
element.IsHitTestVisible = true;
element.Focusable = false;
// 初始化InkCanvas选择设置
if (inkCanvas != null)
@@ -3956,34 +3950,33 @@ namespace Ink_Canvas
inkCanvas.EditingMode = InkCanvasEditingMode.None;
}
inkCanvas.Children.Add(image);
inkCanvas.Children.Add(element);
// 绑定事件处理器
if (image is FrameworkElement elementForEvents)
{
// 鼠标事件
elementForEvents.MouseLeftButtonDown += Element_MouseLeftButtonDown;
elementForEvents.MouseLeftButtonUp += Element_MouseLeftButtonUp;
elementForEvents.MouseMove += Element_MouseMove;
elementForEvents.MouseWheel += Element_MouseWheel;
element.MouseLeftButtonDown += Element_MouseLeftButtonDown;
element.MouseLeftButtonUp += Element_MouseLeftButtonUp;
element.MouseMove += Element_MouseMove;
element.MouseWheel += Element_MouseWheel;
// 触摸事件
elementForEvents.TouchDown += Element_TouchDown;
elementForEvents.TouchUp += Element_TouchUp;
elementForEvents.IsManipulationEnabled = true;
elementForEvents.ManipulationDelta += Element_ManipulationDelta;
elementForEvents.ManipulationCompleted += Element_ManipulationCompleted;
// 触摸事件
element.TouchDown += Element_TouchDown;
element.TouchUp += Element_TouchUp;
element.IsManipulationEnabled = true;
element.ManipulationDelta += Element_ManipulationDelta;
element.ManipulationCompleted += Element_ManipulationCompleted;
// 设置光标
elementForEvents.Cursor = Cursors.Hand;
}
// 设置光标
element.Cursor = Cursors.Hand;
timeMachine.CommitElementInsertHistory(image);
timeMachine.CommitElementInsertHistory(element);
// 插入图片后切换到选择模式并刷新浮动栏高光显示
SetCurrentToolMode(InkCanvasEditingMode.Select);
UpdateCurrentToolMode("select");
HideSubPanels("select");
if (element is PdfEmbeddedView)
_pdfSidebarNextPositionUseHostTransform = true;
SyncPdfPageSidebarWithCanvas();
}
}
}
+124
View File
@@ -4,6 +4,7 @@ using Microsoft.Office.Core;
using Microsoft.Office.Interop.PowerPoint;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
@@ -167,6 +168,18 @@ namespace Ink_Canvas
/// 断开连接后退出PPT模式的延迟时间(毫秒),即连接断开后多长时间才退出PPT模式。
/// </summary>
private const int ExitPPTModeAfterDisconnectDelayMs = 1200;
/// <summary>
/// 仅PPT模式下周期性探测放映界面(COM 失效时依赖 Win32),间隔不宜过小以免多余开销。
/// </summary>
private DispatcherTimer _pptOnlyVisibilityProbeTimer;
private const int PptOnlyVisibilityProbeIntervalMs = 800;
/// <summary>
/// PowerPoint 全屏放映顶层窗口类名(与编辑态 PPTFrameClass 区分)。
/// </summary>
private const string PowerPointSlideShowWindowClassName = "screenClass";
#endregion
#region PPT Managers
@@ -638,6 +651,8 @@ namespace Ink_Canvas
ClosePowerPointApplication();
ClearStaticInteropState();
StopPptOnlyVisibilityProbeTimer();
LogHelper.WriteLogToFile("PPT管理器已释放", LogHelper.LogType.Event);
}
catch (Exception ex)
@@ -700,6 +715,104 @@ namespace Ink_Canvas
}
#endregion
#region PPT模式可见性COM + Win32
/// <summary>
/// 在启用「仅PPT模式」时启动轻量探测,COM 事件延迟或失效时仍可根据全屏放映窗口显示主窗口。
/// </summary>
internal void EnsurePptOnlyVisibilityProbeTimer()
{
try
{
if (!Settings.ModeSettings.IsPPTOnlyMode)
{
StopPptOnlyVisibilityProbeTimer();
return;
}
if (_pptOnlyVisibilityProbeTimer == null)
{
_pptOnlyVisibilityProbeTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(PptOnlyVisibilityProbeIntervalMs)
};
_pptOnlyVisibilityProbeTimer.Tick += (_, __) => CheckMainWindowVisibility();
}
if (!_pptOnlyVisibilityProbeTimer.IsEnabled)
_pptOnlyVisibilityProbeTimer.Start();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"仅PPT可见性探测计时器启动失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
internal void StopPptOnlyVisibilityProbeTimer()
{
try
{
_pptOnlyVisibilityProbeTimer?.Stop();
}
catch
{
}
}
/// <summary>
/// 检测是否存在 PowerPoint 全屏放映顶层窗口(类名 screenClass,进程 powerpnt),用于 COM 不可用时的兜底。
/// </summary>
internal bool IsPowerPointSlideshowSurfacePresentWin32()
{
if (!Settings.ModeSettings.IsPPTOnlyMode)
return false;
try
{
bool found = false;
EnumWindows((hWnd, _) =>
{
if (!IsWindow(hWnd) || !IsWindowVisible(hWnd))
return true;
var cls = new StringBuilder(256);
if (GetClassName(hWnd, cls, cls.Capacity) == 0)
return true;
if (!string.Equals(cls.ToString(), PowerPointSlideShowWindowClassName, StringComparison.OrdinalIgnoreCase))
return true;
try
{
GetWindowThreadProcessId(hWnd, out uint pid);
using (var proc = Process.GetProcessById((int)pid))
{
var name = proc.ProcessName;
if (string.Equals(name, "POWERPNT", StringComparison.OrdinalIgnoreCase))
{
found = true;
return false;
}
}
}
catch
{
}
return true;
}, IntPtr.Zero);
return found;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"Win32 检测 PPT 放映窗口失败: {ex.Message}", LogHelper.LogType.Trace);
return false;
}
}
#endregion
#region New PPT Event Handlers
/// <summary>
/// 处理 PowerPoint 连接状态的变更:更新界面连接/放映状态,并在断开时启动一个短延迟以安全退出 PPT 模式。
@@ -731,6 +844,8 @@ namespace Ink_Canvas
_ = HandleManualSlideShowEnd();
if (Settings.PowerPointSettings.UseRotPptLink)
_pptManager?.ReloadConnection();
CheckMainWindowVisibility();
}
});
}
@@ -1114,6 +1229,9 @@ namespace Ink_Canvas
// 加载当前页墨迹
LoadCurrentSlideInk(currentSlide);
// 仅PPT模式:放映开始立即同步主窗口可见性(勿仅依赖 SlideShowStateChanged 定时器)
CheckMainWindowVisibility();
});
if (!isFloatingBarFolded)
@@ -1399,6 +1517,8 @@ namespace Ink_Canvas
currentMode = 0;
}
SyncPdfPageSidebarWithCanvas();
ClearStrokes(true);
// 清空备份历史记录,防止退出白板时恢复已结束PPT的墨迹
// 注意:这里只清空索引0的备份,不影响白板页面的墨迹(索引1及以上)
@@ -1435,6 +1555,8 @@ namespace Ink_Canvas
UpdateCurrentToolMode("cursor");
SetFloatingBarHighlightPosition("cursor");
CheckMainWindowVisibility();
}
catch (Exception ex)
{
@@ -2291,6 +2413,7 @@ namespace Ink_Canvas
_pptUIManager?.UpdateSlideShowStatus(false);
_pptUIManager?.UpdateSidebarExitButtons(false);
LogHelper.WriteLogToFile("手动更新放映结束UI状态", LogHelper.LogType.Trace);
CheckMainWindowVisibility();
});
// 手动处理自动收纳,因为OnPPTSlideShowEnd事件可能未触发
@@ -2323,6 +2446,7 @@ namespace Ink_Canvas
{
_pptUIManager?.UpdateSlideShowStatus(false);
_pptUIManager?.UpdateSidebarExitButtons(false);
CheckMainWindowVisibility();
});
// 异常情况下也手动处理自动收纳
+55 -33
View File
@@ -1,3 +1,4 @@
using Ink_Canvas.Controls;
using Ink_Canvas.Helpers;
using Newtonsoft.Json;
using System;
@@ -16,6 +17,7 @@ using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using System.Xml;
using System.Xml.Linq;
using Color = System.Drawing.Color;
@@ -28,16 +30,58 @@ namespace Ink_Canvas
// 1. 定义元素信息结构
public class CanvasElementInfo
{
public string Type { get; set; } // "Image"
public string Type { get; set; } // "Image" | "Pdf"
public string SourcePath { get; set; }
public double Left { get; set; }
public double Top { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public string Stretch { get; set; } = "Fill"; // 默认为Fill
/// <summary>PDF 当前页(从 0 开始),仅 Type == Pdf 时有效。</summary>
public int? PdfCurrentPage { get; set; }
/// <summary>保存时的 PDF 总页数,用于校验;仅 Type == Pdf 时有效。</summary>
public int? PdfPageCount { get; set; }
}
public partial class MainWindow : Window
{
/// <summary>收集画布上图片与 PDF 的元数据,写入 .elements.json(与墨迹文件同路径)。</summary>
private void CollectCanvasElementsMetadata(List<CanvasElementInfo> elementInfos)
{
if (elementInfos == null || inkCanvas == null) return;
foreach (var child in inkCanvas.Children)
{
if (child is Image img && img.Source is BitmapImage bmp)
{
elementInfos.Add(new CanvasElementInfo
{
Type = "Image",
SourcePath = bmp.UriSource?.LocalPath ?? "",
Left = InkCanvas.GetLeft(img),
Top = InkCanvas.GetTop(img),
Width = img.Width,
Height = img.Height,
Stretch = img.Stretch.ToString()
});
}
else if (child is PdfEmbeddedView pdf && !string.IsNullOrEmpty(pdf.PdfPath))
{
elementInfos.Add(new CanvasElementInfo
{
Type = "Pdf",
SourcePath = pdf.PdfPath,
Left = InkCanvas.GetLeft(pdf),
Top = InkCanvas.GetTop(pdf),
Width = pdf.Width,
Height = pdf.Height,
Stretch = "Uniform",
PdfCurrentPage = (int)pdf.CurrentPageIndex,
PdfPageCount = (int)pdf.PageCount
});
}
}
}
/// <summary>
/// 保存墨迹的鼠标释放事件处理
/// </summary>
@@ -419,22 +463,7 @@ namespace Ink_Canvas
// 保存元素信息
var elementInfos = new List<CanvasElementInfo>();
foreach (var child in inkCanvas.Children)
{
if (child is Image img && img.Source is BitmapImage bmp)
{
elementInfos.Add(new CanvasElementInfo
{
Type = "Image",
SourcePath = bmp.UriSource?.LocalPath ?? "",
Left = InkCanvas.GetLeft(img),
Top = InkCanvas.GetTop(img),
Width = img.Width,
Height = img.Height,
Stretch = img.Stretch.ToString()
});
}
}
CollectCanvasElementsMetadata(elementInfos);
string elementsPath = Settings.Automation.IsSaveStrokesAsXML ? Path.ChangeExtension(savePathWithName, ".elements.json") : Path.ChangeExtension(savePathWithName, ".elements.json");
File.WriteAllText(elementsPath, JsonConvert.SerializeObject(elementInfos, Newtonsoft.Json.Formatting.Indented));
}
@@ -485,22 +514,7 @@ namespace Ink_Canvas
// 同时保存元素信息
var elementInfos = new List<CanvasElementInfo>();
foreach (var child in inkCanvas.Children)
{
if (child is Image img && img.Source is BitmapImage bmp)
{
elementInfos.Add(new CanvasElementInfo
{
Type = "Image",
SourcePath = bmp.UriSource?.LocalPath ?? "",
Left = InkCanvas.GetLeft(img),
Top = InkCanvas.GetTop(img),
Width = img.Width,
Height = img.Height,
Stretch = img.Stretch.ToString()
});
}
}
CollectCanvasElementsMetadata(elementInfos);
File.WriteAllText(Path.ChangeExtension(xmlPath, ".elements.json"), JsonConvert.SerializeObject(elementInfos, Newtonsoft.Json.Formatting.Indented));
// 异步上传到Dlass
@@ -1266,6 +1280,10 @@ namespace Ink_Canvas
InkCanvas.SetTop(img, info.Top);
inkCanvas.Children.Add(img);
}
else if (string.Equals(info.Type, "Pdf", StringComparison.OrdinalIgnoreCase) && File.Exists(info.SourcePath))
{
Dispatcher.BeginInvoke(new Action(() => { _ = RestorePdfFromElementInfoAsync(info); }), DispatcherPriority.Loaded);
}
}
}
}
@@ -1416,6 +1434,10 @@ namespace Ink_Canvas
InkCanvas.SetTop(img, info.Top);
inkCanvas.Children.Add(img);
}
else if (string.Equals(info.Type, "Pdf", StringComparison.OrdinalIgnoreCase) && File.Exists(info.SourcePath))
{
Dispatcher.BeginInvoke(new Action(() => { _ = RestorePdfFromElementInfoAsync(info); }), DispatcherPriority.Loaded);
}
}
}
@@ -1,3 +1,4 @@
using Ink_Canvas.Controls;
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Controls;
using System;
@@ -610,7 +611,7 @@ namespace Ink_Canvas
// 检查是否有图片元素被选中(通过InkCanvas的选中元素)
var selectedElements = inkCanvas.GetSelectedElements();
bool hasImageElement = selectedElements.Any(element => element is Image);
bool hasImageElement = selectedElements.Any(element => element is Image || element is PdfEmbeddedView);
// 如果有图片元素被选中,不显示选择框
if (hasImageElement)
@@ -621,7 +622,7 @@ namespace Ink_Canvas
}
// 检查是否有图片元素被选中(通过currentSelectedElement
if (currentSelectedElement != null && currentSelectedElement is Image)
if (currentSelectedElement != null && (currentSelectedElement is Image || currentSelectedElement is PdfEmbeddedView))
{
GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
HideSelectionDisplay();
+13 -1
View File
@@ -1,4 +1,4 @@
using Hardcodet.Wpf.TaskbarNotification;
using H.NotifyIcon;
using Ink_Canvas.Helpers;
using Newtonsoft.Json;
using OSVersionExtension;
@@ -3597,6 +3597,11 @@ namespace Ink_Canvas
RestoreNonStrokeElements(preservedElements);
isInMultiTouchMode = true;
palmEraserWasEnabledBeforeMultiTouch = Settings.Canvas.EnablePalmEraser;
Settings.Canvas.EnablePalmEraser = false;
if (ToggleSwitchEnablePalmEraser != null)
ToggleSwitchEnablePalmEraser.IsOn = false;
// 恢复到之前的编辑状态
inkCanvas.EditingMode = currentEditingMode;
drawingShapeMode = currentDrawingShapeMode;
@@ -3627,6 +3632,13 @@ namespace Ink_Canvas
RestoreNonStrokeElements(preservedElements);
isInMultiTouchMode = false;
if (palmEraserWasEnabledBeforeMultiTouch)
{
Settings.Canvas.EnablePalmEraser = true;
if (ToggleSwitchEnablePalmEraser != null)
ToggleSwitchEnablePalmEraser.IsOn = true;
}
// 恢复到之前的编辑状态
inkCanvas.EditingMode = currentEditingMode;
drawingShapeMode = currentDrawingShapeMode;
@@ -1,4 +1,4 @@
using Hardcodet.Wpf.TaskbarNotification;
using H.NotifyIcon;
using Ink_Canvas.Helpers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -1502,7 +1502,7 @@ namespace Ink_Canvas
Settings defaultSettings = new Settings();
// 将默认配置和用户配置都序列化为JObject
JObject defaultConfigObj = JObject.FromObject(defaultSettings); EnsureDefaultConfigSchemaIncludesIgnoredNullKeys(defaultConfigObj);
JObject defaultConfigObj = JObject.FromObject(defaultSettings); EnsureDefaultConfigSchemaIncludesIgnoredNullKeys(defaultConfigObj);
JObject userConfigObj = JObject.Parse(userConfigJson);
// 记录是否有清理操作
@@ -142,14 +142,24 @@ namespace Ink_Canvas
}
}
private void RunStrokeCollectedPostShapeRecognitionTail(InkCanvasStrokeCollectedEventArgs e, bool wasStraightened)
/// <summary>
/// 收笔后压感/墨迹平滑等尾部处理。返回「当前应登记到手写字形替换批次」的画布笔画引用:
/// 同步贝塞尔平滑若替换了笔画,则为新 <see cref="Stroke"/>;否则为 <paramref name="e"/>.Stroke。
/// 直线拉直后事件参数中的笔画可能已不在画布上,调用方需另行传入画布上的笔画(见收笔处)。
/// </summary>
private Stroke RunStrokeCollectedPostShapeRecognitionTail(InkCanvasStrokeCollectedEventArgs e, bool wasStraightened)
{
if (e?.Stroke == null)
return null;
var handwritingScheduleStroke = e.Stroke;
try
{
foreach (var stylusPoint in e.Stroke.StylusPoints)
if ((stylusPoint.PressureFactor > 0.501 || stylusPoint.PressureFactor < 0.5) &&
stylusPoint.PressureFactor != 0)
return;
return e.Stroke;
try
{
@@ -166,85 +176,89 @@ namespace Ink_Canvas
}
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
switch (Settings.Canvas.InkStyle)
// 「屏蔽压感」已在收笔主路径将点集归一成 0.5;此处若再跑 InkStyle 0/1 会重写 PressureFactor,造成假压感。
if (!Settings.Canvas.DisablePressure)
{
case 1:
if (penType == 0)
try
{
var stylusPoints = new StylusPointCollection();
var n = e.Stroke.StylusPoints.Count - 1;
for (var i = 0; i <= n; i++)
switch (Settings.Canvas.InkStyle)
{
case 1:
if (penType == 0)
try
{
var speed = GetPointSpeed(e.Stroke.StylusPoints[Math.Max(i - 1, 0)].ToPoint(),
e.Stroke.StylusPoints[i].ToPoint(),
e.Stroke.StylusPoints[Math.Min(i + 1, n)].ToPoint());
var point = new StylusPoint
{
PressureFactor = RateBasedPressureFactorFromPointSpeed(speed),
X = e.Stroke.StylusPoints[i].X,
Y = e.Stroke.StylusPoints[i].Y
};
stylusPoints.Add(point);
}
var stylusPoints = new StylusPointCollection();
var n = e.Stroke.StylusPoints.Count - 1;
e.Stroke.StylusPoints = stylusPoints;
}
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
break;
case 0:
if (penType == 0)
try
{
var stylusPoints = new StylusPointCollection();
var n = e.Stroke.StylusPoints.Count - 1;
var pressure = 0.1;
var x = 10;
if (n == 1) return;
if (n >= x)
{
for (var i = 0; i < n - x; i++)
{
var point = new StylusPoint();
point.PressureFactor = (float)0.5;
point.X = e.Stroke.StylusPoints[i].X;
point.Y = e.Stroke.StylusPoints[i].Y;
stylusPoints.Add(point);
}
for (var i = n - x; i <= n; i++)
{
var point = new StylusPoint();
point.PressureFactor = (float)((0.5 - pressure) * (n - i) / x + pressure);
point.X = e.Stroke.StylusPoints[i].X;
point.Y = e.Stroke.StylusPoints[i].Y;
stylusPoints.Add(point);
}
}
else
{
for (var i = 0; i <= n; i++)
{
var point = new StylusPoint();
point.PressureFactor = (float)(0.4 * (n - i) / n + pressure);
point.X = e.Stroke.StylusPoints[i].X;
point.Y = e.Stroke.StylusPoints[i].Y;
var speed = GetPointSpeed(e.Stroke.StylusPoints[Math.Max(i - 1, 0)].ToPoint(),
e.Stroke.StylusPoints[i].ToPoint(),
e.Stroke.StylusPoints[Math.Min(i + 1, n)].ToPoint());
var point = new StylusPoint
{
PressureFactor = RateBasedPressureFactorFromPointSpeed(speed),
X = e.Stroke.StylusPoints[i].X,
Y = e.Stroke.StylusPoints[i].Y
};
stylusPoints.Add(point);
}
e.Stroke.StylusPoints = stylusPoints;
}
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
e.Stroke.StylusPoints = stylusPoints;
}
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
break;
case 0:
if (penType == 0)
try
{
var stylusPoints = new StylusPointCollection();
var n = e.Stroke.StylusPoints.Count - 1;
var pressure = 0.1;
var x = 10;
if (n == 1) return e.Stroke;
if (n >= x)
{
for (var i = 0; i < n - x; i++)
{
var point = new StylusPoint();
break;
case 3:
break;
point.PressureFactor = (float)0.5;
point.X = e.Stroke.StylusPoints[i].X;
point.Y = e.Stroke.StylusPoints[i].Y;
stylusPoints.Add(point);
}
for (var i = n - x; i <= n; i++)
{
var point = new StylusPoint();
point.PressureFactor = (float)((0.5 - pressure) * (n - i) / x + pressure);
point.X = e.Stroke.StylusPoints[i].X;
point.Y = e.Stroke.StylusPoints[i].Y;
stylusPoints.Add(point);
}
}
else
{
for (var i = 0; i <= n; i++)
{
var point = new StylusPoint();
point.PressureFactor = (float)(0.4 * (n - i) / n + pressure);
point.X = e.Stroke.StylusPoints[i].X;
point.Y = e.Stroke.StylusPoints[i].Y;
stylusPoints.Add(point);
}
}
e.Stroke.StylusPoints = stylusPoints;
}
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
break;
case 3:
break;
}
}
}
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
@@ -276,6 +290,7 @@ namespace Ink_Canvas
inkCanvas.Strokes.Remove(e.Stroke);
inkCanvas.Strokes.Add(smoothedStroke);
_currentCommitType = CommitReason.UserInput;
handwritingScheduleStroke = smoothedStroke;
}
}
}
@@ -293,6 +308,8 @@ namespace Ink_Canvas
{
drawingAttributes.FitToCurve = true;
}
return handwritingScheduleStroke;
}
/// <summary>
@@ -325,6 +342,10 @@ namespace Ink_Canvas
&& Math.Abs(strokeDrawingAttributes.Width - BoardBrushInkWidth) < 0.01
&& Math.Abs(strokeDrawingAttributes.Height - BoardBrushInkHeight) < 0.01;
// 手写识别须与画布显示分离:在压感/触摸模拟/笔锋/直线拉直等修改 e.Stroke 之前快照原始落笔点集。
var handwritingRawPointsForRecognizer =
CloneStylusPointCollectionForHandwritingInput(e.Stroke?.StylusPoints);
// 检查是否启用墨迹渐隐功能
if (Settings.Canvas.EnableInkFade && !isBoardBrushStroke)
{
@@ -372,6 +393,8 @@ namespace Ink_Canvas
// 标记是否进行了直线拉直
bool wasStraightened = false;
StylusPointCollection preBrushHandwritingPoints = null;
Stroke strokeForHandwritingBeautify = null;
if (Settings.Canvas.FitToCurve) drawingAttributes.FitToCurve = false;
@@ -379,7 +402,7 @@ namespace Ink_Canvas
{
inkCanvas.Opacity = 1;
var touchPressureSimulationApplied = false;
var preBrushHandwritingPoints = CloneStylusPointCollectionForHandwritingInput(e.Stroke?.StylusPoints);
preBrushHandwritingPoints = handwritingRawPointsForRecognizer;
if (Settings.Canvas.DisablePressure)
{
@@ -488,7 +511,11 @@ namespace Ink_Canvas
// 实时笔锋:勿依赖 DrawingAttributes.IgnorePressure。无压感触摸/鼠标等设备上,运行时仍可能为 true,
// 会导致不进入逻辑或进入后渲染仍忽略 PressureFactor;具体在 ApplyVelocityBrushTipFromSpeed 内关闭。
// 「屏蔽压感」时必须跳过:否则会重写 PressureFactor 并强制 IgnorePressure=false,与归一压感冲突。
// VelocityBrushTipMix <= 0 时 ApplyVelocityBrushTipFromSpeed 为空操作,无需调用。
if (Settings.Canvas.InkStyle == 3
&& Settings.Canvas.VelocityBrushTipMix > 0
&& !Settings.Canvas.DisablePressure
&& !touchPressureSimulationApplied
&& penType != 1
&& e.Stroke?.DrawingAttributes != null
@@ -500,6 +527,8 @@ namespace Ink_Canvas
// Apply line straightening and endpoint snapping if ink-to-shape is enabled
Stroke straightStrokeForHandwritingKey = null;
if (Settings.InkToShape.IsInkToShapeEnabled)
{
// 检查是否启用了直线自动拉直功能
@@ -542,6 +571,8 @@ namespace Ink_Canvas
inkCanvas.Strokes.Add(straightStroke);
_currentCommitType = CommitReason.UserInput;
straightStrokeForHandwritingKey = straightStroke;
// We can't modify e.Stroke directly, but we need to update newStrokes
// to ensure proper shape recognition for the straightened line
if (newStrokes.Contains(e.Stroke))
@@ -555,12 +586,12 @@ namespace Ink_Canvas
}
}
Stroke strokeForHandwritingBeautify = e.Stroke;
if (wasStraightened && inkCanvas.Strokes.Count > 0)
strokeForHandwritingBeautify = e.Stroke;
if (wasStraightened && straightStrokeForHandwritingKey != null)
strokeForHandwritingBeautify = straightStrokeForHandwritingKey;
else if (wasStraightened && inkCanvas.Strokes.Count > 0)
strokeForHandwritingBeautify = inkCanvas.Strokes[inkCanvas.Strokes.Count - 1];
if (wasStraightened && strokeForHandwritingBeautify != null)
preBrushHandwritingPoints = CloneStylusPointCollectionForHandwritingInput(strokeForHandwritingBeautify.StylusPoints);
if (ShapeRecognitionRouter.ShouldRunShapeRecognition(
Settings.InkToShape.IsInkToShapeEnabled,
@@ -912,9 +943,15 @@ namespace Ink_Canvas
try
{
await InkToShapeProcessCoreAsync();
var strokeAfterTail = RunStrokeCollectedPostShapeRecognitionTail(e, wsTail);
if (Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify)
ScheduleHandwritingGlyphReplaceAfterStrokeCollected(strokeHw, isBoardBrushStroke, preBrushHwPts);
RunStrokeCollectedPostShapeRecognitionTail(e, wsTail);
{
var canvasStrokeForHw = wsTail ? strokeHw : strokeAfterTail;
ScheduleHandwritingGlyphReplaceAfterStrokeCollected(
canvasStrokeForHw,
isBoardBrushStroke,
preBrushHwPts);
}
}
catch (Exception ex)
{
@@ -927,14 +964,21 @@ namespace Ink_Canvas
if (InkToShapeProcess())
return;
}
else if (Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify)
{
ScheduleHandwritingGlyphReplaceAfterStrokeCollected(strokeForHandwritingBeautify, isBoardBrushStroke, preBrushHandwritingPoints);
}
}
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
RunStrokeCollectedPostShapeRecognitionTail(e, wasStraightened);
var strokeAfterTailSync = RunStrokeCollectedPostShapeRecognitionTail(e, wasStraightened);
if (Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify
&& !ShapeRecognitionRouter.ShouldRunShapeRecognition(
Settings.InkToShape.IsInkToShapeEnabled,
ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine)))
{
var canvasStrokeForHw = wasStraightened ? strokeForHandwritingBeautify : strokeAfterTailSync;
ScheduleHandwritingGlyphReplaceAfterStrokeCollected(
canvasStrokeForHw,
isBoardBrushStroke,
preBrushHandwritingPoints);
}
}
/// <summary>
@@ -969,6 +1013,8 @@ namespace Ink_Canvas
inkCanvas.Strokes.Remove(original);
inkCanvas.Strokes.Add(smoothed);
_currentCommitType = CommitReason.UserInput;
// 收笔尾部仍以 original 登记手写批次;异步平滑后画布对象变为 smoothed,须迁移引用,否则防抖识别时字典 miss 会退回画布几何(非实时笔锋常见)。
MigrateHandwritingBeautifyCanvasStrokeReference(original, smoothed);
}
else
{
@@ -2145,12 +2191,16 @@ namespace Ink_Canvas
/// <summary>
/// 将沿线速度映射为压感并与硬件压感混合,快写略细、慢写略粗;在落笔时(及手写笔移动时由调用方)统一施加。
/// 无压感设备上系统可能将 <see cref="DrawingAttributes.IgnorePressure"/> 置为 true,此处强制关闭以便粗细随合成压感变化(与「屏蔽压感」无关:调用方已保证未屏蔽)
/// 无压感设备上系统可能将 <see cref="DrawingAttributes.IgnorePressure"/> 置为 true,此处强制关闭以便粗细随合成压感变化。
/// 若 <see cref="Settings.Canvas.DisablePressure"/> 为 true,本方法直接返回且不修改 IgnorePressure。
/// </summary>
private void ApplyVelocityBrushTipFromSpeed(Stroke stroke)
{
try
{
if (Settings.Canvas.DisablePressure)
return;
var mix = Settings.Canvas.VelocityBrushTipMix;
if (mix <= 0 || stroke == null) return;
if (mix > 1) mix = 1;
@@ -2732,6 +2782,29 @@ namespace Ink_Canvas
return corners.OrderBy(p => Math.Atan2(p.Y - center.Y, p.X - center.X)).ToList();
}
/// <summary>
/// 异步墨迹平滑将画布上的 <paramref name="fromStroke"/> 替换为 <paramref name="toStroke"/> 后,把手写字形替换批次里的画布引用一并迁移,使识别仍命中「原始点快照」字典项。
/// </summary>
private void MigrateHandwritingBeautifyCanvasStrokeReference(Stroke fromStroke, Stroke toStroke)
{
if (fromStroke == null || toStroke == null || ReferenceEquals(fromStroke, toStroke))
return;
if (!Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify)
return;
if (_handwritingBeautifyInkInputByCanvasStroke.TryGetValue(fromStroke, out var inkInput))
{
_handwritingBeautifyInkInputByCanvasStroke.Remove(fromStroke);
_handwritingBeautifyInkInputByCanvasStroke[toStroke] = inkInput;
}
for (var i = 0; i < _handwritingRecentStrokesForBeautify.Count; i++)
{
if (ReferenceEquals(_handwritingRecentStrokesForBeautify[i], fromStroke))
_handwritingRecentStrokesForBeautify[i] = toStroke;
}
}
/// <summary>
/// 收笔后:在墨迹转形状(若启用)完成之后,将笔画并入批次并启动/重置停笔防抖计时器,再于延迟后多笔合并矫正。
/// </summary>
@@ -2790,10 +2863,19 @@ namespace Ink_Canvas
if (preBrushHandwritingPoints != null && preBrushHandwritingPoints.Count > 0)
{
_handwritingBeautifyInkInputByCanvasStroke[strokeForBeautify] = new Stroke(preBrushHandwritingPoints)
// 再拷贝一份给识别专用 Stroke,避免与外部 StylusPointCollection 或 WPF Stroke 内部共享后被改写。
var ptsForRecognizer = CloneStylusPointCollectionForHandwritingInput(preBrushHandwritingPoints);
if (ptsForRecognizer != null && ptsForRecognizer.Count > 0)
{
DrawingAttributes = strokeForBeautify.DrawingAttributes.Clone()
};
_handwritingBeautifyInkInputByCanvasStroke[strokeForBeautify] = new Stroke(ptsForRecognizer)
{
DrawingAttributes = strokeForBeautify.DrawingAttributes.Clone()
};
}
else
{
_handwritingBeautifyInkInputByCanvasStroke.Remove(strokeForBeautify);
}
}
else
{
@@ -2815,7 +2897,11 @@ namespace Ink_Canvas
PruneHandwritingBeautifyBatch();
while (_handwritingRecentStrokesForBeautify.Count > HandwritingBeautifyMaxRecentStrokes)
{
var evicted = _handwritingRecentStrokesForBeautify[0];
_handwritingRecentStrokesForBeautify.RemoveAt(0);
_handwritingBeautifyInkInputByCanvasStroke.Remove(evicted);
}
EnsureHandwritingBeautifyDebounceTimer();
_handwritingBeautifyDebounceTimer.Stop();
@@ -2858,9 +2944,17 @@ namespace Ink_Canvas
continue;
canvasStrokes.Add(s);
if (_handwritingBeautifyInkInputByCanvasStroke.TryGetValue(s, out var inkInput) && inkInput != null)
{
recognitionInput.Add(inkInput);
}
else
{
LogHelper.WriteLogToFile(
"[手写体] 批次识别输入回退为画布笔画(未命中原始点快照)。画布点数=" +
(s.StylusPoints?.Count ?? 0),
LogHelper.LogType.Info);
recognitionInput.Add(s);
}
}
if (canvasStrokes.Count == 0)
+188 -40
View File
@@ -44,6 +44,9 @@ namespace Ink_Canvas
/// 多点触控延迟时间(毫秒)
/// </summary>
private const double MULTI_TOUCH_DELAY_MS = 100;
private bool isMultiTouchTimerActive;
private bool isPalmEraserActive;
private bool palmEraserWasEnabledBeforeMultiTouch;
/// <summary>
/// 保存画布上的非笔画元素(如图片、媒体元素等)
@@ -227,6 +230,12 @@ namespace Ink_Canvas
RestoreNonStrokeElements(preservedElements);
isInMultiTouchMode = false;
if (palmEraserWasEnabledBeforeMultiTouch)
{
Settings.Canvas.EnablePalmEraser = true;
if (ToggleSwitchEnablePalmEraser != null)
ToggleSwitchEnablePalmEraser.IsOn = true;
}
}
else
{
@@ -247,6 +256,11 @@ namespace Ink_Canvas
// 恢复非笔画元素
RestoreNonStrokeElements(preservedElements);
isInMultiTouchMode = true;
palmEraserWasEnabledBeforeMultiTouch = Settings.Canvas.EnablePalmEraser;
Settings.Canvas.EnablePalmEraser = false;
if (ToggleSwitchEnablePalmEraser != null)
ToggleSwitchEnablePalmEraser.IsOn = false;
}
}
@@ -543,10 +557,12 @@ namespace Ink_Canvas
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
@@ -719,21 +735,13 @@ namespace Ink_Canvas
/// <param name="e">触摸事件参数</param>
/// <returns>返回触摸边界宽度</returns>
/// <remarks>
/// 根据触摸事件参数计算触摸边界宽度,包括以下逻辑:
/// 1. 获取触摸点的边界
/// 2. 如果不是四边红外屏幕,使用边界宽度
/// 3. 如果是四边红外屏幕,使用边界宽度和高度的平方根
/// 4. 如果是特殊屏幕,乘以触摸倍数
/// 5. 返回计算得到的触摸边界宽度
/// 手掌擦阈值与特殊屏 <c>TouchMultiplier</c> 在激活逻辑中单独参与计算,此处仅返回几何接触尺寸。
/// </remarks>
public double GetTouchBoundWidth(TouchEventArgs e)
{
var args = e.GetTouchPoint(null).Bounds;
double value;
if (!Settings.Advanced.IsQuadIR) value = args.Width;
else value = Math.Sqrt(args.Width * args.Height); //四边红外
if (Settings.Advanced.IsSpecialScreen) value *= Settings.Advanced.TouchMultiplier;
return value;
if (!Settings.Advanced.IsQuadIR) return args.Width;
return Math.Sqrt(args.Width * args.Height);
}
/// <summary>
@@ -757,28 +765,146 @@ namespace Ink_Canvas
/// </remarks>
private void InkCanvas_PreviewTouchDown(object sender, TouchEventArgs e)
{
var touchPointForBar = e.GetTouchPoint(this);
var floatingBarBounds = ViewboxFloatingBar.TransformToAncestor(this).TransformBounds(
new Rect(0, 0, ViewboxFloatingBar.ActualWidth, ViewboxFloatingBar.ActualHeight));
if (floatingBarBounds.Contains(touchPointForBar.Position))
return;
if ((inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint
|| inkCanvas.EditingMode == InkCanvasEditingMode.EraseByStroke)
&& !isPalmEraserActive)
{
return;
}
if (drawingShapeMode != 0)
{
inkCanvas.EditingMode = InkCanvasEditingMode.None;
SetCursorBasedOnEditingMode(inkCanvas);
inkCanvas.CaptureTouch(e.TouchDevice);
ViewboxFloatingBar.IsHitTestVisible = false;
BlackboardUIGridForInkReplay.IsHitTestVisible = false;
isTouchDown = true;
if (dec.Count == 0)
{
var inkTouchPoint = e.GetTouchPoint(inkCanvas);
if (drawingShapeMode == 24 || drawingShapeMode == 25)
{
if (drawMultiStepShapeCurrentStep == 0)
iniP = inkTouchPoint.Position;
}
else
{
iniP = inkTouchPoint.Position;
}
lastTouchDownStrokeCollection = inkCanvas.Strokes.Clone();
}
dec.Add(e.TouchDevice.Id);
return;
}
SetCursorBasedOnEditingMode(inkCanvas);
inkCanvas.CaptureTouch(e.TouchDevice);
ViewboxFloatingBar.IsHitTestVisible = false;
BlackboardUIGridForInkReplay.IsHitTestVisible = false;
lastTouchDownTime = DateTime.Now;
dec.Add(e.TouchDevice.Id);
//设备1个的时候,记录中心点
if (Settings.Canvas.EnablePalmEraser && !isPalmEraserActive && drawingShapeMode == 0)
{
var touchPoint = e.GetTouchPoint(inkCanvas);
double boundWidth = GetTouchBoundWidth(e);
if ((Settings.Advanced.TouchMultiplier != 0 || !Settings.Advanced.IsSpecialScreen)
&& (boundWidth > BoundsWidth))
{
double thresholdMultiplier;
switch (Settings.Canvas.PalmEraserSensitivity)
{
case 0:
thresholdMultiplier = 3.0;
break;
case 1:
thresholdMultiplier = 2.5;
break;
case 2:
default:
thresholdMultiplier = 2.0;
break;
}
double EraserThresholdValue = Settings.Startup.IsEnableNibMode
? Settings.Advanced.NibModeBoundsWidthThresholdValue
: Settings.Advanced.FingerModeBoundsWidthThresholdValue;
if (boundWidth > BoundsWidth * EraserThresholdValue * thresholdMultiplier)
{
boundWidth *= Settings.Startup.IsEnableNibMode
? Settings.Advanced.NibModeBoundsWidthEraserSize
: Settings.Advanced.FingerModeBoundsWidthEraserSize;
if (Settings.Advanced.IsSpecialScreen)
boundWidth *= Settings.Advanced.TouchMultiplier;
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
isPalmEraserActive = true;
EnableEraserOverlay();
eraserWidth = boundWidth;
UpdateEraserStyle();
touchPoint = e.GetTouchPoint(inkCanvas);
EraserOverlay_PointerDown(sender);
EraserOverlay_PointerMove(sender, touchPoint.Position);
if (Settings.Canvas.IsShowCursor)
{
inkCanvas.ForceCursor = false;
inkCanvas.UseCustomCursor = false;
}
}
}
}
if (dec.Count == 1)
{
var touchPoint = e.GetTouchPoint(inkCanvas);
centerPoint = touchPoint.Position;
//记录第一根手指点击时的 StrokeCollection
lastTouchDownStrokeCollection = inkCanvas.Strokes.Clone();
}
//设备两个及两个以上,将画笔功能关闭
if (dec.Count > 1 || isSingleFingerDragMode || !Settings.Gesture.IsEnableTwoFingerGesture)
{
if (isInMultiTouchMode || !Settings.Gesture.IsEnableTwoFingerGesture) return;
if (inkCanvas.EditingMode == InkCanvasEditingMode.None ||
inkCanvas.EditingMode == InkCanvasEditingMode.Select) return;
var timeSinceLastTouch = (DateTime.Now - lastTouchDownTime).TotalMilliseconds;
if (timeSinceLastTouch < MULTI_TOUCH_DELAY_MS && inkCanvas.EditingMode == InkCanvasEditingMode.Ink)
{
if (!isMultiTouchTimerActive)
{
isMultiTouchTimerActive = true;
var remainingTime = MULTI_TOUCH_DELAY_MS - timeSinceLastTouch;
Task.Delay((int)remainingTime).ContinueWith(_ =>
{
Dispatcher.Invoke(() =>
{
if (dec.Count > 1 && inkCanvas.EditingMode == InkCanvasEditingMode.Ink)
inkCanvas.EditingMode = InkCanvasEditingMode.None;
isMultiTouchTimerActive = false;
});
});
}
return;
}
lastInkCanvasEditingMode = inkCanvas.EditingMode;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke
&& drawingShapeMode == 0)
{
inkCanvas.EditingMode = InkCanvasEditingMode.None;
}
}
}
@@ -792,6 +918,11 @@ namespace Ink_Canvas
/// </remarks>
private void InkCanvas_PreviewTouchMove(object sender, TouchEventArgs e)
{
if (isPalmEraserActive)
{
var touchPoint = e.GetTouchPoint(inkCanvas);
EraserOverlay_PointerMove(sender, touchPoint.Position);
}
}
/// <summary>
@@ -818,32 +949,18 @@ namespace Ink_Canvas
/// </remarks>
private void InkCanvas_PreviewTouchUp(object sender, TouchEventArgs e)
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint && !isPalmEraserActive)
{
return;
}
inkCanvas.ReleaseAllTouchCaptures();
ViewboxFloatingBar.IsHitTestVisible = true;
BlackboardUIGridForInkReplay.IsHitTestVisible = true;
//手势完成后切回之前的状态
if (dec.Count > 1)
if (inkCanvas.EditingMode == InkCanvasEditingMode.None)
inkCanvas.EditingMode = lastInkCanvasEditingMode;
dec.Remove(e.TouchDevice.Id);
if (dec.Count == 0)
{
isSingleFingerDragMode = false;
isWaitUntilNextTouchDown = false;
if (drawingShapeMode == 0
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke
&& inkCanvas.EditingMode != InkCanvasEditingMode.Select
&& inkCanvas.EditingMode != InkCanvasEditingMode.None)
{
if (lastInkCanvasEditingMode != InkCanvasEditingMode.None)
{
inkCanvas.EditingMode = lastInkCanvasEditingMode;
}
}
}
if (dec.Count <= 1)
isMultiTouchTimerActive = false;
if (drawingShapeMode != 0)
{
@@ -855,12 +972,10 @@ namespace Ink_Canvas
{
if (drawMultiStepShapeCurrentStep == 0)
{
// 第一笔完成,进入第二笔
drawMultiStepShapeCurrentStep = 1;
}
else
{
// 第二笔完成,完成绘制
var mouseArgs = new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left)
{
RoutedEvent = MouseLeftButtonUpEvent,
@@ -880,6 +995,36 @@ namespace Ink_Canvas
}
}
if (drawingShapeMode == 0)
{
if (dec.Count > 1)
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.None)
{
if (lastInkCanvasEditingMode != InkCanvasEditingMode.EraseByPoint)
inkCanvas.EditingMode = lastInkCanvasEditingMode;
}
}
else if (dec.Count == 0)
{
isSingleFingerDragMode = false;
isWaitUntilNextTouchDown = false;
if (inkCanvas.EditingMode == InkCanvasEditingMode.None &&
lastInkCanvasEditingMode != InkCanvasEditingMode.None &&
lastInkCanvasEditingMode != InkCanvasEditingMode.EraseByPoint)
{
inkCanvas.EditingMode = lastInkCanvasEditingMode;
}
if (isPalmEraserActive)
{
isPalmEraserActive = false;
DisableEraserOverlay();
}
}
}
inkCanvas.Opacity = 1;
if (dec.Count == 0)
@@ -977,6 +1122,9 @@ namespace Ink_Canvas
/// </remarks>
private void Main_Grid_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
return;
if (isInMultiTouchMode || !Settings.Gesture.IsEnableTwoFingerGesture) return;
bool hasMultipleManipulators = e.Manipulators.Count() >= 2;
+90 -1
View File
@@ -1,4 +1,4 @@
using Hardcodet.Wpf.TaskbarNotification;
using H.NotifyIcon;
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Controls;
using System;
@@ -9,6 +9,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms;
using System.Windows.Interop;
using System.Windows.Threading;
using Application = System.Windows.Application;
using ContextMenu = System.Windows.Controls.ContextMenu;
using MenuItem = System.Windows.Controls.MenuItem;
@@ -17,6 +18,11 @@ namespace Ink_Canvas
{
public partial class App : Application
{
private const int TrayTemporaryShowMinutes = 2;
private DispatcherTimer _trayTemporaryShowTimer;
private bool _trayTemporaryShowRestoreHideChecked;
/// <summary>
/// 系统托盘菜单打开时的事件处理方法
@@ -131,6 +137,85 @@ namespace Ink_Canvas
}
}
private void TempShowMainWindowTrayIconMenuItem_Clicked(object sender, RoutedEventArgs e)
{
var mainWin = Current.MainWindow as MainWindow;
if (mainWin?.IsLoaded != true)
return;
MenuItem hideItem = null;
try
{
var trayMenu = ((TaskbarIcon)Current.Resources["TaskbarTrayIcon"]).ContextMenu;
hideItem = trayMenu?.Items.OfType<MenuItem>()
.FirstOrDefault(mi => mi.Name == "HideICCMainWindowTrayIconMenuItem");
}
catch
{
}
_trayTemporaryShowRestoreHideChecked = hideItem?.IsChecked == true;
EnsureMainWindowReadyForSettings(mainWin);
global::Ink_Canvas.MainWindow.TrayTemporaryShowUntilUtc = DateTime.UtcNow.AddMinutes(TrayTemporaryShowMinutes);
_trayTemporaryShowTimer?.Stop();
if (_trayTemporaryShowTimer == null)
{
_trayTemporaryShowTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMinutes(TrayTemporaryShowMinutes)
};
_trayTemporaryShowTimer.Tick += TrayTemporaryShowTimer_OnTick;
}
else
{
_trayTemporaryShowTimer.Interval = TimeSpan.FromMinutes(TrayTemporaryShowMinutes);
}
_trayTemporaryShowTimer.Start();
}
private void TrayTemporaryShowTimer_OnTick(object sender, EventArgs e)
{
_trayTemporaryShowTimer?.Stop();
global::Ink_Canvas.MainWindow.TrayTemporaryShowUntilUtc = null;
var mainWin = Current.MainWindow as MainWindow;
if (mainWin?.IsLoaded != true)
{
_trayTemporaryShowRestoreHideChecked = false;
return;
}
try
{
if (_trayTemporaryShowRestoreHideChecked)
{
var trayMenu = ((TaskbarIcon)Current.Resources["TaskbarTrayIcon"]).ContextMenu;
var hideItem = trayMenu?.Items.OfType<MenuItem>()
.FirstOrDefault(mi => mi.Name == "HideICCMainWindowTrayIconMenuItem");
if (hideItem != null)
hideItem.IsChecked = true;
else
mainWin.Hide();
}
else
{
mainWin.CheckMainWindowVisibility();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"托盘临时显示计时结束处理失败: {ex.Message}", LogHelper.LogType.Warning);
}
finally
{
_trayTemporaryShowRestoreHideChecked = false;
}
}
private void OpenSettingsTrayIconMenuItem_Clicked(object sender, RoutedEventArgs e)
{
var mainWin = Current.MainWindow as MainWindow;
@@ -326,6 +411,10 @@ namespace Ink_Canvas
/// </remarks>
private void HideICCMainWindowTrayIconMenuItem_Checked(object sender, RoutedEventArgs e)
{
_trayTemporaryShowTimer?.Stop();
global::Ink_Canvas.MainWindow.TrayTemporaryShowUntilUtc = null;
_trayTemporaryShowRestoreHideChecked = false;
var mi = (MenuItem)sender;
var mainWin = (MainWindow)Current.MainWindow;
if (mainWin != null && mainWin.IsLoaded)
+3 -1
View File
@@ -19,7 +19,7 @@ namespace Ink_Canvas.Properties
[CompilerGenerated]
public static class Strings
{
private const string EmbeddedEnUsResxName = "Ink_Canvas.Properties.Strings.enUS.xml";
private const string EmbeddedEnUsResxName = "Ink_Canvas.Properties.Strings.en-US.resx";
private static readonly object EnUsLock = new object();
private static Dictionary<string, string> _embeddedEnUs;
private static ResourceManager _resourceMan;
@@ -119,5 +119,7 @@ namespace Ink_Canvas.Properties
public static string Nav_About => GetString(nameof(Nav_About)) ?? "关于";
public static string App_Title => GetString(nameof(App_Title)) ?? "InkCanvasforClass";
public static string Booth_Resolution_Tooltip => GetString(nameof(Booth_Resolution_Tooltip)) ?? "展台/截图分辨率";
public static string Tray_TempShowMainWindow => GetString(nameof(Tray_TempShowMainWindow)) ?? "显示主窗口(2分钟)";
public static string Tray_OpenSettings => GetString(nameof(Tray_OpenSettings)) ?? "打开设置";
}
}
File diff suppressed because it is too large Load Diff
-766
View File
@@ -1,766 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Nav_Plugins" xml:space="preserve">
<value>Plugins</value>
</data>
<data name="Nav_Startup" xml:space="preserve">
<value>Startup</value>
</data>
<data name="Nav_Canvas" xml:space="preserve">
<value>Canvas</value>
</data>
<data name="Nav_CrashAction" xml:space="preserve">
<value>Crash Action</value>
</data>
<data name="Nav_Gesture" xml:space="preserve">
<value>Gesture</value>
</data>
<data name="Nav_InkRecognition" xml:space="preserve">
<value>Ink Recognition</value>
</data>
<data name="Nav_PPT" xml:space="preserve">
<value>PPT</value>
</data>
<data name="Nav_Advanced" xml:space="preserve">
<value>Advanced</value>
</data>
<data name="Nav_Automation" xml:space="preserve">
<value>Automation</value>
</data>
<data name="Nav_RandomWindow" xml:space="preserve">
<value>Random Picker</value>
</data>
<data name="Nav_Theme" xml:space="preserve">
<value>Theme</value>
</data>
<data name="Nav_Shortcuts" xml:space="preserve">
<value>Shortcuts</value>
</data>
<data name="Nav_About" xml:space="preserve">
<value>About</value>
</data>
<data name="App_Title" xml:space="preserve">
<value>InkCanvasforClass</value>
</data>
<data name="Booth_Resolution_Tooltip" xml:space="preserve">
<value>Booth / Screenshot resolution</value>
</data>
<data name="Nav_Gesture_Settings" xml:space="preserve"><value>Gesture</value></data>
<data name="Nav_Theme_Settings" xml:space="preserve"><value>Appearance</value></data>
<data name="Nav_PPT_Settings" xml:space="preserve"><value>PPT</value></data>
<data name="Nav_Advanced_Settings" xml:space="preserve"><value>Advanced</value></data>
<data name="Nav_Automation_Settings" xml:space="preserve"><value>Automation</value></data>
<data name="Nav_RandomWindow_Settings" xml:space="preserve"><value>Random Picker</value></data>
<data name="Nav_Shortcuts_Settings" xml:space="preserve"><value>Shortcuts</value></data>
<data name="CollapseNavSidebar" xml:space="preserve"><value>Collapse sidebar</value></data>
<data name="ShowNavSidebar" xml:space="preserve"><value>Show sidebar</value></data>
<data name="Tooltip_IccProtocol" xml:space="preserve"><value>Control via icc:// protocol</value></data>
<data name="Settings_Title" xml:space="preserve"><value>Settings</value></data>
<data name="Settings_AutoSaveHint" xml:space="preserve"><value>Changes are saved automatically; some require restart.</value></data>
<data name="Btn_Restart" xml:space="preserve"><value>Restart</value></data>
<data name="Btn_Reset" xml:space="preserve"><value>Reset</value></data>
<data name="Btn_Exit" xml:space="preserve"><value>Exit</value></data>
<data name="Settings_Mode" xml:space="preserve"><value>Mode</value></data>
<data name="Settings_ModeDesc" xml:space="preserve"><value>Choose run mode. In PPT-only mode the app is hidden until slide show. (Experimental)</value></data>
<data name="Mode_Normal" xml:space="preserve"><value>Normal</value></data>
<data name="Mode_PPTOnly" xml:space="preserve"><value>PPT only</value></data>
<data name="Settings_NewWindow" xml:space="preserve"><value>New settings window</value></data>
<data name="Settings_NewWindowDesc" xml:space="preserve"><value>Open a new settings window. (In development)</value></data>
<data name="Btn_OpenNewSettings" xml:space="preserve"><value>Open new settings</value></data>
<data name="Settings_Plugins" xml:space="preserve"><value>Plugins</value></data>
<data name="Settings_PluginsDesc" xml:space="preserve"><value>Extend Ink Canvas with plugins. Enable, disable, or load custom plugins.</value></data>
<data name="Btn_OpenPluginManager" xml:space="preserve"><value>Open plugin manager</value></data>
<data name="Startup_Start" xml:space="preserve"><value>Startup</value></data>
<data name="Startup_NoFocusMode" xml:space="preserve"><value>No-focus mode</value></data>
<data name="Startup_NoBorderMode" xml:space="preserve"><value>Borderless</value></data>
<data name="Startup_TopMost" xml:space="preserve"><value>Topmost</value></data>
<data name="Startup_UIATopMost" xml:space="preserve"><value>UIA topmost</value></data>
<data name="Startup_UIATopMostHint" xml:space="preserve"><value># UIA topmost requires admin to take effect.</value></data>
<data name="Header_AutoUpdate" xml:space="preserve"><value>Auto-update</value></data>
<data name="Header_SilentUpdate" xml:space="preserve"><value>Silent update</value></data>
<data name="SilentUpdate_Hint" xml:space="preserve"><value># Silent update installs when app is idle.</value></data>
<data name="Update_Channel" xml:space="preserve"><value>Update channel</value></data>
<data name="Channel_Release" xml:space="preserve"><value>Stable (Release)</value></data>
<data name="Channel_Preview" xml:space="preserve"><value>Preview</value></data>
<data name="Channel_Beta" xml:space="preserve"><value>Beta</value></data>
<data name="Channel_Hint" xml:space="preserve"><value># Stable for reliability; Preview for new features.</value></data>
<data name="Btn_ManualUpdate" xml:space="preserve"><value>Check for updates</value></data>
<data name="ManualUpdate_Hint" xml:space="preserve"><value># Check and download now.</value></data>
<data name="Btn_VersionFix" xml:space="preserve"><value>Version fix</value></data>
<data name="VersionFix_Hint" xml:space="preserve"><value># Download and install latest for current channel.</value></data>
<data name="Btn_Rollback" xml:space="preserve"><value>Rollback</value></data>
<data name="Rollback_Hint" xml:space="preserve"><value># Open rollback page.</value></data>
<data name="SilentUpdate_AfterDownloadHint" xml:space="preserve"><value># When silent update is off, you will be prompted after download.</value></data>
<data name="SilentUpdate_TimeRange" xml:space="preserve"><value>Silent update time range</value></data>
<data name="Time_Start" xml:space="preserve"><value>Start time</value></data>
<data name="Time_End" xml:space="preserve"><value>End time</value></data>
<data name="TimeRange_Hint" xml:space="preserve"><value># If end &lt; start…</value></data>
<data name="Startup_RunAtLogin" xml:space="preserve"><value>Run at login</value></data>
<data name="Startup_MinimizeToSidebar" xml:space="preserve"><value>Minimize to sidebar at startup</value></data>
<data name="Canvas_AndInk" xml:space="preserve"><value>Canvas &amp; ink</value></data>
<data name="Canvas_ShowPenCursor" xml:space="preserve"><value>Show pen cursor</value></data>
<data name="Canvas_PressureTouch" xml:space="preserve"><value>Pressure-sensitive touch</value></data>
<data name="Canvas_PressureTouchHint" xml:space="preserve"><value># Touch devices will support pressure.</value></data>
<data name="Canvas_IgnorePressure" xml:space="preserve"><value>Ignore pressure</value></data>
<data name="Canvas_IgnorePressureHint" xml:space="preserve"><value># Ignore all device pressure.</value></data>
<data name="Canvas_EraserSize" xml:space="preserve"><value>Eraser size</value></data>
<data name="Size_VerySmall" xml:space="preserve"><value>Very small</value></data>
<data name="Size_Small" xml:space="preserve"><value>Small</value></data>
<data name="Size_Medium" xml:space="preserve"><value>Medium</value></data>
<data name="Size_Large" xml:space="preserve"><value>Large</value></data>
<data name="Size_VeryLarge" xml:space="preserve"><value>Very large</value></data>
<data name="EraserSize_SwitchHint" xml:space="preserve"><value># Takes effect on next area eraser use.</value></data>
<data name="Canvas_HideInkOnExit" xml:space="preserve"><value>Hide ink when leaving canvas</value></data>
<data name="Canvas_HideInkOnExitHint" xml:space="preserve"><value># When enabled…</value></data>
<data name="Canvas_ClearInkHistory" xml:space="preserve"><value>Clear ink history when clearing</value></data>
<data name="Canvas_ClearImageOnClear" xml:space="preserve"><value>Clear images with canvas</value></data>
<data name="Canvas_CompressImage" xml:space="preserve"><value>Compress images &gt;1920×1080</value></data>
<data name="Canvas_KeepAsymptote" xml:space="preserve"><value>Keep hyperbola asymptotes</value></data>
<data name="Yes" xml:space="preserve"><value>Yes</value></data>
<data name="No" xml:space="preserve"><value>No</value></data>
<data name="AskEachTime" xml:space="preserve"><value>Ask each time</value></data>
<data name="Canvas_AsymptoteHint" xml:space="preserve"><value># Disabling may cause undo bugs.</value></data>
<data name="Canvas_ShowCircleCenter" xml:space="preserve"><value>Show circle center</value></data>
<data name="Canvas_LaunchSeewoVideoShowcaseForWhiteboardBooth" xml:space="preserve"><value>Whiteboard booth opens Seewo Video Showcase</value></data>
<data name="Canvas_LaunchSeewoVideoShowcaseForWhiteboardBoothHint" xml:space="preserve"><value>When enabled, the whiteboard toolbar Booth button launches Seewo Video Showcase (must be installed). When disabled, the built-in booth is used.</value></data>
<data name="Canvas_WPFBezier" xml:space="preserve"><value>WPF default Bezier smoothing</value></data>
<data name="Canvas_AdvancedSmoothing" xml:space="preserve"><value>Advanced curve smoothing (recommended)</value></data>
<data name="Canvas_InkFade" xml:space="preserve"><value>Ink fade</value></data>
<data name="Canvas_InkFadeHint" xml:space="preserve"><value># Ink will not be drawn on canvas when enabled.</value></data>
<data name="Canvas_InkFadeTime" xml:space="preserve"><value>Ink fade time</value></data>
<data name="Canvas_HideFadeInPenMenu" xml:space="preserve"><value>Hide fade in pen menu</value></data>
<data name="Canvas_HideFadeInPenMenuHint" xml:space="preserve"><value># Fade control will be hidden in pen context menu.</value></data>
<data name="Color" xml:space="preserve"><value>Color</value></data>
<data name="Color_Default" xml:space="preserve"><value>Default</value></data>
<data name="Color_Black" xml:space="preserve"><value>Black</value></data>
<data name="Color_White" xml:space="preserve"><value>White</value></data>
<data name="Color_Red" xml:space="preserve"><value>Red</value></data>
<data name="Color_Yellow" xml:space="preserve"><value>Yellow</value></data>
<data name="Color_Blue" xml:space="preserve"><value>Blue</value></data>
<data name="Color_Green" xml:space="preserve"><value>Green</value></data>
<data name="Color_Orange" xml:space="preserve"><value>Orange</value></data>
<data name="Color_Purple" xml:space="preserve"><value>Purple</value></data>
<data name="Msg_UpdateReady" xml:space="preserve"><value>Update downloaded. It will install when you close the app.</value></data>
<data name="Msg_UpdateReadyTitle" xml:space="preserve"><value>Update ready</value></data>
<data name="Msg_UpdateDownloadFailed" xml:space="preserve"><value>Update download failed. Please check your network and try again.</value></data>
<data name="Msg_DownloadFailedTitle" xml:space="preserve"><value>Download failed</value></data>
<data name="Msg_SkipVersion" xml:space="preserve"><value>Version {0} skipped; you will not be prompted until a newer version is released.</value></data>
<data name="Msg_SkipVersionTitle" xml:space="preserve"><value>Version skipped</value></data>
<data name="Msg_UnexpectedError" xml:space="preserve"><value>An unexpected error occurred. Save your ink and restart the app.</value></data>
<data name="Msg_RestartLimitTitle" xml:space="preserve"><value>Too many restarts</value></data>
<data name="Msg_RestartLimit" xml:space="preserve"><value>App has restarted 5 times. Auto-restart stopped. Contact the developer or check the system.</value></data>
<data name="Splash_Starting" xml:space="preserve"><value>Starting Ink Canvas...</value></data>
<data name="Crash_Title" xml:space="preserve"><value>Crash action</value></data>
<data name="Crash_Desc" xml:space="preserve"><value>Choose what to do when an unhandled exception occurs:</value></data>
<data name="Crash_SilentRestart" xml:space="preserve"><value>Silent restart</value></data>
<data name="Crash_NoAction" xml:space="preserve"><value>No action</value></data>
<data name="Crash_Hint" xml:space="preserve"><value># Silent restart: automatically restart without prompt. No action: only log, do not restart.</value></data>
<data name="Gesture_Title" xml:space="preserve"><value>Gestures</value></data>
<data name="Gesture_AutoToggleTwoFinger" xml:space="preserve"><value>Auto-toggle two-finger move in/out of whiteboard</value></data>
<data name="Gesture_AutoToggleHint" xml:space="preserve"><value># When enabled: leaving canvas disables two-finger move; entering whiteboard enables it.</value></data>
<data name="Gesture_AllowRotateScale" xml:space="preserve"><value>Allow rotate &amp; scale selected ink</value></data>
<data name="Gesture_AllowRotateScaleHint" xml:space="preserve"><value># Allows scaling selected ink with two or more fingers (independent of rotate setting).</value></data>
<data name="Gesture_EnablePalmEraser" xml:space="preserve"><value>Enable palm eraser</value></data>
<data name="Gesture_PalmSensitivity" xml:space="preserve"><value>Palm eraser sensitivity</value></data>
<data name="Gesture_PalmSensitivityLow" xml:space="preserve"><value>Low sensitivity</value></data>
<data name="Gesture_PalmSensitivityMedium" xml:space="preserve"><value>Medium sensitivity</value></data>
<data name="Gesture_PalmSensitivityHigh" xml:space="preserve"><value>High sensitivity</value></data>
<data name="Gesture_PalmHint" xml:space="preserve"><value># Low: larger area/more touches required (less false positive); High: easier to trigger but may mis-detect fingers.</value></data>
<data name="InkRecog_Title" xml:space="preserve"><value>Ink correction</value></data>
<data name="InkRecog_EnableInkRecognition" xml:space="preserve"><value>Enable ink recognition</value></data>
<data name="InkRecog_ShapeEngine" xml:space="preserve"><value>Recognition engine</value></data>
<data name="InkRecog_ShapeEngineHint" xml:space="preserve"><value># Auto: WinRT on 64-bit (Windows 10+), IACore on 32-bit. You can force IACore or WinRT.</value></data>
<data name="InkRecog_ShapeEngineAuto" xml:space="preserve"><value>Auto</value></data>
<data name="InkRecog_ShapeEngineIACore" xml:space="preserve"><value>IACore</value></data>
<data name="InkRecog_ShapeEngineWinRT" xml:space="preserve"><value>WinRT</value></data>
<data name="InkRecog_HandwritingBeautify" xml:space="preserve"><value>WinRT: recognized text to handwriting glyphs</value></data>
<data name="InkRecog_HandwritingBeautifyHint" xml:space="preserve"><value># When the ink correction API runs: WinRT recognizes ink words, then replaces recognized text with outline strokes from a handwriting-style font (default Ink Free / KaiTi / Segoe Script; set handwritingCorrectionFontFamily in Settings JSON). 64-bit + WinRT.</value></data>
<data name="InkRecog_BlockRectFakePressure" xml:space="preserve"><value>Block fake pressure on corrected rectangles</value></data>
<data name="InkRecog_BlockTriFakePressure" xml:space="preserve"><value>Block fake pressure on corrected triangles</value></data>
<data name="InkRecog_FixTriangle" xml:space="preserve"><value>Correct freehand triangles</value></data>
<data name="InkRecog_FixRectangle" xml:space="preserve"><value>Correct freehand rectangles</value></data>
<data name="InkRecog_FixEllipse" xml:space="preserve"><value>Correct circles and ellipses</value></data>
<data name="InkRecog_AutoStraightLine" xml:space="preserve"><value>Auto-straighten lines</value></data>
<data name="InkRecog_LengthThreshold" xml:space="preserve"><value>Length threshold</value></data>
<data name="InkRecog_Sensitivity" xml:space="preserve"><value>Sensitivity</value></data>
<data name="InkRecog_HighPrecisionStraighten" xml:space="preserve"><value>High-precision straightening</value></data>
<data name="InkRecog_HighPrecisionHint" xml:space="preserve"><value># When enabled, lines longer than the threshold will be straightened. Sensitivity 0.052.0: smaller = stricter; larger = easier to treat as straight. High-precision samples every 10px for better judgement.</value></data>
<data name="InkRecog_LineEndpointSnapping" xml:space="preserve"><value>Line endpoint snapping</value></data>
<data name="InkRecog_SnappingDistance" xml:space="preserve"><value>Snapping distance</value></data>
<data name="Theme_GroupTitle" xml:space="preserve"><value>Personalization</value></data>
<data name="Theme_Label" xml:space="preserve"><value>Theme</value></data>
<data name="Theme_Light" xml:space="preserve"><value>Light theme</value></data>
<data name="Theme_Dark" xml:space="preserve"><value>Dark theme</value></data>
<data name="Theme_System" xml:space="preserve"><value>Follow system</value></data>
<data name="Theme_EnableSplash" xml:space="preserve"><value>Enable startup animation</value></data>
<data name="Theme_SplashStyle" xml:space="preserve"><value>Startup animation style</value></data>
<data name="Theme_Splash_Random" xml:space="preserve"><value>Random</value></data>
<data name="Theme_Splash_Seasonal" xml:space="preserve"><value>Follow seasons</value></data>
<data name="Theme_Splash_Spring" xml:space="preserve"><value>Spring</value></data>
<data name="Theme_Splash_Summer" xml:space="preserve"><value>Summer</value></data>
<data name="Theme_Splash_Autumn" xml:space="preserve"><value>Autumn</value></data>
<data name="Theme_Splash_Winter" xml:space="preserve"><value>Winter</value></data>
<data name="Theme_Splash_Horse" xml:space="preserve"><value>Year-of-Horse special</value></data>
<data name="Theme_FloatingBarIcon" xml:space="preserve"><value>Floating toolbar icon</value></data>
<data name="Theme_FloatingIcon_IccDefault" xml:space="preserve"><value>“ICC-CE” default</value></data>
<data name="Theme_FloatingIcon_IccNoShadow" xml:space="preserve"><value>“ICC-CE” no shadow</value></data>
<data name="Theme_FloatingIcon_IccDark" xml:space="preserve"><value>“ICC-CE” dark</value></data>
<data name="Theme_FloatingIcon_IccDarkBreath" xml:space="preserve"><value>“ICC-CE” dark breathing</value></data>
<data name="Theme_FloatingIcon_IccWhiteTransparent" xml:space="preserve"><value>“ICC-CE” white transparent</value></data>
<data name="Theme_FloatingIcon_IccBlackTransparent" xml:space="preserve"><value>“ICC-CE” black transparent</value></data>
<data name="Theme_FloatingIcon_CoolapkCrossEye" xml:space="preserve"><value>Coolapk cross-eye emoji</value></data>
<data name="Theme_FloatingIcon_CoolapkAbused" xml:space="preserve"><value>Coolapk abused emoji</value></data>
<data name="Theme_FloatingIcon_CoolapkSmile" xml:space="preserve"><value>Coolapk grin emoji</value></data>
<data name="Theme_FloatingIcon_CoolapkUnderwear" xml:space="preserve"><value>Coolapk underwear emoji</value></data>
<data name="Theme_FloatingIcon_CoolapkGreenHatDoge" xml:space="preserve"><value>Coolapk green-hat Doge</value></data>
<data name="Theme_FloatingIcon_TiebaEmoji" xml:space="preserve"><value>Tieba emoji</value></data>
<data name="Theme_CustomFloatingIconLabel" xml:space="preserve"><value>Custom floating icon</value></data>
<data name="Theme_Upload" xml:space="preserve"><value>Upload</value></data>
<data name="Theme_Manage" xml:space="preserve"><value>Manage</value></data>
<data name="Theme_FloatingBarScale" xml:space="preserve"><value>Floating toolbar scale</value></data>
<data name="Theme_FloatingBarOpacity" xml:space="preserve"><value>Floating toolbar opacity</value></data>
<data name="Theme_FloatingBarOpacityInPPT" xml:space="preserve"><value>Floating bar opacity in PPT</value></data>
<data name="Theme_FloatingBarOpacityInPPTHint" xml:space="preserve"><value># Takes effect after re-entering slide show</value></data>
<data name="Theme_ShowNibButton" xml:space="preserve"><value>Show nib-mode button in palette</value></data>
<data name="Theme_BlackboardScale80" xml:space="preserve"><value>Whiteboard UI 80% scale</value></data>
<data name="Theme_ShowTimeInWhiteboard" xml:space="preserve"><value>Show time and date in whiteboard</value></data>
<data name="Theme_ShowQuoteInWhiteboard" xml:space="preserve"><value>Show quotes in whiteboard</value></data>
<data name="Theme_QuoteSource" xml:space="preserve"><value>Where is the quote from?</value></data>
<data name="Theme_QuoteSource_OsuQuotes" xml:space="preserve"><value>osu! player quotes</value></data>
<data name="Theme_QuoteSource_Mottos" xml:space="preserve"><value>Inspirational mottos</value></data>
<data name="Theme_QuoteSource_GaokaoBless" xml:space="preserve"><value>Gaokao blessings</value></data>
<data name="Theme_QuoteSource_Hitokoto" xml:space="preserve"><value>Hitokoto API</value></data>
<data name="Theme_Customize" xml:space="preserve"><value>Custom</value></data>
<data name="Theme_EnableQuickPanel" xml:space="preserve"><value>Enable quick panel in docked mode</value></data>
<data name="Theme_UnfoldButtonIcon" xml:space="preserve"><value>Un-dock button icon</value></data>
<data name="Theme_UnfoldIcon_Arrow" xml:space="preserve"><value>Arrow</value></data>
<data name="Theme_UnfoldIcon_Pen" xml:space="preserve"><value>Pen</value></data>
<data name="Theme_FloatingBarButtonsTitle" xml:space="preserve"><value>Floating bar buttons</value></data>
<data name="Theme_UseLegacyFloatingBarUI" xml:space="preserve"><value>Use legacy floating bar UI</value></data>
<data name="Theme_ShowShapeButton" xml:space="preserve"><value>Show shape button</value></data>
<data name="Theme_ShowUndoButton" xml:space="preserve"><value>Show undo button</value></data>
<data name="Theme_ShowRedoButton" xml:space="preserve"><value>Show redo button</value></data>
<data name="Theme_ShowClearButton" xml:space="preserve"><value>Show clear button</value></data>
<data name="Theme_ShowWhiteboardButton" xml:space="preserve"><value>Show whiteboard button</value></data>
<data name="Theme_ShowHideButton" xml:space="preserve"><value>Show hide button</value></data>
<data name="Theme_ShowLassoButton" xml:space="preserve"><value>Show lasso select button</value></data>
<data name="Theme_ShowClearAndMouseButton" xml:space="preserve"><value>Show clear+mouse button</value></data>
<data name="Theme_ShowQuickPalette" xml:space="preserve"><value>Show quick palette</value></data>
<data name="Theme_QuickPaletteMode" xml:space="preserve"><value>Quick palette display mode</value></data>
<data name="Theme_QuickPalette_SingleRow" xml:space="preserve"><value>Single row (6 colors)</value></data>
<data name="Theme_QuickPalette_DoubleRow" xml:space="preserve"><value>Double row (8 colors)</value></data>
<data name="Theme_EraserButtonDisplay" xml:space="preserve"><value>Eraser button display</value></data>
<data name="Theme_EraserDisplay_Both" xml:space="preserve"><value>Show both</value></data>
<data name="Theme_EraserDisplay_AreaOnly" xml:space="preserve"><value>Area eraser only</value></data>
<data name="Theme_EraserDisplay_LineOnly" xml:space="preserve"><value>Line eraser only</value></data>
<data name="Theme_EraserDisplay_None" xml:space="preserve"><value>Hide all</value></data>
<data name="Tray_GroupTitle" xml:space="preserve"><value>Taskbar tray icon</value></data>
<data name="Tray_EnableTrayIcon" xml:space="preserve"><value>Enable tray icon</value></data>
<data name="PPT_GroupTitle" xml:space="preserve"><value>PPT integration</value></data>
<data name="PPT_GroupHint" xml:space="preserve"><value>These settings apply during slide show and override others.</value></data>
<data name="PPT_SupportPowerPoint" xml:space="preserve"><value>Microsoft PowerPoint support</value></data>
<data name="PPT_Enhancement" xml:space="preserve"><value>PowerPoint enhancement</value></data>
<data name="PPT_SkipAnimations" xml:space="preserve"><value>Steal focus to skip animations (PPT)</value></data>
<data name="PPT_UseRot" xml:space="preserve"><value>Use ROT integration</value></data>
<data name="PPT_SupportWPS" xml:space="preserve"><value>WPS support</value></data>
<data name="PPT_KillWppProcess" xml:space="preserve"><value>Kill WPP process (avoid leftovers)</value></data>
<data name="PPT_KillWppHint" xml:space="preserve"><value># When disabled, leftover WPP processes may cause slow close or cannot exit completely.</value></data>
<data name="PPT_WpsHint1" xml:space="preserve"><value># If you only use PowerPoint, do not enable WPS integration. If you use WPS, it is recommended not to use PowerPoint together.</value></data>
<data name="PPT_WpsLagWarning" xml:space="preserve"><value>Enabling WPS support may cause lag when closing WPS!</value></data>
<data name="PPT_WpsSupportHint" xml:space="preserve"><value># WPS is supported, but MS Office and WPS cannot be supported at the same time. To enable WPS support, make sure “WPS Office compatibility with third-party systems and software” is enabled in the WPS config tool, otherwise WPS cannot be detected.</value></data>
<data name="Canvas_HideStrokeWhenSelecting" xml:space="preserve"><value>Hide ink when exiting board mode</value></data>
<data name="Canvas_HideStrokeWhenSelectingHint" xml:space="preserve"><value># When this option is on, ink will not be shown in PPT mode if not in annotation mode.</value></data>
<data name="Canvas_ClearInkAlsoClearHistory" xml:space="preserve"><value>Clear ink history when clearing ink</value></data>
<data name="Canvas_ClearCanvasAlsoClearImages" xml:space="preserve"><value>Clear images when clearing canvas</value></data>
<data name="Canvas_CompressPicturesUploaded" xml:space="preserve"><value>Auto-compress images when inserting (larger than 1920x1080)</value></data>
<data name="PPT_FlipButtonsTitle" xml:space="preserve"><value>PPT page-turn buttons</value></data>
<data name="PPT_ShowFlipButtons" xml:space="preserve"><value>Show page-turn buttons in PPT mode</value></data>
<data name="PPT_Position_LeftBottom" xml:space="preserve"><value>Bottom left</value></data>
<data name="PPT_Position_RightBottom" xml:space="preserve"><value>Bottom right</value></data>
<data name="PPT_Position_Left" xml:space="preserve"><value>Left</value></data>
<data name="PPT_Position_Right" xml:space="preserve"><value>Right</value></data>
<data name="PPT_LeftOffset" xml:space="preserve"><value>Left offset</value></data>
<data name="PPT_LeftOpacity" xml:space="preserve"><value>Left opacity</value></data>
<data name="PPT_RightOffset" xml:space="preserve"><value>Right offset</value></data>
<data name="PPT_RightOpacity" xml:space="preserve"><value>Right opacity</value></data>
<data name="PPT_OffsetHint" xml:space="preserve"><value># Increase for up, decrease for down; 0 = no offset, centered.</value></data>
<data name="PPT_LeftBottomOffset" xml:space="preserve"><value>Bottom left offset</value></data>
<data name="PPT_LeftBottomOpacity" xml:space="preserve"><value>Bottom left opacity</value></data>
<data name="PPT_RightBottomOffset" xml:space="preserve"><value>Bottom right offset</value></data>
<data name="PPT_RightBottomOpacity" xml:space="preserve"><value>Bottom right opacity</value></data>
<data name="PPT_OffsetHintHorizontal" xml:space="preserve"><value># Increase for right, decrease for left; 0 = no offset, centered.</value></data>
<data name="PPT_SideGroupTitle" xml:space="preserve"><value>Sides</value></data>
<data name="PPT_ShowPageNumber" xml:space="preserve"><value>Show page number</value></data>
<data name="PPT_HalfOpacity" xml:space="preserve"><value>Half opacity</value></data>
<data name="PPT_BlackBackground" xml:space="preserve"><value>Black background</value></data>
<data name="PPT_BottomGroupTitle" xml:space="preserve"><value>Bottom left &amp; right</value></data>
<data name="PPT_PageButtonClickable" xml:space="preserve"><value>PPT page button clickable</value></data>
<data name="PPT_PageButtonClickableHint" xml:space="preserve"><value># When enabled, clicking the page button opens PowerPoint grid thumbnails. Not supported in WPS.</value></data>
<data name="PPT_LongPressPageTurn" xml:space="preserve"><value>PPT long-press to turn page</value></data>
<data name="PPT_LongPressPageTurnHint" xml:space="preserve"><value># When enabled, long-press on PPT page button to turn pages continuously.</value></data>
<data name="Startup_UIAccessTopMostHint" xml:space="preserve"><value># With UIA topmost on, app needs admin to stay on top. To turn off, fully quit then start again; restart will not disable it.</value></data>
<data name="Header_SilentUpdate" xml:space="preserve"><value>Silent update</value></data>
<data name="Startup_SilentUpdateHint" xml:space="preserve"><value># Silent update installs when the app is idle; no manual action needed.</value></data>
<data name="Startup_UpdateChannel" xml:space="preserve"><value>Update channel</value></data>
<data name="Update_Release" xml:space="preserve"><value>Stable (Release)</value></data>
<data name="Update_Preview" xml:space="preserve"><value>Preview</value></data>
<data name="Update_Beta" xml:space="preserve"><value>Beta</value></data>
<data name="Startup_UpdateChannelHint" xml:space="preserve"><value># Stable: reliable updates. Preview: new features with better stability than Beta. Beta: earliest new features.</value></data>
<data name="Btn_ManualUpdate" xml:space="preserve"><value>Check for updates</value></data>
<data name="Startup_ManualUpdateHint" xml:space="preserve"><value># Check and download the latest version now.</value></data>
<data name="Btn_FixVersion" xml:space="preserve"><value>Repair installation</value></data>
<data name="Startup_FixVersionHint" xml:space="preserve"><value># Repair downloads the latest build for the selected channel and reinstalls; use to fix broken installs.</value></data>
<data name="Btn_HistoryRollback" xml:space="preserve"><value>Rollback to previous version</value></data>
<data name="Startup_HistoryRollbackHint" xml:space="preserve"><value># Opens a page to manually roll back to an earlier version.</value></data>
<data name="Startup_SilentUpdateFullHint" xml:space="preserve"><value># When silent update is off, you will be prompted after download. When on, every 10 minutes the app checks: 1) within silent-update time window 2) not in writing mode 3) not in canvas. If all pass, it will close and update.</value></data>
<data name="Startup_SilentUpdateTimePeriod" xml:space="preserve"><value>Silent update time window</value></data>
<data name="Startup_StartTime" xml:space="preserve"><value>Start time</value></data>
<data name="Startup_EndTime" xml:space="preserve"><value>End time</value></data>
<data name="Startup_TimePeriodHint" xml:space="preserve"><value># If end &lt; start, end is next day. If start = end, window is 24h.</value></data>
<data name="Startup_RunAtStartup" xml:space="preserve"><value>Run at startup</value></data>
<data name="Startup_FoldAtStartup" xml:space="preserve"><value>Dock to sidebar after startup</value></data>
<data name="Canvas_GroupTitle" xml:space="preserve"><value>Canvas and ink</value></data>
<data name="Canvas_ShowCursor" xml:space="preserve"><value>Show pen cursor</value></data>
<data name="Canvas_EnablePressureTouch" xml:space="preserve"><value>Enable pressure-sensitive touch</value></data>
<data name="Canvas_EnablePressureTouchHint" xml:space="preserve"><value># When on, touch screens that support pressure will show pressure; for devices not recognized by the system.</value></data>
<data name="Canvas_DisablePressure" xml:space="preserve"><value>Ignore pressure</value></data>
<data name="Canvas_DisablePressureHint" xml:space="preserve"><value># When on, all strokes use uniform thickness; mutually exclusive with pressure-sensitive touch.</value></data>
<data name="Canvas_EraserSize" xml:space="preserve"><value>Eraser size</value></data>
<data name="Canvas_EraserSize_VerySmall" xml:space="preserve"><value>Very small</value></data>
<data name="Canvas_EraserSize_Small" xml:space="preserve"><value>Small</value></data>
<data name="Canvas_EraserSize_Medium" xml:space="preserve"><value>Medium</value></data>
<data name="Canvas_EraserSize_Large" xml:space="preserve"><value>Large</value></data>
<data name="Canvas_EraserSize_VeryLarge" xml:space="preserve"><value>Very large</value></data>
<data name="Canvas_EraserSizeHint" xml:space="preserve"><value># Change takes effect next time you use area eraser.</value></data>
<data name="Canvas_KeepHyperbolaAsymptote" xml:space="preserve"><value>Keep hyperbola asymptotes</value></data>
<data name="Canvas_Yes" xml:space="preserve"><value>Yes</value></data>
<data name="Canvas_No" xml:space="preserve"><value>No</value></data>
<data name="Canvas_AskEachTime" xml:space="preserve"><value>Ask each time</value></data>
<data name="Canvas_HyperbolaAsymptoteHint" xml:space="preserve"><value># If not kept, undo-related bugs may occur.</value></data>
<data name="Canvas_ShowCircleCenter" xml:space="preserve"><value>Show circle center when drawing</value></data>
<data name="Canvas_WPFBezierSmoothing" xml:space="preserve"><value>Use WPF default Bezier smoothing</value></data>
<data name="Canvas_AdvancedBezierSmoothing" xml:space="preserve"><value>Use advanced curve smoothing (recommended)</value></data>
<data name="Canvas_EnableInkFade" xml:space="preserve"><value>Enable ink fade</value></data>
<data name="Canvas_EnableInkFadeHint" xml:space="preserve"><value># When on, ink is not committed to canvas; it fades after the set time.</value></data>
<data name="Canvas_InkFadeTime" xml:space="preserve"><value>Ink fade time</value></data>
<data name="Canvas_HideInkFadeInPenMenu" xml:space="preserve"><value>Hide ink fade control in pen menu</value></data>
<data name="Canvas_HideInkFadeInPenMenuHint" xml:space="preserve"><value># When on, the pen context menu will not show the ink fade control.</value></data>
<data name="Canvas_BrushAutoRestore" xml:space="preserve"><value>Enable brush auto-restore</value></data>
<data name="Canvas_BrushAutoRestoreHint" xml:space="preserve"><value># When on, temporary brush changes will restore at the configured time(s) to the color/opacity/width set here.</value></data>
<data name="Canvas_AutoRestoreTimePoints" xml:space="preserve"><value>Auto-restore time points (HH:mm, multiple with ;)</value></data>
<data name="Canvas_RestoreColor" xml:space="preserve"><value>Restore color</value></data>
<data name="Canvas_Color_Default" xml:space="preserve"><value>Default</value></data>
<data name="Canvas_Color_Black" xml:space="preserve"><value>Black</value></data>
<data name="Canvas_Color_White" xml:space="preserve"><value>White</value></data>
<data name="Canvas_Color_Red" xml:space="preserve"><value>Red</value></data>
<data name="Canvas_Color_Yellow" xml:space="preserve"><value>Yellow</value></data>
<data name="Canvas_Color_Blue" xml:space="preserve"><value>Blue</value></data>
<data name="Canvas_Color_Green" xml:space="preserve"><value>Green</value></data>
<data name="Canvas_Color_Orange" xml:space="preserve"><value>Orange</value></data>
<data name="Canvas_Color_Purple" xml:space="preserve"><value>Purple</value></data>
<data name="Canvas_RestoreWidth" xml:space="preserve"><value>Restore stroke width</value></data>
<data name="Canvas_RestoreOpacity" xml:space="preserve"><value>Restore opacity</value></data>
<data name="Canvas_SwitchBackAfterEraser" xml:space="preserve"><value>Switch back to annotation after eraser</value></data>
<data name="Canvas_SwitchBackAfterEraserHint" xml:space="preserve"><value># When on, after erasing, staying idle for a while will switch back to annotation mode.</value></data>
<data name="Canvas_SwitchBackDelay" xml:space="preserve"><value>Auto switch delay</value></data>
<data name="Canvas_SwitchBackDelayHint" xml:space="preserve"><value># If you erase again within the delay, the timer resets.</value></data>
<data name="InkRecog_LineEndpointSnappingHint" xml:space="preserve"><value># When on, line endpoints near other endpoints will snap and connect.</value></data>
<data name="PPT_EnterAnnotationOnShow" xml:space="preserve"><value>Enter annotation mode when starting PPT slide show</value></data>
<data name="PPT_ConflictWithAutoFold" xml:space="preserve"><value>Conflicts with "Auto fold when playing PPT" in Automation!</value></data>
<data name="PPT_TwoFingerGesture" xml:space="preserve"><value>Allow two-finger gestures in slide show</value></data>
<data name="PPT_FingerGestureSlide" xml:space="preserve"><value>Allow finger gesture to turn slides</value></data>
<data name="PPT_FingerGestureSlideHint" xml:space="preserve"><value># When canvas is on, finger swipe (not pen) can turn slides in show mode when canvas has no ink.</value></data>
<data name="PPT_ShowGestureButtonInShow" xml:space="preserve"><value>Show gesture buttons in PPT slide show</value></data>
<data name="PPT_ShowGestureButtonInShowHint" xml:space="preserve"><value># When on, gesture buttons are shown in PPT slide show.</value></data>
<data name="PPT_TimeCapsule" xml:space="preserve"><value>PPT time capsule</value></data>
<data name="PPT_TimeCapsuleHint" xml:space="preserve"><value># When on, show time capsule in PPT show; can replace minimized timer window.</value></data>
<data name="PPT_TimeCapsulePosition" xml:space="preserve"><value>Time capsule position:</value></data>
<data name="PPT_TimeCapsulePos_TL" xml:space="preserve"><value>Top left</value></data>
<data name="PPT_TimeCapsulePos_TR" xml:space="preserve"><value>Top right</value></data>
<data name="PPT_TimeCapsulePos_Center" xml:space="preserve"><value>Top center</value></data>
<data name="PPT_ShowQuickPanelInShow" xml:space="preserve"><value>Show quick panel in PPT slide show</value></data>
<data name="PPT_ShowQuickPanelInShowHint" xml:space="preserve"><value># When off, quick panel is hidden in PPT slide show.</value></data>
<data name="PPT_AutoScreenshot" xml:space="preserve"><value>Auto screenshot on slide change</value></data>
<data name="PPT_AutoScreenshotHint" xml:space="preserve"><value># When on, auto-screenshot when turning page with ink on slide.</value></data>
<data name="PPT_AutoSaveStrokes" xml:space="preserve"><value>Auto-save slide ink</value></data>
<data name="PPT_AutoSaveStrokesHint" xml:space="preserve"><value># When on, ink is saved when ending slide show and loaded next time (same file and page).</value></data>
<data name="PPT_RememberLastPage" xml:space="preserve"><value>Remember and prompt last slide position</value></data>
<data name="PPT_RememberLastPageHint" xml:space="preserve"><value># When on, last page is recorded; choose Yes to jump to it.</value></data>
<data name="PPT_GoToFirstPageOnReenter" xml:space="preserve"><value>Go to first slide when entering show</value></data>
<data name="PPT_NotifyHiddenPage" xml:space="preserve"><value>Warn about hidden slides</value></data>
<data name="PPT_NotifyAutoPlay" xml:space="preserve"><value>Warn if auto-play is enabled</value></data>
<data name="Advanced_Title" xml:space="preserve"><value>Advanced</value></data>
<data name="Advanced_TouchMultiplierHint" xml:space="preserve"><value>Adjust when finger-touch shows circle eraser or palm eraser is much larger than palm</value></data>
<data name="Advanced_SpecialScreenMode" xml:space="preserve"><value>Special screen mode</value></data>
<data name="Advanced_TouchMultiplier" xml:space="preserve"><value>Touch multiplier</value></data>
<data name="Advanced_TouchMultiplierCalibrateHint" xml:space="preserve"><value>Tap with pen in the area below to estimate touch size multiplier</value></data>
<data name="Advanced_TouchMultiplierValueHint" xml:space="preserve"><value># Value is for reference only</value></data>
<data name="Advanced_EraserBindTouchMultiplier" xml:space="preserve"><value>Bind eraser to touch size multiplier</value></data>
<data name="Advanced_EraserBindTouchHint" xml:space="preserve"><value># BoundsWidth is used as contact area threshold</value></data>
<data name="Advanced_QuadIRMode" xml:space="preserve"><value>Quad IR mode</value></data>
<data name="Advanced_Logging" xml:space="preserve"><value>Enable logging</value></data>
<data name="Advanced_LogByDate" xml:space="preserve"><value>Save logs by date</value></data>
<data name="Advanced_LogRotateHint" xml:space="preserve"><value># Log files over 512 KB are auto-deleted. With date save, logs go to Logs folder; folder is cleared when over 5 MB.</value></data>
<data name="Advanced_ConfirmExit" xml:space="preserve"><value>Confirm exit with dialog</value></data>
<data name="Advanced_FullScreenHelper" xml:space="preserve"><value>Enable FullScreenHelper</value></data>
<data name="Advanced_Experimental" xml:space="preserve"><value>Experimental</value></data>
<data name="Advanced_FullScreenHelperHint" xml:space="preserve"><value># Thanks to lindexi for FullScreenHelper; reduces taskbar pop-up and supports multi-monitor fullscreen. Disable if you see odd issues; restart ICC to apply.</value></data>
<data name="Advanced_AvoidFullScreenHelper" xml:space="preserve"><value>Enable AvoidFullScreenHelper</value></data>
<data name="Advanced_AvoidFullScreenHelperHint" xml:space="preserve"><value># Avoid canvas fullscreen; may fix taskbar not on top and Win11 taskbar unclickable. Can cause floating bar offset with AppBar on left/top. Restart ICC to apply.</value></data>
<data name="Advanced_EdgeGestureUtil" xml:space="preserve"><value>Enable EdgeGestureUtil</value></data>
<data name="Tools_MoreFeaturesTitle" xml:space="preserve"><value>More features</value></data>
<data name="Tools_Timer" xml:space="preserve"><value>Timer</value></data>
<data name="Tools_RandomDraw" xml:space="preserve"><value>Random draw</value></data>
<data name="Tools_SingleDraw" xml:space="preserve"><value>Single draw</value></data>
<data name="Tools_Save" xml:space="preserve"><value>Save</value></data>
<data name="Tools_Open" xml:space="preserve"><value>Open...</value></data>
<data name="Tools_Replay" xml:space="preserve"><value>Replay</value></data>
<data name="Tools_Screenshot" xml:space="preserve"><value>Screenshot</value></data>
<data name="Tools_Manual" xml:space="preserve"><value>Manual</value></data>
<data name="Tools_Settings" xml:space="preserve"><value>Settings</value></data>
<data name="QuickPanel_SingleDraw" xml:space="preserve"><value>Single draw</value></data>
<data name="QuickPanel_RandomDraw" xml:space="preserve"><value>Random draw</value></data>
<data name="QuickPanel_Timer" xml:space="preserve"><value>Timer</value></data>
<data name="QuickPanel_Whiteboard" xml:space="preserve"><value>Whiteboard</value></data>
<data name="QuickPanel_ExitShow" xml:space="preserve"><value>Exit slide show</value></data>
<data name="QuickPanel_Show" xml:space="preserve"><value>Show</value></data>
<data name="QuickPanel_Exit" xml:space="preserve"><value>Exit</value></data>
<data name="Backup_Title" xml:space="preserve"><value>Settings backup &amp; restore</value></data>
<data name="Backup_Desc" xml:space="preserve"><value># You can manually back up current settings or restore previous backups; backups are also created automatically before updates.</value></data>
<data name="Backup_AutoBeforeUpdate" xml:space="preserve"><value>Backup before update</value></data>
<data name="Backup_AutoPeriodic" xml:space="preserve"><value>Periodic auto-backup</value></data>
<data name="Backup_Interval" xml:space="preserve"><value>Backup interval</value></data>
<data name="Backup_Interval_1Day" xml:space="preserve"><value>1 day</value></data>
<data name="Backup_Interval_3Days" xml:space="preserve"><value>3 days</value></data>
<data name="Backup_Interval_7Days" xml:space="preserve"><value>7 days</value></data>
<data name="Backup_Interval_14Days" xml:space="preserve"><value>14 days</value></data>
<data name="Backup_Interval_30Days" xml:space="preserve"><value>30 days</value></data>
<data name="Backup_Interval_DefaultHint" xml:space="preserve"><value>(default: 7 days)</value></data>
<data name="Backup_Manual" xml:space="preserve"><value>Backup now</value></data>
<data name="Backup_Restore" xml:space="preserve"><value>Restore backup</value></data>
<data name="ConfigProfiles_Title" xml:space="preserve"><value>Config profiles &amp; hot reload</value></data>
<data name="ConfigProfiles_Desc" xml:space="preserve"><value># Selecting a profile switches and hot-reloads it; \"Save as\" saves current settings as a new profile.</value></data>
<data name="ConfigProfiles_Label" xml:space="preserve"><value>Profile:</value></data>
<data name="ConfigProfiles_Delete" xml:space="preserve"><value>Delete profile</value></data>
<data name="ConfigProfiles_SaveAs" xml:space="preserve"><value>Save as profile</value></data>
<data name="Automation_Title" xml:space="preserve"><value>Automation</value></data>
<data name="Automation_AutoFoldTitle" xml:space="preserve"><value>Auto fold</value></data>
<data name="AutoFold_App_SeewoBoard5" xml:space="preserve"><value>Seewo Whiteboard 5</value></data>
<data name="AutoFold_App_SeewoCamera" xml:space="preserve"><value>Seewo Visual Presenter</value></data>
<data name="AutoFold_App_SeewoBoard3" xml:space="preserve"><value>Seewo Whiteboard 3</value></data>
<data name="AutoFold_App_SeewoLightBoard" xml:space="preserve"><value>Seewo Lite Whiteboard</value></data>
<data name="AutoFold_App_SeewoLightBoard5C" xml:space="preserve"><value>Seewo Lite Whiteboard 5C</value></data>
<data name="AutoFold_App_SeewoPinco" xml:space="preserve"><value>Seewo Pinco</value></data>
<data name="AutoFold_App_HiteBoard" xml:space="preserve"><value>HiteBoard</value></data>
<data name="AutoFold_App_HiteCamera" xml:space="preserve"><value>Hite visual presenter</value></data>
<data name="AutoFold_App_HiteLightBoard" xml:space="preserve"><value>Hite Lite Whiteboard</value></data>
<data name="AutoFold_App_WenXiangBoard" xml:space="preserve"><value>WenXiang Whiteboard</value></data>
<data name="AutoFold_App_MSWhiteboard" xml:space="preserve"><value>Microsoft Whiteboard</value></data>
<data name="AutoFold_App_AdmoxBoard" xml:space="preserve"><value>Admox Whiteboard</value></data>
<data name="AutoFold_App_AdmoxBooth" xml:space="preserve"><value>Admox visual presenter</value></data>
<data name="AutoFold_App_YiYunBoard" xml:space="preserve"><value>YiYun Whiteboard</value></data>
<data name="AutoFold_App_YiYunBooth" xml:space="preserve"><value>YiYun visual presenter</value></data>
<data name="AutoFold_App_MaxHubBoard" xml:space="preserve"><value>MaxHub Whiteboard</value></data>
<data name="AutoFold_IgnoreEasiNoteDesktopAnno" xml:space="preserve"><value>Ignore EN5 desktop annotation window when auto folding</value></data>
<data name="AutoFold_OldZyBoard" xml:space="preserve"><value>Auto fold when entering old ZhongYuan whiteboard</value></data>
<data name="Automation_AutoFoldInPPT" xml:space="preserve"><value>Auto fold while playing PPT</value></data>
<data name="Automation_KeepFoldAfterExit" xml:space="preserve"><value>Keep folded after app exit</value></data>
<data name="Automation_KeepFoldAfterExitHint" xml:space="preserve"><value># When on, apps that trigger auto fold will stay folded even after they exit.</value></data>
<data name="AutoKill_Title" xml:space="preserve"><value>Auto kill</value></data>
<data name="AutoKill_PptTools" xml:space="preserve"><value>Auto kill Seewo PPT tools</value></data>
<data name="AutoKill_PptToolsHint" xml:space="preserve"><value># Killing PPT tools disables Seewo classroom helper. Delete Office.dll in its install folder to stop the PPT toolbar without auto kill.</value></data>
<data name="AutoKill_EasiNote5" xml:space="preserve"><value>Auto kill Seewo Whiteboard 5</value></data>
<data name="AutoKill_HiteAnnotation" xml:space="preserve"><value>Auto kill Hite screen writing</value></data>
<data name="AutoKill_HiteAfterKillEnterAnnotation" xml:space="preserve"><value>Enter annotation after killing Hite screen writing</value></data>
<data name="AutoKill_YouJiao" xml:space="preserve"><value>Auto kill YouJiao teacher</value></data>
<data name="AutoKill_SeewoDesktop2Anno" xml:space="preserve"><value>Auto kill Seewo Desktop 2.0 annotation</value></data>
<data name="AutoKill_SeewoDesktop2AnnoHint" xml:space="preserve"><value># Seewo Desktop 2.0 annotation is 64-bit so ICC (32-bit) cannot inspect it deeply; only process name DesktopAnnotation is matched. If you have another app with the same name, keep this off.</value></data>
<data name="AutoKill_SameAppTitle" xml:space="preserve"><value>Kill similar apps</value></data>
<data name="AutoKill_InkCanvasIC" xml:space="preserve"><value>Auto kill Ink Canvas and IC+</value></data>
<data name="AutoKill_ICA" xml:space="preserve"><value>Auto kill ICA (both new &amp; old)</value></data>
<data name="AutoKill_Inkeys" xml:space="preserve"><value>Auto kill Inkeys (new only)</value></data>
<data name="FileAssoc_Title" xml:space="preserve"><value>File association</value></data>
<data name="FileAssoc_Desc" xml:space="preserve"><value>Manage .icstk file association so double-click opens in Ink Canvas.</value></data>
<data name="FileAssoc_Unregister" xml:space="preserve"><value>Remove association</value></data>
<data name="FileAssoc_Check" xml:space="preserve"><value>Check status</value></data>
<data name="FileAssoc_Register" xml:space="preserve"><value>Register association</value></data>
<data name="FloatingInterceptor_Title" xml:space="preserve"><value>Floating window interceptor</value></data>
<data name="FloatingInterceptor_Desc" xml:space="preserve"><value>Detect and block floating windows from similar software</value></data>
<data name="FloatingInterceptor_Enable" xml:space="preserve"><value>Enable floating window interceptor</value></data>
<data name="FloatingInterceptor_StatusNotRunning" xml:space="preserve"><value>Interceptor not running</value></data>
<data name="Storage_AutoScreenshotOnClear" xml:space="preserve"><value>Auto screenshot on clear</value></data>
<data name="Storage_ScreenshotsByDateFolder" xml:space="preserve"><value>Save screenshots in date folders</value></data>
<data name="Storage_AutoSaveInkOnScreenshot" xml:space="preserve"><value>Auto-save ink when screenshotting</value></data>
<data name="Storage_AutoSaveInk" xml:space="preserve"><value>Auto-save ink periodically</value></data>
<data name="Storage_AutoSaveInterval" xml:space="preserve"><value>Save interval</value></data>
<data name="Storage_AutoSaveInterval_1Min" xml:space="preserve"><value>1 minute</value></data>
<data name="Storage_AutoSaveInterval_3Min" xml:space="preserve"><value>3 minutes</value></data>
<data name="Storage_AutoSaveInterval_5Min" xml:space="preserve"><value>5 minutes</value></data>
<data name="Storage_AutoSaveInterval_10Min" xml:space="preserve"><value>10 minutes</value></data>
<data name="Storage_AutoSaveInterval_15Min" xml:space="preserve"><value>15 minutes</value></data>
<data name="Storage_AutoSaveInterval_30Min" xml:space="preserve"><value>30 minutes</value></data>
<data name="Storage_AutoSaveInterval_60Min" xml:space="preserve"><value>60 minutes</value></data>
<data name="Storage_AutoSaveHint" xml:space="preserve"><value># When on, strokes are auto-saved at the set interval, only when canvas is visible and has ink.</value></data>
<data name="Storage_SaveFullPageStrokes" xml:space="preserve"><value>Save full-page strokes</value></data>
<data name="Storage_SaveFullPageHint" xml:space="preserve"><value># When on, auto/manual saves store all pages in fullscreen; multiple pages are packed in one archive (whiteboard strokes open only in whiteboard mode; PPT strokes only in slide show mode).</value></data>
<data name="Storage_SaveAsXml" xml:space="preserve"><value>Save as XML format</value></data>
<data name="Storage_SaveAsXmlHint" xml:space="preserve"><value># When on, strokes are saved as XML (ISF) for easier inspection and editing.</value></data>
<data name="Storage_AutoScreenshotMinInk" xml:space="preserve"><value>Minimum ink for auto screenshot</value></data>
<data name="Storage_PathTitle" xml:space="preserve"><value>Stroke and screenshot save path</value></data>
<data name="Storage_PathBrowse" xml:space="preserve"><value>Browse</value></data>
<data name="Storage_PathSetToD" xml:space="preserve"><value>Set save path to D:\Ink Canvas</value></data>
<data name="Storage_PathSetToDocuments" xml:space="preserve"><value>Set save path to Documents</value></data>
<data name="Storage_PathPermissionHint" xml:space="preserve"><value># Please ensure the save folder is writable.</value></data>
<data name="Storage_AutoDeleteTitle" xml:space="preserve"><value>Auto delete old strokes and screenshots</value></data>
<data name="Storage_AutoDeleteHint" xml:space="preserve"><value># When on, all .icstk and .png files in the auto-save folder may be deleted!</value></data>
<data name="Storage_RetentionTitle" xml:space="preserve"><value>Retention duration</value></data>
<data name="Storage_RetentionUnitDays" xml:space="preserve"><value>days</value></data>
<data name="CloudStorage_Manage" xml:space="preserve"><value>Cloud storage management</value></data>
<data name="FoldMode_Title" xml:space="preserve"><value>Fold mode</value></data>
<data name="FoldMode_ExitToAnnotation" xml:space="preserve"><value>Switch to annotation when exiting fold mode</value></data>
<data name="FoldMode_ExitToAnnotationHint" xml:space="preserve"><value># When on, exiting fold mode switches back to annotation for convenience.</value></data>
<data name="FoldMode_AutoFoldAfterPPT" xml:space="preserve"><value>Auto fold floating bar after PPT show</value></data>
<data name="FoldMode_AutoFoldAfterPPTHint" xml:space="preserve"><value># When on, floating bar is auto-folded after exiting PPT slide show.</value></data>
<data name="FoldMode_AutoFoldAfterWhiteboard" xml:space="preserve"><value>Auto fold when exiting whiteboard</value></data>
<data name="FoldMode_AutoFoldAfterWhiteboardHint" xml:space="preserve"><value># When on, exiting whiteboard folds back to sidebar.</value></data>
<data name="Random_Title" xml:space="preserve"><value>Random roll call</value></data>
<data name="Random_ShowEditNamesButton" xml:space="preserve"><value>Show button to edit name list</value></data>
<data name="Random_BackgroundSettingsTitle" xml:space="preserve"><value>Roll-call window background (legacy UI only)</value></data>
<data name="Random_BackgroundSelectLabel" xml:space="preserve"><value>Background:</value></data>
<data name="Random_Background_Default" xml:space="preserve"><value>Default background</value></data>
<data name="Random_CustomBackgroundLabel" xml:space="preserve"><value>Custom background:</value></data>
<data name="Random_CustomBackground_Upload" xml:space="preserve"><value>Upload</value></data>
<data name="Random_CustomBackground_Manage" xml:space="preserve"><value>Manage</value></data>
<data name="Random_EnableButtons" xml:space="preserve"><value>Enable random &amp; single-draw buttons</value></data>
<data name="Random_EnableQuickButton" xml:space="preserve"><value>Enable quick-draw floating button</value></data>
<data name="Random_UseExternal" xml:space="preserve"><value>Use external roll-call app</value></data>
<data name="Random_ExternalTypeLabel" xml:space="preserve"><value>Roll-call type</value></data>
<data name="Random_ExternalType_ClassIsland" xml:space="preserve"><value>ClassIsland</value></data>
<data name="Random_ExternalType_SecRandom" xml:space="preserve"><value>SecRandom</value></data>
<data name="Random_ExternalType_NamePicker" xml:space="preserve"><value>NamePicker</value></data>
<data name="Random_OnceCloseDelay" xml:space="preserve"><value>Single-draw window close delay</value></data>
<data name="Random_OnceMaxStudents" xml:space="preserve"><value>Max students per single draw</value></data>
<data name="Random_NewUI_Title" xml:space="preserve"><value>New roll-call UI</value></data>
<data name="Random_NewUI_Enable" xml:space="preserve"><value>Enable new roll-call UI</value></data>
<data name="Random_ML_AvoidRepeat" xml:space="preserve"><value>Use machine learning to avoid repeats</value></data>
<data name="Random_ML_HistoryCount" xml:space="preserve"><value>History count for avoidance</value></data>
<data name="Random_ML_Weight" xml:space="preserve"><value>Avoidance weight</value></data>
<data name="Random_ML_Hint" xml:space="preserve"><value># ML analyzes recent roll-call history to avoid repeating the same students.</value></data>
<data name="Timer_Title" xml:space="preserve"><value>Timer settings</value></data>
<data name="Timer_UseLegacyButtons" xml:space="preserve"><value>Use legacy timer button UI</value></data>
<data name="Timer_NewUI" xml:space="preserve"><value>New timer UI</value></data>
<data name="Timer_EnableCountUp" xml:space="preserve"><value>Enable count-up after timeout</value></data>
<data name="Timer_OvertimeHighlight" xml:space="preserve"><value>Highlight numbers when overtime</value></data>
<data name="Timer_Volume" xml:space="preserve"><value>Timer alert volume</value></data>
<data name="Timer_CustomSoundLabel" xml:space="preserve"><value>Custom alert sound:</value></data>
<data name="Timer_SelectFile" xml:space="preserve"><value>Select file</value></data>
<data name="Timer_Reset" xml:space="preserve"><value>Reset</value></data>
<data name="Timer_Progressive" xml:space="preserve"><value>Progressive reminder</value></data>
<data name="Timer_ProgressiveVolume" xml:space="preserve"><value>Progressive reminder volume</value></data>
<data name="Timer_ProgressiveCustomLabel" xml:space="preserve"><value>Custom progressive reminder audio:</value></data>
<data name="Timer_ProgressiveSelectFile" xml:space="preserve"><value>Select file</value></data>
<data name="Timer_ProgressiveReset" xml:space="preserve"><value>Reset</value></data>
<data name="About_Title" xml:space="preserve"><value>About</value></data>
<data name="About_DeviceInfo" xml:space="preserve"><value>Device information</value></data>
<data name="About_DeviceIdLabel" xml:space="preserve"><value>Device ID:</value></data>
<data name="About_UsageFrequencyLabel" xml:space="preserve"><value>Usage frequency:</value></data>
<data name="About_UpdatePriorityLabel" xml:space="preserve"><value>Update priority:</value></data>
<data name="About_LaunchCountLabel" xml:space="preserve"><value>Launch count:</value></data>
<data name="About_TotalUsageLabel" xml:space="preserve"><value>Total usage time:</value></data>
<data name="About_DeviceInfo_Loading" xml:space="preserve"><value>Loading...</value></data>
<data name="About_RefreshDeviceInfo" xml:space="preserve"><value>Refresh device info</value></data>
<data name="About_PrivacyCheckboxPrefix" xml:space="preserve"><value>I have read and agree to the </value></data>
<data name="About_PrivacyCheckboxSuffix" xml:space="preserve"><value> privacy statement</value></data>
<data name="About_TelemetryLabel" xml:space="preserve"><value>Anonymous usage data upload:</value></data>
<data name="About_Telemetry_Off" xml:space="preserve"><value>Off (no upload)</value></data>
<data name="About_Telemetry_Basic" xml:space="preserve"><value>Upload basic data</value></data>
<data name="About_Telemetry_Optional" xml:space="preserve"><value>Upload basic + optional data</value></data>
<data name="About_LicenseHint" xml:space="preserve"><value># Before using or distributing this software, you must be aware of the related open-source licenses. This software is based on https://github.com/WXRIW/Ink-Canvas.</value></data>
<data name="About_LicenseTitle" xml:space="preserve"><value>This software, ICA and Ink Canvas are all open sourced under a license</value></data>
<data name="About_LicenseBody" xml:space="preserve"><value>The strong copyleft license requires that complete source code and modifications of the licensed work (including large works using it) be provided under the same license. Copyright and license notices must be retained. Contributors explicitly grant patent rights.</value></data>
<data name="About_DevelopersLabel" xml:space="preserve"><value>Developers:</value></data>
<data name="About_Dev_ICCCE" xml:space="preserve"><value>Developer of ICC CE</value></data>
<data name="About_Dev_ICC" xml:space="preserve"><value>Developer of ICC</value></data>
<data name="About_Dev_ICA" xml:space="preserve"><value>Developer of ICA</value></data>
<data name="About_Dev_InkCanvas" xml:space="preserve"><value>Developer of Ink Canvas</value></data>
<data name="About_Source_ICC" xml:space="preserve"><value>ICC repository:</value></data>
<data name="About_Source_ICA" xml:space="preserve"><value>ICA repository:</value></data>
<data name="About_Source_InkCanvas" xml:space="preserve"><value>Ink Canvas repository:</value></data>
<data name="About_ThanksContributors" xml:space="preserve"><value>Thanks to the following contributors:</value></data>
<data name="About_Copyright" xml:space="preserve"><value>© 2025-2026 CJK_mkp. All rights reserved.</value></data>
<data name="About_OpenSourceSlogan" xml:space="preserve"><value>We love open-source forever!</value></data>
<data name="About_VersionLabel" xml:space="preserve"><value>Version:</value></data>
<data name="Common_Close" xml:space="preserve"><value>Close</value></data>
<data name="Common_On" xml:space="preserve"><value>On</value></data>
<data name="Common_Off" xml:space="preserve"><value>Off</value></data>
<data name="Advanced_UriSchemeName" xml:space="preserve"><value>External URI scheme (icc://)</value></data>
<data name="Advanced_NibModeBoundsWidthHeader" xml:space="preserve"><value>Nib mode BoundsWidth</value></data>
<data name="Advanced_FingerModeBoundsWidthHeader" xml:space="preserve"><value>Finger mode BoundsWidth</value></data>
<data name="Advanced_EdgeGestureUtilHint_Part1" xml:space="preserve"><value># EdgeGestureUtil is newly introduced in ICC to temporarily block edge gestures when using touch (e.g., on Windows 10: swipe from the left edge to Task View, from the right edge to Action Center; on Windows 11: swipe up from the bottom to open Start). It works by using</value></data>
<data name="Advanced_EdgeGestureUtilHint_Part2" xml:space="preserve"><value>(When the app window is active and in full-screen mode (or an owned window is active), prevents edge gesture behavior.) If anything is abnormal, turn this option off; it should take effect immediately. (Not available on Windows 7/8.)</value></data>
<data name="Advanced_ForceFullScreen" xml:space="preserve"><value>Enable ForceFullScreen</value></data>
<data name="Advanced_ForceFullScreenHint" xml:space="preserve"><value># When a window size change is detected, automatically uses Win32 API to set this window size to the primary monitor size (in device pixels). Turn it off if you don't need it; takes effect immediately.</value></data>
<data name="Advanced_DPIChangeDetection" xml:space="preserve"><value>Enable DPIChangeDetection</value></data>
<data name="Advanced_DPIChangeDetectionHint" xml:space="preserve"><value># When a system DPI change is detected, it tries to keep FloatingBar visible. If it goes off-screen, it will attempt to move it into the visible area. (Increasing DPI may trigger this; decreasing DPI won't auto-move—adjust manually.)</value></data>
<data name="Advanced_ResolutionChangeDetection" xml:space="preserve"><value>Enable ResolutionChangeDetection</value></data>
<data name="Advanced_ResolutionChangeDetectionHint" xml:space="preserve"><value># When a screen resolution change is detected, it tries to keep FloatingBar visible. If it goes off-screen, it will attempt to move it into the visible area. (Reducing resolution may trigger this; if it's still on-screen it won't auto-adjust—adjust manually.)</value></data>
<data name="FloatingInterceptor_App_SeewoBoard3" xml:space="preserve"><value>Seewo Whiteboard 3</value></data>
<data name="FloatingInterceptor_App_SeewoBoard5" xml:space="preserve"><value>Seewo Whiteboard 5</value></data>
<data name="FloatingInterceptor_App_SeewoBoard5C" xml:space="preserve"><value>Seewo Whiteboard 5C</value></data>
<data name="FloatingInterceptor_App_SeewoPinco" xml:space="preserve"><value>Seewo Pinco</value></data>
<data name="FloatingInterceptor_App_SeewoPincoDrawing" xml:space="preserve"><value>Seewo Pinco pen</value></data>
<data name="FloatingInterceptor_App_SeewoPPTTools" xml:space="preserve"><value>Seewo PPT Tools</value></data>
<data name="FloatingInterceptor_App_AiClass" xml:space="preserve"><value>AiClass</value></data>
<data name="FloatingInterceptor_App_HiteAnnotation" xml:space="preserve"><value>Hite screen writing</value></data>
<data name="FloatingInterceptor_App_ChangYanClass" xml:space="preserve"><value>Changyan smart classroom</value></data>
<data name="FloatingInterceptor_App_ChangYanPPT" xml:space="preserve"><value>Changyan PPT</value></data>
<data name="FloatingInterceptor_App_IntelligentClass" xml:space="preserve"><value>Tianyu Education Cloud</value></data>
<data name="FloatingInterceptor_App_SeewoDesktopAnnotation" xml:space="preserve"><value>Seewo desktop pen</value></data>
<data name="FloatingInterceptor_App_SeewoDesktopSideBar" xml:space="preserve"><value>Seewo desktop sidebar</value></data>
<data name="Board_MultiTouchWriting" xml:space="preserve"><value>Multi-touch writing</value></data>
<data name="Board_TwoFingerMove" xml:space="preserve"><value>Two-finger move</value></data>
<data name="Board_TwoFingerZoom" xml:space="preserve"><value>Two-finger zoom</value></data>
<data name="Board_TwoFingerRotate" xml:space="preserve"><value>Two-finger rotate</value></data>
<data name="Board_Background" xml:space="preserve"><value>Background</value></data>
<data name="Board_Select" xml:space="preserve"><value>Select</value></data>
<data name="Board_Pen" xml:space="preserve"><value>Pen</value></data>
<data name="Board_Highlighter" xml:space="preserve"><value>Highlighter</value></data>
<data name="Board_Eraser" xml:space="preserve"><value>Eraser</value></data>
<data name="Board_EraserOptions" xml:space="preserve"><value>Eraser options</value></data>
<data name="Board_Size" xml:space="preserve"><value>Size</value></data>
<data name="Board_EraserShape" xml:space="preserve"><value>Eraser shape</value></data>
<data name="Board_EraserShape_Circle" xml:space="preserve"><value>Circle</value></data>
<data name="Board_EraserShape_Blackboard" xml:space="preserve"><value>Blackboard</value></data>
<data name="Board_ClearInk" xml:space="preserve"><value>Clear ink</value></data>
<data name="Board_ClearInkAndHistory" xml:space="preserve"><value>Clear ink &amp; history</value></data>
<data name="Board_StrokeEraser" xml:space="preserve"><value>Stroke eraser</value></data>
<data name="Board_Shape" xml:space="preserve"><value>Shapes</value></data>
<data name="Board_ShapeHintLongPress" xml:space="preserve"><value>(Long-press in first row to keep selected)</value></data>
<data name="Board_AutoHide" xml:space="preserve"><value>Auto-hide</value></data>
<data name="Board_InsertImage" xml:space="preserve"><value>Insert image</value></data>
<data name="Board_SelectImage" xml:space="preserve"><value>Select image</value></data>
<data name="Board_Screenshot" xml:space="preserve"><value>Screenshot</value></data>
<data name="Board_Undo" xml:space="preserve"><value>Undo</value></data>
<data name="Board_Redo" xml:space="preserve"><value>Redo</value></data>
<data name="Board_Tools" xml:space="preserve"><value>Tools</value></data>
<data name="Board_Exit" xml:space="preserve"><value>Exit</value></data>
<data name="Board_NewPage" xml:space="preserve"><value>New page</value></data>
<data name="Board_PreviousPage" xml:space="preserve"><value>Previous</value></data>
<data name="Board_NextPage" xml:space="preserve"><value>Next</value></data>
<data name="Board_Page" xml:space="preserve"><value>Page</value></data>
<data name="Board_DeleteThisPage" xml:space="preserve"><value>Delete this page</value></data>
<data name="Notification_TestText" xml:space="preserve"><value>Test text</value></data>
<data name="OldUI_Exit" xml:space="preserve"><value>Exit</value></data>
<data name="OldUI_Thickness" xml:space="preserve"><value>Thickness</value></data>
<data name="OldUI_Dark" xml:space="preserve"><value>Dark</value></data>
<data name="OldUI_Background" xml:space="preserve"><value>Background</value></data>
<data name="OldUI_HideCanvas" xml:space="preserve"><value>Hide&#x000A;canvas</value></data>
<data name="OldUI_Check" xml:space="preserve"><value>Check</value></data>
<data name="OldUI_SlideshowFromStart" xml:space="preserve"><value>From start&#x000A;slideshow</value></data>
<data name="OldUI_SlideshowEnd" xml:space="preserve"><value>End&#x000A;slideshow</value></data>
<data name="OldUI_SingleFingerDrag" xml:space="preserve"><value>One finger&#x000A;drag</value></data>
<data name="OldUI_Restore" xml:space="preserve"><value>Restore</value></data>
<data name="OldUI_ClearAndHide" xml:space="preserve"><value>Clear&#x000A;&amp;&#x000A;Hide</value></data>
<data name="FloatingBar_Mouse" xml:space="preserve"><value>Mouse</value></data>
<data name="FloatingBar_Annotate" xml:space="preserve"><value>Annotate</value></data>
<data name="FloatingBar_Clear" xml:space="preserve"><value>Clear</value></data>
<data name="Booth_Title" xml:space="preserve"><value>Visual presenter</value></data>
<data name="Booth_CapturedPhotos" xml:space="preserve"><value>Captured photos</value></data>
<data name="Booth_CameraDevices" xml:space="preserve"><value>Camera devices</value></data>
<data name="Booth_Present" xml:space="preserve"><value>Present</value></data>
<data name="Booth_Correction" xml:space="preserve"><value>Correct</value></data>
<data name="Booth_Capture" xml:space="preserve"><value>Capture</value></data>
<data name="Booth_Rotate" xml:space="preserve"><value>Rotate</value></data>
<data name="Theme_LanguageLabel" xml:space="preserve"><value>UI language</value></data>
<data name="Theme_Language_System" xml:space="preserve"><value>Follow system</value></data>
<data name="Theme_Language_ChineseSimplified" xml:space="preserve"><value>Chinese (Simplified)</value></data>
<data name="Theme_Language_English" xml:space="preserve"><value>English</value></data>
<data name="Theme_Language_RestartHint" xml:space="preserve"><value>You need to restart the app for language changes to fully take effect.</value></data>
<data name="FloatingBar_AreaEraser" xml:space="preserve"><value>Area eraser</value></data>
<data name="FloatingBar_StrokeEraser" xml:space="preserve"><value>Stroke eraser</value></data>
<data name="FloatingBar_LassoSelect" xml:space="preserve"><value>Lasso</value></data>
<data name="FloatingBar_Geometry" xml:space="preserve"><value>Geometry</value></data>
<data name="FloatingBar_ClearAndMouse" xml:space="preserve"><value>Clear &amp; cursor</value></data>
<data name="FloatingBar_Whiteboard" xml:space="preserve"><value>Board</value></data>
<data name="FloatingBar_Hide" xml:space="preserve"><value>Hide</value></data>
<data name="Geometry_Title" xml:space="preserve"><value>Geometry drawing</value></data>
<data name="Geometry_DrawLine" xml:space="preserve"><value>Line</value></data>
<data name="Geometry_DrawDashedLine" xml:space="preserve"><value>Dashed line</value></data>
<data name="Geometry_DrawDottedLine" xml:space="preserve"><value>Dotted line</value></data>
<data name="Geometry_DrawArrow" xml:space="preserve"><value>Arrow</value></data>
<data name="Geometry_DrawParallelLines" xml:space="preserve"><value>4 parallel lines</value></data>
<data name="Geometry_DrawCenteredSquare" xml:space="preserve"><value>Centered square</value></data>
<data name="Geometry_DrawCenteredCircle" xml:space="preserve"><value>Centered circle</value></data>
<data name="Geometry_DrawCenteredDashedCircle" xml:space="preserve"><value>Centered dashed circle</value></data>
<data name="Geometry_DrawCenteredEllipse" xml:space="preserve"><value>Centered ellipse</value></data>
<data name="Geometry_DrawCuboid" xml:space="preserve"><value>Cuboid</value></data>
<data name="Geometry_DrawSquare" xml:space="preserve"><value>Square</value></data>
<data name="Geometry_DrawCylinder" xml:space="preserve"><value>Cylinder</value></data>
<data name="Geometry_DrawCone" xml:space="preserve"><value>Cone</value></data>
<data name="FloatingBar_GestureButton" xml:space="preserve"><value>Gesture</value></data>
<data name="FloatingBar_GesturePanelTitle" xml:space="preserve"><value>Gesture options</value></data>
<data name="FloatingBar_Gesture_MultiTouchWriting" xml:space="preserve"><value>Multi-touch writing</value></data>
<data name="FloatingBar_Gesture_TwoFingerMove" xml:space="preserve"><value>Two-finger move</value></data>
<data name="FloatingBar_Gesture_TwoFingerZoom" xml:space="preserve"><value>Two-finger zoom</value></data>
<data name="FloatingBar_Gesture_TwoFingerRotate" xml:space="preserve"><value>Two-finger rotate</value></data>
<data name="Board_Gesture" xml:space="preserve"><value>Gesture</value></data>
<data name="Board_GestureOptions" xml:space="preserve"><value>Gesture options</value></data>
</root>
File diff suppressed because it is too large Load Diff
+55 -51
View File
@@ -1,66 +1,70 @@
<UserControl x:Class="Ink_Canvas.Windows.HotkeyItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
mc:Ignorable="d"
d:DesignHeight="60" d:DesignWidth="600">
<Border Background="White"
BorderBrush="#E0E0E0"
BorderThickness="1"
CornerRadius="5"
Margin="0,2">
<Grid Margin="15,10">
mc:Ignorable="d"
d:DesignHeight="60"
d:DesignWidth="600">
<Border Background="#2d2d30"
BorderBrush="#3f3f46"
BorderThickness="1"
CornerRadius="6"
Margin="0,2"
SnapsToDevicePixels="True">
<Grid Margin="14,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- 左侧信息 -->
<ikw:SimpleStackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock x:Name="TitleTextBlock"
Text="快捷键标题"
FontSize="14"
FontWeight="SemiBold"
Foreground="#333333"/>
<TextBlock x:Name="DescriptionTextBlock"
Text="快捷键描述"
FontSize="12"
Foreground="#666666"
Margin="0,2,0,0"/>
<TextBlock x:Name="TitleTextBlock"
Text="快捷键标题"
FontSize="14"
FontWeight="SemiBold"
Foreground="#fafafa" />
<TextBlock x:Name="DescriptionTextBlock"
Text="快捷键描述"
FontSize="12"
Foreground="#a1a1aa"
Margin="0,2,0,0"
TextWrapping="Wrap" />
</ikw:SimpleStackPanel>
<!-- 当前快捷键显示 -->
<Border Grid.Column="1"
Background="#F5F5F5"
BorderBrush="#D0D0D0"
BorderThickness="1"
CornerRadius="3"
Margin="10,0,0,0"
Padding="8,4">
<TextBlock x:Name="CurrentHotkeyTextBlock"
Text="未设置"
FontSize="12"
Foreground="#666666"
MinWidth="80"
TextAlignment="Center"/>
<Border Grid.Column="1"
Background="#3f3f46"
BorderBrush="#71717a"
BorderThickness="1"
CornerRadius="4"
Margin="10,0,0,0"
Padding="10,6"
VerticalAlignment="Center">
<TextBlock x:Name="CurrentHotkeyTextBlock"
Text="未设置"
FontSize="12"
FontFamily="Consolas"
FontWeight="SemiBold"
Foreground="#fafafa"
MinWidth="88"
TextAlignment="Center"
VerticalAlignment="Center" />
</Border>
<!-- 设置按钮 -->
<Button x:Name="BtnSetHotkey"
Grid.Column="2"
Content="设置"
Width="60"
Height="28"
Background="#0066BF"
Foreground="White"
FontSize="12"
<Button x:Name="BtnSetHotkey"
Grid.Column="2"
Content="设置"
Width="72"
Height="30"
FontSize="12"
Margin="10,0,0,0"
Click="BtnSetHotkey_Click"/>
VerticalAlignment="Center"
Style="{DynamicResource PrimaryButtonStyle}"
Padding="12,4"
Click="BtnSetHotkey_Click" />
</Grid>
</Border>
</UserControl>
</UserControl>
+13 -3
View File
@@ -11,6 +11,16 @@ namespace Ink_Canvas.Windows
/// </summary>
public partial class HotkeyItem : UserControl
{
private static readonly SolidColorBrush HotkeyValueForeground = CreateFrozenBrush(0xFA, 0xFA, 0xFA);
private static readonly SolidColorBrush HotkeyPlaceholderForeground = CreateFrozenBrush(0xA1, 0xA1, 0xAA);
private static SolidColorBrush CreateFrozenBrush(byte r, byte g, byte b)
{
var brush = new SolidColorBrush(Color.FromRgb(r, g, b));
brush.Freeze();
return brush;
}
#region Events
/// <summary>
/// 快捷键变更事件
@@ -80,13 +90,13 @@ namespace Ink_Canvas.Windows
if (_currentKey == Key.None)
{
CurrentHotkeyTextBlock.Text = "未设置";
CurrentHotkeyTextBlock.Foreground = Brushes.Gray;
CurrentHotkeyTextBlock.Foreground = HotkeyPlaceholderForeground;
}
else
{
var modifiersText = _currentModifiers == ModifierKeys.None ? "" : $"{_currentModifiers}+";
CurrentHotkeyTextBlock.Text = $"{modifiersText}{_currentKey}";
CurrentHotkeyTextBlock.Foreground = Brushes.Black;
CurrentHotkeyTextBlock.Foreground = HotkeyValueForeground;
}
}
@@ -106,7 +116,7 @@ namespace Ink_Canvas.Windows
private void StopHotkeyCapture()
{
BtnSetHotkey.Content = "设置";
BtnSetHotkey.Background = Brushes.DodgerBlue;
BtnSetHotkey.ClearValue(Button.BackgroundProperty);
// 移除键盘事件处理器
KeyDown -= HotkeyItem_KeyDown;
+350 -191
View File
@@ -6,211 +6,370 @@
xmlns:local="clr-namespace:Ink_Canvas.Windows"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
ui:ThemeManager.RequestedTheme="Light"
Background="#F9F9F9"
AllowsTransparency="True"
mc:Ignorable="d"
WindowStyle="None"
mc:Ignorable="d"
Title="快捷键设置"
Height="600"
Width="800"
WindowStartupLocation="CenterScreen"
ResizeMode="CanResize"
Title="快捷键设置"
Height="600"
Width="800">
FontFamily="Microsoft YaHei UI"
ui:ThemeManager.IsThemeAware="True"
ui:TitleBar.ExtendViewIntoTitleBar="True"
ui:WindowHelper.SystemBackdropType="Mica"
ui:WindowHelper.UseModernWindowStyle="True"
ui:TitleBar.Height="48">
<Window.Resources>
<ResourceDictionary>
<SolidColorBrush x:Key="WindowBackground" Color="#1e1e1e" />
<SolidColorBrush x:Key="BorderBrush" Color="#3f3f46" />
<SolidColorBrush x:Key="TextForeground" Color="#fafafa" />
<SolidColorBrush x:Key="TextSecondary" Color="#a1a1aa" />
<SolidColorBrush x:Key="AccentColor" Color="#3b82f6" />
<SolidColorBrush x:Key="TitleForeground" Color="#fafafa" />
<Style x:Key="PrimaryButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{StaticResource AccentColor}" />
<Setter Property="Foreground" Value="White" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Padding" Value="16,8" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#2563eb" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#1d4ed8" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SecondaryButtonStyle" TargetType="Button">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="{StaticResource TextForeground}" />
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Padding" Value="16,8" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#27272a" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- 标题栏 -->
<Border Grid.Row="0" Background="#3B82F6" Height="60"
<!-- 标题栏(与云存储管理等新设置窗口一致) -->
<Border x:Name="Border_TitleBarRoot"
Grid.Row="0"
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=(ui:TitleBar.Height)}"
MouseLeftButtonDown="TitleBar_MouseLeftButtonDown">
<Grid>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="20,0,0,0">
<TextBlock Text="快捷键设置"
Foreground="White"
FontSize="22"
FontWeight="SemiBold"
VerticalAlignment="Center"/>
</StackPanel>
<Button x:Name="BtnClose"
Content="&#xE8BB;"
FontFamily="Segoe MDL2 Assets"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Margin="0,0,20,0"
Background="Transparent"
BorderThickness="0"
FontSize="16"
Foreground="White"
Click="BtnClose_Click"/>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=Title}"
VerticalAlignment="Center"
Margin="12,0,0,0"
FontSize="12"
FontWeight="SemiBold"
Foreground="{StaticResource TitleForeground}" />
<ikw:SimpleStackPanel x:Name="StackPanel_RightButtons"
Grid.Column="1"
Orientation="Horizontal"
Spacing="5"
VerticalAlignment="Center" />
<Rectangle Grid.Column="2"
Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=(ui:TitleBar.SystemOverlayRightInset)}" />
</Grid>
</Border>
<!-- 主内容 -->
<ScrollViewer Grid.Row="1" Margin="20" VerticalScrollBarVisibility="Auto">
<ikw:SimpleStackPanel Margin="20">
<!-- 说明文字 -->
<TextBlock Text="在这里可以自定义全局快捷键设置。全局快捷键在任何情况下都能生效,即使应用程序不在焦点状态。"
TextWrapping="Wrap"
Margin="0,0,0,20"
Foreground="#666666"/>
<!-- 主内容 -->
<Border Grid.Row="1"
Background="{StaticResource WindowBackground}"
Padding="20,10,20,16">
<ui:ScrollViewerEx VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"
PanningMode="VerticalOnly"
ui:ThemeManager.RequestedTheme="Dark">
<ikw:SimpleStackPanel Spacing="14">
<TextBlock Text="在这里可以自定义全局快捷键设置。全局快捷键在任何情况下都能生效,即使应用程序不在焦点状态。"
TextWrapping="Wrap"
Foreground="{StaticResource TextSecondary}"
FontSize="14"
LineHeight="20" />
<!-- 鼠标模式快捷键设置 -->
<GroupBox Header="鼠标模式设置" Margin="0,0,0,20">
<ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Orientation="Horizontal" VerticalAlignment="Center">
<ui:ToggleSwitch x:Name="ToggleSwitchEnableHotkeysInMouseMode"
Header="在鼠标模式下启用快捷键"
Margin="0,0,10,0"
Width="200"/>
<Border BorderThickness="1"
BorderBrush="{StaticResource BorderBrush}"
CornerRadius="8"
Padding="18,16"
Background="#27272a">
<ikw:SimpleStackPanel Spacing="10">
<TextBlock Text="鼠标模式"
FontSize="18"
FontWeight="SemiBold"
Foreground="{StaticResource TextForeground}" />
<Border Height="1"
Background="{StaticResource BorderBrush}"
HorizontalAlignment="Stretch" />
<ikw:SimpleStackPanel Orientation="Horizontal" VerticalAlignment="Center">
<ui:ToggleSwitch x:Name="ToggleSwitchEnableHotkeysInMouseMode"
Header="在鼠标模式下启用快捷键"
OnContent=""
OffContent=""
FontFamily="Microsoft YaHei UI"
FontWeight="Bold"
Margin="0,0,10,0"
Width="220" />
</ikw:SimpleStackPanel>
<TextBlock Text="开启后,即使在鼠标模式下快捷键也会生效"
Foreground="{StaticResource TextSecondary}"
TextWrapping="Wrap"
FontSize="13" />
</ikw:SimpleStackPanel>
<TextBlock Text="开启后,即使在鼠标模式下快捷键也会生效"
Foreground="#666666"
VerticalAlignment="Center"
TextWrapping="Wrap"/>
</Border>
<ikw:SimpleStackPanel x:Name="HotkeyList" Spacing="12">
<!-- 基本操作 -->
<Border BorderThickness="1"
BorderBrush="{StaticResource BorderBrush}"
CornerRadius="8"
Padding="18,16"
Background="#27272a">
<ikw:SimpleStackPanel Spacing="10">
<TextBlock Text="基本操作"
FontSize="18"
FontWeight="SemiBold"
Foreground="{StaticResource TextForeground}" />
<Border Height="1"
Background="{StaticResource BorderBrush}"
HorizontalAlignment="Stretch" />
<ikw:SimpleStackPanel Spacing="4">
<local:HotkeyItem x:Name="UndoHotkey"
Title="撤销"
Description="撤销上一步操作"
DefaultKey="Z"
DefaultModifiers="Control" />
<local:HotkeyItem x:Name="RedoHotkey"
Title="重做"
Description="重做上一步操作"
DefaultKey="Y"
DefaultModifiers="Control" />
<local:HotkeyItem x:Name="ClearHotkey"
Title="清空"
Description="清空当前画板内容"
DefaultKey="E"
DefaultModifiers="Control" />
<local:HotkeyItem x:Name="PasteHotkey"
Title="粘贴"
Description="粘贴剪贴板内容"
DefaultKey="V"
DefaultModifiers="Control" />
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
</Border>
<!-- 工具切换 -->
<Border BorderThickness="1"
BorderBrush="{StaticResource BorderBrush}"
CornerRadius="8"
Padding="18,16"
Background="#27272a">
<ikw:SimpleStackPanel Spacing="10">
<TextBlock Text="工具切换"
FontSize="18"
FontWeight="SemiBold"
Foreground="{StaticResource TextForeground}" />
<Border Height="1"
Background="{StaticResource BorderBrush}"
HorizontalAlignment="Stretch" />
<ikw:SimpleStackPanel Spacing="4">
<local:HotkeyItem x:Name="SelectToolHotkey"
Title="选择工具"
Description="切换到选择工具"
DefaultKey="S"
DefaultModifiers="Alt" />
<local:HotkeyItem x:Name="DrawToolHotkey"
Title="绘图工具"
Description="切换到绘图工具"
DefaultKey="D"
DefaultModifiers="Alt" />
<local:HotkeyItem x:Name="EraserToolHotkey"
Title="橡皮擦工具"
Description="切换到橡皮擦工具"
DefaultKey="E"
DefaultModifiers="Alt" />
<local:HotkeyItem x:Name="BlackboardToolHotkey"
Title="黑板工具"
Description="切换到黑板工具"
DefaultKey="B"
DefaultModifiers="Alt" />
<local:HotkeyItem x:Name="QuitDrawToolHotkey"
Title="退出绘图/白板"
Description="退出绘图模式或白板模式"
DefaultKey="Q"
DefaultModifiers="Alt" />
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
</Border>
<!-- 画笔设置 -->
<Border BorderThickness="1"
BorderBrush="{StaticResource BorderBrush}"
CornerRadius="8"
Padding="18,16"
Background="#27272a">
<ikw:SimpleStackPanel Spacing="10">
<TextBlock Text="画笔设置"
FontSize="18"
FontWeight="SemiBold"
Foreground="{StaticResource TextForeground}" />
<Border Height="1"
Background="{StaticResource BorderBrush}"
HorizontalAlignment="Stretch" />
<ikw:SimpleStackPanel Spacing="4">
<local:HotkeyItem x:Name="Pen1Hotkey"
Title="画笔1"
Description="选择画笔1"
DefaultKey="D1"
DefaultModifiers="Alt" />
<local:HotkeyItem x:Name="Pen2Hotkey"
Title="画笔2"
Description="选择画笔2"
DefaultKey="D2"
DefaultModifiers="Alt" />
<local:HotkeyItem x:Name="Pen3Hotkey"
Title="画笔3"
Description="选择画笔3"
DefaultKey="D3"
DefaultModifiers="Alt" />
<local:HotkeyItem x:Name="Pen4Hotkey"
Title="画笔4"
Description="选择画笔4"
DefaultKey="D4"
DefaultModifiers="Alt" />
<local:HotkeyItem x:Name="Pen5Hotkey"
Title="画笔5"
Description="选择画笔5"
DefaultKey="D5"
DefaultModifiers="Alt" />
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
</Border>
<!-- 功能快捷键 -->
<Border BorderThickness="1"
BorderBrush="{StaticResource BorderBrush}"
CornerRadius="8"
Padding="18,16"
Background="#27272a">
<ikw:SimpleStackPanel Spacing="10">
<TextBlock Text="功能快捷键"
FontSize="18"
FontWeight="SemiBold"
Foreground="{StaticResource TextForeground}" />
<Border Height="1"
Background="{StaticResource BorderBrush}"
HorizontalAlignment="Stretch" />
<ikw:SimpleStackPanel Spacing="4">
<local:HotkeyItem x:Name="DrawLineHotkey"
Title="绘制直线"
Description="绘制直线工具"
DefaultKey="L"
DefaultModifiers="Alt" />
<local:HotkeyItem x:Name="ScreenshotHotkey"
Title="截图"
Description="保存屏幕截图到桌面"
DefaultKey="C"
DefaultModifiers="Alt" />
<local:HotkeyItem x:Name="QuickDrawHotkey"
Title="快抽"
Description="打开快抽窗口(与悬浮快抽按钮相同)"
DefaultKey="K"
DefaultModifiers="Alt" />
<local:HotkeyItem x:Name="HideHotkey"
Title="隐藏"
Description="隐藏应用程序"
DefaultKey="V"
DefaultModifiers="Alt" />
<local:HotkeyItem x:Name="ExitHotkey"
Title="退出"
Description="退出当前模式或应用程序"
DefaultKey="Escape"
DefaultModifiers="None" />
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
</Border>
</ikw:SimpleStackPanel>
</GroupBox>
<!-- 快捷键列表 -->
<ikw:SimpleStackPanel x:Name="HotkeyList" Margin="0,0,0,20">
<!-- 基本操作 -->
<GroupBox Header="基本操作" Margin="0,0,0,15">
<ikw:SimpleStackPanel>
<local:HotkeyItem x:Name="UndoHotkey"
Title="撤销"
Description="撤销上一步操作"
DefaultKey="Z"
DefaultModifiers="Control"/>
<local:HotkeyItem x:Name="RedoHotkey"
Title="重做"
Description="重做上一步操作"
DefaultKey="Y"
DefaultModifiers="Control"/>
<local:HotkeyItem x:Name="ClearHotkey"
Title="清空"
Description="清空当前画板内容"
DefaultKey="E"
DefaultModifiers="Control"/>
<local:HotkeyItem x:Name="PasteHotkey"
Title="粘贴"
Description="粘贴剪贴板内容"
DefaultKey="V"
DefaultModifiers="Control"/>
</ikw:SimpleStackPanel>
</GroupBox>
<!-- 工具切换 -->
<GroupBox Header="工具切换" Margin="0,0,0,15">
<ikw:SimpleStackPanel>
<local:HotkeyItem x:Name="SelectToolHotkey"
Title="选择工具"
Description="切换到选择工具"
DefaultKey="S"
DefaultModifiers="Alt"/>
<local:HotkeyItem x:Name="DrawToolHotkey"
Title="绘图工具"
Description="切换到绘图工具"
DefaultKey="D"
DefaultModifiers="Alt"/>
<local:HotkeyItem x:Name="EraserToolHotkey"
Title="橡皮擦工具"
Description="切换到橡皮擦工具"
DefaultKey="E"
DefaultModifiers="Alt"/>
<local:HotkeyItem x:Name="BlackboardToolHotkey"
Title="黑板工具"
Description="切换到黑板工具"
DefaultKey="B"
DefaultModifiers="Alt"/>
<local:HotkeyItem x:Name="QuitDrawToolHotkey"
Title="退出绘图/白板"
Description="退出绘图模式或白板模式"
DefaultKey="Q"
DefaultModifiers="Alt"/>
</ikw:SimpleStackPanel>
</GroupBox>
<!-- 画笔设置 -->
<GroupBox Header="画笔设置" Margin="0,0,0,15">
<ikw:SimpleStackPanel>
<local:HotkeyItem x:Name="Pen1Hotkey"
Title="画笔1"
Description="选择画笔1"
DefaultKey="D1"
DefaultModifiers="Alt"/>
<local:HotkeyItem x:Name="Pen2Hotkey"
Title="画笔2"
Description="选择画笔2"
DefaultKey="D2"
DefaultModifiers="Alt"/>
<local:HotkeyItem x:Name="Pen3Hotkey"
Title="画笔3"
Description="选择画笔3"
DefaultKey="D3"
DefaultModifiers="Alt"/>
<local:HotkeyItem x:Name="Pen4Hotkey"
Title="画笔4"
Description="选择画笔4"
DefaultKey="D4"
DefaultModifiers="Alt"/>
<local:HotkeyItem x:Name="Pen5Hotkey"
Title="画笔5"
Description="选择画笔5"
DefaultKey="D5"
DefaultModifiers="Alt"/>
</ikw:SimpleStackPanel>
</GroupBox>
<!-- 功能快捷键 -->
<GroupBox Header="功能快捷键" Margin="0,0,0,15">
<ikw:SimpleStackPanel>
<local:HotkeyItem x:Name="DrawLineHotkey"
Title="绘制直线"
Description="绘制直线工具"
DefaultKey="L"
DefaultModifiers="Alt"/>
<local:HotkeyItem x:Name="ScreenshotHotkey"
Title="截图"
Description="保存屏幕截图到桌面"
DefaultKey="C"
DefaultModifiers="Alt"/>
<local:HotkeyItem x:Name="HideHotkey"
Title="隐藏"
Description="隐藏应用程序"
DefaultKey="V"
DefaultModifiers="Alt"/>
<local:HotkeyItem x:Name="ExitHotkey"
Title="退出"
Description="退出当前模式或应用程序"
DefaultKey="Escape"
DefaultModifiers="None"/>
</ikw:SimpleStackPanel>
</GroupBox>
</ikw:SimpleStackPanel></ikw:SimpleStackPanel>
</ScrollViewer>
</ikw:SimpleStackPanel>
</ui:ScrollViewerEx>
</Border>
<!-- 底部操作栏 -->
<Border Grid.Row="2" Background="#F0F0F0" Height="60">
<Grid>
<ikw:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,20,0">
<Button x:Name="BtnResetToDefault"
Content="重置为默认"
Width="100"
Height="35"
Margin="0,0,10,0"
Click="BtnResetToDefault_Click"/>
<Button x:Name="BtnSave"
Content="保存设置"
Width="100"
Height="35"
Background="#3B82F6"
Foreground="White"
Click="BtnSave_Click"/>
</ikw:SimpleStackPanel>
</Grid>
<Border Grid.Row="2"
Background="#18181b"
BorderBrush="{StaticResource BorderBrush}"
BorderThickness="0,1,0,0"
Padding="16,12">
<ikw:SimpleStackPanel Orientation="Horizontal"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Spacing="10">
<Button x:Name="BtnResetToDefault"
Content="重置为默认"
MinWidth="100"
Height="36"
Style="{StaticResource SecondaryButtonStyle}"
Click="BtnResetToDefault_Click" />
<Button x:Name="BtnSave"
Content="保存设置"
MinWidth="100"
Height="36"
Style="{StaticResource PrimaryButtonStyle}"
Click="BtnSave_Click" />
</ikw:SimpleStackPanel>
</Border>
</Grid>
</Window>
</Window>
@@ -135,6 +135,9 @@ namespace Ink_Canvas.Windows
_hotkeyItems["Screenshot"] = ScreenshotHotkey;
ScreenshotHotkey.HotkeyName = "Screenshot";
_hotkeyItems["QuickDraw"] = QuickDrawHotkey;
QuickDrawHotkey.HotkeyName = "QuickDraw";
_hotkeyItems["Hide"] = HideHotkey;
HideHotkey.HotkeyName = "Hide";
@@ -243,6 +246,9 @@ namespace Ink_Canvas.Windows
case "Screenshot":
hotkeyItem.SetCurrentHotkey(Key.C, ModifierKeys.Alt);
break;
case "QuickDraw":
hotkeyItem.SetCurrentHotkey(Key.K, ModifierKeys.Alt);
break;
case "Hide":
hotkeyItem.SetCurrentHotkey(Key.V, ModifierKeys.Alt);
break;
@@ -472,6 +478,8 @@ namespace Ink_Canvas.Windows
return () => _mainWindow.BtnDrawLine_Click(null, null);
case "Screenshot":
return () => _mainWindow.SaveScreenShotToDesktop();
case "QuickDraw":
return () => _mainWindow.OpenQuickDrawFromHotkey();
case "Hide":
return () => _mainWindow.SymbolIconEmoji_MouseUp(null, null);
case "Exit":
@@ -533,11 +541,6 @@ namespace Ink_Canvas.Windows
#endregion
#region Event Handlers
private void BtnClose_Click(object sender, RoutedEventArgs e)
{
Close();
}
/// <summary>
/// 标题栏拖拽事件
/// </summary>
+204 -87
View File
@@ -3,99 +3,216 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Ink_Canvas"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
ui:ThemeManager.RequestedTheme="Light" Topmost="True" Background="Transparent"
mc:Ignorable="d" WindowStyle="None" AllowsTransparency="True"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d"
Title="Ink Canvas Annotation 使用指南"
Height="600" Width="500">
<Border Background="{DynamicResource OperatingGuideWindowBackground}" CornerRadius="10" BorderThickness="1" BorderBrush="{DynamicResource OperatingGuideWindowBorderBrush}" Margin="10,10,10,50">
<Grid>
<Border MouseMove="WindowDragMove" Visibility="Visible" Width="64" Height="15" CornerRadius="8" Background="Gray" Margin="0,0,0,5" HorizontalAlignment="Center" VerticalAlignment="Bottom" />
<ScrollViewer Margin="0,0,0,30" VerticalScrollBarVisibility="Auto"
PanningMode="VerticalOnly" ui:ThemeManager.RequestedTheme="Light"
ManipulationBoundaryFeedback="SCManipulationBoundaryFeedback">
<ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="20,20,20,0" Orientation="Horizontal" HorizontalAlignment="Left">
<Image Margin="5, 0" Source="{DynamicResource OperatingGuideWindowKeyboardIcon}"
RenderOptions.BitmapScalingMode="HighQuality" Height="40" Width="40"/>
<TextBlock FontSize="22" Text="软件快捷键" TextWrapping="Wrap" VerticalAlignment="Center" Foreground="{DynamicResource OperatingGuideWindowTextForeground}"/>
Height="600"
Width="520"
MinHeight="400"
MinWidth="400"
WindowStartupLocation="CenterScreen"
Topmost="True"
ResizeMode="CanResize"
FontFamily="Microsoft YaHei UI"
ui:ThemeManager.IsThemeAware="True"
ui:TitleBar.ExtendViewIntoTitleBar="True"
ui:WindowHelper.SystemBackdropType="Mica"
ui:WindowHelper.UseModernWindowStyle="True"
ui:TitleBar.Height="48">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- 自定义标题栏(与 OOBE / 隐私说明等 Fluent 窗口一致) -->
<Border x:Name="Border_TitleBarRoot"
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=(ui:TitleBar.Height)}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=Title}"
VerticalAlignment="Center"
Margin="12,0,0,0"
FontSize="12"
FontWeight="SemiBold" />
<Rectangle Grid.Column="2"
Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=(ui:TitleBar.SystemOverlayRightInset)}" />
<ikw:SimpleStackPanel x:Name="StackPanel_RightButtons"
Grid.Column="1"
Orientation="Horizontal"
Spacing="5" />
</Grid>
</Border>
<Grid Grid.Row="1" Background="{DynamicResource SettingsPageBackground}">
<ScrollViewer Padding="0"
VerticalScrollBarVisibility="Auto"
PanningMode="VerticalOnly"
ManipulationBoundaryFeedback="SCManipulationBoundaryFeedback">
<ikw:SimpleStackPanel Margin="24,20,24,28" Spacing="4">
<ikw:SimpleStackPanel Margin="0,0,0,12"
Orientation="Horizontal"
HorizontalAlignment="Left"
Spacing="12">
<Image Height="32"
Width="32"
Source="{DynamicResource OperatingGuideWindowKeyboardIcon}"
RenderOptions.BitmapScalingMode="HighQuality" />
<TextBlock FontSize="20"
FontWeight="SemiBold"
Text="软件快捷键"
TextWrapping="Wrap"
VerticalAlignment="Center"
Foreground="{DynamicResource SettingsPageForeground}" />
</ikw:SimpleStackPanel>
<Viewbox Height="320" HorizontalAlignment="Left">
<ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="20, 0" Height="20" Orientation="Horizontal" HorizontalAlignment="Left">
<Image Source="{DynamicResource OperatingGuideWindowControlIcon1}"
RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20"/>
<TextBlock FontSize="10" Text=" + Z —— 撤销" TextWrapping="Wrap" VerticalAlignment="Center" Foreground="{DynamicResource OperatingGuideWindowTextForeground}"/>
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="20, 0" Height="20" Orientation="Horizontal" HorizontalAlignment="Left">
<Image Source="{DynamicResource OperatingGuideWindowControlIcon2}"
RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20"/>
<TextBlock FontSize="10" Text=" + Y —— 重做" TextWrapping="Wrap" VerticalAlignment="Center" Foreground="{DynamicResource OperatingGuideWindowTextForeground}"/>
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="20, 0" Height="20" Orientation="Horizontal" HorizontalAlignment="Left">
<Image Source="{DynamicResource OperatingGuideWindowControlIcon3}"
RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20"/>
<TextBlock FontSize="10" Text=" + E —— 清屏" TextWrapping="Wrap" VerticalAlignment="Center" Foreground="{DynamicResource OperatingGuideWindowTextForeground}"/>
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="20, 0" Height="20" Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock FontSize="10" Text=" Alt + V —— 显示/隐藏笑脸右侧工具栏(Visibility" TextWrapping="Wrap" VerticalAlignment="Center" Foreground="{DynamicResource OperatingGuideWindowTextForeground}"/>
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="20, 0" Height="20" Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock FontSize="10" Text=" Alt + C —— 截屏(Capture" TextWrapping="Wrap" VerticalAlignment="Center" Foreground="{DynamicResource OperatingGuideWindowTextForeground}"/>
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="20, 0" Height="20" Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock FontSize="10" Text=" Alt + S —— 切换至选择模式(Select" TextWrapping="Wrap" VerticalAlignment="Center" Foreground="{DynamicResource OperatingGuideWindowTextForeground}"/>
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="20, 0" Height="20" Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock FontSize="10" Text=" Alt + D —— 切换至批注模式 / 墨迹颜色选择器(Draw" TextWrapping="Wrap" VerticalAlignment="Center" Foreground="{DynamicResource OperatingGuideWindowTextForeground}"/>
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="20, 0" Height="20" Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock FontSize="10" Text=" Alt + Q —— 退出批注模式(Quit" TextWrapping="Wrap" VerticalAlignment="Center" Foreground="{DynamicResource OperatingGuideWindowTextForeground}"/>
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="20, 0" Height="20" Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock FontSize="10" Text=" Alt + B —— 切换/退出画板模式(Board" TextWrapping="Wrap" VerticalAlignment="Center" Foreground="{DynamicResource OperatingGuideWindowTextForeground}"/>
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="20, 0" Height="20" Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock FontSize="10" Text=" Alt + E —— 切换至面积擦/墨迹擦功能(Eraser" TextWrapping="Wrap" VerticalAlignment="Center" Foreground="{DynamicResource OperatingGuideWindowTextForeground}"/>
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="20, 0" Height="20" Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock FontSize="10" Text=" Alt + L —— 切换至单次直线绘制功能(Line" TextWrapping="Wrap" VerticalAlignment="Center" Foreground="{DynamicResource OperatingGuideWindowTextForeground}"/>
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="20, 0" Height="20" Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock FontSize="10" Text="Shift + Esc —— 退出 PPT 放映" TextWrapping="Wrap" VerticalAlignment="Center" Foreground="{DynamicResource OperatingGuideWindowTextForeground}"/>
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
</Viewbox>
<ikw:SimpleStackPanel Margin="0,0,0,8"
MinHeight="28"
Orientation="Horizontal"
HorizontalAlignment="Left"
Spacing="8">
<Image Source="{DynamicResource OperatingGuideWindowControlIcon1}"
RenderOptions.BitmapScalingMode="HighQuality"
Height="20"
Width="20"
VerticalAlignment="Center" />
<TextBlock FontSize="14"
Text=" + Z —— 撤销"
TextWrapping="Wrap"
VerticalAlignment="Center"
Foreground="{DynamicResource SettingsPageForeground}" />
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="0,0,0,8"
MinHeight="28"
Orientation="Horizontal"
HorizontalAlignment="Left"
Spacing="8">
<Image Source="{DynamicResource OperatingGuideWindowControlIcon2}"
RenderOptions.BitmapScalingMode="HighQuality"
Height="20"
Width="20"
VerticalAlignment="Center" />
<TextBlock FontSize="14"
Text=" + Y —— 重做"
TextWrapping="Wrap"
VerticalAlignment="Center"
Foreground="{DynamicResource SettingsPageForeground}" />
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="0,0,0,8"
MinHeight="28"
Orientation="Horizontal"
HorizontalAlignment="Left"
Spacing="8">
<Image Source="{DynamicResource OperatingGuideWindowControlIcon3}"
RenderOptions.BitmapScalingMode="HighQuality"
Height="20"
Width="20"
VerticalAlignment="Center" />
<TextBlock FontSize="14"
Text=" + E —— 清屏"
TextWrapping="Wrap"
VerticalAlignment="Center"
Foreground="{DynamicResource SettingsPageForeground}" />
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="0,0,0,8"
MinHeight="28"
Orientation="Horizontal"
HorizontalAlignment="Left">
<TextBlock FontSize="14"
Text=" Alt + V —— 显示/隐藏笑脸右侧工具栏(Visibility"
TextWrapping="Wrap"
VerticalAlignment="Center"
Foreground="{DynamicResource SettingsPageForeground}" />
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="0,0,0,8"
MinHeight="28"
Orientation="Horizontal"
HorizontalAlignment="Left">
<TextBlock FontSize="14"
Text=" Alt + C —— 截屏(Capture"
TextWrapping="Wrap"
VerticalAlignment="Center"
Foreground="{DynamicResource SettingsPageForeground}" />
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="0,0,0,8"
MinHeight="28"
Orientation="Horizontal"
HorizontalAlignment="Left">
<TextBlock FontSize="14"
Text=" Alt + S —— 切换至选择模式(Select)"
TextWrapping="Wrap"
VerticalAlignment="Center"
Foreground="{DynamicResource SettingsPageForeground}" />
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="0,0,0,8"
MinHeight="28"
Orientation="Horizontal"
HorizontalAlignment="Left">
<TextBlock FontSize="14"
Text=" Alt + D —— 切换至批注模式 / 墨迹颜色选择器(Draw)"
TextWrapping="Wrap"
VerticalAlignment="Center"
Foreground="{DynamicResource SettingsPageForeground}" />
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="0,0,0,8"
MinHeight="28"
Orientation="Horizontal"
HorizontalAlignment="Left">
<TextBlock FontSize="14"
Text=" Alt + Q —— 退出批注模式(Quit"
TextWrapping="Wrap"
VerticalAlignment="Center"
Foreground="{DynamicResource SettingsPageForeground}" />
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="0,0,0,8"
MinHeight="28"
Orientation="Horizontal"
HorizontalAlignment="Left">
<TextBlock FontSize="14"
Text=" Alt + B —— 切换/退出画板模式(Board)"
TextWrapping="Wrap"
VerticalAlignment="Center"
Foreground="{DynamicResource SettingsPageForeground}" />
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="0,0,0,8"
MinHeight="28"
Orientation="Horizontal"
HorizontalAlignment="Left">
<TextBlock FontSize="14"
Text=" Alt + E —— 切换至面积擦/墨迹擦功能(Eraser)"
TextWrapping="Wrap"
VerticalAlignment="Center"
Foreground="{DynamicResource SettingsPageForeground}" />
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="0,0,0,8"
MinHeight="28"
Orientation="Horizontal"
HorizontalAlignment="Left">
<TextBlock FontSize="14"
Text=" Alt + L —— 切换至单次直线绘制功能(Line)"
TextWrapping="Wrap"
VerticalAlignment="Center"
Foreground="{DynamicResource SettingsPageForeground}" />
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel MinHeight="28"
Orientation="Horizontal"
HorizontalAlignment="Left">
<TextBlock FontSize="14"
Text="Shift + Esc —— 退出 PPT 放映"
TextWrapping="Wrap"
VerticalAlignment="Center"
Foreground="{DynamicResource SettingsPageForeground}" />
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
</ScrollViewer>
<Viewbox Visibility="{Binding ElementName=BigViewController, Path=Visibility}" Margin="20,20,20,20" HorizontalAlignment="Right">
<ikw:SimpleStackPanel Height="180" Orientation="Horizontal">
<Border x:Name="BtnFullscreen" MouseUp="BtnFullscreen_MouseUp" HorizontalAlignment="Right" VerticalAlignment="Bottom"
Margin="5"
Background="{DynamicResource OperatingGuideWindowFullscreenButtonBackground}" Height="20" Width="20" CornerRadius="100">
<Border.Effect>
<DropShadowEffect Direction="0" ShadowDepth="0" Opacity="0.1" BlurRadius="3"/>
</Border.Effect>
<Viewbox Margin="5.5">
<ui:FontIcon Name="FontIconFullscreen" Icon="{x:Static ui:SegoeFluentIcons.FullScreen}" Foreground="{DynamicResource OperatingGuideWindowFullscreenButtonForeground}"/>
</Viewbox>
</Border>
<Border x:Name="BtnClose" MouseUp="BtnClose_MouseUp" HorizontalAlignment="Right" VerticalAlignment="Bottom"
Margin="5"
Background="{DynamicResource OperatingGuideWindowCloseButtonBackground}" Height="20" Width="20" CornerRadius="100">
<Border.Effect>
<DropShadowEffect Direction="0" ShadowDepth="0" Opacity="0.1" BlurRadius="3"/>
</Border.Effect>
<Viewbox Margin="5.5">
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Clear}" Foreground="{DynamicResource OperatingGuideWindowCloseButtonForeground}"/>
</Viewbox>
</Border>
</ikw:SimpleStackPanel>
</Viewbox>
</Grid>
</Border>
</Grid>
</Window>
@@ -1,6 +1,5 @@
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Modern;
using iNKORE.UI.WPF.Modern.Common.IconKeys;
using System;
using System.Windows;
using System.Windows.Input;
@@ -15,33 +14,10 @@ namespace Ink_Canvas
public OperatingGuideWindow()
{
InitializeComponent();
RefreshTheme();
AnimationsHelper.ShowWithSlideFromBottomAndFade(this, 0.25);
}
private void BtnClose_MouseUp(object sender, MouseButtonEventArgs e)
{
Close();
}
private void WindowDragMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed) DragMove();
}
private void BtnFullscreen_MouseUp(object sender, MouseButtonEventArgs e)
{
if (WindowState == WindowState.Normal)
{
WindowState = WindowState.Maximized;
FontIconFullscreen.Icon = SegoeFluentIcons.BackToWindow;
}
else
{
WindowState = WindowState.Normal;
FontIconFullscreen.Icon = SegoeFluentIcons.FullScreen;
}
}
private void SCManipulationBoundaryFeedback(object sender, ManipulationBoundaryFeedbackEventArgs e)
{
e.Handled = true;
+42 -37
View File
@@ -1,3 +1,4 @@
using Ink_Canvas.Controls;
using Ink_Canvas.Helpers;
using Microsoft.Win32;
using Newtonsoft.Json;
@@ -101,7 +102,7 @@ namespace Ink_Canvas.Windows
private double _collapsedOffset = 200; // 折叠时的偏移量(隐藏内容区域)
private MainWindow _mainWindow;
private Dictionary<System.Windows.Controls.Image, int> _pptImages = new Dictionary<System.Windows.Controls.Image, int>();
private Dictionary<FrameworkElement, int> _pptImages = new Dictionary<FrameworkElement, int>();
private Dictionary<int, List<string>> _pptImagePaths = new Dictionary<int, List<string>>();
@@ -183,9 +184,9 @@ namespace Ink_Canvas.Windows
{
foreach (var item in e.OldItems)
{
if (item is System.Windows.Controls.Image image)
if (item is FrameworkElement fe && _pptImages.ContainsKey(fe))
{
RemoveImageFromPPT(image);
RemoveImageFromPPT(fe);
}
}
}
@@ -196,20 +197,20 @@ namespace Ink_Canvas.Windows
}
}
private void RemoveImageFromPPT(System.Windows.Controls.Image image)
private void RemoveImageFromPPT(FrameworkElement element)
{
try
{
if (image == null) return;
if (element == null) return;
if (_pptImages.ContainsKey(image))
if (_pptImages.ContainsKey(element))
{
int slideNumber = _pptImages[image];
_pptImages.Remove(image);
int slideNumber = _pptImages[element];
_pptImages.Remove(element);
if (_pptImagePaths.ContainsKey(slideNumber))
{
string imagePath = image.Tag as string;
string imagePath = element.Tag as string;
if (!string.IsNullOrEmpty(imagePath) && _pptImagePaths[slideNumber].Contains(imagePath))
{
_pptImagePaths[slideNumber].Remove(imagePath);
@@ -929,7 +930,7 @@ namespace Ink_Canvas.Windows
{
var dialog = new OpenFileDialog
{
Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif"
Filter = "图片与 PDF|*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.pdf|图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif|PDF|*.pdf"
};
if (dialog.ShowDialog() == true)
@@ -941,14 +942,14 @@ namespace Ink_Canvas.Windows
if (createImageMethod != null)
{
var imageTask = createImageMethod.Invoke(_mainWindow, new object[] { filePath }) as System.Threading.Tasks.Task<System.Windows.Controls.Image>;
var imageTask = createImageMethod.Invoke(_mainWindow, new object[] { filePath }) as System.Threading.Tasks.Task<FrameworkElement>;
if (imageTask != null)
{
var image = await imageTask;
if (image != null)
var inserted = await imageTask;
if (inserted != null)
{
image.Tag = filePath;
await InsertImageToMainWindow(image, filePath);
inserted.Tag = filePath;
await InsertImageToMainWindow(inserted, filePath);
}
}
}
@@ -1185,9 +1186,9 @@ namespace Ink_Canvas.Windows
}, DispatcherPriority.Normal);
}
private async System.Threading.Tasks.Task InsertImageToMainWindow(System.Windows.Controls.Image image, string originalFilePath = null, bool saveToJson = true)
private async System.Threading.Tasks.Task InsertImageToMainWindow(FrameworkElement element, string originalFilePath = null, bool saveToJson = true)
{
if (_mainWindow == null || image == null) return;
if (_mainWindow == null || element == null) return;
// 确保在UI线程上执行
await Application.Current.Dispatcher.InvokeAsync(() =>
@@ -1196,11 +1197,11 @@ namespace Ink_Canvas.Windows
{
// 生成唯一名称
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
image.Name = timestamp;
element.Name = timestamp;
// 设置图片属性
image.IsHitTestVisible = true;
image.Focusable = false;
element.IsHitTestVisible = true;
element.Focusable = false;
System.Windows.Controls.InkCanvas inkCanvas = null;
var inkCanvasField = _mainWindow.GetType().GetField("inkCanvas",
@@ -1274,7 +1275,7 @@ namespace Ink_Canvas.Windows
// 如果在PPT模式下,记录图片和页面编号的关联,并保存图片路径
if (currentSlideNumber > 0 && !string.IsNullOrEmpty(originalFilePath) && saveToJson)
{
_pptImages[image] = currentSlideNumber;
_pptImages[element] = currentSlideNumber;
// 添加到页面图片路径列表
if (!_pptImagePaths.ContainsKey(currentSlideNumber))
@@ -1289,14 +1290,14 @@ namespace Ink_Canvas.Windows
else if (currentSlideNumber > 0)
{
// 即使不保存到JSON,也要记录图片和页面编号的关联(用于翻页显示/隐藏)
_pptImages[image] = currentSlideNumber;
_pptImages[element] = currentSlideNumber;
}
// 先添加到画布(与MainWindow的实现保持一致)
inkCanvas.Children.Add(image);
inkCanvas.Children.Add(element);
// 等待图片加载完成后再进行后续处理
image.Loaded += (s, args) =>
element.Loaded += (s, args) =>
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
@@ -1305,19 +1306,19 @@ namespace Ink_Canvas.Windows
// 初始化TransformGroup
var initializeTransformMethod = _mainWindow.GetType().GetMethod("InitializeElementTransform",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
initializeTransformMethod?.Invoke(_mainWindow, new object[] { image });
initializeTransformMethod?.Invoke(_mainWindow, new object[] { element });
// 居中缩放
var centerMethod = _mainWindow.GetType().GetMethod("CenterAndScaleElement",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
centerMethod?.Invoke(_mainWindow, new object[] { image });
centerMethod?.Invoke(_mainWindow, new object[] { element });
// 绑定事件处理器
var bindEventsMethod = _mainWindow.GetType().GetMethod("BindElementEvents",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
bindEventsMethod?.Invoke(_mainWindow, new object[] { image });
bindEventsMethod?.Invoke(_mainWindow, new object[] { element });
LogHelper.WriteLogToFile($"图片插入完成: {image.Name}, PPT页面: {currentSlideNumber}");
LogHelper.WriteLogToFile($"图片插入完成: {element.Name}, PPT页面: {currentSlideNumber}");
}
catch (Exception ex)
{
@@ -1333,7 +1334,7 @@ namespace Ink_Canvas.Windows
if (timeMachine != null)
{
var commitMethod = timeMachine.GetType().GetMethod("CommitElementInsertHistory");
commitMethod?.Invoke(timeMachine, new object[] { image });
commitMethod?.Invoke(timeMachine, new object[] { element });
}
// 切换到选择模式
@@ -1699,12 +1700,16 @@ namespace Ink_Canvas.Windows
// 检查已存在的图片路径(通过Tag)
var existingImagePaths = new HashSet<string>();
foreach (System.Windows.Controls.Image existingImage in inkCanvas.Children.OfType<System.Windows.Controls.Image>())
foreach (var existingImage in inkCanvas.Children.OfType<System.Windows.Controls.Image>())
{
if (existingImage.Tag is string tagPath && !string.IsNullOrEmpty(tagPath))
{
existingImagePaths.Add(tagPath);
}
}
foreach (var existingPdf in inkCanvas.Children.OfType<PdfEmbeddedView>())
{
if (existingPdf.Tag is string tagPath && !string.IsNullOrEmpty(tagPath))
existingImagePaths.Add(tagPath);
}
// 使用反射调用MainWindow的CreateAndCompressImageAsync方法
@@ -1733,17 +1738,17 @@ namespace Ink_Canvas.Windows
continue;
}
var imageTask = createImageMethod.Invoke(_mainWindow, new object[] { imagePath }) as System.Threading.Tasks.Task<System.Windows.Controls.Image>;
var imageTask = createImageMethod.Invoke(_mainWindow, new object[] { imagePath }) as System.Threading.Tasks.Task<FrameworkElement>;
if (imageTask != null)
{
var image = await imageTask;
if (image != null)
var inserted = await imageTask;
if (inserted != null)
{
// 保存原始文件路径到Tag
image.Tag = imagePath;
inserted.Tag = imagePath;
// 插入图片(不保存路径,因为已经存在)
await InsertImageToMainWindow(image, imagePath, false);
await InsertImageToMainWindow(inserted, imagePath, false);
}
}
}
@@ -1,179 +0,0 @@
<Window x:Class="Ink_Canvas.Windows.PluginSettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
xmlns:local="clr-namespace:Ink_Canvas.Windows"
mc:Ignorable="d"
Title="插件管理" Height="550" Width="800"
WindowStartupLocation="CenterScreen"
ResizeMode="CanResize"
Topmost="True"
ui:ThemeManager.IsThemeAware="True"
ui:TitleBar.ExtendViewIntoTitleBar="True"
ui:WindowHelper.SystemBackdropType="Mica"
ui:WindowHelper.UseModernWindowStyle="True"
ui:TitleBar.Height="48">
<Window.Resources>
<!-- 定义必要的资源 -->
<SolidColorBrush x:Key="BorderBrush" Color="#DDDDDD"/>
<SolidColorBrush x:Key="SystemAccentColorLight1" Color="#3B82F6"/>
<SolidColorBrush x:Key="SystemAccentColor" Color="#2563EB"/>
<SolidColorBrush x:Key="SystemControlBackgroundChromeMediumBrush" Color="#F0F0F0"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 自定义标题栏 -->
<Border x:Name="Border_TitleBarRoot"
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=(ui:TitleBar.Height)}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=Title}"
VerticalAlignment="Center" Margin="12,0,0,0" FontSize="12" FontWeight="SemiBold"/>
<!--Right Inset-->
<Rectangle Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=(ui:TitleBar.SystemOverlayRightInset)}"
Grid.Column="2"/>
<!--Right Buttons-->
<ikw:SimpleStackPanel x:Name="StackPanel_RightButtons"
Orientation="Horizontal" Grid.Column="1" Spacing="5">
</ikw:SimpleStackPanel>
</Grid>
</Border>
<!-- 内容区域背景 -->
<Border Grid.Row="1" Grid.RowSpan="2"
Background="{DynamicResource SystemControlBackgroundAltHighBrush}"
BorderThickness="0,1,0,0"
BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"/>
<!-- 主内容区 -->
<Grid Grid.Row="1" Margin="20,20,20,20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 左侧插件列表 -->
<Border Grid.Column="0" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}" Margin="0,0,10,0" CornerRadius="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="插件列表" Margin="10,10,0,5" FontSize="16" FontWeight="SemiBold" Foreground="Black"/>
<ListView Grid.Row="1" x:Name="PluginListView" BorderThickness="0" Margin="0,5,0,0"
SelectionChanged="PluginListView_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="0,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" Grid.Column="0" VerticalAlignment="Center"
Foreground="Black" FontWeight="Normal" FontSize="14"/>
<ui:ToggleSwitch Grid.Column="1" IsOn="{Binding IsEnabled}"
Toggled="PluginToggleSwitch_Toggled"
Tag="{Binding}" MinWidth="40"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Border>
<!-- 右侧插件详情和设置 -->
<ScrollViewer Grid.Column="1" Margin="10,0,0,0" VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- 插件详情 -->
<Border Grid.Row="0" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}" Padding="15" CornerRadius="5">
<Grid x:Name="PluginDetailGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="名称:" FontWeight="SemiBold" Margin="0,0,0,5" Foreground="Black"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Name}" Margin="0,0,0,5" Foreground="Black"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="版本:" FontWeight="SemiBold" Margin="0,0,0,5" Foreground="Black"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Version}" Margin="0,0,0,5" Foreground="Black"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="作者:" FontWeight="SemiBold" Margin="0,0,0,5" Foreground="Black"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Author}" Margin="0,0,0,5" Foreground="Black"/>
<TextBlock Grid.Row="3" Grid.Column="0" Text="描述:" FontWeight="SemiBold" Margin="0,0,0,5" Foreground="Black"/>
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding Description}" TextWrapping="Wrap" Margin="0,0,0,5" Foreground="Black"/>
<Button Grid.Row="3" Grid.Column="2" x:Name="BtnDeletePlugin" Content="删除插件"
Visibility="Collapsed" Click="BtnDeletePlugin_Click"
Margin="10,0,0,0" Padding="8,3" HorizontalAlignment="Right"
Background="#FFEE5555" Foreground="White"/>
</Grid>
</Border>
<!-- 插件设置区域 -->
<Border Grid.Row="1" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}" Margin="0,10,0,0" Padding="15" CornerRadius="5">
<StackPanel>
<TextBlock Text="插件设置" FontSize="16" FontWeight="SemiBold" Margin="0,0,0,10" Foreground="Black"/>
<ContentControl x:Name="PluginSettingsContainer" MinHeight="50"/>
</StackPanel>
</Border>
<!-- 插件配置内容区 -->
<Border Grid.Row="2" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}" Margin="0,10,0,0" Padding="0" CornerRadius="5">
<ContentControl x:Name="PluginContentContainer" Margin="0"/>
</Border>
</Grid>
</ScrollViewer>
</Grid>
<!-- 底部操作栏 -->
<Border Grid.Row="2" Background="{DynamicResource SystemControlBackgroundAltHighBrush}" Height="60">
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="20,0,0,0">
<Button x:Name="BtnLoadPlugin" Content="加载本地插件" Click="BtnLoadPlugin_Click"
Padding="15,5" Background="{DynamicResource SystemAccentColor}" Foreground="White"/>
<Button x:Name="BtnSaveConfig" Content="保存状态" Click="BtnSaveConfig_Click"
Padding="15,5" Margin="10,0,0,0" Background="{DynamicResource SystemAccentColor}" Foreground="White"
ToolTip="手动保存当前所有插件的启用/禁用状态到配置文件"/>
<Button x:Name="BtnExportPlugin" Content="导出插件" Click="BtnExportPlugin_Click"
Padding="15,5" Margin="10,0,0,0" Background="{DynamicResource SystemAccentColor}" Foreground="White"/>
</StackPanel>
</Grid>
</Border>
</Grid>
</Window>
@@ -1,727 +0,0 @@
using Ink_Canvas.Helpers;
using Ink_Canvas.Helpers.Plugins;
using Ink_Canvas.Helpers.Plugins.BuiltIn;
using Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher;
using iNKORE.UI.WPF.Modern.Controls;
using Microsoft.Win32;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
namespace Ink_Canvas.Windows
{
/// <summary>
/// PluginSettingsWindow.xaml 的交互逻辑
/// </summary>
public partial class PluginSettingsWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// 刷新插件列表
/// </summary>
public void RefreshPluginList()
{
LoadPlugins();
// 如果当前选中的插件仍然存在,保持其选中状态
if (SelectedPlugin != null)
{
var matchingPlugin = Plugins.FirstOrDefault(p => p.Plugin.GetType().FullName == SelectedPlugin.GetType().FullName);
if (matchingPlugin != null)
{
PluginListView.SelectedItem = matchingPlugin;
}
}
OnPropertyChanged(nameof(SelectedPlugin));
LogHelper.WriteLogToFile("插件列表已刷新");
}
private IPlugin _selectedPlugin;
/// <summary>
/// 当前选中的插件
/// </summary>
public IPlugin SelectedPlugin
{
get => _selectedPlugin;
set
{
if (_selectedPlugin != value)
{
_selectedPlugin = value;
OnPropertyChanged(nameof(SelectedPlugin));
OnPropertyChanged(nameof(Name));
OnPropertyChanged(nameof(Version));
OnPropertyChanged(nameof(Author));
OnPropertyChanged(nameof(Description));
OnPropertyChanged(nameof(IsEnabled));
OnPropertyChanged(nameof(IsBuiltIn));
}
}
}
public new string Name => SelectedPlugin?.Name ?? string.Empty;
public string Version => SelectedPlugin?.Version?.ToString() ?? string.Empty;
public string Author => SelectedPlugin?.Author ?? string.Empty;
public string Description => SelectedPlugin?.Description ?? string.Empty;
public new bool IsEnabled => SelectedPlugin is PluginBase plugin && plugin.IsEnabled;
public bool IsBuiltIn => SelectedPlugin?.IsBuiltIn ?? false;
/// <summary>
/// 插件列表
/// </summary>
public ObservableCollection<PluginViewModel> Plugins { get; } = new ObservableCollection<PluginViewModel>();
public PluginSettingsWindow()
{
InitializeComponent();
// 设置数据上下文
PluginDetailGrid.DataContext = this;
// 设置导出按钮初始状态
BtnExportPlugin.IsEnabled = false;
BtnExportPlugin.ToolTip = "请先选择要导出的插件";
// 加载插件列表
LoadPlugins();
// 注册窗口关闭事件
Closing += PluginSettingsWindow_Closing;
}
/// <summary>
/// 窗口关闭事件处理
/// </summary>
private void PluginSettingsWindow_Closing(object sender, CancelEventArgs e)
{
try
{
// 保存插件配置
LogHelper.WriteLogToFile("插件管理窗口关闭,保存插件配置...");
PluginManager.Instance.SaveConfig();
LogHelper.WriteLogToFile("插件配置已保存");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"关闭窗口时保存插件配置出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 加载插件列表
/// </summary>
private void LoadPlugins()
{
Plugins.Clear();
LogHelper.WriteLogToFile($"开始加载插件列表到UI,插件总数: {PluginManager.Instance.Plugins.Count}");
// 添加所有已加载的插件
foreach (var plugin in PluginManager.Instance.Plugins)
{
bool isEnabled = false;
// 从插件实例获取启用状态
if (plugin is PluginBase pluginBase)
{
isEnabled = pluginBase.IsEnabled;
}
// 记录插件详细信息
LogHelper.WriteLogToFile($"正在加载插件到UI: 类型={plugin.GetType().FullName}, 名称={plugin.Name ?? ""}, 状态={isEnabled}");
// 创建视图模型并添加到集合
var viewModel = new PluginViewModel(plugin)
{
IsEnabled = isEnabled
};
Plugins.Add(viewModel);
LogHelper.WriteLogToFile($"已加载插件到UI列表: {plugin.Name},状态: {(isEnabled ? "" : "")}");
}
// 绑定到ListView
LogHelper.WriteLogToFile($"绑定 {Plugins.Count} 个插件到ListView");
PluginListView.ItemsSource = Plugins;
// 如果有插件,选择第一个
if (Plugins.Count > 0)
{
LogHelper.WriteLogToFile($"选择第一个插件: {Plugins[0].Name}");
PluginListView.SelectedIndex = 0;
}
else
{
LogHelper.WriteLogToFile("没有找到任何插件", LogHelper.LogType.Warning);
}
}
/// <summary>
/// 更新属性变更通知
/// </summary>
/// <param name="propertyName">属性名称</param>
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// 插件列表选择变更事件
/// </summary>
private void PluginListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (PluginListView.SelectedItem is PluginViewModel viewModel)
{
// 设置当前选中的插件
SelectedPlugin = viewModel.Plugin;
// 加载插件设置界面
PluginSettingsContainer.Content = SelectedPlugin.GetSettingsView();
// 设置删除按钮的可见性
BtnDeletePlugin.Visibility = !SelectedPlugin.IsBuiltIn ? Visibility.Visible : Visibility.Collapsed;
// 设置导出按钮的可用状态
BtnExportPlugin.IsEnabled = !SelectedPlugin.IsBuiltIn;
if (SelectedPlugin.IsBuiltIn)
{
BtnExportPlugin.ToolTip = "内置插件无法导出";
}
else
{
BtnExportPlugin.ToolTip = "将插件导出为.iccpp文件";
}
}
else
{
SelectedPlugin = null;
PluginSettingsContainer.Content = null;
BtnDeletePlugin.Visibility = Visibility.Collapsed;
BtnExportPlugin.IsEnabled = false;
BtnExportPlugin.ToolTip = "请先选择要导出的插件";
}
}
/// <summary>
/// 加载本地插件按钮点击事件
/// </summary>
private void BtnLoadPlugin_Click(object sender, RoutedEventArgs e)
{
try
{
// 创建文件对话框
OpenFileDialog dialog = new OpenFileDialog
{
Filter = "ICC插件文件(*.iccpp)|*.iccpp",
Title = "选择要加载的插件文件"
};
// 显示对话框
if (dialog.ShowDialog() == true)
{
// 获取插件文件路径
string pluginPath = dialog.FileName;
// 检查是否在Plugins目录下
string pluginsDirectory = Path.Combine(App.RootPath, "Plugins");
string targetPath = Path.Combine(pluginsDirectory, Path.GetFileName(pluginPath));
// 确保Plugins目录存在
if (!Directory.Exists(pluginsDirectory))
{
Directory.CreateDirectory(pluginsDirectory);
}
// 如果插件不在Plugins目录下,复制过去
if (!string.Equals(pluginPath, targetPath, StringComparison.OrdinalIgnoreCase))
{
File.Copy(pluginPath, targetPath, true);
pluginPath = targetPath;
}
// 加载插件
IPlugin plugin = PluginManager.Instance.LoadExternalPlugin(pluginPath);
if (plugin != null)
{
// 刷新插件列表
LoadPlugins();
// 选择新加载的插件
foreach (var item in Plugins)
{
if (item.Plugin == plugin)
{
PluginListView.SelectedItem = item;
break;
}
}
MessageBox.Show($"插件 {plugin.Name} v{plugin.Version} 已成功加载!", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
}
else
{
MessageBox.Show("插件加载失败,请检查文件是否有效。", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
catch (Exception ex)
{
MessageBox.Show($"加载插件时发生错误:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 删除插件按钮点击事件
/// </summary>
private void BtnDeletePlugin_Click(object sender, RoutedEventArgs e)
{
if (SelectedPlugin == null) return;
// 不能删除内置插件
if (SelectedPlugin.IsBuiltIn)
{
MessageBox.Show("内置插件无法删除。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// 保存插件名称,以便在删除后使用
string pluginName = SelectedPlugin.Name;
// 确认删除
MessageBoxResult result = MessageBox.Show(
$"确定要删除插件 {pluginName} 吗?\n此操作将永久删除插件文件,无法恢复。",
"删除确认",
MessageBoxButton.YesNo,
MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
// 删除插件
bool success = PluginManager.Instance.DeletePlugin(SelectedPlugin);
if (success)
{
// 刷新插件列表
LoadPlugins();
// 如果还有插件,选择第一个
if (Plugins.Count > 0)
{
PluginListView.SelectedIndex = 0;
}
MessageBox.Show($"插件 {pluginName} 已成功删除。", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
}
else
{
MessageBox.Show($"删除插件 {pluginName} 失败,请稍后重试。", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
/// <summary>
/// 导出插件按钮点击事件
/// </summary>
private void BtnExportPlugin_Click(object sender, RoutedEventArgs e)
{
try
{
// 检查是否有选中的插件
if (SelectedPlugin == null)
{
MessageBox.Show("请先选择要导出的插件", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// 检查是否为内置插件
if (SelectedPlugin.IsBuiltIn)
{
MessageBox.Show("内置插件无法导出", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// 检查插件文件是否存在
string pluginPath = null;
if (SelectedPlugin is PluginBase pluginBase)
{
pluginPath = pluginBase.PluginPath;
}
if (string.IsNullOrEmpty(pluginPath) || !File.Exists(pluginPath))
{
MessageBox.Show("插件文件不存在或无法访问", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
// 创建保存文件对话框
SaveFileDialog dialog = new SaveFileDialog
{
Filter = "ICC插件文件(*.iccpp)|*.iccpp",
Title = "导出插件",
FileName = Path.GetFileName(pluginPath)
};
// 显示对话框
if (dialog.ShowDialog() == true)
{
// 获取目标路径
string targetPath = dialog.FileName;
// 如果目标文件已存在,询问是否覆盖
if (File.Exists(targetPath) && !string.Equals(pluginPath, targetPath, StringComparison.OrdinalIgnoreCase))
{
MessageBoxResult result = MessageBox.Show("目标文件已存在,是否覆盖?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result != MessageBoxResult.Yes)
{
return;
}
}
// 复制插件文件到目标路径
if (!string.Equals(pluginPath, targetPath, StringComparison.OrdinalIgnoreCase))
{
File.Copy(pluginPath, targetPath, true);
}
LogHelper.WriteLogToFile($"插件 {SelectedPlugin.Name} 已成功导出到: {targetPath}");
MessageBox.Show($"插件 {SelectedPlugin.Name} 已成功导出!", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"导出插件时出错: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"导出插件时发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 插件开关切换事件
/// </summary>
private void PluginToggleSwitch_Toggled(object sender, RoutedEventArgs e)
{
try
{
if (sender is ToggleSwitch toggleSwitch &&
toggleSwitch.Tag is IPlugin plugin)
{
// 记录当前开关状态
bool targetState = toggleSwitch.IsOn;
// 记录插件类型名称和名称,用于稍后查找重载后的插件
string pluginTypeName = plugin.GetType().FullName;
string pluginName = plugin.Name;
bool wasBuiltIn = plugin.IsBuiltIn;
LogHelper.WriteLogToFile($"UI开关切换: {pluginName}, 目标状态: {(targetState ? "" : "")}");
// 切换插件状态
PluginManager.Instance.TogglePlugin(plugin, targetState);
// 立即同步保存配置到文件(确保状态被立即持久化)
PluginManager.Instance.SaveConfig();
LogHelper.WriteLogToFile("插件状态已立即保存到配置文件");
// 延迟一下再检查状态,确保变更已应用
Dispatcher.BeginInvoke(new Action(() =>
{
try
{
// 查找最新的插件实例
IPlugin currentPlugin = null;
foreach (var p in PluginManager.Instance.Plugins)
{
if (p.GetType().FullName == pluginTypeName || p.Name == pluginName)
{
currentPlugin = p;
break;
}
}
if (currentPlugin == null)
{
LogHelper.WriteLogToFile($"无法找到插件: {pluginName}UI状态可能不准确", LogHelper.LogType.Warning);
return;
}
// 检查实际状态
bool actualState = currentPlugin is PluginBase pb && pb.IsEnabled;
LogHelper.WriteLogToFile($"插件 {pluginName} 实际状态: {(actualState ? "" : "")}");
// 更新视图模型
PluginViewModel viewModel = null;
if (toggleSwitch.DataContext is PluginViewModel vm)
{
viewModel = vm;
}
else
{
viewModel = Plugins.FirstOrDefault(p => p.Plugin == currentPlugin);
}
if (viewModel != null)
{
// 确保视图模型状态与实际状态一致
if (viewModel.IsEnabled != actualState)
{
LogHelper.WriteLogToFile($"同步视图模型状态: {(actualState ? "" : "")}");
viewModel.IsEnabled = actualState;
}
// 确保UI开关状态与实际状态一致
if (toggleSwitch.IsOn != actualState)
{
LogHelper.WriteLogToFile($"同步UI开关状态: {(actualState ? "" : "")}");
toggleSwitch.IsOn = actualState;
}
}
// 如果是当前选中的插件,更新属性
if (currentPlugin == SelectedPlugin)
{
OnPropertyChanged(nameof(IsEnabled));
}
// 对于内置插件,特别处理
if (wasBuiltIn)
{
// 特殊插件刷新逻辑,如果是超级启动台插件,立即刷新UI
if (currentPlugin is SuperLauncherPlugin &&
PluginSettingsContainer.Content is LauncherSettingsControl)
{
// 重新获取设置界面
PluginSettingsContainer.Content = currentPlugin.GetSettingsView();
}
}
LogHelper.WriteLogToFile($"插件 {pluginName} UI状态同步完成");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"同步UI状态时出错: {ex.Message}", LogHelper.LogType.Error);
}
}), DispatcherPriority.Background);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"切换插件状态时出错: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"切换插件状态时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 刷新插件列表按钮点击事件
/// </summary>
private void Button_Refresh_Click(object sender, RoutedEventArgs e)
{
try
{
// 刷新插件列表
RefreshPluginList();
LogHelper.WriteLogToFile("用户点击刷新按钮,刷新插件列表");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"刷新插件列表时出错: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"刷新插件列表时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 保存插件状态按钮点击事件
/// </summary>
private void BtnSaveConfig_Click(object sender, RoutedEventArgs e)
{
try
{
int syncCount = 0;
// 遍历界面上所有插件视图模型,获取开关状态
foreach (var viewModel in Plugins)
{
try
{
if (viewModel.Plugin != null)
{
// 获取UI中开关的当前状态(从界面控件读取)
bool uiState = viewModel.IsEnabled;
// 获取插件类型名,用于查找配置
string pluginTypeName = viewModel.Plugin.GetType().FullName;
// 查找实际的插件实例(可能与viewModel.Plugin不同,因为可能已经重新加载)
IPlugin actualPlugin = null;
foreach (var p in PluginManager.Instance.Plugins)
{
if (p.GetType().FullName == pluginTypeName)
{
actualPlugin = p;
break;
}
}
// 如果找不到对应的实际插件实例,跳过
if (actualPlugin == null)
{
LogHelper.WriteLogToFile($"手动保存:无法找到与UI对应的插件实例:{viewModel.Name}", LogHelper.LogType.Warning);
continue;
}
// 获取插件实际状态
bool pluginState = false;
if (actualPlugin is PluginBase pluginBase)
{
pluginState = pluginBase.IsEnabled;
}
// 如果界面状态与插件实际状态不一致,应用界面状态
if (uiState != pluginState)
{
// 应用界面的状态到插件
PluginManager.Instance.TogglePlugin(actualPlugin, uiState);
LogHelper.WriteLogToFile($"手动保存:同步插件 {actualPlugin.Name} 状态 {pluginState} -> {uiState}");
syncCount++;
}
// 确保配置中的状态也与界面一致
if (PluginManager.Instance.PluginStates.TryGetValue(pluginTypeName, out bool configState) && configState != uiState)
{
PluginManager.Instance.PluginStates[pluginTypeName] = uiState;
LogHelper.WriteLogToFile($"手动保存:更新配置中插件 {actualPlugin.Name} 状态 {configState} -> {uiState}");
syncCount++;
}
}
}
catch (Exception pluginEx)
{
// 单个插件处理失败不应该影响其他插件
LogHelper.WriteLogToFile($"手动保存:处理插件 {viewModel.Name} 时出错: {pluginEx.Message}", LogHelper.LogType.Error);
}
}
// 保存插件状态配置
PluginManager.Instance.SaveConfig();
// 记录日志
LogHelper.WriteLogToFile($"用户手动保存插件状态配置,同步了 {syncCount} 个状态变更");
// 刷新插件列表,确保UI与最新状态同步
RefreshPluginList();
// 显示保存成功提示
string message = syncCount > 0
? $"插件状态已成功保存,同步了 {syncCount} 个状态变更"
: "插件状态已成功保存,所有插件状态已是最新";
MessageBox.Show(message, "保存成功", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
// 记录错误日志
LogHelper.WriteLogToFile($"手动保存插件状态时出错: {ex.Message}", LogHelper.LogType.Error);
// 显示错误信息
MessageBox.Show($"保存插件状态时发生错误: {ex.Message}", "保存失败", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
/// <summary>
/// 插件视图模型
/// </summary>
public class PluginViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// 插件实例
/// </summary>
public IPlugin Plugin { get; }
/// <summary>
/// 插件名称
/// </summary>
public string Name
{
get
{
string name = Plugin?.Name ?? "未命名插件";
LogHelper.WriteLogToFile($"获取插件名称: {name},类型: {Plugin?.GetType().FullName ?? ""}");
return name;
}
}
/// <summary>
/// 插件是否启用
/// </summary>
private bool _isEnabled;
public bool IsEnabled
{
get => _isEnabled;
set
{
if (_isEnabled != value)
{
_isEnabled = value;
OnPropertyChanged(nameof(IsEnabled));
}
}
}
public PluginViewModel(IPlugin plugin)
{
Plugin = plugin;
// 获取实际状态
_isEnabled = plugin is PluginBase pluginBase && pluginBase.IsEnabled;
// 记录日志
LogHelper.WriteLogToFile($"创建插件视图模型: {plugin?.GetType().FullName ?? ""}, 名称: {plugin?.Name ?? ""}");
// 注册插件状态变更事件
if (plugin is PluginBase pb)
{
pb.EnabledStateChanged += Plugin_EnabledStateChanged;
}
}
/// <summary>
/// 处理插件状态变更事件
/// </summary>
private void Plugin_EnabledStateChanged(object sender, bool isEnabled)
{
// 在UI线程上更新状态
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
IsEnabled = isEnabled;
// 确保配置立即保存
if (sender is IPlugin plugin)
{
LogHelper.WriteLogToFile($"视图模型捕获到插件 {plugin.Name} 状态变更: {(isEnabled ? "" : "")}");
PluginManager.Instance.SaveConfig();
LogHelper.WriteLogToFile("视图模型已触发配置保存");
}
}));
}
/// <summary>
/// 属性变更通知
/// </summary>
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
@@ -333,33 +333,8 @@ namespace Ink_Canvas.Windows.SettingsViews
{
if (window.GetType().Name == "SettingsWindow")
{
// 根据控件名称确定需要同步的面板
var panelToSync = GetPanelForControl(controlName);
if (panelToSync != null)
{
// 获取对应的面板属性
var panelProp = window.GetType().GetProperty(panelToSync, BindingFlags.Public | BindingFlags.Instance);
if (panelProp != null)
{
var panel = panelProp.GetValue(window) as System.Windows.Controls.UserControl;
if (panel != null)
{
// 调用 LoadSettings 方法重新加载设置
var loadMethod = panel.GetType().GetMethod("LoadSettings",
BindingFlags.Public | BindingFlags.Instance);
if (loadMethod != null)
{
loadMethod.Invoke(panel, null);
}
}
}
}
else
{
// 如果无法确定具体面板,则同步所有面板(保守策略)
SyncAllPanels(window);
}
// 同步所有面板(保守策略)
SyncAllPanels(window);
break; // 通常只有一个设置窗口
}
}
@@ -371,102 +346,6 @@ namespace Ink_Canvas.Windows.SettingsViews
}
}
/// <summary>
/// 根据控件名称获取对应的面板名称
/// </summary>
private static string GetPanelForControl(string controlName)
{
// 定义控件名称到面板名称的映射
var controlToPanel = new Dictionary<string, string>
{
// StartupPanel
{ "ToggleSwitchIsAutoUpdate", "StartupPanel" },
{ "ToggleSwitchIsAutoUpdateWithSilence", "StartupPanel" },
{ "ToggleSwitchRunAtStartup", "StartupPanel" },
{ "ToggleSwitchFoldAtStartup", "StartupPanel" },
{ "AutoUpdateWithSilenceStartTimeComboBox", "StartupPanel" },
{ "AutoUpdateWithSilenceEndTimeComboBox", "StartupPanel" },
// ThemePanel
{ "ComboBoxTheme", "ThemePanel" },
{ "ToggleSwitchEnableSplashScreen", "ThemePanel" },
{ "ComboBoxSplashScreenStyle", "ThemePanel" },
{ "ToggleSwitchEnableTrayIcon", "ThemePanel" },
{ "ComboBoxFloatingBarImg", "ThemePanel" },
{ "ComboBoxUnFoldBtnImg", "ThemePanel" },
{ "ComboBoxChickenSoupSource", "ThemePanel" },
{ "ComboBoxQuickColorPaletteDisplayMode", "ThemePanel" },
{ "ComboBoxEraserDisplayOption", "ThemePanel" },
{ "ToggleSwitchEnableQuickPanel", "ThemePanel" },
{ "ViewboxFloatingBarScaleTransformValueSlider", "ThemePanel" },
{ "ViewboxFloatingBarOpacityValueSlider", "ThemePanel" },
{ "ViewboxFloatingBarOpacityInPPTValueSlider", "ThemePanel" },
// PowerPointPanel
{ "ToggleSwitchSupportPowerPoint", "PowerPointPanel" },
{ "ToggleSwitchShowPPTButton", "PowerPointPanel" },
{ "ToggleSwitchEnablePPTButtonPageClickable", "PowerPointPanel" },
{ "ToggleSwitchShowCanvasAtNewSlideShow", "PowerPointPanel" },
{ "PPTButtonLeftPositionValueSlider", "PowerPointPanel" },
{ "PPTButtonRightPositionValueSlider", "PowerPointPanel" },
// GesturesPanel
{ "ToggleSwitchEnableTwoFingerRotationOnSelection", "GesturesPanel" },
{ "ToggleSwitchEnablePalmEraser", "GesturesPanel" },
{ "ComboBoxPalmEraserSensitivity", "GesturesPanel" },
// CanvasAndInkPanel
{ "ToggleSwitchShowCursor", "CanvasAndInkPanel" },
{ "ToggleSwitchDisablePressure", "CanvasAndInkPanel" },
{ "ToggleSwitchEnablePressureTouchMode", "CanvasAndInkPanel" },
{ "ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth", "CanvasAndInkPanel" },
{ "ComboBoxEraserSize", "CanvasAndInkPanel" },
{ "ComboBoxHyperbolaAsymptoteOption", "CanvasAndInkPanel" },
{ "ComboBoxAutoSaveStrokesInterval", "CanvasAndInkPanel" },
// SnapshotPanel
{ "AutoSavedStrokesLocation", "SnapshotPanel" },
{ "ComboBoxAutoDelSavedFilesDaysThreshold", "SnapshotPanel" },
{ "ToggleSwitchAutoDelSavedFiles", "SnapshotPanel" },
// AdvancedPanel
{ "ComboBoxAutoBackupInterval", "AdvancedPanel" },
{ "ToggleSwitchIsQuadIR", "AdvancedPanel" },
{ "ToggleSwitchIsLogEnabled", "AdvancedPanel" },
{ "ToggleSwitchIsSaveLogByDate", "AdvancedPanel" },
{ "ToggleSwitchIsSecondConfimeWhenShutdownApp", "AdvancedPanel" },
{ "ToggleSwitchIsEnableFullScreenHelper", "AdvancedPanel" },
{ "ToggleSwitchIsEnableAvoidFullScreenHelper", "AdvancedPanel" },
{ "ToggleSwitchIsEnableEdgeGestureUtil", "AdvancedPanel" },
{ "ToggleSwitchIsEnableForceFullScreen", "AdvancedPanel" },
{ "ToggleSwitchIsEnableDPIChangeDetection", "AdvancedPanel" },
{ "ToggleSwitchIsEnableResolutionChangeDetection", "AdvancedPanel" },
{ "ToggleSwitchIsAutoBackupBeforeUpdate", "AdvancedPanel" },
{ "ToggleSwitchIsAutoBackupEnabled", "AdvancedPanel" },
{ "ToggleSwitchIsEnableUriScheme", "AdvancedPanel" },
// LuckyRandomPanel
{ "ToggleSwitchDisplayRandWindowNamesInputBtn", "LuckyRandomPanel" },
{ "ToggleSwitchShowRandomAndSingleDraw", "LuckyRandomPanel" },
{ "ToggleSwitchEnableQuickDraw", "LuckyRandomPanel" },
{ "ToggleSwitchExternalCaller", "LuckyRandomPanel" },
{ "ComboBoxExternalCallerType", "LuckyRandomPanel" },
{ "RandWindowOnceCloseLatencySlider", "LuckyRandomPanel" },
{ "RandWindowOnceMaxStudentsSlider", "LuckyRandomPanel" },
};
// 查找匹配的面板
foreach (var kvp in controlToPanel)
{
if (controlName.Contains(kvp.Key) || kvp.Key.Contains(controlName))
{
return kvp.Value;
}
}
return null;
}
/// <summary>
/// 同步所有面板的状态(保守策略)
/// </summary>
@@ -474,36 +353,36 @@ namespace Ink_Canvas.Windows.SettingsViews
{
try
{
// 获取所有面属性
var panelProperties = settingsWindow.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.PropertyType.Name.EndsWith("Panel") &&
p.PropertyType.IsSubclassOf(typeof(System.Windows.Controls.UserControl)));
// 获取所有面属性
var pageProperties = settingsWindow.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.PropertyType.Name.EndsWith("Page") &&
p.PropertyType.IsSubclassOf(typeof(System.Windows.Controls.Page)));
foreach (var panelProp in panelProperties)
foreach (var pageProp in pageProperties)
{
try
{
var panel = panelProp.GetValue(settingsWindow) as System.Windows.Controls.UserControl;
if (panel != null)
var page = pageProp.GetValue(settingsWindow) as System.Windows.Controls.Page;
if (page != null)
{
// 调用 LoadSettings 方法
var loadMethod = panel.GetType().GetMethod("LoadSettings",
var loadMethod = page.GetType().GetMethod("LoadSettings",
BindingFlags.Public | BindingFlags.Instance);
if (loadMethod != null)
{
loadMethod.Invoke(panel, null);
loadMethod.Invoke(page, null);
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"同步面 {panelProp.Name} 状态失败: {ex.Message}");
System.Diagnostics.Debug.WriteLine($"同步面 {pageProp.Name} 状态失败: {ex.Message}");
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"同步所有面状态失败: {ex.Message}");
System.Diagnostics.Debug.WriteLine($"同步所有面状态失败: {ex.Message}");
}
}
@@ -568,7 +447,7 @@ namespace Ink_Canvas.Windows.SettingsViews
}
/// <summary>
/// 通知设置窗口更新所有面的主题
/// 通知设置窗口更新所有面的主题
/// </summary>
private static void NotifySettingsWindowThemeUpdate()
{
@@ -580,14 +459,6 @@ namespace Ink_Canvas.Windows.SettingsViews
// 使用类型名称匹配,因为 SettingsWindow 在不同的命名空间中
if (window.GetType().Name == "SettingsWindow")
{
// 使用反射调用 ApplyThemeToAllPanels 方法
var method = window.GetType().GetMethod("ApplyThemeToAllPanels",
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
if (method != null)
{
method.Invoke(window, null);
}
// 同时调用 ApplyTheme 方法更新窗口本身
var applyThemeMethod = window.GetType().GetMethod("ApplyTheme",
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
@@ -607,7 +478,7 @@ namespace Ink_Canvas.Windows.SettingsViews
}
/// <summary>
/// 强制更新所有设置面的主题(公共方法,可在外部调用)
/// 强制更新所有设置面的主题(公共方法,可在外部调用)
/// </summary>
public static void ForceUpdateAllPanelsTheme()
{
@@ -711,4 +582,3 @@ namespace Ink_Canvas.Windows.SettingsViews
}
}
}
@@ -0,0 +1,17 @@
<ui:Page
x:Class="Ink_Canvas.Windows.SettingsViews.Pages.AboutPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
Title="关于"
mc:Ignorable="d">
<Grid>
<StackPanel Margin="24">
<TextBlock Text="关于 InkCanvasForClass" Style="{DynamicResource TitleTextBlockStyle}" Margin="0,0,0,16" />
<TextBlock Text="这里是关于页面" Style="{DynamicResource BodyTextBlockStyle}" />
</StackPanel>
</Grid>
</ui:Page>
@@ -0,0 +1,12 @@
using iNKORE.UI.WPF.Modern.Controls;
namespace Ink_Canvas.Windows.SettingsViews.Pages
{
public partial class AboutPage : Page
{
public AboutPage()
{
InitializeComponent();
}
}
}
@@ -0,0 +1,49 @@
<Page x:Class="Ink_Canvas.Windows.SettingsViews.Pages.AppearancePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ink_Canvas.Windows.SettingsViews.Pages"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="外观">
<Grid Margin="24">
<StackPanel>
<TextBlock Text="外观设置" Style="{DynamicResource TitleTextBlockStyle}" Margin="0,0,0,16" />
<ui:SettingsCard
Header="主题"
Description="点击跳转到主题设置页面"
IsClickEnabled="True"
Click="SettingsCard_Click">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Color}"/>
</ui:SettingsCard.HeaderIcon>
</ui:SettingsCard>
<ui:SettingsCard
Header="颜色"
Description="点击跳转到颜色设置页面"
IsClickEnabled="True"
Click="SettingsCard_Click_1"
Margin="0,8,0,0">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Color}"/>
</ui:SettingsCard.HeaderIcon>
</ui:SettingsCard>
<ui:SettingsCard
Header="字体"
Description="点击跳转到字体设置页面"
IsClickEnabled="True"
Click="SettingsCard_Click_2"
Margin="0,8,0,0">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Font}"/>
</ui:SettingsCard.HeaderIcon>
</ui:SettingsCard>
</StackPanel>
</Grid>
</Page>
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Ink_Canvas.Windows.SettingsViews.Pages
{
public partial class AppearancePage : Page
{
public AppearancePage()
{
InitializeComponent();
}
private void SettingsCard_Click(object sender, RoutedEventArgs e)
{
SettingsWindow settingsWindow = Window.GetWindow(this) as SettingsWindow;
if (settingsWindow != null)
{
settingsWindow.NavigateToPage("ThemePage");
}
}
private void SettingsCard_Click_1(object sender, RoutedEventArgs e)
{
SettingsWindow settingsWindow = Window.GetWindow(this) as SettingsWindow;
if (settingsWindow != null)
{
settingsWindow.NavigateToPage("ColorsPage");
}
}
private void SettingsCard_Click_2(object sender, RoutedEventArgs e)
{
SettingsWindow settingsWindow = Window.GetWindow(this) as SettingsWindow;
if (settingsWindow != null)
{
settingsWindow.NavigateToPage("FontsPage");
}
}
}
}
@@ -0,0 +1,48 @@
<Page x:Class="Ink_Canvas.Windows.SettingsViews.Pages.BasicPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ink_Canvas.Windows.SettingsViews.Pages"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
xmlns:i18n="clr-namespace:Ink_Canvas.MarkupExtensions"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="基本">
<ScrollViewer Padding="59,0,59,0">
<!-- These styles can be referenced to create a consistent SettingsPage layout -->
<FrameworkElement.Resources>
<!-- Spacing between cards -->
<sys:Double x:Key="SettingsCardSpacing">4</sys:Double>
<!-- Style (inc. the correct spacing) of a section header -->
<Style x:Key="SettingsSectionHeaderTextBlockStyle"
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
TargetType="TextBlock">
<Style.Setters>
<Setter Property="Margin" Value="1,30,0,6" />
</Style.Setters>
</Style>
</FrameworkElement.Resources>
<Grid>
<ikw:SimpleStackPanel MaxWidth="800"
HorizontalAlignment="Stretch"
Spacing="{StaticResource SettingsCardSpacing}">
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="启动时行为" />
<ui:SettingsCard
Header="启动设置"
Description="点击跳转到启动设置页面"
IsClickEnabled="True"
Click="SettingsCard_Click">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Play}"/>
</ui:SettingsCard.HeaderIcon>
</ui:SettingsCard>
</ikw:SimpleStackPanel>
</Grid>
</ScrollViewer>
</Page>
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Ink_Canvas.Windows.SettingsViews.Pages
{
/// <summary>
/// Basic.xaml 的交互逻辑
/// </summary>
public partial class BasicPage : Page
{
public BasicPage()
{
InitializeComponent();
}
private void SettingsCard_Click(object sender, RoutedEventArgs e)
{
// 找到SettingsWindow窗口
SettingsWindow settingsWindow = Window.GetWindow(this) as SettingsWindow;
if (settingsWindow != null)
{
// 调用NavigateToPage方法导航到启动页面
settingsWindow.NavigateToPage("StartupPage");
}
}
}
}
@@ -0,0 +1,17 @@
<ui:Page
x:Class="Ink_Canvas.Windows.SettingsViews.Pages.ColorsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
Title="颜色设置"
mc:Ignorable="d">
<Grid>
<StackPanel Margin="24">
<TextBlock Text="颜色设置" Style="{DynamicResource TitleTextBlockStyle}" Margin="0,0,0,16" />
<TextBlock Text="这里是颜色设置页面" Style="{DynamicResource BodyTextBlockStyle}" />
</StackPanel>
</Grid>
</ui:Page>
@@ -0,0 +1,12 @@
using iNKORE.UI.WPF.Modern.Controls;
namespace Ink_Canvas.Windows.SettingsViews.Pages
{
public partial class ColorsPage : Page
{
public ColorsPage()
{
InitializeComponent();
}
}
}
@@ -0,0 +1,38 @@
<Page x:Class="Ink_Canvas.Windows.SettingsViews.Pages.DesignPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ink_Canvas.Windows.SettingsViews.Pages"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="设计">
<Grid Margin="24">
<StackPanel>
<TextBlock Text="设计设置" Style="{DynamicResource TitleTextBlockStyle}" Margin="0,0,0,16" />
<ui:SettingsCard
Header="图标"
Description="点击跳转到图标设置页面"
IsClickEnabled="True"
Click="SettingsCard_Click">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Home}"/>
</ui:SettingsCard.HeaderIcon>
</ui:SettingsCard>
<ui:SettingsCard
Header="排版"
Description="点击跳转到排版设置页面"
IsClickEnabled="True"
Click="SettingsCard_Click_1"
Margin="0,8,0,0">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Font}"/>
</ui:SettingsCard.HeaderIcon>
</ui:SettingsCard>
</StackPanel>
</Grid>
</Page>
@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Ink_Canvas.Windows.SettingsViews.Pages
{
public partial class DesignPage : Page
{
public DesignPage()
{
InitializeComponent();
}
private void SettingsCard_Click(object sender, RoutedEventArgs e)
{
SettingsWindow settingsWindow = Window.GetWindow(this) as SettingsWindow;
if (settingsWindow != null)
{
settingsWindow.NavigateToPage("IconographyPage");
}
}
private void SettingsCard_Click_1(object sender, RoutedEventArgs e)
{
SettingsWindow settingsWindow = Window.GetWindow(this) as SettingsWindow;
if (settingsWindow != null)
{
settingsWindow.NavigateToPage("TypographyPage");
}
}
}
}
@@ -0,0 +1,17 @@
<ui:Page
x:Class="Ink_Canvas.Windows.SettingsViews.Pages.FontsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
Title="字体设置"
mc:Ignorable="d">
<Grid>
<StackPanel Margin="24">
<TextBlock Text="字体设置" Style="{DynamicResource TitleTextBlockStyle}" Margin="0,0,0,16" />
<TextBlock Text="这里是字体设置页面" Style="{DynamicResource BodyTextBlockStyle}" />
</StackPanel>
</Grid>
</ui:Page>
@@ -0,0 +1,12 @@
using iNKORE.UI.WPF.Modern.Controls;
namespace Ink_Canvas.Windows.SettingsViews.Pages
{
public partial class FontsPage : Page
{
public FontsPage()
{
InitializeComponent();
}
}
}
@@ -0,0 +1,78 @@
<Page x:Class="Ink_Canvas.Windows.SettingsViews.Pages.HomePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ink_Canvas.Windows.SettingsViews.Pages"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
xmlns:i18n="clr-namespace:Ink_Canvas.MarkupExtensions"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="首页">
<ScrollViewer Padding="59,0,59,0">
<!-- These styles can be referenced to create a consistent SettingsPage layout -->
<FrameworkElement.Resources>
<!-- Spacing between cards -->
<sys:Double x:Key="SettingsCardSpacing">4</sys:Double>
<!-- Style (inc. the correct spacing) of a section header -->
<Style x:Key="SettingsSectionHeaderTextBlockStyle"
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
TargetType="TextBlock">
<Style.Setters>
<Setter Property="Margin" Value="1,30,0,6" />
</Style.Setters>
</Style>
</FrameworkElement.Resources>
<Grid>
<ikw:SimpleStackPanel MaxWidth="800"
HorizontalAlignment="Stretch"
Spacing="{StaticResource SettingsCardSpacing}">
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="快速导航" />
<ui:SettingsCard
Header="基本设置"
Description="点击跳转到基本设置页面"
IsClickEnabled="True"
Click="SettingsCard_Basic_Click">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Home}"/>
</ui:SettingsCard.HeaderIcon>
</ui:SettingsCard>
<ui:SettingsCard
Header="页面 2"
Description="点击跳转到页面 2"
IsClickEnabled="True"
Click="SettingsCard_Page2_Click">
<ui:SettingsCard.HeaderIcon>
<ui:SymbolIcon Symbol="Document" />
</ui:SettingsCard.HeaderIcon>
</ui:SettingsCard>
<ui:SettingsCard
Header="设计设置"
Description="点击跳转到设计设置页面"
IsClickEnabled="True"
Click="SettingsCard_Design_Click">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Design}"/>
</ui:SettingsCard.HeaderIcon>
</ui:SettingsCard>
<ui:SettingsCard
Header="外观设置"
Description="点击跳转到外观设置页面"
IsClickEnabled="True"
Click="SettingsCard_Appearance_Click">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Color}"/>
</ui:SettingsCard.HeaderIcon>
</ui:SettingsCard>
</ikw:SimpleStackPanel>
</Grid>
</ScrollViewer>
</Page>
@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Ink_Canvas.Windows.SettingsViews.Pages
{
/// <summary>
/// HomePage.xaml 的交互逻辑
/// </summary>
public partial class HomePage : Page
{
public HomePage()
{
InitializeComponent();
}
private void SettingsCard_Basic_Click(object sender, RoutedEventArgs e)
{
// 找到SettingsWindow窗口
SettingsWindow settingsWindow = Window.GetWindow(this) as SettingsWindow;
if (settingsWindow != null)
{
// 调用NavigateToPage方法导航到基本设置页面
settingsWindow.NavigateToPage("BasicPage");
}
}
private void SettingsCard_Page2_Click(object sender, RoutedEventArgs e)
{
// 找到SettingsWindow窗口
SettingsWindow settingsWindow = Window.GetWindow(this) as SettingsWindow;
if (settingsWindow != null)
{
// 调用NavigateToPage方法导航到页面2
settingsWindow.NavigateToPage("Page2Page");
}
}
private void SettingsCard_Design_Click(object sender, RoutedEventArgs e)
{
// 找到SettingsWindow窗口
SettingsWindow settingsWindow = Window.GetWindow(this) as SettingsWindow;
if (settingsWindow != null)
{
// 调用NavigateToPage方法导航到设计设置页面
settingsWindow.NavigateToPage("DesignPage");
}
}
private void SettingsCard_Appearance_Click(object sender, RoutedEventArgs e)
{
// 找到SettingsWindow窗口
SettingsWindow settingsWindow = Window.GetWindow(this) as SettingsWindow;
if (settingsWindow != null)
{
// 调用NavigateToPage方法导航到外观设置页面
settingsWindow.NavigateToPage("AppearancePage");
}
}
}
}
@@ -0,0 +1,39 @@
<ui:Page
x:Class="Ink_Canvas.Windows.SettingsViews.Pages.IconographyPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:controls="clr-namespace:Ink_Canvas.Controls"
Title="图标设置"
mc:Ignorable="d">
<Grid x:Name="RootGrid">
<StackPanel Margin="24">
<ui:SettingsExpander x:Name="settingsCard" VerticalAlignment="Top"
Description="The SettingsExpander has the same properties as a Card, and you can set SettingsCard as part of the Items collection."
Header="SettingsExpander" IsEnabled="True">
<ui:SettingsExpander.HeaderIcon>
<ui:FontIcon Glyph="&#xE91B;"/>
</ui:SettingsExpander.HeaderIcon>
<controls:CopyButton Text="You can override the Left indention of a SettingsCard by overriding the SettingsCardLeftIndention"/>
<ui:SettingsExpander.Items>
<ui:SettingsCard Description="You can override the Left indention of a SettingsCard by overriding the SettingsCardLeftIndention"
Header="Customization">
<ui:SettingsCard.Resources>
<sys:Double x:Key="SettingsCardLeftIndention">0</sys:Double>
</ui:SettingsCard.Resources>
</ui:SettingsCard>
</ui:SettingsExpander.Items>
</ui:SettingsExpander>
</StackPanel>
</Grid>
</ui:Page>
@@ -0,0 +1,10 @@
namespace Ink_Canvas.Windows.SettingsViews.Pages
{
public partial class IconographyPage : iNKORE.UI.WPF.Modern.Controls.Page
{
public IconographyPage()
{
InitializeComponent();
}
}
}
@@ -0,0 +1,14 @@
<Page x:Class="Ink_Canvas.Windows.SettingsViews.Pages.Page2Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ink_Canvas.Windows.SettingsViews.Pages"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="Page2">
<Grid>
</Grid>
</Page>
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Ink_Canvas.Windows.SettingsViews.Pages
{
/// <summary>
/// Page2.xaml 的交互逻辑
/// </summary>
public partial class Page2Page : Page
{
public Page2Page()
{
InitializeComponent();
}
}
}
@@ -0,0 +1,17 @@
<ui:Page
x:Class="Ink_Canvas.Windows.SettingsViews.Pages.SettingsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
Title="设置"
mc:Ignorable="d">
<Grid>
<StackPanel Margin="24">
<TextBlock Text="设置" Style="{DynamicResource TitleTextBlockStyle}" Margin="0,0,0,16" />
<TextBlock Text="这里是设置页面" Style="{DynamicResource BodyTextBlockStyle}" />
</StackPanel>
</Grid>
</ui:Page>
@@ -0,0 +1,12 @@
using iNKORE.UI.WPF.Modern.Controls;
namespace Ink_Canvas.Windows.SettingsViews.Pages
{
public partial class SettingsPage : Page
{
public SettingsPage()
{
InitializeComponent();
}
}
}
@@ -0,0 +1,117 @@
<Page x:Class="Ink_Canvas.Windows.SettingsViews.Pages.StartupPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ink_Canvas.Windows.SettingsViews.Pages"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
xmlns:i18n="clr-namespace:Ink_Canvas.MarkupExtensions"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
d:DesignHeight="950" d:DesignWidth="800"
Title="启动">
<ScrollViewer Padding="59,0,59,0">
<FrameworkElement.Resources>
<!-- Spacing between cards -->
<sys:Double x:Key="SettingsCardSpacing">4</sys:Double>
<!-- Style (inc. the correct spacing) of a section header -->
<Style x:Key="SettingsSectionHeaderTextBlockStyle"
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
TargetType="TextBlock">
<Style.Setters>
<Setter Property="Margin" Value="1,30,0,6" />
</Style.Setters>
</Style>
</FrameworkElement.Resources>
<Grid>
<ikw:SimpleStackPanel MaxWidth="1000"
HorizontalAlignment="Stretch"
Spacing="{StaticResource SettingsCardSpacing}">
<!-- 窗口设置 -->
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="窗口设置" />
<!-- 窗口无焦点模式 -->
<ui:SettingsCard Description="{i18n:I18n Key=Startup_NoFocusModeHint}"
Header="{i18n:I18n Key=Startup_NoFocusMode}">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.View}" />
</ui:SettingsCard.HeaderIcon>
<ui:ToggleSwitch Name="ToggleSwitchNoFocusMode" Toggled="ToggleSwitchNoFocusMode_Toggled" />
</ui:SettingsCard>
<!-- 窗口无边框模式 -->
<ui:SettingsCard Description="{i18n:I18n Key=Startup_NoBorderModeHint}"
Header="{i18n:I18n Key=Startup_NoBorderMode}">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.FullScreen}" />
</ui:SettingsCard.HeaderIcon>
<ui:ToggleSwitch Name="ToggleSwitchWindowMode" Toggled="ToggleSwitchWindowMode_Toggled" />
</ui:SettingsCard>
<!-- 窗口置顶 -->
<ui:SettingsCard Description="{i18n:I18n Key=Startup_TopMostHint}"
Header="{i18n:I18n Key=Startup_TopMost}">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Pinned}" />
</ui:SettingsCard.HeaderIcon>
<ui:ToggleSwitch Name="ToggleSwitchAlwaysOnTop" Toggled="ToggleSwitchAlwaysOnTop_Toggled" />
</ui:SettingsCard>
<!-- UIA置顶 -->
<ui:SettingsCard Description="{i18n:I18n Key=Startup_UIAccessTopMostHint_1}"
Header="{i18n:I18n Key=Startup_UIATopMost}">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Shield}" />
</ui:SettingsCard.HeaderIcon>
<ui:ToggleSwitch Name="ToggleSwitchUIAccessTopMost" Toggled="ToggleSwitchUIAccessTopMost_Toggled" />
</ui:SettingsCard>
<!-- 启动设置 -->
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="启动设置" />
<!-- 开机时运行 -->
<ui:SettingsCard Description="{i18n:I18n Key=Startup_RunAtStartupHint}"
Header="{i18n:I18n Key=Startup_RunAtStartup}">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Settings}"/>
</ui:SettingsCard.HeaderIcon>
<ui:ToggleSwitch Name="ToggleSwitchRunAtStartup" Toggled="ToggleSwitchRunAtStartup_Toggled" />
</ui:SettingsCard>
<!-- 开机运行后收纳到侧边栏 -->
<ui:SettingsCard Description="{i18n:I18n Key=Startup_FoldAtStartupHint}"
Header="{i18n:I18n Key=Startup_FoldAtStartup}">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.ChevronLeft}" />
</ui:SettingsCard.HeaderIcon>
<ui:ToggleSwitch Name="ToggleSwitchFoldAtStartup" Toggled="ToggleSwitchFoldAtStartup_Toggled" />
</ui:SettingsCard>
<!-- 模式设置 -->
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="模式设置" />
<ui:SettingsExpander Description="{i18n:I18n Key=Settings_ModeDesc}"
Header="{i18n:I18n Key=Settings_ModeDesc_1}">
<ui:SettingsExpander.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Settings}" />
</ui:SettingsExpander.HeaderIcon>
<ui:SettingsExpander.Items>
<ui:SettingsCard Header="仅PPT模式">
<ui:ToggleSwitch Name="ToggleSwitchPPTOnlyMode" Toggled="ToggleSwitchPPTOnlyMode_Toggled" />
</ui:SettingsCard>
</ui:SettingsExpander.Items>
</ui:SettingsExpander>
<!-- 底部空白 -->
<Rectangle Height="48" />
</ikw:SimpleStackPanel>
</Grid>
</ScrollViewer>
</Page>
@@ -0,0 +1,247 @@
using System;
using System.Windows;
using System.Windows.Controls;
namespace Ink_Canvas.Windows.SettingsViews.Pages
{
/// <summary>
/// StartupPage.xaml 的交互逻辑
/// </summary>
public partial class StartupPage : Page
{
private bool _isLoaded = false;
public StartupPage()
{
InitializeComponent();
Loaded += StartupPage_Loaded;
}
private void StartupPage_Loaded(object sender, RoutedEventArgs e)
{
LoadSettings();
_isLoaded = true;
}
/// <summary>
/// 加载设置到UI
/// </summary>
private void LoadSettings()
{
if (MainWindow.Settings == null) return;
_isLoaded = false;
try
{
// 窗口无焦点模式
if (MainWindow.Settings.Advanced != null)
{
ToggleSwitchNoFocusMode.IsOn = MainWindow.Settings.Advanced.IsNoFocusMode;
}
// 窗口无边框模式
if (MainWindow.Settings.Advanced != null)
{
ToggleSwitchWindowMode.IsOn = MainWindow.Settings.Advanced.WindowMode;
}
// 窗口置顶
if (MainWindow.Settings.Advanced != null)
{
ToggleSwitchAlwaysOnTop.IsOn = MainWindow.Settings.Advanced.IsAlwaysOnTop;
}
// UIA置顶
if (MainWindow.Settings.Advanced != null)
{
ToggleSwitchUIAccessTopMost.IsOn = MainWindow.Settings.Advanced.EnableUIAccessTopMost;
}
// 开机时运行
bool runAtStartup = System.IO.File.Exists(
System.Environment.GetFolderPath(System.Environment.SpecialFolder.Startup) + "\\Ink Canvas Annotation.lnk");
ToggleSwitchRunAtStartup.IsOn = runAtStartup;
// 启动时折叠
if (MainWindow.Settings.Startup != null)
{
ToggleSwitchFoldAtStartup.IsOn = MainWindow.Settings.Startup.IsFoldAtStartup;
}
// 仅PPT模式
if (MainWindow.Settings.ModeSettings != null)
{
ToggleSwitchPPTOnlyMode.IsOn = MainWindow.Settings.ModeSettings.IsPPTOnlyMode;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"加载启动设置时出错: {ex.Message}");
}
_isLoaded = true;
}
#region
/// <summary>
/// 窗口无焦点模式开关事件
/// </summary>
private void ToggleSwitchNoFocusMode_Toggled(object sender, RoutedEventArgs e)
{
if (!_isLoaded) return;
try
{
bool newState = ToggleSwitchNoFocusMode.IsOn;
// 使用Helper类更新设置并应用
Windows.SettingsViews.MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchNoFocusMode", newState);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"设置窗口无焦点模式时出错: {ex.Message}");
}
}
/// <summary>
/// 窗口无边框模式开关事件
/// </summary>
private void ToggleSwitchWindowMode_Toggled(object sender, RoutedEventArgs e)
{
if (!_isLoaded) return;
try
{
bool newState = ToggleSwitchWindowMode.IsOn;
// 使用Helper类更新设置并应用
Windows.SettingsViews.MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchWindowMode", newState);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"设置窗口无边框模式时出错: {ex.Message}");
}
}
/// <summary>
/// 窗口置顶开关事件
/// </summary>
private void ToggleSwitchAlwaysOnTop_Toggled(object sender, RoutedEventArgs e)
{
if (!_isLoaded) return;
try
{
bool newState = ToggleSwitchAlwaysOnTop.IsOn;
// 使用Helper类更新设置并应用
Windows.SettingsViews.MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchAlwaysOnTop", newState);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"设置窗口置顶时出错: {ex.Message}");
}
}
/// <summary>
/// UIA置顶开关事件
/// </summary>
private void ToggleSwitchUIAccessTopMost_Toggled(object sender, RoutedEventArgs e)
{
if (!_isLoaded) return;
try
{
bool newState = ToggleSwitchUIAccessTopMost.IsOn;
// 更新Settings对象
if (MainWindow.Settings.Advanced != null)
{
MainWindow.Settings.Advanced.EnableUIAccessTopMost = newState;
}
// 保存设置
MainWindow.SaveSettingsToFile();
// 通知其他面板同步状态
Windows.SettingsViews.MainWindowSettingsHelper.NotifySettingsPanelsSyncState("ToggleSwitchUIAccessTopMost");
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"设置UIA置顶时出错: {ex.Message}");
}
}
#endregion
#region
/// <summary>
/// 开机时运行开关事件
/// </summary>
private void ToggleSwitchRunAtStartup_Toggled(object sender, RoutedEventArgs e)
{
if (!_isLoaded) return;
try
{
bool newState = ToggleSwitchRunAtStartup.IsOn;
// 使用Helper类更新设置并应用
Windows.SettingsViews.MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchRunAtStartup", newState);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"设置开机启动时出错: {ex.Message}");
}
}
/// <summary>
/// 开机运行后收纳到侧边栏开关事件
/// </summary>
private void ToggleSwitchFoldAtStartup_Toggled(object sender, RoutedEventArgs e)
{
if (!_isLoaded) return;
try
{
bool newState = ToggleSwitchFoldAtStartup.IsOn;
// 使用Helper类更新设置并应用
Windows.SettingsViews.MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchFoldAtStartup", newState);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"设置开机折叠时出错: {ex.Message}");
}
}
#endregion
#region
/// <summary>
/// 仅PPT模式开关事件
/// </summary>
private void ToggleSwitchPPTOnlyMode_Toggled(object sender, RoutedEventArgs e)
{
if (!_isLoaded) return;
try
{
bool newState = ToggleSwitchPPTOnlyMode.IsOn;
// 使用Helper类更新设置并应用
Windows.SettingsViews.MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchMode", newState);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"设置仅PPT模式时出错: {ex.Message}");
}
}
#endregion
}
}
@@ -0,0 +1,17 @@
<ui:Page
x:Class="Ink_Canvas.Windows.SettingsViews.Pages.ThemePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
Title="主题设置"
mc:Ignorable="d">
<Grid>
<StackPanel Margin="24">
<TextBlock Text="主题设置" Style="{DynamicResource TitleTextBlockStyle}" Margin="0,0,0,16" />
<TextBlock Text="这里是主题设置页面" Style="{DynamicResource BodyTextBlockStyle}" />
</StackPanel>
</Grid>
</ui:Page>
@@ -0,0 +1,12 @@
using iNKORE.UI.WPF.Modern.Controls;
namespace Ink_Canvas.Windows.SettingsViews.Pages
{
public partial class ThemePage : Page
{
public ThemePage()
{
InitializeComponent();
}
}
}
@@ -0,0 +1,17 @@
<ui:Page
x:Class="Ink_Canvas.Windows.SettingsViews.Pages.TypographyPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
Title="排版设置"
mc:Ignorable="d">
<Grid>
<StackPanel Margin="24">
<TextBlock Text="排版设置" Style="{DynamicResource TitleTextBlockStyle}" Margin="0,0,0,16" />
<TextBlock Text="这里是排版设置页面" Style="{DynamicResource BodyTextBlockStyle}" />
</StackPanel>
</Grid>
</ui:Page>
@@ -0,0 +1,12 @@
using iNKORE.UI.WPF.Modern.Controls;
namespace Ink_Canvas.Windows.SettingsViews.Pages
{
public partial class TypographyPage : Page
{
public TypographyPage()
{
InitializeComponent();
}
}
}
@@ -1,191 +0,0 @@
<UserControl x:Class="Ink_Canvas.Windows.SettingsViews.AboutPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ink_Canvas.Windows.SettingsViews"
mc:Ignorable="d"
d:DesignHeight="950" d:DesignWidth="640">
<UserControl.Resources>
<Style x:Key="ScrollBarThumb" TargetType="{x:Type Thumb}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="IsTabStop" Value="false"/>
<EventSetter Event="PreviewMouseDown" Handler="ScrollbarThumb_MouseDown"/>
<EventSetter Event="PreviewMouseUp" Handler="ScrollbarThumb_MouseUp"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border Name="ScrollbarThumbEx"
SnapsToDevicePixels="True"
Background="#c3c3c3"
Opacity="0.5"
CornerRadius="1.5"
Height="{TemplateBinding Height}"
Width="3" HorizontalAlignment="Center"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ScrollBar}">
<EventSetter Event="Scroll" Handler="ScrollBar_Scroll"/>
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="false"/>
<Setter Property="Width" Value="8"/>
<Setter Property="Margin" Value="-6 3 0 0" />
<Setter Property="MinWidth" Value="{Binding Height, RelativeSource={RelativeSource Self}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollBar}">
<Grid x:Name="ScrollbarGrid" SnapsToDevicePixels="true">
<Border Width="3" CornerRadius="1.5" Background="#e0e0e0" Opacity="0" IsHitTestVisible="False" Margin="0 4 -2 4" x:Name="ScrollBarBorderTrackBackground"/>
<Border Padding="0 4" Background="Transparent" MouseEnter="ScrollBarTrack_MouseEnter"
MouseLeave="ScrollBarTrack_MouseLeave">
<Track x:Name="PART_Track"
IsDirectionReversed="true"
IsEnabled="True"
Width="6"
Margin="0,0,0,0"
HorizontalAlignment="Right">
<Track.DecreaseRepeatButton>
<RepeatButton Opacity="0" Command="{x:Static ScrollBar.PageUpCommand}" />
</Track.DecreaseRepeatButton>
<Track.IncreaseRepeatButton>
<RepeatButton Opacity="0" Command="{x:Static ScrollBar.PageDownCommand}" />
</Track.IncreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource ScrollBarThumb }" />
</Track.Thumb>
</Track>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid>
<ScrollViewer ScrollChanged="ScrollViewerEx_ScrollChanged" IsManipulationEnabled="True" Name="AboutScrollViewerEx" IsDeferredScrollingEnabled="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled" IsTabStop="False" TabIndex="-1" Margin="0,0,2,2" PanningMode="VerticalOnly">
<StackPanel Margin="0,12,0,24" HorizontalAlignment="Center" Width="524">
<Border BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<Image Name="CopyrightBannerImage"/>
</Border>
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical">
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#9a9996" FontSize="12" Margin="0,0,0,3" Text="设备ID" HorizontalAlignment="Left"/>
<TextBlock Name="AboutDeviceID" Foreground="#2e3436" FontSize="14.5" Text="正在获取..." HorizontalAlignment="Left"/>
</StackPanel>
</Grid>
</StackPanel>
</Border>
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical">
<Grid Height="54">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="软件版本" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="18,0,0,0"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,18,0">
<TextBlock Name="AboutAppVersion" Foreground="#878787" FontSize="14.5" Text="InkCanvasForClass v1.7.18.4" VerticalAlignment="Center" />
<Image Name="UpdateAvailableIcon" Margin="12,0,0,0" Width="18" Height="18" VerticalAlignment="Center" Visibility="Collapsed">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V16 H16 V0 H0 Z">
<DrawingGroup.Transform>
<TranslateTransform X="6.1023980379104614E-05" Y="0" />
</DrawingGroup.Transform>
<GeometryDrawing Brush="#FFFF7800" Geometry="F1 M16,16z M0,0z M8.00493,0C7.74518,0,7.49541,0.0999063,7.2956,0.289728L5.5872,1.99813 2.99963,1.99813C2.45014,1.99813,2.00056,2.44771,2.00056,2.99719L2.00056,5.58476 0.292165,7.29316C-0.0974697,7.6828,-0.0974697,8.31221,0.292165,8.70184L2.00056,10.4102 2.00056,12.9978C2.00056,13.5473,2.45014,13.9969,2.99963,13.9969L5.5872,13.9969 7.2956,15.7053C7.68523,16.0949,8.31464,16.0949,8.70428,15.7053L10.4127,13.9969 13.0003,13.9969C13.5497,13.9969,13.9993,13.5473,13.9993,12.9978L13.9993,10.4102 15.7077,8.70184C16.0973,8.31221,16.0973,7.6828,15.7077,7.29316L13.9993,5.58476 13.9993,2.99719C13.9993,2.44771,13.5497,1.99813,13.0003,1.99813L10.4127,1.99813 8.70428,0.289728C8.50447,0.0899157,8.2547,0,7.99494,0L8.00493,0z M8.00493,4.99532C8.26469,4.99532,8.51446,5.09522,8.71427,5.28505L10.7124,7.28317C10.9022,7.47299,11.0021,7.72276,11.0021,7.99251L11.0021,8.99157 9.004,8.99157 9.004,10.9897 7.00587,10.9897 7.00587,8.99157 5.00774,8.99157 5.00774,7.99251C5.00774,7.73275,5.10765,7.47299,5.29747,7.28317L7.2956,5.28505C7.49541,5.08523,7.74518,4.99532,8.00493,4.99532z" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</StackPanel>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="系统版本" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="18,0,0,0"/>
<TextBlock Name="AboutSystemVersion" Foreground="#878787" FontSize="14.5" Text="Windows 10 专业版 19045.3758" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,18,0"/>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="触摸设备" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="18,0,0,0"/>
<TextBlock Name="AboutTouchTabletText" Foreground="#878787" FontSize="14.5" Text="无触摸支持" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,18,0"/>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="构建时间" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="18,0,0,0"/>
<TextBlock Name="AboutBuildTime" Foreground="#878787" FontSize="14.5" Text="2024.8.22" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,18,0"/>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="版权信息" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="18,0,0,0"/>
<TextBlock Name="AboutCopyright" Foreground="#878787" FontSize="14.5" Text="正在获取..." VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,18,0"/>
</Grid>
</StackPanel>
</Border>
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical">
<Grid Height="54">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="ICC 官方网站" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="18,0,0,0"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,18,0">
<TextBlock Name="LinkOfficialWebsite" Foreground="#1d4ed8" TextDecorations="Underline" FontSize="14.5" Text="inkcanvasforclass.github.io" VerticalAlignment="Center" HorizontalAlignment="Right" Cursor="Hand" MouseLeftButtonDown="LinkOfficialWebsite_MouseLeftButtonDown"/>
<Image Margin="12,0,0,0" Width="16" Height="16" VerticalAlignment="Center">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V16 H16 V0 H0 Z">
<GeometryDrawing Brush="#FF222222" Geometry="F1 M16,16z M0,0z M3,2C1.338,2,0,3.338,0,5L0,13C0,14.662,1.338,16,3,16L11,16C12.662,16,14,14.662,14,13L14,9C13.9998,8.4477 13.5522,8 13,8 12.4478,8 12.0002,8.4477 12,9L12,13C12,13.554,11.554,14,11,14L3,14C2.446,14,2,13.554,2,13L2,5C2,4.446,2.446,4,3,4L7,4C7.5523,4 8,3.5523 8,3 8,2.4477 7.5523,2 7,2L3,2z M10,0C9.4477,0 9,0.4477 9,1 9,1.5523 9.4477,2 10,2L12.584,2 7.29303,7.29102C6.90133,7.68172 6.90133,8.31627 7.29303,8.70697 7.68353,9.09737 8.31659,9.09737 8.70709,8.70697L14,3.41412 14,6C14,6.5523 14.4477,7 15,7 15.5523,7 16,6.5523 16,6L16,1C15.9996,0.913 15.988,0.826187 15.965,0.742188 15.942,0.657187 15.909,0.576 15.865,0.5 15.822,0.424 15.768,0.35483 15.7068,0.29303 15.6918,0.28103 15.6758,0.268996 15.6598,0.257996 15.6048,0.207996 15.5433,0.164989 15.4782,0.129089 15.4412,0.110089 15.4022,0.0940781 15.363,0.0800781 15.312,0.0590781 15.2601,0.0431279 15.2067,0.0311279 15.1667,0.0211279 15.1267,0.0171226 15.0856,0.0131226 15.0566,0.00312256 15.0286,0.00309863 14.9996,0.00109863L10,0z" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</StackPanel>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="Github 仓库" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="18,0,0,0"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,18,0">
<TextBlock Name="LinkGithubRepo" Foreground="#1d4ed8" TextDecorations="Underline" FontSize="14.5" Text="github.com/InkCanvasForClass/community" VerticalAlignment="Center" HorizontalAlignment="Right" Cursor="Hand" MouseLeftButtonDown="LinkGithubRepo_MouseLeftButtonDown"/>
<Image Margin="12,0,0,0" Width="16" Height="16" VerticalAlignment="Center">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V16 H16 V0 H0 Z">
<GeometryDrawing Brush="#FF222222" Geometry="F1 M16,16z M0,0z M3,2C1.338,2,0,3.338,0,5L0,13C0,14.662,1.338,16,3,16L11,16C12.662,16,14,14.662,14,13L14,9C13.9998,8.4477 13.5522,8 13,8 12.4478,8 12.0002,8.4477 12,9L12,13C12,13.554,11.554,14,11,14L3,14C2.446,14,2,13.554,2,13L2,5C2,4.446,2.446,4,3,4L7,4C7.5523,4 8,3.5523 8,3 8,2.4477 7.5523,2 7,2L3,2z M10,0C9.4477,0 9,0.4477 9,1 9,1.5523 9.4477,2 10,2L12.584,2 7.29303,7.29102C6.90133,7.68172 6.90133,8.31627 7.29303,8.70697 7.68353,9.09737 8.31659,9.09737 8.70709,8.70697L14,3.41412 14,6C14,6.5523 14.4477,7 15,7 15.5523,7 16,6.5523 16,6L16,1C15.9996,0.913 15.988,0.826187 15.965,0.742188 15.942,0.657187 15.909,0.576 15.865,0.5 15.822,0.424 15.768,0.35483 15.7068,0.29303 15.6918,0.28103 15.6758,0.268996 15.6598,0.257996 15.6048,0.207996 15.5433,0.164989 15.4782,0.129089 15.4412,0.110089 15.4022,0.0940781 15.363,0.0800781 15.312,0.0590781 15.2601,0.0431279 15.2067,0.0311279 15.1667,0.0211279 15.1267,0.0171226 15.0856,0.0131226 15.0566,0.00312256 15.0286,0.00309863 14.9996,0.00109863L10,0z" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</StackPanel>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="贡献者名单" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="18,0,0,0"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,18,0">
<TextBlock Name="LinkContributors" Foreground="#1d4ed8" TextDecorations="Underline" FontSize="14.5" Text="github.com/InkCanvasForClass/community#贡献者" VerticalAlignment="Center" HorizontalAlignment="Right" Cursor="Hand" MouseLeftButtonDown="LinkContributors_MouseLeftButtonDown"/>
<Image Margin="12,0,0,0" Width="16" Height="16" VerticalAlignment="Center">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V16 H16 V0 H0 Z">
<GeometryDrawing Brush="#FF222222" Geometry="F1 M16,16z M0,0z M3,2C1.338,2,0,3.338,0,5L0,13C0,14.662,1.338,16,3,16L11,16C12.662,16,14,14.662,14,13L14,9C13.9998,8.4477 13.5522,8 13,8 12.4478,8 12.0002,8.4477 12,9L12,13C12,13.554,11.554,14,11,14L3,14C2.446,14,2,13.554,2,13L2,5C2,4.446,2.446,4,3,4L7,4C7.5523,4 8,3.5523 8,3 8,2.4477 7.5523,2 7,2L3,2z M10,0C9.4477,0 9,0.4477 9,1 9,1.5523 9.4477,2 10,2L12.584,2 7.29303,7.29102C6.90133,7.68172 6.90133,8.31627 7.29303,8.70697 7.68353,9.09737 8.31659,9.09737 8.70709,8.70697L14,3.41412 14,6C14,6.5523 14.4477,7 15,7 15.5523,7 16,6.5523 16,6L16,1C15.9996,0.913 15.988,0.826187 15.965,0.742188 15.942,0.657187 15.909,0.576 15.865,0.5 15.822,0.424 15.768,0.35483 15.7068,0.29303 15.6918,0.28103 15.6758,0.268996 15.6598,0.257996 15.6048,0.207996 15.5433,0.164989 15.4782,0.129089 15.4412,0.110089 15.4022,0.0940781 15.363,0.0800781 15.312,0.0590781 15.2601,0.0431279 15.2067,0.0311279 15.1667,0.0211279 15.1267,0.0171226 15.0856,0.0131226 15.0566,0.00312256 15.0286,0.00309863 14.9996,0.00109863L10,0z" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</StackPanel>
</Grid>
</StackPanel>
</Border>
<StackPanel Orientation="Vertical" Margin="0,16,0,0">
<TextBlock Name="AboutBottomCopyright" Foreground="#2e3436" FontWeight="Bold" FontSize="12" Text="正在获取..." VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,15,0"/>
<TextBlock Foreground="#878787" Width="356" FlowDirection="RightToLeft" TextWrapping="Wrap" FontSize="12" Text="ICC 的部分元素设计参考来源于:© iNKORE! 名下产品 Inkways,已经过授权使用,最终解释权归 Yoojun Zhou 所有" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,6,15,0"/>
<TextBlock Foreground="#878787" FontWeight="Bold" Width="276" FlowDirection="RightToLeft" TextWrapping="Wrap" FontSize="12" Text="ICC 使用了 iNKORE! 开发的 UI 组件库:iNKORE.UI.WPF.Modern" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,6,15,0"/>
</StackPanel>
</StackPanel>
</ScrollViewer>
</Grid>
</UserControl>
@@ -1,478 +0,0 @@
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Helpers;
using OSVersionExtension;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Management;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Ink_Canvas.Windows.SettingsViews
{
/// <summary>
/// AboutPanel.xaml 的交互逻辑
/// </summary>
public partial class AboutPanel : UserControl
{
public AboutPanel()
{
InitializeComponent();
// 关于页面图片横幅
if (File.Exists(App.RootPath + "icc-about-illustrations.png"))
{
try
{
CopyrightBannerImage.Visibility = Visibility.Visible;
CopyrightBannerImage.Source =
new BitmapImage(new Uri($"file://{App.RootPath + "icc-about-illustrations.png"}"));
}
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
}
else
{
CopyrightBannerImage.Visibility = Visibility.Collapsed;
}
try
{
var version = Assembly.GetExecutingAssembly().GetName().Version;
AboutAppVersion.Text = $"InkCanvasForClass v{version}";
}
catch (Exception ex)
{
AboutAppVersion.Text = "InkCanvasForClass v未知";
System.Diagnostics.Debug.WriteLine($"获取软件版本失败: {ex.Message}");
}
try
{
string deviceId = DeviceIdentifier.GetDeviceId();
AboutDeviceID.Text = deviceId;
}
catch (Exception ex)
{
AboutDeviceID.Text = "获取失败";
System.Diagnostics.Debug.WriteLine($"获取设备ID失败: {ex.Message}");
}
try
{
var copyright = Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyCopyrightAttribute>();
if (copyright != null && !string.IsNullOrEmpty(copyright.Copyright))
{
var copyrightText = copyright.Copyright;
AboutCopyright.Text = copyrightText;
AboutBottomCopyright.Text = copyrightText.Replace("Copyright ©", "© Copyright") + " 所有";
}
else
{
AboutCopyright.Text = "© Copyright 2025-2026 CJK_mkp 所有";
AboutBottomCopyright.Text = "© Copyright 2025-2026 CJK_mkp 所有";
}
}
catch (Exception ex)
{
AboutCopyright.Text = "© Copyright 2025-2026 CJK_mkp 所有";
AboutBottomCopyright.Text = "© Copyright 2025-2026 CJK_mkp 所有";
System.Diagnostics.Debug.WriteLine($"获取版权信息失败: {ex.Message}");
}
try
{
var assembly = Assembly.GetExecutingAssembly();
var filePath = assembly.Location;
if (File.Exists(filePath))
{
var bt = File.GetCreationTime(filePath);
var m = bt.Month.ToString().PadLeft(2, '0');
var d = bt.Day.ToString().PadLeft(2, '0');
var h = bt.Hour.ToString().PadLeft(2, '0');
var min = bt.Minute.ToString().PadLeft(2, '0');
var s = bt.Second.ToString().PadLeft(2, '0');
AboutBuildTime.Text = $"{bt.Year}-{m}-{d} {h}:{min}:{s}";
}
else
{
AboutBuildTime.Text = "获取失败";
}
}
catch (Exception ex)
{
AboutBuildTime.Text = "获取失败";
System.Diagnostics.Debug.WriteLine($"获取构建时间失败: {ex.Message}");
}
AboutSystemVersion.Text = $"{OSVersion.GetOperatingSystem()} {OSVersion.GetOSVersion().Version}";
var _t_touch = new Thread(() =>
{
try
{
var support = TouchTabletDetectHelper.IsTouchEnabled();
var touchcount = TouchTabletDetectHelper.GetTouchTabletDevices().Count;
Dispatcher.BeginInvoke(() =>
{
if (support)
{
if (touchcount > 0)
{
AboutTouchTabletText.Text = $"{touchcount}个设备,支持触摸设备";
}
else
{
AboutTouchTabletText.Text = "支持触摸设备";
}
}
else
{
AboutTouchTabletText.Text = "无触摸支持";
}
});
}
catch (Exception ex)
{
Dispatcher.BeginInvoke(() =>
AboutTouchTabletText.Text = "检测失败");
System.Diagnostics.Debug.WriteLine($"检测触摸设备失败: {ex.Message}");
}
});
_t_touch.Start();
CheckUpdateStatus();
}
private void CheckUpdateStatus()
{
try
{
var mainWindow = Application.Current.MainWindow as MainWindow;
if (mainWindow != null)
{
var field = typeof(MainWindow).GetField("AvailableLatestVersion", BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null)
{
var availableVersion = field.GetValue(mainWindow) as string;
if (!string.IsNullOrEmpty(availableVersion))
{
UpdateAvailableIcon.Visibility = Visibility.Visible;
}
}
}
}
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
}
public static class TouchTabletDetectHelper
{
[DllImport("user32.dll")]
public static extern int GetSystemMetrics(int nIndex);
public static bool IsTouchEnabled()
{
const int MAXTOUCHES_INDEX = 95;
int maxTouches = GetSystemMetrics(MAXTOUCHES_INDEX);
return maxTouches > 0;
}
public class USBDeviceInfo
{
public USBDeviceInfo(string deviceID, string pnpDeviceID, string description)
{
DeviceID = deviceID;
PnpDeviceID = pnpDeviceID;
Description = description;
}
public string DeviceID { get; private set; }
public string PnpDeviceID { get; private set; }
public string Description { get; private set; }
}
public static List<USBDeviceInfo> GetTouchTabletDevices()
{
List<USBDeviceInfo> devices = new List<USBDeviceInfo>();
try
{
ManagementObjectCollection collection;
using (var searcher = new ManagementObjectSearcher(@"Select * From Win32_PnPEntity"))
collection = searcher.Get();
foreach (var device in collection)
{
try
{
var name = device.GetPropertyValue("Name")?.ToString() ?? "";
var description = device.GetPropertyValue("Description")?.ToString() ?? "";
if (string.IsNullOrEmpty(name)) continue;
var nameLower = name.ToLower();
var descLower = description.ToLower();
if (nameLower.Contains("pentablet") ||
nameLower.Contains("tablet") ||
nameLower.Contains("touch") ||
nameLower.Contains("digitizer") ||
descLower.Contains("touch") ||
descLower.Contains("digitizer"))
{
devices.Add(new USBDeviceInfo(
device.GetPropertyValue("DeviceID")?.ToString() ?? "",
device.GetPropertyValue("PNPDeviceID")?.ToString() ?? "",
description
));
}
}
catch
{
continue;
}
}
collection.Dispose();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"获取触摸设备列表失败: {ex.Message}");
}
return devices;
}
}
public static class FileBuildTimeHelper
{
public struct _IMAGE_FILE_HEADER
{
public ushort Machine;
public ushort NumberOfSections;
public uint TimeDateStamp;
public uint PointerToSymbolTable;
public uint NumberOfSymbols;
public ushort SizeOfOptionalHeader;
public ushort Characteristics;
};
public static DateTimeOffset? GetBuildDateTime(Assembly assembly)
{
try
{
var path = assembly.Location;
if (string.IsNullOrEmpty(path) || !File.Exists(path))
{
return null;
}
using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
var peHeader = new byte[4];
fileStream.Position = 0x3C;
fileStream.Read(peHeader, 0, 4);
var peHeaderOffset = BitConverter.ToUInt32(peHeader, 0);
fileStream.Position = peHeaderOffset;
var signature = new byte[4];
fileStream.Read(signature, 0, 4);
if (signature[0] != 0x50 || signature[1] != 0x45 || signature[2] != 0x00 || signature[3] != 0x00)
{
return null;
}
var fileHeader = new byte[Marshal.SizeOf(typeof(_IMAGE_FILE_HEADER))];
fileStream.Read(fileHeader, 0, fileHeader.Length);
var pinnedBuffer = GCHandle.Alloc(fileHeader, GCHandleType.Pinned);
try
{
var coffHeader = (_IMAGE_FILE_HEADER)Marshal.PtrToStructure(pinnedBuffer.AddrOfPinnedObject(), typeof(_IMAGE_FILE_HEADER));
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var buildTime = epoch.AddSeconds(coffHeader.TimeDateStamp);
return new DateTimeOffset(buildTime.ToLocalTime());
}
finally
{
pinnedBuffer.Free();
}
}
}
catch
{
return null;
}
}
}
public event EventHandler<RoutedEventArgs> IsTopBarNeedShadowEffect;
public event EventHandler<RoutedEventArgs> IsTopBarNeedNoShadowEffect;
private void ScrollViewerEx_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
var scrollViewer = (ScrollViewer)sender;
if (scrollViewer.VerticalOffset >= 10)
{
IsTopBarNeedShadowEffect?.Invoke(this, new RoutedEventArgs());
}
else
{
IsTopBarNeedNoShadowEffect?.Invoke(this, new RoutedEventArgs());
}
}
private void ScrollBar_Scroll(object sender, RoutedEventArgs e)
{
var scrollbar = (ScrollBar)sender;
var scrollviewer = scrollbar.FindAscendant<ScrollViewer>();
if (scrollviewer != null) scrollviewer.ScrollToVerticalOffset(scrollbar.Track.Value);
}
private void ScrollBarTrack_MouseEnter(object sender, MouseEventArgs e)
{
var border = (Border)sender;
if (border.Child is Track track)
{
track.Width = 16;
track.Margin = new Thickness(0, 0, -2, 0);
var scrollbar = track.FindAscendant<ScrollBar>();
if (scrollbar != null) scrollbar.Width = 16;
var grid = track.FindAscendant<Grid>();
if (grid.FindDescendantByName("ScrollBarBorderTrackBackground") is Border backgroundBorder)
{
backgroundBorder.Width = 8;
backgroundBorder.CornerRadius = new CornerRadius(4);
backgroundBorder.Opacity = 1;
}
var thumb = track.Thumb.Template.FindName("ScrollbarThumbEx", track.Thumb);
if (thumb != null)
{
var _thumb = thumb as Border;
_thumb.CornerRadius = new CornerRadius(4);
_thumb.Width = 8;
_thumb.Margin = new Thickness(-0.75, 0, 1, 0);
_thumb.Background = new SolidColorBrush(Color.FromRgb(138, 138, 138));
}
}
}
private void ScrollBarTrack_MouseLeave(object sender, MouseEventArgs e)
{
var border = (Border)sender;
border.Background = new SolidColorBrush(Colors.Transparent);
border.CornerRadius = new CornerRadius(0);
if (border.Child is Track track)
{
track.Width = 6;
track.Margin = new Thickness(0, 0, 0, 0);
var scrollbar = track.FindAscendant<ScrollBar>();
if (scrollbar != null) scrollbar.Width = 6;
var grid = track.FindAscendant<Grid>();
if (grid.FindDescendantByName("ScrollBarBorderTrackBackground") is Border backgroundBorder)
{
backgroundBorder.Width = 3;
backgroundBorder.CornerRadius = new CornerRadius(1.5);
backgroundBorder.Opacity = 0;
}
var thumb = track.Thumb.Template.FindName("ScrollbarThumbEx", track.Thumb);
if (thumb != null)
{
var _thumb = thumb as Border;
_thumb.CornerRadius = new CornerRadius(1.5);
_thumb.Width = 3;
_thumb.Margin = new Thickness(0);
_thumb.Background = new SolidColorBrush(Color.FromRgb(195, 195, 195));
}
}
}
private void ScrollbarThumb_MouseDown(object sender, MouseButtonEventArgs e)
{
var thumb = (Thumb)sender;
var border = thumb.Template.FindName("ScrollbarThumbEx", thumb);
((Border)border).Background = new SolidColorBrush(Color.FromRgb(95, 95, 95));
}
private void ScrollbarThumb_MouseUp(object sender, MouseButtonEventArgs e)
{
var thumb = (Thumb)sender;
var border = thumb.Template.FindName("ScrollbarThumbEx", thumb);
((Border)border).Background = new SolidColorBrush(Color.FromRgb(138, 138, 138));
}
private void LinkOfficialWebsite_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
try
{
Process.Start(new ProcessStartInfo
{
FileName = "https://inkcanvasforclass.github.io",
UseShellExecute = true
});
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"打开官方网站失败: {ex.Message}");
}
}
private void LinkGithubRepo_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
try
{
Process.Start(new ProcessStartInfo
{
FileName = "https://github.com/InkCanvasForClass/community",
UseShellExecute = true
});
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"打开GitHub仓库失败: {ex.Message}");
}
}
private void LinkContributors_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
try
{
Process.Start(new ProcessStartInfo
{
FileName = "https://github.com/InkCanvasForClass/community#贡献者",
UseShellExecute = true
});
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"打开贡献者名单失败: {ex.Message}");
}
}
/// <summary>
/// 应用主题
/// </summary>
public void ApplyTheme()
{
try
{
ThemeHelper.ApplyThemeToControl(this);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"AboutPanel 应用主题时出错: {ex.Message}");
}
}
}
}
@@ -1,669 +0,0 @@
<UserControl x:Class="Ink_Canvas.Windows.SettingsViews.AdvancedPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ink_Canvas.Windows.SettingsViews"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
mc:Ignorable="d"
d:DesignHeight="950" d:DesignWidth="640">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ComboBoxStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!-- 开关样式模板 -->
<Style x:Key="ToggleSwitchStyle" TargetType="Border">
<Setter Property="Width" Value="48"/>
<Setter Property="Height" Value="25"/>
<Setter Property="CornerRadius" Value="12"/>
<Setter Property="Padding" Value="3,0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="Margin" Value="0,0,15,0"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<ScrollViewer ScrollChanged="ScrollViewerEx_ScrollChanged" IsManipulationEnabled="True" Name="ScrollViewerEx" IsDeferredScrollingEnabled="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled" IsTabStop="False" TabIndex="-1" Margin="0,0,2,2" PanningMode="VerticalOnly">
<StackPanel Margin="60,12,60,24">
<!-- 特殊屏幕和触摸设置 -->
<Border BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical" Margin="18,18,18,18">
<TextBlock Text="可在手指触摸画板时显示圆形橡皮或手掌触摸画板时显示的橡皮比手掌大很多时调整"
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11" Margin="0,0,0,12"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="特殊屏幕模式" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="启用特殊屏幕模式以优化触摸体验" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchIsSpecialScreen" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="IsSpecialScreen" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="触摸倍数" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="调整触摸大小的倍数" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0">
<Slider x:Name="TouchMultiplierSlider" Width="150" Minimum="0" Maximum="2" Value="0.3" TickFrequency="0.01" IsSnapToTickEnabled="True" ValueChanged="TouchMultiplierSlider_ValueChanged"/>
<TextBlock x:Name="TouchMultiplierText" Text="0.30" VerticalAlignment="Center" FontSize="14" Margin="12,0,0,0" Foreground="#2e3436"/>
</StackPanel>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="橡皮擦绑定触摸大小倍数" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="橡皮擦大小与触摸大小倍数绑定" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchEraserBindTouchMultiplier" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="EraserBindTouchMultiplier" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="笔尖模式 BoundsWidth" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="笔尖模式的接触面积区分界限" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0">
<Slider x:Name="NibModeBoundsWidthSlider" Width="150" Minimum="1" Maximum="50" Value="5" TickFrequency="1" IsSnapToTickEnabled="True"/>
<TextBlock x:Name="NibModeBoundsWidthText" Text="5" VerticalAlignment="Center" FontSize="14" Margin="12,0,0,0" Foreground="#2e3436"/>
</StackPanel>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="手指模式 BoundsWidth" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="手指模式的接触面积区分界限" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0">
<Slider x:Name="FingerModeBoundsWidthSlider" Width="150" Minimum="1" Maximum="50" Value="20" TickFrequency="1" IsSnapToTickEnabled="True"/>
<TextBlock x:Name="FingerModeBoundsWidthText" Text="20" VerticalAlignment="Center" FontSize="14" Margin="12,0,0,0" Foreground="#2e3436"/>
</StackPanel>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="四边红外模式" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="启用四边红外触摸屏模式" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchIsQuadIR" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
</StackPanel>
</Border>
<!-- 日志和系统设置 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical" Margin="18,18,18,18">
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="记录日志" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="启用日志记录功能" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchIsLogEnabled" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid MinHeight="54">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical" Grid.Column="0" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,8,0,8">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="日志以日期保存" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" TextWrapping="Wrap" Text="日志文件超过 512 KB 时会自动删除。开启日期保存后,日志将保存在Logs文件夹中,当文件夹大小超过5MB时自动清空" HorizontalAlignment="Left" MaxWidth="450"/>
</StackPanel>
<Border x:Name="ToggleSwitchIsSaveLogByDate" Grid.Column="1" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" VerticalAlignment="Top" Margin="0,8,15,8">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="关闭软件时二次弹窗确认" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="关闭软件时显示确认对话框" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchIsSecondConfimeWhenShutdownApp" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
</StackPanel>
</Border>
<!-- 实验性功能 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical" Margin="18,18,18,18">
<TextBlock Text="实验性选项" FontWeight="Bold" Foreground="#2e3436" FontSize="18" Margin="0,0,0,12"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Border Height="20" CornerRadius="3" Background="#7f1d1d" Padding="4,0" Margin="0,0,12,0">
<StackPanel Orientation="Horizontal">
<Image Width="15" Height="15" VerticalAlignment="Center">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
<GeometryDrawing Brush="#fca5a5"
Geometry="F1 M24,24z M0,0z M20.1758,20.0951L15.1058,9.97512C15.0358,9.83512,14.9958,9.68512,14.9958,9.52512L14.9958,2.99512 15.4958,2.99512C16.0458,2.99512 16.4958,2.54512 16.4958,1.99512 16.4958,1.44512 16.0458,0.995117 15.4958,0.995117L8.49582,0.995117C7.94582,0.995117 7.49582,1.44512 7.49582,1.99512 7.49582,2.54512 7.94582,2.99512 8.49582,2.99512L8.99582,2.99512 8.99582,9.52512C8.99582,9.67512,8.95582,9.83512,8.89582,9.97512L3.82582,20.0951C3.67582,20.4051 3.59582,20.7451 3.61582,21.0851 3.62582,21.4251 3.73582,21.7651 3.91582,22.0551 4.09582,22.3451 4.34582,22.5851 4.64582,22.7551 4.94582,22.9151 5.27582,23.0051 5.60582,23.0051L18.3758,23.0051C18.7558,23.0051 19.0558,22.9251 19.3558,22.7551 19.6558,22.5851 19.9058,22.3451 20.0858,22.0551 20.2658,21.7651 20.3658,21.4351 20.3858,21.0851 20.3958,20.7451 20.3258,20.4051 20.1758,20.1051L20.1758,20.0951z M10.6858,10.8651C10.8958,10.4451,11.0058,9.98512,11.0058,9.52512L11.0058,2.99512 13.0058,2.99512 13.0058,9.52512C13.0058,9.98512,13.1158,10.4551,13.3258,10.8651L15.3858,14.9951 8.62582,14.9951 10.6958,10.8651 10.6858,10.8651z" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<TextBlock Foreground="#fca5a5" Text="实验性选项" FontSize="12" VerticalAlignment="Center" Margin="4,0,0,0"/>
</StackPanel>
</Border>
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="启用FullScreenHelper" VerticalAlignment="Center"/>
</StackPanel>
<Border x:Name="ToggleSwitchIsEnableFullScreenHelper" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<TextBlock Text="感谢lindexi大佬提供的FullScreenHelper,可以减少任务栏弹出的问题,且支持多显示器自动全屏"
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11" Margin="0,8,0,0"/>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Border Height="20" CornerRadius="3" Background="#7f1d1d" Padding="4,0" Margin="0,0,12,0">
<StackPanel Orientation="Horizontal">
<Image Width="15" Height="15" VerticalAlignment="Center">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
<GeometryDrawing Brush="#fca5a5"
Geometry="F1 M24,24z M0,0z M20.1758,20.0951L15.1058,9.97512C15.0358,9.83512,14.9958,9.68512,14.9958,9.52512L14.9958,2.99512 15.4958,2.99512C16.0458,2.99512 16.4958,2.54512 16.4958,1.99512 16.4958,1.44512 16.0458,0.995117 15.4958,0.995117L8.49582,0.995117C7.94582,0.995117 7.49582,1.44512 7.49582,1.99512 7.49582,2.54512 7.94582,2.99512 8.49582,2.99512L8.99582,2.99512 8.99582,9.52512C8.99582,9.67512,8.95582,9.83512,8.89582,9.97512L3.82582,20.0951C3.67582,20.4051 3.59582,20.7451 3.61582,21.0851 3.62582,21.4251 3.73582,21.7651 3.91582,22.0551 4.09582,22.3451 4.34582,22.5851 4.64582,22.7551 4.94582,22.9151 5.27582,23.0051 5.60582,23.0051L18.3758,23.0051C18.7558,23.0051 19.0558,22.9251 19.3558,22.7551 19.6558,22.5851 19.9058,22.3451 20.0858,22.0551 20.2658,21.7651 20.3658,21.4351 20.3858,21.0851 20.3958,20.7451 20.3258,20.4051 20.1758,20.1051L20.1758,20.0951z M10.6858,10.8651C10.8958,10.4451,11.0058,9.98512,11.0058,9.52512L11.0058,2.99512 13.0058,2.99512 13.0058,9.52512C13.0058,9.98512,13.1158,10.4551,13.3258,10.8651L15.3858,14.9951 8.62582,14.9951 10.6958,10.8651 10.6858,10.8651z" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<TextBlock Foreground="#fca5a5" Text="实验性选项" FontSize="12" VerticalAlignment="Center" Margin="4,0,0,0"/>
</StackPanel>
</Border>
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="启用AvoidFullScreenHelper" VerticalAlignment="Center"/>
</StackPanel>
<Border x:Name="ToggleSwitchIsEnableAvoidFullScreenHelper" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<TextBlock Text="避免画布全屏,可解决开启dock栏软件后浮动工具栏偏高的问题"
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11" Margin="0,8,0,0"/>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Border Height="20" CornerRadius="3" Background="#7f1d1d" Padding="4,0" Margin="0,0,12,0">
<StackPanel Orientation="Horizontal">
<Image Width="15" Height="15" VerticalAlignment="Center">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
<GeometryDrawing Brush="#fca5a5"
Geometry="F1 M24,24z M0,0z M20.1758,20.0951L15.1058,9.97512C15.0358,9.83512,14.9958,9.68512,14.9958,9.52512L14.9958,2.99512 15.4958,2.99512C16.0458,2.99512 16.4958,2.54512 16.4958,1.99512 16.4958,1.44512 16.0458,0.995117 15.4958,0.995117L8.49582,0.995117C7.94582,0.995117 7.49582,1.44512 7.49582,1.99512 7.49582,2.54512 7.94582,2.99512 8.49582,2.99512L8.99582,2.99512 8.99582,9.52512C8.99582,9.67512,8.95582,9.83512,8.89582,9.97512L3.82582,20.0951C3.67582,20.4051 3.59582,20.7451 3.61582,21.0851 3.62582,21.4251 3.73582,21.7651 3.91582,22.0551 4.09582,22.3451 4.34582,22.5851 4.64582,22.7551 4.94582,22.9151 5.27582,23.0051 5.60582,23.0051L18.3758,23.0051C18.7558,23.0051 19.0558,22.9251 19.3558,22.7551 19.6558,22.5851 19.9058,22.3451 20.0858,22.0551 20.2658,21.7651 20.3658,21.4351 20.3858,21.0851 20.3958,20.7451 20.3258,20.4051 20.1758,20.1051L20.1758,20.0951z M10.6858,10.8651C10.8958,10.4451,11.0058,9.98512,11.0058,9.52512L11.0058,2.99512 13.0058,2.99512 13.0058,9.52512C13.0058,9.98512,13.1158,10.4551,13.3258,10.8651L15.3858,14.9951 8.62582,14.9951 10.6958,10.8651 10.6858,10.8651z" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<TextBlock Foreground="#fca5a5" Text="实验性选项" FontSize="12" VerticalAlignment="Center" Margin="4,0,0,0"/>
</StackPanel>
</Border>
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="启用EdgeGestureUtil" VerticalAlignment="Center"/>
</StackPanel>
<Border x:Name="ToggleSwitchIsEnableEdgeGestureUtil" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<TextBlock Text="EdgeGestureUtil可以暂时阻止在使用触摸时触发边缘手势(如Windows10环境下,屏幕左边缘滑动进入任务视图,右边缘滑动弹出通知中心;Windows11环境下,底部向上滑动打开开始菜单)"
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11" Margin="0,8,0,0"/>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Border Height="20" CornerRadius="3" Background="#7f1d1d" Padding="4,0" Margin="0,0,12,0">
<StackPanel Orientation="Horizontal">
<Image Width="15" Height="15" VerticalAlignment="Center">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
<GeometryDrawing Brush="#fca5a5"
Geometry="F1 M24,24z M0,0z M20.1758,20.0951L15.1058,9.97512C15.0358,9.83512,14.9958,9.68512,14.9958,9.52512L14.9958,2.99512 15.4958,2.99512C16.0458,2.99512 16.4958,2.54512 16.4958,1.99512 16.4958,1.44512 16.0458,0.995117 15.4958,0.995117L8.49582,0.995117C7.94582,0.995117 7.49582,1.44512 7.49582,1.99512 7.49582,2.54512 7.94582,2.99512 8.49582,2.99512L8.99582,2.99512 8.99582,9.52512C8.99582,9.67512,8.95582,9.83512,8.89582,9.97512L3.82582,20.0951C3.67582,20.4051 3.59582,20.7451 3.61582,21.0851 3.62582,21.4251 3.73582,21.7651 3.91582,22.0551 4.09582,22.3451 4.34582,22.5851 4.64582,22.7551 4.94582,22.9151 5.27582,23.0051 5.60582,23.0051L18.3758,23.0051C18.7558,23.0051 19.0558,22.9251 19.3558,22.7551 19.6558,22.5851 19.9058,22.3451 20.0858,22.0551 20.2658,21.7651 20.3658,21.4351 20.3858,21.0851 20.3958,20.7451 20.3258,20.4051 20.1758,20.1051L20.1758,20.0951z M10.6858,10.8651C10.8958,10.4451,11.0058,9.98512,11.0058,9.52512L11.0058,2.99512 13.0058,2.99512 13.0058,9.52512C13.0058,9.98512,13.1158,10.4551,13.3258,10.8651L15.3858,14.9951 8.62582,14.9951 10.6958,10.8651 10.6858,10.8651z" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<TextBlock Foreground="#fca5a5" Text="实验性选项" FontSize="12" VerticalAlignment="Center" Margin="4,0,0,0"/>
</StackPanel>
</Border>
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="启用ForceFullScreen" VerticalAlignment="Center"/>
</StackPanel>
<Border x:Name="ToggleSwitchIsEnableForceFullScreen" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<TextBlock Text="当检测到窗口大小变化时,自动使用Win32API将本窗口的大小设置为主显示器大小(设备像素大小),不需要可以关闭,实时生效"
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11" Margin="0,8,0,0"/>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Border Height="20" CornerRadius="3" Background="#7f1d1d" Padding="4,0" Margin="0,0,12,0">
<StackPanel Orientation="Horizontal">
<Image Width="15" Height="15" VerticalAlignment="Center">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
<GeometryDrawing Brush="#fca5a5"
Geometry="F1 M24,24z M0,0z M20.1758,20.0951L15.1058,9.97512C15.0358,9.83512,14.9958,9.68512,14.9958,9.52512L14.9958,2.99512 15.4958,2.99512C16.0458,2.99512 16.4958,2.54512 16.4958,1.99512 16.4958,1.44512 16.0458,0.995117 15.4958,0.995117L8.49582,0.995117C7.94582,0.995117 7.49582,1.44512 7.49582,1.99512 7.49582,2.54512 7.94582,2.99512 8.49582,2.99512L8.99582,2.99512 8.99582,9.52512C8.99582,9.67512,8.95582,9.83512,8.89582,9.97512L3.82582,20.0951C3.67582,20.4051 3.59582,20.7451 3.61582,21.0851 3.62582,21.4251 3.73582,21.7651 3.91582,22.0551 4.09582,22.3451 4.34582,22.5851 4.64582,22.7551 4.94582,22.9151 5.27582,23.0051 5.60582,23.0051L18.3758,23.0051C18.7558,23.0051 19.0558,22.9251 19.3558,22.7551 19.6558,22.5851 19.9058,22.3451 20.0858,22.0551 20.2658,21.7651 20.3658,21.4351 20.3858,21.0851 20.3958,20.7451 20.3258,20.4051 20.1758,20.1051L20.1758,20.0951z M10.6858,10.8651C10.8958,10.4451,11.0058,9.98512,11.0058,9.52512L11.0058,2.99512 13.0058,2.99512 13.0058,9.52512C13.0058,9.98512,13.1158,10.4551,13.3258,10.8651L15.3858,14.9951 8.62582,14.9951 10.6958,10.8651 10.6858,10.8651z" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<TextBlock Foreground="#fca5a5" Text="实验性选项" FontSize="12" VerticalAlignment="Center" Margin="4,0,0,0"/>
</StackPanel>
</Border>
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="启用DPIChangeDetection" VerticalAlignment="Center"/>
</StackPanel>
<Border x:Name="ToggleSwitchIsEnableDPIChangeDetection" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<TextBlock Text="当检测到系统DPI变化时,会尝试检测FloatingBar是否在屏幕内显示,如果不在屏幕内显示将会尝试移动到屏幕内可见区域"
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11" Margin="0,8,0,0"/>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Border Height="20" CornerRadius="3" Background="#7f1d1d" Padding="4,0" Margin="0,0,12,0">
<StackPanel Orientation="Horizontal">
<Image Width="15" Height="15" VerticalAlignment="Center">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
<GeometryDrawing Brush="#fca5a5"
Geometry="F1 M24,24z M0,0z M20.1758,20.0951L15.1058,9.97512C15.0358,9.83512,14.9958,9.68512,14.9958,9.52512L14.9958,2.99512 15.4958,2.99512C16.0458,2.99512 16.4958,2.54512 16.4958,1.99512 16.4958,1.44512 16.0458,0.995117 15.4958,0.995117L8.49582,0.995117C7.94582,0.995117 7.49582,1.44512 7.49582,1.99512 7.49582,2.54512 7.94582,2.99512 8.49582,2.99512L8.99582,2.99512 8.99582,9.52512C8.99582,9.67512,8.95582,9.83512,8.89582,9.97512L3.82582,20.0951C3.67582,20.4051 3.59582,20.7451 3.61582,21.0851 3.62582,21.4251 3.73582,21.7651 3.91582,22.0551 4.09582,22.3451 4.34582,22.5851 4.64582,22.7551 4.94582,22.9151 5.27582,23.0051 5.60582,23.0051L18.3758,23.0051C18.7558,23.0051 19.0558,22.9251 19.3558,22.7551 19.6558,22.5851 19.9058,22.3451 20.0858,22.0551 20.2658,21.7651 20.3658,21.4351 20.3858,21.0851 20.3958,20.7451 20.3258,20.4051 20.1758,20.1051L20.1758,20.0951z M10.6858,10.8651C10.8958,10.4451,11.0058,9.98512,11.0058,9.52512L11.0058,2.99512 13.0058,2.99512 13.0058,9.52512C13.0058,9.98512,13.1158,10.4551,13.3258,10.8651L15.3858,14.9951 8.62582,14.9951 10.6958,10.8651 10.6858,10.8651z" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<TextBlock Foreground="#fca5a5" Text="实验性选项" FontSize="12" VerticalAlignment="Center" Margin="4,0,0,0"/>
</StackPanel>
</Border>
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="启用ResolutionChangeDetection" VerticalAlignment="Center"/>
</StackPanel>
<Border x:Name="ToggleSwitchIsEnableResolutionChangeDetection" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<TextBlock Text="当检测到系统分辨率变化时,会尝试检测FloatingBar是否在屏幕内显示,如果不在屏幕内显示将会尝试移动到屏幕内可见区域"
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11" Margin="0,8,0,0"/>
</StackPanel>
</Border>
<!-- 设置备份与还原 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical" Margin="18,18,18,18">
<TextBlock Text="设置备份与还原" FontWeight="Bold" Foreground="#2e3436" FontSize="18" Margin="0,0,0,12"/>
<TextBlock Text="可手动备份当前设置或还原之前的备份,自动更新前也会自动备份"
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11" Margin="0,0,0,12"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动更新前备份" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在自动更新前自动备份当前设置" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchIsAutoBackupBeforeUpdate" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="定期自动备份" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="定期自动备份设置文件" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchIsAutoBackupEnabled" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="备份间隔" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="设置定期备份的时间间隔" HorizontalAlignment="Left"/>
</StackPanel>
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0">
<Border x:Name="AutoBackupInterval1DayBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="AutoBackupInterval_1" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="1天"/>
</Border>
<Border x:Name="AutoBackupInterval3DaysBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="AutoBackupInterval_3" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="3天"/>
</Border>
<Border x:Name="AutoBackupInterval7DaysBorder" Padding="13,7" CornerRadius="8" Background="#e1e1e1" Cursor="Hand" Tag="AutoBackupInterval_7" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" FontWeight="Bold" Text="7天"/>
</Border>
<Border x:Name="AutoBackupInterval14DaysBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="AutoBackupInterval_14" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="14天"/>
</Border>
<Border x:Name="AutoBackupInterval30DaysBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="AutoBackupInterval_30" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="30天"/>
</Border>
</WrapPanel>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,8,0,0">
<Button x:Name="BtnManualBackup" Tag="manual_backup" Content="手动备份" Padding="12,6" Margin="0,0,12,0" Background="#2563eb" Foreground="White" Click="Button_Click"/>
<Button x:Name="BtnRestoreBackup" Tag="restore_backup" Content="还原备份" Padding="12,6" Background="#2563eb" Foreground="White" Click="Button_Click"/>
</StackPanel>
</StackPanel>
</Border>
<!-- 文件关联管理 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical" Margin="18,18,18,18">
<TextBlock Text="文件关联管理" FontWeight="Bold" Foreground="#2e3436" FontSize="18" Margin="0,0,0,12"/>
<TextBlock Text="管理.icstk文件的关联设置,双击.icstk文件可直接在Ink Canvas中打开"
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11" Margin="0,0,0,12"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,8,0,0">
<Button x:Name="BtnUnregisterFileAssociation" Tag="unregister" Content="取消文件关联" Padding="15,5" Margin="0,0,8,0" Background="#2563eb" Foreground="White" Click="Button_Click"/>
<Button x:Name="BtnCheckFileAssociation" Tag="check" Content="检查关联状态" Padding="15,5" Margin="0,0,8,0" Background="#2563eb" Foreground="White" Click="Button_Click"/>
<Button x:Name="BtnRegisterFileAssociation" Tag="register" Content="重新注册关联" Padding="15,5" Background="#2563eb" Foreground="White" Click="Button_Click"/>
</StackPanel>
</StackPanel>
</Border>
<!-- 外部协议调用 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical" Margin="18,18,18,18">
<TextBlock Text="外部协议调用" FontWeight="Bold" Foreground="#2e3436" FontSize="18" Margin="0,0,0,12"/>
<TextBlock Text="允许通过 icc:// 协议调用软件,支持从浏览器或其他应用快速控制软件"
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11" Margin="0,0,0,12"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="启用外部协议 (icc://)" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后可通过 icc://fold (收纳) 或 icc://unfold (展开) 等命令控制" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchIsEnableUriScheme" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="IsEnableUriScheme" MouseLeftButtonDown="ToggleSwitch_Click"
Focusable="True" KeyboardNavigation.IsTabStop="True" KeyDown="ToggleSwitch_KeyDown"
AutomationProperties.Name="启用外部协议 (icc://)"
AutomationProperties.HelpText="开启后可通过 icc://fold (收纳) 或 icc://unfold (展开) 等命令控制">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
</StackPanel>
</Border>
<!-- 悬浮窗拦截 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical" Margin="18,18,18,18">
<TextBlock Text="悬浮窗拦截" FontWeight="Bold" Foreground="#2e3436" FontSize="18" Margin="0,0,0,12"/>
<TextBlock Text="自动检测并拦截同类软件的悬浮窗"
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11" Margin="0,0,0,12"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="启用悬浮窗拦截" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后自动检测并拦截同类软件的悬浮窗" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchFloatingWindowInterceptorEnabled" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<!-- 拦截规则网格 -->
<Grid x:Name="FloatingWindowInterceptorGrid" Margin="0,12,0,0" Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<!-- 希沃白板3 -->
<StackPanel Grid.Row="0" Grid.Column="0" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/EasiNote3.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="希沃白板3" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchSeewoWhiteboard3Floating" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<!-- 希沃白板5 -->
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/EasiNote.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="希沃白板5" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchSeewoWhiteboard5Floating" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<!-- 希沃白板5C -->
<StackPanel Grid.Row="0" Grid.Column="2" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/EasiNote5C.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="希沃白板5C" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchSeewoWhiteboard5CFloating" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<!-- 希沃品课 -->
<StackPanel Grid.Row="0" Grid.Column="3" Orientation="Vertical" Margin="0,0,0,12">
<Image Source="/Resources/Icons-png/SeewoPinco.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="希沃品课" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchSeewoPincoSideBarFloating" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<!-- 希沃品课画笔 -->
<StackPanel Grid.Row="1" Grid.Column="0" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/SeewoPinco.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="希沃品课画笔" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchSeewoPincoDrawingFloating" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<!-- 希沃PPT小工具 -->
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/PPTTools.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="希沃PPT小工具" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchSeewoPPTFloating" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<!-- AiClass -->
<StackPanel Grid.Row="1" Grid.Column="2" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/AiClass.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="AiClass" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchAiClassFloating" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<!-- 鸿合屏幕书写 -->
<StackPanel Grid.Row="1" Grid.Column="3" Orientation="Vertical" Margin="0,0,0,12">
<Image Source="/Resources/Icons-png/HiteAnnotation.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="鸿合屏幕书写" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchHiteAnnotationFloating" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<!-- 畅言智慧课堂 -->
<StackPanel Grid.Row="2" Grid.Column="0" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/畅言智慧课堂.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="畅言智慧课堂" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchChangYanFloating" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<!-- 畅言PPT -->
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/畅言智慧课堂.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="畅言PPT" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchChangYanPptFloating" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<!-- 天喻教育云 -->
<StackPanel Grid.Row="2" Grid.Column="2" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/天喻教育云.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="天喻教育云" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchIntelligentClassFloating" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<!-- 希沃桌面画笔 -->
<StackPanel Grid.Row="2" Grid.Column="3" Orientation="Vertical" Margin="0,0,0,12">
<Image Source="/Resources/Icons-png/SeewoPinco.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="希沃桌面画笔" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchSeewoDesktopAnnotationFloating" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<!-- 希沃桌面侧栏 -->
<StackPanel Grid.Row="3" Grid.Column="0" Orientation="Vertical" Margin="0,0,8,0">
<Image Source="/Resources/Icons-png/SeewoPinco.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="希沃桌面侧栏" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchSeewoDesktopSideBarFloating" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
</Grid>
<TextBlock x:Name="TextBlockFloatingWindowInterceptorStatus" Text="拦截器未启动"
Foreground="#9a9996" FontSize="12" Margin="0,8,0,0" Visibility="Collapsed"/>
</StackPanel>
</Border>
<!-- 云存储管理 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical" Margin="18,18,18,18">
<TextBlock Text="云存储管理" FontWeight="Bold" Foreground="#2e3436" FontSize="18" Margin="0,0,0,12"/>
<Button x:Name="BtnDlassSettingsManage" Tag="dlass_settings" Content="云存储管理" Padding="15,5" HorizontalAlignment="Left" Background="#2563eb" Foreground="White" Click="Button_Click"/>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</UserControl>
@@ -1,596 +0,0 @@
using iNKORE.UI.WPF.Helpers;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Application = System.Windows.Application;
namespace Ink_Canvas.Windows.SettingsViews
{
/// <summary>
/// AdvancedPanel.xaml 的交互逻辑
/// </summary>
public partial class AdvancedPanel : UserControl
{
private bool _isLoaded = false;
public AdvancedPanel()
{
InitializeComponent();
Loaded += AdvancedPanel_Loaded;
}
private void AdvancedPanel_Loaded(object sender, RoutedEventArgs e)
{
LoadSettings();
// 添加触摸支持
EnableTouchSupport();
// 应用主题
ApplyTheme();
_isLoaded = true;
}
/// <summary>
/// 为面板中的所有交互控件启用触摸支持
/// </summary>
private void EnableTouchSupport()
{
try
{
// 延迟执行,确保所有控件都已加载
Dispatcher.BeginInvoke(new Action(() =>
{
MainWindowSettingsHelper.EnableTouchSupportForControls(this);
}), System.Windows.Threading.DispatcherPriority.Loaded);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"AdvancedPanel 启用触摸支持时出错: {ex.Message}");
}
}
public event EventHandler<RoutedEventArgs> IsTopBarNeedShadowEffect;
public event EventHandler<RoutedEventArgs> IsTopBarNeedNoShadowEffect;
private void ScrollViewerEx_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
var scrollViewer = (ScrollViewer)sender;
if (scrollViewer.VerticalOffset >= 10)
{
IsTopBarNeedShadowEffect?.Invoke(this, new RoutedEventArgs());
}
else
{
IsTopBarNeedNoShadowEffect?.Invoke(this, new RoutedEventArgs());
}
}
/// <summary>
/// 加载设置到UI
/// </summary>
public void LoadSettings()
{
if (MainWindow.Settings == null || MainWindow.Settings.Advanced == null) return;
_isLoaded = false;
try
{
var advanced = MainWindow.Settings.Advanced;
// 特殊屏幕模式
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchIsSpecialScreen"), advanced.IsSpecialScreen);
// 触摸倍数
if (TouchMultiplierSlider != null)
{
TouchMultiplierSlider.Value = advanced.TouchMultiplier;
if (TouchMultiplierText != null)
{
TouchMultiplierText.Text = advanced.TouchMultiplier.ToString("F2");
}
}
// 橡皮擦绑定触摸大小倍数
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchEraserBindTouchMultiplier"), advanced.EraserBindTouchMultiplier);
// 笔尖模式 BoundsWidth
if (NibModeBoundsWidthSlider != null)
{
NibModeBoundsWidthSlider.Value = advanced.NibModeBoundsWidth;
if (NibModeBoundsWidthText != null)
{
NibModeBoundsWidthText.Text = advanced.NibModeBoundsWidth.ToString();
}
}
// 手指模式 BoundsWidth
if (FingerModeBoundsWidthSlider != null)
{
FingerModeBoundsWidthSlider.Value = advanced.FingerModeBoundsWidth;
if (FingerModeBoundsWidthText != null)
{
FingerModeBoundsWidthText.Text = advanced.FingerModeBoundsWidth.ToString();
}
}
// 四边红外模式
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchIsQuadIR"), advanced.IsQuadIR);
// 记录日志
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchIsLogEnabled"), advanced.IsLogEnabled);
// 日志以日期保存
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchIsSaveLogByDate"), advanced.IsSaveLogByDate);
// 关闭软件时二次弹窗确认
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchIsSecondConfimeWhenShutdownApp"), advanced.IsSecondConfirmWhenShutdownApp);
// 实验性功能
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchIsEnableFullScreenHelper"), advanced.IsEnableFullScreenHelper);
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchIsEnableAvoidFullScreenHelper"), advanced.IsEnableAvoidFullScreenHelper);
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchIsEnableEdgeGestureUtil"), advanced.IsEnableEdgeGestureUtil);
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchIsEnableForceFullScreen"), advanced.IsEnableForceFullScreen);
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchIsEnableDPIChangeDetection"), advanced.IsEnableDPIChangeDetection);
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchIsEnableResolutionChangeDetection"), advanced.IsEnableResolutionChangeDetection);
// 备份设置
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchIsAutoBackupBeforeUpdate"), advanced.IsAutoBackupBeforeUpdate);
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchIsAutoBackupEnabled"), advanced.IsAutoBackupEnabled);
SetOptionButtonState("AutoBackupInterval", advanced.AutoBackupIntervalDays);
// 外部协议
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchIsEnableUriScheme"), advanced.IsEnableUriScheme);
// 悬浮窗拦截
// 注意:IsEnableFloatingWindowInterception 可能不在 Advanced 类中,需要确认
// 这里先假设它在 Advanced 类中,如果不在,需要调整
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"加载高级设置时出错: {ex.Message}");
}
_isLoaded = true;
}
/// <summary>
/// 查找ToggleSwitch控件
/// </summary>
private Border FindToggleSwitch(string name)
{
return this.FindDescendantByName(name) as Border;
}
/// <summary>
/// 设置ToggleSwitch状态
/// </summary>
private void SetToggleSwitchState(Border toggleSwitch, bool isOn)
{
if (toggleSwitch == null) return;
toggleSwitch.Background = isOn
? new SolidColorBrush(Color.FromRgb(53, 132, 228))
: (ThemeHelper.IsDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(225, 225, 225)));
var innerBorder = toggleSwitch.Child as Border;
if (innerBorder != null)
{
innerBorder.HorizontalAlignment = isOn ? HorizontalAlignment.Right : HorizontalAlignment.Left;
innerBorder.Background = new SolidColorBrush(Colors.White);
}
}
/// <summary>
/// 设置选项按钮状态
/// </summary>
private void SetOptionButtonState(string group, int selectedValue)
{
var buttons = new Dictionary<string, Dictionary<int, string>>
{
{ "AutoBackupInterval", new Dictionary<int, string> { { 1, "1Day" }, { 3, "3Days" }, { 7, "7Days" }, { 14, "14Days" }, { 30, "30Days" } } }
};
if (!buttons.ContainsKey(group)) return;
var buttonNames = buttons[group];
if (!buttonNames.ContainsKey(selectedValue)) return;
string buttonName = buttonNames[selectedValue];
var button = this.FindDescendantByName($"{group}{buttonName}Border") as Border;
if (button != null)
{
bool isDarkTheme = ThemeHelper.IsDarkTheme;
var selectedBrush = isDarkTheme ? new SolidColorBrush(Color.FromRgb(25, 25, 25)) : new SolidColorBrush(Color.FromRgb(225, 225, 225));
var unselectedBrush = new SolidColorBrush(Colors.Transparent);
var parent = button.Parent as Panel;
if (parent != null)
{
foreach (var child in parent.Children)
{
if (child is Border childBorder && childBorder != button)
{
string childTag = childBorder.Tag?.ToString();
if (!string.IsNullOrEmpty(childTag) && childTag.StartsWith(group + "_"))
{
childBorder.Background = unselectedBrush;
var textBlock = childBorder.Child as TextBlock;
if (textBlock != null)
{
textBlock.FontWeight = FontWeights.Normal;
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
}
}
}
}
}
button.Background = selectedBrush;
var currentTextBlock = button.Child as TextBlock;
if (currentTextBlock != null)
{
currentTextBlock.FontWeight = FontWeights.Bold;
currentTextBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
}
}
}
private bool GetCurrentSettingValue(string tag)
{
if (MainWindow.Settings?.Advanced == null) return false;
try
{
var advanced = MainWindow.Settings.Advanced;
switch (tag)
{
case "IsSpecialScreen":
return advanced.IsSpecialScreen;
case "EraserBindTouchMultiplier":
return advanced.EraserBindTouchMultiplier;
case "IsQuadIR":
return advanced.IsQuadIR;
case "IsLogEnabled":
return advanced.IsLogEnabled;
case "IsSaveLogByDate":
return advanced.IsSaveLogByDate;
case "IsSecondConfirmWhenShutdownApp":
return advanced.IsSecondConfirmWhenShutdownApp;
case "IsEnableFullScreenHelper":
return advanced.IsEnableFullScreenHelper;
case "IsEnableAvoidFullScreenHelper":
return advanced.IsEnableAvoidFullScreenHelper;
case "IsEnableEdgeGestureUtil":
return advanced.IsEnableEdgeGestureUtil;
case "IsEnableForceFullScreen":
return advanced.IsEnableForceFullScreen;
case "IsEnableDPIChangeDetection":
return advanced.IsEnableDPIChangeDetection;
case "IsEnableResolutionChangeDetection":
return advanced.IsEnableResolutionChangeDetection;
case "IsAutoBackupBeforeUpdate":
return advanced.IsAutoBackupBeforeUpdate;
case "IsAutoBackupEnabled":
return advanced.IsAutoBackupEnabled;
case "IsEnableUriScheme":
return advanced.IsEnableUriScheme;
default:
return false;
}
}
catch
{
return false;
}
}
/// <summary>
/// ToggleSwitch点击事件处理
/// </summary>
private void ToggleSwitch_Click(object sender, RoutedEventArgs e)
{
if (!_isLoaded) return;
var border = sender as Border;
if (border == null) return;
string tag = border.Tag?.ToString();
if (string.IsNullOrEmpty(tag)) return;
bool currentState = GetCurrentSettingValue(tag);
bool newState = !currentState;
SetToggleSwitchState(border, newState);
var advanced = MainWindow.Settings.Advanced;
if (advanced == null) return;
switch (tag)
{
case "IsSpecialScreen":
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsSpecialScreen", newState);
break;
case "EraserBindTouchMultiplier":
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchEraserBindTouchMultiplier", newState);
break;
case "IsQuadIR":
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsQuadIR", newState);
break;
case "IsLogEnabled":
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsLogEnabled", newState);
break;
case "IsSaveLogByDate":
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsSaveLogByDate", newState);
break;
case "IsSecondConfirmWhenShutdownApp":
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsSecondConfimeWhenShutdownApp", newState);
break;
case "IsEnableFullScreenHelper":
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsEnableFullScreenHelper", newState);
break;
case "IsEnableAvoidFullScreenHelper":
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsEnableAvoidFullScreenHelper", newState);
break;
case "IsEnableEdgeGestureUtil":
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsEnableEdgeGestureUtil", newState);
break;
case "IsEnableForceFullScreen":
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsEnableForceFullScreen", newState);
break;
case "IsEnableDPIChangeDetection":
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsEnableDPIChangeDetection", newState);
break;
case "IsEnableResolutionChangeDetection":
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsEnableResolutionChangeDetection", newState);
break;
case "IsAutoBackupBeforeUpdate":
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsAutoBackupBeforeUpdate", newState);
break;
case "IsAutoBackupEnabled":
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsAutoBackupEnabled", newState);
break;
case "IsEnableUriScheme":
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsEnableUriScheme", newState);
break;
}
}
/// <summary>
/// ToggleSwitch键盘事件处理
/// </summary>
private void ToggleSwitch_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Space || e.Key == System.Windows.Input.Key.Enter)
{
ToggleSwitch_Click(sender, new RoutedEventArgs());
e.Handled = true;
}
}
/// <summary>
/// Slider值变化事件处理
/// </summary>
private void TouchMultiplierSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (!_isLoaded) return;
if (TouchMultiplierSlider != null && TouchMultiplierText != null)
{
double value = TouchMultiplierSlider.Value;
TouchMultiplierText.Text = value.ToString("F2");
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeSliderValueChanged("TouchMultiplierSlider", value);
}
}
private void NibModeBoundsWidthSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (!_isLoaded) return;
if (NibModeBoundsWidthSlider != null && NibModeBoundsWidthText != null)
{
double value = NibModeBoundsWidthSlider.Value;
NibModeBoundsWidthText.Text = ((int)value).ToString();
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeSliderValueChanged("NibModeBoundsWidthSlider", value);
}
}
private void FingerModeBoundsWidthSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (!_isLoaded) return;
if (FingerModeBoundsWidthSlider != null && FingerModeBoundsWidthText != null)
{
double value = FingerModeBoundsWidthSlider.Value;
FingerModeBoundsWidthText.Text = ((int)value).ToString();
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeSliderValueChanged("FingerModeBoundsWidthSlider", value);
}
}
/// <summary>
/// 选项按钮点击事件处理
/// </summary>
private void OptionButton_Click(object sender, RoutedEventArgs e)
{
if (!_isLoaded) return;
var border = sender as Border;
if (border == null) return;
string tag = border.Tag?.ToString();
if (string.IsNullOrEmpty(tag)) return;
string[] parts = tag.Split('_');
if (parts.Length < 2) return;
string group = parts[0];
string value = parts[1];
bool isDarkTheme = ThemeHelper.IsDarkTheme;
var selectedBrush = isDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(225, 225, 225));
var unselectedBrush = new SolidColorBrush(Colors.Transparent);
var parent = border.Parent as Panel;
if (parent != null)
{
foreach (var child in parent.Children)
{
if (child is Border childBorder && childBorder != border)
{
string childTag = childBorder.Tag?.ToString();
if (!string.IsNullOrEmpty(childTag) && childTag.StartsWith(group + "_"))
{
childBorder.Background = unselectedBrush;
var textBlock = childBorder.Child as TextBlock;
if (textBlock != null)
{
textBlock.FontWeight = FontWeights.Normal;
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
}
}
}
}
}
border.Background = selectedBrush;
var currentTextBlock = border.Child as TextBlock;
if (currentTextBlock != null)
{
currentTextBlock.FontWeight = FontWeights.Bold;
currentTextBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
}
if (MainWindow.Settings.Advanced == null) return;
switch (group)
{
case "AutoBackupInterval":
int days;
if (int.TryParse(value, out days))
{
// 尝试调用 MainWindow 中的方法
var mainWindow = Application.Current.MainWindow as MainWindow;
if (mainWindow != null)
{
var comboBox = mainWindow.FindName("ComboBoxAutoBackupInterval") as System.Windows.Controls.ComboBox;
if (comboBox != null)
{
// 找到对应的选项并设置
foreach (ComboBoxItem item in comboBox.Items)
{
if (item.Tag != null && int.TryParse(item.Tag.ToString(), out int tagValue) && tagValue == days)
{
comboBox.SelectedItem = item;
MainWindowSettingsHelper.InvokeComboBoxSelectionChanged("ComboBoxAutoBackupInterval", item);
break;
}
}
}
else
{
// 如果找不到控件,直接更新设置
MainWindowSettingsHelper.UpdateSettingDirectly(() =>
{
MainWindow.Settings.Advanced.AutoBackupIntervalDays = days;
}, "ComboBoxAutoBackupInterval");
}
}
}
break;
}
}
/// <summary>
/// 按钮点击事件处理(备份还原、文件关联等)
/// </summary>
private void Button_Click(object sender, RoutedEventArgs e)
{
var button = sender as Button;
if (button == null) return;
string action = button.Tag?.ToString();
if (string.IsNullOrEmpty(action)) action = button.Name;
// 这些按钮的功能可能需要调用 MainWindow 中的方法
// 暂时先留空,后续可以根据需要实现
switch (action)
{
case "manual_backup":
case "BtnManualBackup":
// TODO: 调用 MainWindow 的备份方法
break;
case "restore_backup":
case "BtnRestoreBackup":
// TODO: 调用 MainWindow 的还原方法
break;
case "unregister":
case "BtnUnregisterFileAssociation":
// TODO: 调用 MainWindow 的取消文件关联方法
break;
case "check":
case "BtnCheckFileAssociation":
// TODO: 调用 MainWindow 的检查文件关联状态方法
break;
case "register":
case "BtnRegisterFileAssociation":
// TODO: 调用 MainWindow 的注册文件关联方法
break;
case "dlass_settings":
case "BtnDlassSettingsManage":
// TODO: 调用 MainWindow 的 Dlass 设置管理方法
break;
}
}
/// <summary>
/// 应用主题
/// </summary>
public void ApplyTheme()
{
try
{
ThemeHelper.ApplyThemeToControl(this);
if (_isLoaded)
{
LoadSettings();
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"AdvancedPanel 应用主题时出错: {ex.Message}");
}
}
}
}
@@ -1,494 +0,0 @@
<UserControl x:Class="Ink_Canvas.Windows.SettingsViews.AppearancePanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ink_Canvas.Windows.SettingsViews"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
mc:Ignorable="d"
d:DesignHeight="950" d:DesignWidth="640">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ComboBoxStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!-- 开关样式模板 -->
<Style x:Key="ToggleSwitchStyle" TargetType="Border">
<Setter Property="Width" Value="48"/>
<Setter Property="Height" Value="25"/>
<Setter Property="CornerRadius" Value="12"/>
<Setter Property="Padding" Value="3,0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="Margin" Value="0,0,15,0"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<ScrollViewer ScrollChanged="ScrollViewerEx_ScrollChanged" IsManipulationEnabled="True" Name="ScrollViewerEx" IsDeferredScrollingEnabled="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled" IsTabStop="False" TabIndex="-1" Margin="0,0,2,2" PanningMode="VerticalOnly">
<StackPanel Margin="60,12,60,24">
<!-- 主题设置 -->
<Border BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical">
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="主题" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="选择应用程序的主题样式" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0">
<Border x:Name="ThemeLightBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="Theme_Light">
<TextBlock Foreground="#2e3436" FontSize="14" Text="浅色主题"/>
</Border>
<Border x:Name="ThemeDarkBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="Theme_Dark" Margin="8,0,0,0">
<TextBlock Foreground="#2e3436" FontSize="14" Text="深色主题"/>
</Border>
<Border x:Name="ThemeSystemBorder" Padding="13,7" CornerRadius="8" Background="#e1e1e1" Cursor="Hand" Tag="Theme_System" Margin="8,0,0,0">
<TextBlock Foreground="#2e3436" FontSize="14" FontWeight="Bold" Text="跟随系统"/>
</Border>
</StackPanel>
</Grid>
</StackPanel>
</Border>
<!-- 启动动画设置 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical">
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="启用启动动画" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="启动时显示动画效果" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchEnableSplashScreen" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<StackPanel Margin="18,8,18,8" x:Name="SplashScreenStylePanel">
<StackPanel Orientation="Vertical" Margin="0,0,0,12">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="启动动画样式" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="选择启动动画的样式" HorizontalAlignment="Left"/>
</StackPanel>
<WrapPanel Orientation="Horizontal">
<Border x:Name="SplashScreenStyleRandomBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="SplashScreenStyle_Random" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="随机"/>
</Border>
<Border x:Name="SplashScreenStyleSeasonBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="SplashScreenStyle_Season" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="跟随四季"/>
</Border>
<Border x:Name="SplashScreenStyleSpringBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="SplashScreenStyle_Spring" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="春季"/>
</Border>
<Border x:Name="SplashScreenStyleSummerBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="SplashScreenStyle_Summer" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="夏季"/>
</Border>
<Border x:Name="SplashScreenStyleAutumnBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="SplashScreenStyle_Autumn" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="秋季"/>
</Border>
<Border x:Name="SplashScreenStyleWinterBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="SplashScreenStyle_Winter" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="冬季"/>
</Border>
<Border x:Name="SplashScreenStyleHorseBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="SplashScreenStyle_Horse" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="马年限定"/>
</Border>
</WrapPanel>
</StackPanel>
</StackPanel>
</Border>
<!-- 浮动工具栏设置 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical">
<StackPanel Margin="18,8,18,8">
<StackPanel Orientation="Vertical" Margin="0,0,0,12">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="浮动工具栏图标" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="选择浮动工具栏的图标样式" HorizontalAlignment="Left"/>
</StackPanel>
<WrapPanel Orientation="Horizontal">
<Border x:Name="FloatingBarImgDefaultBorder" Padding="13,7" CornerRadius="8" Background="#e1e1e1" Cursor="Hand" Tag="FloatingBarImg_Default" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" FontWeight="Bold" Text="&quot;ICC-CE&quot;默认"/>
</Border>
<Border x:Name="FloatingBarImgNoShadowBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="FloatingBarImg_NoShadow" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="&quot;ICC-CE&quot;无阴影"/>
</Border>
<Border x:Name="FloatingBarImgDarkBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="FloatingBarImg_Dark" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="&quot;ICC-CE&quot;深色"/>
</Border>
<Border x:Name="FloatingBarImgDarkBreathingBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="FloatingBarImg_DarkBreathing" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="&quot;ICC-CE&quot;深色呼吸版"/>
</Border>
<Border x:Name="FloatingBarImgWhiteTransparentBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="FloatingBarImg_WhiteTransparent" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="&quot;ICC-CE&quot;白色透明版"/>
</Border>
<Border x:Name="FloatingBarImgBlackTransparentBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="FloatingBarImg_BlackTransparent" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="&quot;ICC-CE&quot;黑色透明版"/>
</Border>
<Border x:Name="FloatingBarImgKuan1Border" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="FloatingBarImg_Kuan1" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="酷安斗鸡眼滑稽"/>
</Border>
<Border x:Name="FloatingBarImgKuan2Border" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="FloatingBarImg_Kuan2" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="酷安受虐滑稽"/>
</Border>
<Border x:Name="FloatingBarImgKuan3Border" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="FloatingBarImg_Kuan3" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="酷安呲牙笑"/>
</Border>
<Border x:Name="FloatingBarImgKuan4Border" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="FloatingBarImg_Kuan4" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="酷安头戴内裤滑稽"/>
</Border>
<Border x:Name="FloatingBarImgKuan5Border" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="FloatingBarImg_Kuan5" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="酷安绿帽Doge"/>
</Border>
<Border x:Name="FloatingBarImgTiebaBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="FloatingBarImg_Tieba" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="贴吧滑稽"/>
</Border>
</WrapPanel>
</StackPanel>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自定义浮动栏图标" HorizontalAlignment="Left" Margin="0,0,12,0"/>
<Button x:Name="ButtonAddCustomIcon" Content="上传" Padding="10,3"/>
<Button x:Name="ButtonManageCustomIcons" Content="管理" Padding="10,3" Margin="5,0,0,0"/>
</StackPanel>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="浮动工具栏缩放" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="调整浮动工具栏的大小" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0">
<Slider x:Name="ViewboxFloatingBarScaleTransformValueSlider" Width="150" Minimum="0.5" Maximum="1.25" Value="1" TickFrequency="0.05" IsSnapToTickEnabled="True"/>
<TextBlock x:Name="ViewboxFloatingBarScaleTransformValueText" Text="1.00" VerticalAlignment="Center" FontSize="14" Margin="12,0,0,0" Foreground="#2e3436"/>
</StackPanel>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="浮动工具栏透明度" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="调整浮动工具栏的透明度" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0">
<Slider x:Name="ViewboxFloatingBarOpacityValueSlider" Width="150" Minimum="0.3" Maximum="1" Value="1" TickFrequency="0.05" IsSnapToTickEnabled="True"/>
<TextBlock x:Name="ViewboxFloatingBarOpacityValueText" Text="1.00" VerticalAlignment="Center" FontSize="14" Margin="12,0,0,0" Foreground="#2e3436"/>
</StackPanel>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="浮栏在PPT下透明度" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在PPT模式下浮动工具栏的透明度(重新进入PPT放映后生效)" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0">
<Slider x:Name="ViewboxFloatingBarOpacityInPPTValueSlider" Width="150" Minimum="0.3" Maximum="1" Value="1" TickFrequency="0.05" IsSnapToTickEnabled="True"/>
<TextBlock x:Name="ViewboxFloatingBarOpacityInPPTValueText" Text="1.00" VerticalAlignment="Center" FontSize="14" Margin="12,0,0,0" Foreground="#2e3436"/>
</StackPanel>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="在调色盘窗口中显示 笔尖模式 按钮" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在调色盘窗口中显示笔尖模式切换按钮" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchEnableDisPlayNibModeToggle" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="使用老版浮动栏按钮UI" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="使用旧版浮动栏按钮的界面样式" HorizontalAlignment="Left"/>
</StackPanel>
<CheckBox x:Name="CheckBoxUseLegacyFloatingBarUI" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0"/>
</Grid>
</StackPanel>
</Border>
<!-- 浮动栏按钮显示设置 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical" Margin="18,18,18,18">
<TextBlock Text="浮动栏按钮显示" FontWeight="Bold" Foreground="#2e3436" FontSize="18" Margin="0,0,0,12"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="显示形状按钮" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在浮动栏中显示形状按钮" HorizontalAlignment="Left"/>
</StackPanel>
<CheckBox x:Name="CheckBoxShowShapeButton" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0" IsChecked="True"/>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="显示撤销按钮" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在浮动栏中显示撤销按钮" HorizontalAlignment="Left"/>
</StackPanel>
<CheckBox x:Name="CheckBoxShowUndoButton" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0" IsChecked="True"/>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="显示重做按钮" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在浮动栏中显示重做按钮" HorizontalAlignment="Left"/>
</StackPanel>
<CheckBox x:Name="CheckBoxShowRedoButton" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0" IsChecked="True"/>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="显示清空按钮" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在浮动栏中显示清空按钮" HorizontalAlignment="Left"/>
</StackPanel>
<CheckBox x:Name="CheckBoxShowClearButton" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0" IsChecked="True"/>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="显示白板按钮" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在浮动栏中显示白板按钮" HorizontalAlignment="Left"/>
</StackPanel>
<CheckBox x:Name="CheckBoxShowWhiteboardButton" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0" IsChecked="True"/>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="显示隐藏按钮" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在浮动栏中显示隐藏按钮" HorizontalAlignment="Left"/>
</StackPanel>
<CheckBox x:Name="CheckBoxShowHideButton" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0" IsChecked="True"/>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="显示套索选择按钮" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在浮动栏中显示套索选择按钮" HorizontalAlignment="Left"/>
</StackPanel>
<CheckBox x:Name="CheckBoxShowLassoSelectButton" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0" IsChecked="True"/>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="显示清并鼠按钮" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在浮动栏中显示清并鼠按钮" HorizontalAlignment="Left"/>
</StackPanel>
<CheckBox x:Name="CheckBoxShowClearAndMouseButton" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0" IsChecked="True"/>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="显示快捷调色盘" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在浮动栏中显示快捷调色盘" HorizontalAlignment="Left"/>
</StackPanel>
<CheckBox x:Name="CheckBoxShowQuickColorPalette" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0"/>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="快捷调色盘显示模式" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="选择快捷调色盘的显示模式" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0">
<Border x:Name="QuickColorPaletteDisplayModeSingleBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="QuickColorPaletteDisplayMode_Single">
<TextBlock Foreground="#2e3436" FontSize="14" Text="单行显示(6色)"/>
</Border>
<Border x:Name="QuickColorPaletteDisplayModeDoubleBorder" Padding="13,7" CornerRadius="8" Background="#e1e1e1" Cursor="Hand" Tag="QuickColorPaletteDisplayMode_Double" Margin="8,0,0,0">
<TextBlock Foreground="#2e3436" FontSize="14" FontWeight="Bold" Text="双行显示(8色)"/>
</Border>
</StackPanel>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Vertical" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,8,0,12">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="橡皮按钮显示" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" TextWrapping="Wrap" Text="选择橡皮按钮的显示方式" HorizontalAlignment="Left"/>
</StackPanel>
<WrapPanel Orientation="Horizontal" Margin="0,0,0,8">
<Border x:Name="EraserDisplayOptionBothBorder" Padding="13,7" CornerRadius="8" Background="#e1e1e1" Cursor="Hand" Tag="EraserDisplayOption_Both" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" FontWeight="Bold" Text="两个都显示"/>
</Border>
<Border x:Name="EraserDisplayOptionAreaBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="EraserDisplayOption_Area" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="仅显示面积擦"/>
</Border>
<Border x:Name="EraserDisplayOptionLineBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="EraserDisplayOption_Line" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="仅显示线擦"/>
</Border>
<Border x:Name="EraserDisplayOptionNoneBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="EraserDisplayOption_None" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="都不显示"/>
</Border>
</WrapPanel>
</StackPanel>
</StackPanel>
</Border>
<!-- 任务栏托盘图标设置 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical" Margin="18,18,18,18">
<TextBlock Text="任务栏托盘图标" FontWeight="Bold" Foreground="#2e3436" FontSize="18" Margin="0,0,0,12"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="启用托盘图标" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在任务栏系统托盘显示图标" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchEnableTrayIcon" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
</StackPanel>
</Border>
<!-- 白板设置 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical">
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="白板 UI 80% 缩放" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="将白板界面缩放至80%" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchEnableViewboxBlackBoardScaleTransform" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="在白板中显示当前时间和日期" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在白板界面显示当前时间和日期" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchEnableTimeDisplayInWhiteboardMode" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="在白板中显示信仰の源1(好喝的/毒的鸡汤)" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在白板界面显示励志语句" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchEnableChickenSoupInWhiteboardMode" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Vertical" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,8,0,12">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="信仰の源出自Where" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" TextWrapping="Wrap" Text="选择励志语句的来源" HorizontalAlignment="Left"/>
</StackPanel>
<WrapPanel Orientation="Horizontal" Margin="0,0,0,8">
<Border x:Name="ChickenSoupSourceOsuBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="ChickenSoupSource_Osu" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="osu!玩家语录"/>
</Border>
<Border x:Name="ChickenSoupSourceMotivationalBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="ChickenSoupSource_Motivational" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="励志立志的名言警句"/>
</Border>
<Border x:Name="ChickenSoupSourceGaokaoBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="ChickenSoupSource_Gaokao" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="高考祝福语"/>
</Border>
<Border x:Name="ChickenSoupSourceHitokotoBorder" Padding="13,7" CornerRadius="8" Background="#e1e1e1" Cursor="Hand" Tag="ChickenSoupSource_Hitokoto" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" FontWeight="Bold" Text="一言(Hitokoto API"/>
</Border>
</WrapPanel>
</StackPanel>
</StackPanel>
</Border>
<!-- 收纳模式设置 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical">
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="在收纳模式下启用快速面板" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在收纳模式下显示快速操作面板" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchEnableQuickPanel" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="取消收纳按钮图标" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="选择取消收纳按钮的图标样式" HorizontalAlignment="Left"/>
</StackPanel>
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0">
<Border x:Name="UnFoldBtnImgArrowBorder" Padding="13,7" CornerRadius="8" Background="#e1e1e1" Cursor="Hand" Tag="UnFoldBtnImg_Arrow" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" FontWeight="Bold" Text="箭头"/>
</Border>
<Border x:Name="UnFoldBtnImgPenBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="UnFoldBtnImg_Pen" Margin="0,0,8,8">
<TextBlock Foreground="#2e3436" FontSize="14" Text="笔"/>
</Border>
</WrapPanel>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="退出收纳模式时自动切换至批注模式" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后,退出收纳模式时将自动切换至批注模式,便于快速批注" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchAutoEnterAnnotationModeWhenExitFoldMode" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="退出PPT放映后自动收纳浮动栏" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后,退出PPT放映后会自动收纳浮动栏" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchAutoFoldAfterPPTSlideShow" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="退出白板时自动收纳" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后,退出白板模式时会自动收纳到侧边栏" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchAutoFoldWhenExitWhiteboard" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</UserControl>
@@ -1,48 +0,0 @@
using System;
using System.Windows;
using System.Windows.Controls;
namespace Ink_Canvas.Windows.SettingsViews
{
/// <summary>
/// AppearancePanel.xaml 的交互逻辑
/// </summary>
public partial class AppearancePanel : UserControl
{
public AppearancePanel()
{
InitializeComponent();
}
public event EventHandler<RoutedEventArgs> IsTopBarNeedShadowEffect;
public event EventHandler<RoutedEventArgs> IsTopBarNeedNoShadowEffect;
private void ScrollViewerEx_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
var scrollViewer = (ScrollViewer)sender;
if (scrollViewer.VerticalOffset >= 10)
{
IsTopBarNeedShadowEffect?.Invoke(this, new RoutedEventArgs());
}
else
{
IsTopBarNeedNoShadowEffect?.Invoke(this, new RoutedEventArgs());
}
}
/// <summary>
/// 应用主题
/// </summary>
public void ApplyTheme()
{
try
{
ThemeHelper.ApplyThemeToControl(this);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"AppearancePanel 应用主题时出错: {ex.Message}");
}
}
}
}
@@ -1,420 +0,0 @@
<UserControl x:Class="Ink_Canvas.Windows.SettingsViews.AutomationPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ink_Canvas.Windows.SettingsViews"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
mc:Ignorable="d"
d:DesignHeight="950" d:DesignWidth="640">
<UserControl.Resources>
<!-- 开关样式模板 -->
<Style x:Key="ToggleSwitchStyle" TargetType="Border">
<Setter Property="Width" Value="48"/>
<Setter Property="Height" Value="25"/>
<Setter Property="CornerRadius" Value="12"/>
<Setter Property="Padding" Value="3,0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="Margin" Value="0,0,15,0"/>
</Style>
</UserControl.Resources>
<ScrollViewer ScrollChanged="ScrollViewerEx_ScrollChanged" IsManipulationEnabled="True" Name="ScrollViewerEx" IsDeferredScrollingEnabled="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled" IsTabStop="False" TabIndex="-1" Margin="0,0,2,2" PanningMode="VerticalOnly">
<StackPanel Margin="60,12,60,24">
<!-- 自动收纳设置 -->
<Border BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical" Margin="18,18,18,18">
<TextBlock Text="自动收纳" FontWeight="Bold" Foreground="#2e3436" FontSize="18" Margin="0,0,0,12"/>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<!-- 第一行 -->
<StackPanel Grid.Row="0" Grid.Column="0" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/EasiNote.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="希沃白板5" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchAutoFoldInEasiNote" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/EasiCamera.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="希沃展台" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchAutoFoldInEasiCamera" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="2" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/HiteBoard.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="鸿合白板" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchAutoFoldInHiteTouchPro" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="3" Orientation="Vertical" Margin="0,0,0,12">
<Image Source="/Resources/Icons-png/EasiNote3.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="希沃白板3" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchAutoFoldInEasiNote3" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<!-- 第二行 -->
<StackPanel Grid.Row="1" Grid.Column="0" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/EasiNote3C.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="希沃轻白板" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchAutoFoldInEasiNote3C" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/EasiNote5C.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="希沃轻白板5C" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchAutoFoldInEasiNote5C" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="2" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/SeewoPinco.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="希沃品课" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchAutoFoldInSeewoPincoTeacher" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="3" Orientation="Vertical" Margin="0,0,0,12">
<Image Source="/Resources/Icons-png/HiteCamera.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="鸿合展台" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchAutoFoldInHiteCamera" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<!-- 第三行 -->
<StackPanel Grid.Row="2" Grid.Column="0" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/HiteLightBoard.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="鸿合轻量白板" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchAutoFoldInHiteLightBoard" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/WenXiang.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="文香白板" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchAutoFoldInWxBoardMain" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="2" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/Whiteboard.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="微软白板" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchAutoFoldInMSWhiteboard" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="3" Orientation="Vertical" Margin="0,0,0,12">
<Image Source="/Resources/Icons-png/AdmoxWhiteboard.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="安道白板" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchAutoFoldInAdmoxWhiteboard" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<!-- 第四行 -->
<StackPanel Grid.Row="3" Grid.Column="0" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/AdmoxBooth.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="安道展台" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchAutoFoldInAdmoxBooth" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<StackPanel Grid.Row="3" Grid.Column="1" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/YiYunWhiteboard.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="艺云白板" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchAutoFoldInQPoint" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<StackPanel Grid.Row="3" Grid.Column="2" Orientation="Vertical" Margin="0,0,8,12">
<Image Source="/Resources/Icons-png/YiYunVisualPresenter.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="艺云展台" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchAutoFoldInYiYunVisualPresenter" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
<StackPanel Grid.Row="3" Grid.Column="3" Orientation="Vertical" Margin="0,0,0,12">
<Image Source="/Resources/Icons-png/MaxHubWhiteboard.png" Width="42" Height="42" HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="MaxHub白板" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,4" Foreground="#2e3436"/>
<Border x:Name="ToggleSwitchAutoFoldInMaxHubWhiteboard" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" HorizontalAlignment="Center" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</StackPanel>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Image Source="/Resources/Icons-png/Powerpoint.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="播放PPT时自动收纳" VerticalAlignment="Center"/>
</StackPanel>
<Border x:Name="ToggleSwitchAutoFoldInPPTSlideShow" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="软件退出后保持收纳模式" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后,执行自动收纳的软件在软件退出后不退出收纳模式,保持收纳状态" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchKeepFoldAfterSoftwareExit" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Image Source="/Resources/Icons-png/EasiNote.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动收纳忽略桌面EN5批注窗口" VerticalAlignment="Center"/>
</StackPanel>
<Border x:Name="ToggleSwitchAutoFoldInEasiNoteIgnoreDesktopAnno" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Image Source="/Resources/Icons-png/Donview.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="进入&quot;中原旧白板&quot;时自动收纳" VerticalAlignment="Center"/>
</StackPanel>
<Border x:Name="ToggleSwitchAutoFoldInOldZyBoard" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
</StackPanel>
</Border>
<!-- 自动查杀设置 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical" Margin="18,18,18,18">
<TextBlock Text="自动查杀" FontWeight="Bold" Foreground="#2e3436" FontSize="18" Margin="0,0,0,12"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Image Source="/Resources/Icons-png/PPTTools.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动查杀希沃&quot;PPT 小工具&quot;" VerticalAlignment="Center"/>
</StackPanel>
<Border x:Name="ToggleSwitchAutoKillPptService" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<TextBlock Text="请注意,查杀 PPT 小工具会导致希沃课堂授课助手无法使用"
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11" Margin="0,8,0,0"/>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Image Source="/Resources/Icons-png/EasiNote.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动查杀 希沃白板5" VerticalAlignment="Center"/>
</StackPanel>
<Border x:Name="ToggleSwitchAutoKillEasiNote" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Image Source="/Resources/Icons-png/HiteAnnotation.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动查杀 鸿合屏幕书写" VerticalAlignment="Center"/>
</StackPanel>
<Border x:Name="ToggleSwitchAutoKillHiteAnnotation" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="鸿合屏幕书写查杀后自动进入批注" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="查杀鸿合屏幕书写后自动进入批注模式" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchAutoEnterAnnotationAfterKillHite" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Image Source="/Resources/Icons-png/VComYouJiao.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动查杀 优教授课端" VerticalAlignment="Center"/>
</StackPanel>
<Border x:Name="ToggleSwitchAutoKillVComYouJiao" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Image Source="/Resources/Icons-png/Seewo2Annotation.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动查杀 希沃桌面2.0 桌面批注" VerticalAlignment="Center"/>
</StackPanel>
<Border x:Name="ToggleSwitchAutoKillSeewoLauncher2DesktopAnnotation" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<TextBlock Text="由于希沃桌面2.0提供的桌面批注是64位应用程序,icc是32位程序无法访问,所以目前暂不做精准匹配,只匹配进程名称DesktopAnnotation"
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11" Margin="0,8,0,0"/>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<TextBlock Text="同类软件查杀" FontWeight="Bold" Foreground="#2e3436" FontSize="18" Margin="0,0,0,12"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Image Source="/Resources/Icons-png/InkCanvas.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动查杀 Ink Canvas 和 IC+" VerticalAlignment="Center"/>
</StackPanel>
<Border x:Name="ToggleSwitchAutoKillInkCanvas" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Image Source="/Resources/Icons-png/ica.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动查杀ICA(新版旧版通杀)" VerticalAlignment="Center"/>
</StackPanel>
<Border x:Name="ToggleSwitchAutoKillICA" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Image Source="/Resources/Icons-png/idt.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动查杀 智绘教Inkeys(仅限新版)" VerticalAlignment="Center"/>
</StackPanel>
<Border x:Name="ToggleSwitchAutoKillIDT" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</UserControl>
@@ -1,204 +0,0 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Ink_Canvas.Windows.SettingsViews
{
/// <summary>
/// AutomationPanel.xaml 的交互逻辑
/// </summary>
public partial class AutomationPanel : UserControl
{
private bool _isLoaded = false;
public AutomationPanel()
{
InitializeComponent();
Loaded += AutomationPanel_Loaded;
}
private void AutomationPanel_Loaded(object sender, RoutedEventArgs e)
{
LoadSettings();
// 添加触摸支持
EnableTouchSupport();
// 应用主题
ApplyTheme();
_isLoaded = true;
}
/// <summary>
/// 加载设置
/// </summary>
private void LoadSettings()
{
try
{
if (MainWindow.Settings == null || MainWindow.Settings.Automation == null) return;
_isLoaded = false;
var automation = MainWindow.Settings.Automation;
// 自动收纳相关
SetToggleSwitchState("ToggleSwitchAutoFoldInEasiNote", automation.IsAutoFoldInEasiNote);
SetToggleSwitchState("ToggleSwitchAutoFoldInEasiCamera", automation.IsAutoFoldInEasiCamera);
SetToggleSwitchState("ToggleSwitchAutoFoldInHiteTouchPro", automation.IsAutoFoldInHiteTouchPro);
SetToggleSwitchState("ToggleSwitchAutoFoldInEasiNote3", automation.IsAutoFoldInEasiNote3);
SetToggleSwitchState("ToggleSwitchAutoFoldInEasiNote3C", automation.IsAutoFoldInEasiNote3C);
SetToggleSwitchState("ToggleSwitchAutoFoldInEasiNote5C", automation.IsAutoFoldInEasiNote5C);
SetToggleSwitchState("ToggleSwitchAutoFoldInSeewoPincoTeacher", automation.IsAutoFoldInSeewoPincoTeacher);
SetToggleSwitchState("ToggleSwitchAutoFoldInHiteCamera", automation.IsAutoFoldInHiteCamera);
SetToggleSwitchState("ToggleSwitchAutoFoldInHiteLightBoard", automation.IsAutoFoldInHiteLightBoard);
SetToggleSwitchState("ToggleSwitchAutoFoldInWxBoardMain", automation.IsAutoFoldInWxBoardMain);
SetToggleSwitchState("ToggleSwitchAutoFoldInMSWhiteboard", automation.IsAutoFoldInMSWhiteboard);
SetToggleSwitchState("ToggleSwitchAutoFoldInAdmoxWhiteboard", automation.IsAutoFoldInAdmoxWhiteboard);
SetToggleSwitchState("ToggleSwitchAutoFoldInAdmoxBooth", automation.IsAutoFoldInAdmoxBooth);
SetToggleSwitchState("ToggleSwitchAutoFoldInQPoint", automation.IsAutoFoldInQPoint);
SetToggleSwitchState("ToggleSwitchAutoFoldInYiYunVisualPresenter", automation.IsAutoFoldInYiYunVisualPresenter);
SetToggleSwitchState("ToggleSwitchAutoFoldInMaxHubWhiteboard", automation.IsAutoFoldInMaxHubWhiteboard);
SetToggleSwitchState("ToggleSwitchAutoFoldInPPTSlideShow", automation.IsAutoFoldInPPTSlideShow);
SetToggleSwitchState("ToggleSwitchAutoFoldInEasiNoteIgnoreDesktopAnno", automation.IsAutoFoldInEasiNoteIgnoreDesktopAnno);
SetToggleSwitchState("ToggleSwitchAutoFoldInOldZyBoard", automation.IsAutoFoldInOldZyBoard);
SetToggleSwitchState("ToggleSwitchKeepFoldAfterSoftwareExit", automation.KeepFoldAfterSoftwareExit);
// 自动查杀相关
SetToggleSwitchState("ToggleSwitchAutoKillPptService", automation.IsAutoKillPptService);
SetToggleSwitchState("ToggleSwitchAutoKillEasiNote", automation.IsAutoKillEasiNote);
SetToggleSwitchState("ToggleSwitchAutoKillHiteAnnotation", automation.IsAutoKillHiteAnnotation);
SetToggleSwitchState("ToggleSwitchAutoEnterAnnotationAfterKillHite", automation.IsAutoEnterAnnotationAfterKillHite);
SetToggleSwitchState("ToggleSwitchAutoKillVComYouJiao", automation.IsAutoKillVComYouJiao);
SetToggleSwitchState("ToggleSwitchAutoKillSeewoLauncher2DesktopAnnotation", automation.IsAutoKillSeewoLauncher2DesktopAnnotation);
SetToggleSwitchState("ToggleSwitchAutoKillInkCanvas", automation.IsAutoKillInkCanvas);
SetToggleSwitchState("ToggleSwitchAutoKillICA", automation.IsAutoKillICA);
SetToggleSwitchState("ToggleSwitchAutoKillIDT", automation.IsAutoKillIDT);
_isLoaded = true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"AutomationPanel 加载设置时出错: {ex.Message}");
_isLoaded = true;
}
}
/// <summary>
/// 设置 ToggleSwitch 状态
/// </summary>
private void SetToggleSwitchState(string name, bool isOn)
{
try
{
var border = FindName(name) as Border;
if (border == null) return;
border.Background = isOn
? new SolidColorBrush(Color.FromRgb(0x35, 0x84, 0xE4))
: (ThemeHelper.IsDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(0xE1, 0xE1, 0xE1)));
var innerBorder = border.Child as Border;
if (innerBorder != null)
{
innerBorder.HorizontalAlignment = isOn ? HorizontalAlignment.Right : HorizontalAlignment.Left;
// 保持滑块为白色(主题切换时也更稳定)
innerBorder.Background = new SolidColorBrush(Colors.White);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"AutomationPanel 设置 ToggleSwitch {name} 状态时出错: {ex.Message}");
}
}
private static bool IsToggleSwitchOn(Border border)
{
try
{
if (border?.Background is SolidColorBrush scb)
{
return scb.Color.A == 0xFF &&
scb.Color.R == 0x35 &&
scb.Color.G == 0x84 &&
scb.Color.B == 0xE4;
}
var s = border?.Background?.ToString();
return string.Equals(s, "#FF3584E4", StringComparison.OrdinalIgnoreCase);
}
catch
{
return false;
}
}
/// <summary>
/// ToggleSwitch 点击事件处理
/// </summary>
private void ToggleSwitch_Click(object sender, RoutedEventArgs e)
{
if (!_isLoaded) return;
var border = sender as Border;
if (border == null) return;
bool newState = !IsToggleSwitchOn(border);
SetToggleSwitchState(border.Name, newState);
// 通过 MainWindowSettingsHelper 调用 MainWindow 中的方法
string toggleSwitchName = border.Name;
MainWindowSettingsHelper.InvokeToggleSwitchToggled(toggleSwitchName, newState);
}
/// <summary>
/// 为面板中的所有交互控件启用触摸支持
/// </summary>
private void EnableTouchSupport()
{
try
{
// 延迟执行,确保所有控件都已加载
Dispatcher.BeginInvoke(new Action(() =>
{
MainWindowSettingsHelper.EnableTouchSupportForControls(this);
}), System.Windows.Threading.DispatcherPriority.Loaded);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"AutomationPanel 启用触摸支持时出错: {ex.Message}");
}
}
public event EventHandler<RoutedEventArgs> IsTopBarNeedShadowEffect;
public event EventHandler<RoutedEventArgs> IsTopBarNeedNoShadowEffect;
private void ScrollViewerEx_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
var scrollViewer = (ScrollViewer)sender;
if (scrollViewer.VerticalOffset >= 10)
{
IsTopBarNeedShadowEffect?.Invoke(this, new RoutedEventArgs());
}
else
{
IsTopBarNeedNoShadowEffect?.Invoke(this, new RoutedEventArgs());
}
}
/// <summary>
/// 应用主题
/// </summary>
public void ApplyTheme()
{
try
{
ThemeHelper.ApplyThemeToControl(this);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"AutomationPanel 应用主题时出错: {ex.Message}");
}
}
}
}
@@ -1,384 +0,0 @@
<UserControl x:Class="Ink_Canvas.Windows.SettingsViews.CanvasAndInkPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ink_Canvas.Windows.SettingsViews"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
mc:Ignorable="d"
d:DesignHeight="950" d:DesignWidth="640">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ComboBoxStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!-- 开关样式模板 -->
<Style x:Key="ToggleSwitchStyle" TargetType="Border">
<Setter Property="Width" Value="48"/>
<Setter Property="Height" Value="25"/>
<Setter Property="CornerRadius" Value="12"/>
<Setter Property="Padding" Value="3,0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="Margin" Value="0,0,15,0"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<ScrollViewer ScrollChanged="ScrollViewerEx_ScrollChanged" IsManipulationEnabled="True" Name="ScrollViewerEx" IsDeferredScrollingEnabled="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled" IsTabStop="False" TabIndex="-1" Margin="0,0,2,2" PanningMode="VerticalOnly">
<StackPanel Margin="60,12,60,24">
<!-- 画笔和光标设置 -->
<Border BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical">
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="显示画笔光标" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="绘制时显示画笔光标" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchShowCursor" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="ShowCursor" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="启用压感触屏模式" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后,触屏设备也将支持压感效果,适用于部分支持压感但无法被系统识别的触屏设备" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchEnablePressureTouchMode" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="EnablePressureTouchMode" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="屏蔽压感" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后,将忽略所有设备的压感信息,使所有笔画具有统一的粗细。与压感触屏模式互斥" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchDisablePressure" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="DisablePressure" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="板擦橡皮大小" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0">
<Border x:Name="EraserSizeVerySmall" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="EraserSize_VerySmall" MouseLeftButtonDown="OptionButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" Text="很小"/>
</Border>
<Border x:Name="EraserSizeSmall" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="EraserSize_Small" Margin="8,0,0,0" MouseLeftButtonDown="OptionButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" Text="较小"/>
</Border>
<Border x:Name="EraserSizeMedium" Padding="13,7" CornerRadius="8" Background="#e1e1e1" Cursor="Hand" Tag="EraserSize_Medium" Margin="8,0,0,0" MouseLeftButtonDown="OptionButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" FontWeight="Bold" Text="中等"/>
</Border>
<Border x:Name="EraserSizeLarge" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="EraserSize_Large" Margin="8,0,0,0" MouseLeftButtonDown="OptionButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" Text="较大"/>
</Border>
<Border x:Name="EraserSizeVeryLarge" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="EraserSize_VeryLarge" Margin="8,0,0,0" MouseLeftButtonDown="OptionButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" Text="很大"/>
</Border>
</StackPanel>
</Grid>
</StackPanel>
</Border>
<!-- 墨迹显示设置 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical">
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="退出画板模式后隐藏墨迹" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后,进入 PPT 模式时未处于批注模式时不会显示墨迹" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchHideStrokeWhenSelecting" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="HideStrokeWhenSelecting" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="清空墨迹时删除墨迹历史记录" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后,清空墨迹后将无法通过&quot;撤销&quot;功能恢复" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchClearCanvasAndClearTimeMachine" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="ClearCanvasAndClearTimeMachine" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="清空画布时同时清空图片" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="清空画布时同时删除所有插入的图片" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchClearCanvasAlsoClearImages" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1" Tag="ClearCanvasAlsoClearImages" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="插入图片时自动压缩(大于1920x1080" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="插入的图片大于1920x1080时自动压缩以节省内存" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchCompressPicturesUploaded" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="CompressPicturesUploaded" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="白板展台按钮启动希沃视频展台" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后,点击白板工具栏「展台」将打开希沃视频展台(需已安装);关闭则使用内置展台" HorizontalAlignment="Left" TextWrapping="Wrap" MaxWidth="420"/>
</StackPanel>
<Border x:Name="ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1" Tag="LaunchSeewoVideoShowcaseForWhiteboardBooth" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
</StackPanel>
</Border>
<!-- 形状和曲线设置 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical">
<Grid Height="54">
<StackPanel Orientation="Horizontal" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="保留双曲线渐近线" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0">
<Border x:Name="HyperbolaAsymptoteYes" Padding="13,7" CornerRadius="8" Background="#e1e1e1" Cursor="Hand" Tag="HyperbolaAsymptote_Yes" MouseLeftButtonDown="OptionButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" FontWeight="Bold" Text="是"/>
</Border>
<Border x:Name="HyperbolaAsymptoteNo" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="HyperbolaAsymptote_No" Margin="8,0,0,0" MouseLeftButtonDown="OptionButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" Text="否"/>
</Border>
<Border x:Name="HyperbolaAsymptoteAsk" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="HyperbolaAsymptote_Ask" Margin="8,0,0,0" MouseLeftButtonDown="OptionButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" Text="每次询问"/>
</Border>
</StackPanel>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="绘制圆时显示圆心位置" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="绘制圆形时显示圆心标记" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchShowCircleCenter" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="ShowCircleCenter" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="使用WPF默认贝塞尔曲线平滑" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="使用WPF内置的曲线平滑算法" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchFitToCurve" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1" Tag="FitToCurve" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="使用高级曲线平滑(推荐)" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="使用改进的贝塞尔曲线平滑算法,提供更好的绘制效果" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchAdvancedBezierSmoothing" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="AdvancedBezierSmoothing" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
</StackPanel>
</Border>
<!-- 墨迹渐隐设置 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical">
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="启用墨迹渐隐功能" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后墨迹不会绘制到画布上,而是保持湿墨迹状态,根据设置的渐隐时间自动消失" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchEnableInkFade" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1" Tag="EnableInkFade" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54" x:Name="InkFadeTimePanel" Visibility="Collapsed">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="墨迹渐隐时间" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="设置墨迹从绘制到完全消失的时间" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0">
<Slider x:Name="InkFadeTimeSlider" Width="150" Minimum="1000" Maximum="10000" Value="3000" TickFrequency="1000" IsSnapToTickEnabled="True" ValueChanged="InkFadeTimeSlider_ValueChanged"/>
<TextBlock x:Name="InkFadeTimeText" Text="3000ms" VerticalAlignment="Center" FontSize="14" Margin="12,0,0,0" Foreground="#2e3436"/>
</StackPanel>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="在笔工具菜单中隐藏墨迹渐隐控制" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后,主工具栏上点击笔工具后弹出的上下文菜单中将不显示墨迹渐隐控制开关" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchHideInkFadeControlInPenMenu" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1" Tag="HideInkFadeControlInPenMenu" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
</StackPanel>
</Border>
<!-- 墨迹保存设置 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical" Margin="18,18,18,18">
<TextBlock Text="墨迹保存设置" FontWeight="Bold" Foreground="#2e3436" FontSize="18" Margin="0,0,0,12"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="定时自动保存墨迹" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后将在设定时间间隔自动保存墨迹,仅在画布可见且有墨迹时才会保存" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchEnableAutoSaveStrokes" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1" Tag="EnableAutoSaveStrokes" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<StackPanel x:Name="AutoSaveIntervalPanel" Visibility="Collapsed">
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="保存间隔" HorizontalAlignment="Left" Margin="0,0,12,0"/>
</StackPanel>
<WrapPanel Orientation="Horizontal" Margin="0,0,15,0">
<Border x:Name="AutoSaveStrokesInterval1MinBorder" Padding="13,7" CornerRadius="8" Background="#e1e1e1" Cursor="Hand" Tag="AutoSaveStrokesInterval_1" Margin="0,0,8,8" MouseLeftButtonDown="AutoSaveIntervalButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" FontWeight="Bold" Text="1分钟"/>
</Border>
<Border x:Name="AutoSaveStrokesInterval3MinBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="AutoSaveStrokesInterval_3" Margin="0,0,8,8" MouseLeftButtonDown="AutoSaveIntervalButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" Text="3分钟"/>
</Border>
<Border x:Name="AutoSaveStrokesInterval5MinBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="AutoSaveStrokesInterval_5" Margin="0,0,8,8" MouseLeftButtonDown="AutoSaveIntervalButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" Text="5分钟"/>
</Border>
<Border x:Name="AutoSaveStrokesInterval10MinBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="AutoSaveStrokesInterval_10" Margin="0,0,8,8" MouseLeftButtonDown="AutoSaveIntervalButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" Text="10分钟"/>
</Border>
<Border x:Name="AutoSaveStrokesInterval15MinBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="AutoSaveStrokesInterval_15" Margin="0,0,8,8" MouseLeftButtonDown="AutoSaveIntervalButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" Text="15分钟"/>
</Border>
<Border x:Name="AutoSaveStrokesInterval30MinBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="AutoSaveStrokesInterval_30" Margin="0,0,8,8" MouseLeftButtonDown="AutoSaveIntervalButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" Text="30分钟"/>
</Border>
<Border x:Name="AutoSaveStrokesInterval60MinBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="AutoSaveStrokesInterval_60" Margin="0,0,8,8" MouseLeftButtonDown="AutoSaveIntervalButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" Text="60分钟"/>
</Border>
</WrapPanel>
</Grid>
</StackPanel>
<Grid MinHeight="54">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical" Grid.Column="0" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,8,0,8">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="墨迹全页面保存" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" TextWrapping="Wrap" Text="开启后自动保存和手动保存墨迹时将以全屏模式保存。如果存在多个画布和墨迹,将把所有页面的墨迹按照每页为单位保存进一个压缩包中" HorizontalAlignment="Left" MaxWidth="450"/>
</StackPanel>
<Border x:Name="ToggleSwitchSaveFullPageStrokes" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1" Grid.Column="1" VerticalAlignment="Top" Margin="0,8,15,8" Tag="SaveFullPageStrokes" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="保存为XML格式" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后保存墨迹时将使用XML格式(ISF格式),便于查看和编辑墨迹数据" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchSaveStrokesAsXML" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1" Tag="SaveStrokesAsXML" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
<Grid MinHeight="54">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical" Grid.Column="0" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,8,0,8">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动保存幻灯片墨迹" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" TextWrapping="Wrap" Text="开启后将在结束幻灯片放映时自动保存已有墨迹,并在下次打开时自动加载(文件名和幻灯片页数都要相同)" HorizontalAlignment="Left" MaxWidth="450"/>
</StackPanel>
<Border x:Name="ToggleSwitchAutoSaveStrokesInPowerPoint" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Grid.Column="1" VerticalAlignment="Top" Margin="0,8,15,8" Tag="AutoSaveStrokesInPowerPoint" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</UserControl>

Some files were not shown because too many files have changed in this diff Show More