diff --git a/AutomaticUpdateVersionControl.txt b/AutomaticUpdateVersionControl.txt index 96b14650..dfebfd9a 100644 --- a/AutomaticUpdateVersionControl.txt +++ b/AutomaticUpdateVersionControl.txt @@ -1 +1 @@ -1.7.9.0 +1.7.10.0 diff --git a/Ink Canvas/App.xaml b/Ink Canvas/App.xaml index 92169636..1ba8b030 100644 --- a/Ink Canvas/App.xaml +++ b/Ink Canvas/App.xaml @@ -10,7 +10,7 @@ - + diff --git a/Ink Canvas/AssemblyInfo.cs b/Ink Canvas/AssemblyInfo.cs index 7e0bc215..dff64f25 100644 --- a/Ink Canvas/AssemblyInfo.cs +++ b/Ink Canvas/AssemblyInfo.cs @@ -49,5 +49,5 @@ using System.Windows; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.7.9.1")] -[assembly: AssemblyFileVersion("1.7.9.1")] +[assembly: AssemblyVersion("1.7.10.0")] +[assembly: AssemblyFileVersion("1.7.10.0")] diff --git a/Ink Canvas/Helpers/AutoUpdateHelper.cs b/Ink Canvas/Helpers/AutoUpdateHelper.cs index d860f4b8..8db4fb88 100644 --- a/Ink Canvas/Helpers/AutoUpdateHelper.cs +++ b/Ink Canvas/Helpers/AutoUpdateHelper.cs @@ -513,7 +513,7 @@ namespace Ink_Canvas.Helpers // 尝试获取当前版本的发布时间 DateTime? currentVersionReleaseTime = await GetVersionReleaseTime(localVersion, channel); - bool shouldPush = DeviceIdentifier.ShouldPushUpdate(apiVersion, releaseTime, true, currentVersionReleaseTime); // 明确标记为自动更新 + bool shouldPush = DeviceIdentifier.ShouldPushUpdate(apiVersion, releaseTime, true, currentVersionReleaseTime, localVersion); // 明确标记为自动更新 if (!shouldPush) { var priority = DeviceIdentifier.GetUpdatePriority(); @@ -569,7 +569,7 @@ namespace Ink_Canvas.Helpers // 尝试获取当前版本的发布时间 DateTime? currentVersionReleaseTime = await GetVersionReleaseTime(localVersion, channel); - bool shouldPush = DeviceIdentifier.ShouldPushUpdate(remoteVersion, DateTime.Now, true, currentVersionReleaseTime); // 明确标记为自动更新 + bool shouldPush = DeviceIdentifier.ShouldPushUpdate(remoteVersion, DateTime.Now, true, currentVersionReleaseTime, localVersion); // 明确标记为自动更新 if (!shouldPush) { var priority = DeviceIdentifier.GetUpdatePriority(); @@ -1610,12 +1610,24 @@ namespace Ink_Canvas.Helpers Directory.CreateDirectory(destinationDir); } + // 定义需要覆盖的文件列表(仅覆盖主程序和配置文件) + string[] filesToOverwrite = { "InkCanvasForClass.exe", "InkCanvasForClass.exe.config" }; + // 复制文件 foreach (FileInfo file in dir.GetFiles()) { + // 只覆盖指定的文件,跳过其他文件 + if (!filesToOverwrite.Contains(file.Name)) + { + LogHelper.WriteLogToFile($"AutoUpdate | 跳过文件(不在覆盖列表中): {file.Name}"); + continue; + } + string targetFilePath = Path.Combine(destinationDir, file.Name); bool fileCopied = false; + LogHelper.WriteLogToFile($"AutoUpdate | 开始覆盖文件: {file.Name}"); + // 重试机制,最多重试3次 for (int retry = 0; retry < 3; retry++) { @@ -1641,6 +1653,7 @@ namespace Ink_Canvas.Helpers await Task.Run(() => file.CopyTo(targetFilePath)); fileCopied = true; + LogHelper.WriteLogToFile($"AutoUpdate | 文件覆盖成功: {file.Name}"); break; } catch (Exception ex) @@ -1687,12 +1700,24 @@ namespace Ink_Canvas.Helpers Directory.CreateDirectory(destinationDir); } + // 定义需要覆盖的文件列表(仅覆盖主程序和配置文件) + string[] filesToOverwrite = { "InkCanvasForClass.exe", "InkCanvasForClass.exe.config" }; + // 复制文件 foreach (FileInfo file in dir.GetFiles()) { + // 只覆盖指定的文件,跳过其他文件 + if (!filesToOverwrite.Contains(file.Name)) + { + LogHelper.WriteLogToFile($"AutoUpdate | 跳过文件(不在覆盖列表中): {file.Name}"); + continue; + } + string targetFilePath = Path.Combine(destinationDir, file.Name); try { + LogHelper.WriteLogToFile($"AutoUpdate | 开始覆盖文件: {file.Name}"); + // 如果目标文件存在且正在使用,先删除 if (File.Exists(targetFilePath)) { @@ -1700,6 +1725,7 @@ namespace Ink_Canvas.Helpers } await Task.Run(() => file.CopyTo(targetFilePath)); + LogHelper.WriteLogToFile($"AutoUpdate | 文件覆盖成功: {file.Name}"); } catch (Exception ex) { diff --git a/Ink Canvas/Helpers/DeviceIdentifier.cs b/Ink Canvas/Helpers/DeviceIdentifier.cs index c079bc98..586dcc0c 100644 --- a/Ink Canvas/Helpers/DeviceIdentifier.cs +++ b/Ink Canvas/Helpers/DeviceIdentifier.cs @@ -868,7 +868,7 @@ namespace Ink_Canvas.Helpers decryptedData[i] = (byte)(data[i] ^ keyBytes[i % keyBytes.Length]); } - // 验证校验和 + // 验证校验 byte[] computedChecksum = sha256.ComputeHash(decryptedData); if (!checksum.SequenceEqual(computedChecksum)) { @@ -893,8 +893,9 @@ namespace Ink_Canvas.Helpers return null; } + /// - /// 保存使用统计到文件(加密) + /// 保存使用统计到文件 /// private static void SaveUsageStatsToFile(string filePath, UsageStats stats) { @@ -1054,7 +1055,7 @@ namespace Ink_Canvas.Helpers /// 是否为自动更新检查(默认true,false表示版本修复) /// 当前版本发布时间 /// 是否应该推送更新 - public static bool ShouldPushUpdate(string updateVersion, DateTime releaseTime, bool isAutoUpdate = true, DateTime? currentVersionReleaseTime = null) + public static bool ShouldPushUpdate(string updateVersion, DateTime releaseTime, bool isAutoUpdate = true, DateTime? currentVersionReleaseTime = null, string localVersion = null) { try { @@ -1086,6 +1087,25 @@ namespace Ink_Canvas.Helpers daysBetweenVersions = (DateTime.Now - releaseTime).TotalDays; } + // 当无法获取版本发布时间时,判断版本号差异 + if (!currentVersionReleaseTime.HasValue && !string.IsNullOrEmpty(localVersion)) + { + int versionDiff = CalculateVersionGenerationDifference(localVersion, updateVersion); + LogHelper.WriteLogToFile($"DeviceIdentifier | 无法获取版本发布时间,使用版本号差异判断 - 本地版本: {localVersion}, 远程版本: {updateVersion}, 代数差异: {versionDiff}"); + + // 当版本号代数差异大于3时自动更新 + if (versionDiff > 3) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 版本号代数差异({versionDiff})大于3,自动更新"); + return true; + } + else + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 版本号代数差异({versionDiff})不大于3,暂不更新"); + return false; + } + } + // 计算最近活跃度(最后一次使用距今的天数) var daysSinceLastUse = (DateTime.Now - stats.LastLaunchTime).TotalDays; @@ -1265,6 +1285,77 @@ namespace Ink_Canvas.Helpers } } + /// + /// 计算版本号代数差异 + /// + /// 本地版本号 + /// 远程版本号 + /// 版本号代数差异,如果无法计算则返回0 + private static int CalculateVersionGenerationDifference(string localVersion, string remoteVersion) + { + try + { + if (string.IsNullOrEmpty(localVersion) || string.IsNullOrEmpty(remoteVersion)) + return 0; + + // 移除可能的前缀(如 "v") + var cleanLocal = localVersion.TrimStart('v', 'V'); + var cleanRemote = remoteVersion.TrimStart('v', 'V'); + + // 解析版本号 (格式: X.X.X.X) + var localParts = cleanLocal.Split('.'); + var remoteParts = cleanRemote.Split('.'); + + if (localParts.Length < 4 || remoteParts.Length < 4) + return 0; + + // 解析四个版本号部分 + if (int.TryParse(localParts[0], out int localMajor) && + int.TryParse(localParts[1], out int localMinor) && + int.TryParse(localParts[2], out int localBuild) && + int.TryParse(localParts[3], out int localRevision) && + int.TryParse(remoteParts[0], out int remoteMajor) && + int.TryParse(remoteParts[1], out int remoteMinor) && + int.TryParse(remoteParts[2], out int remoteBuild) && + int.TryParse(remoteParts[3], out int remoteRevision)) + { + // 计算代数差异:主版本号差异 * 1000 + 次版本号差异 * 100 + 构建号差异 * 10 + 修订号差异 + int majorDiff = remoteMajor - localMajor; + int minorDiff = remoteMinor - localMinor; + int buildDiff = remoteBuild - localBuild; + int revisionDiff = remoteRevision - localRevision; + + // 如果主版本号不同,则代数差异很大 + if (majorDiff != 0) + { + return majorDiff * 1000 + minorDiff * 100 + buildDiff * 10 + revisionDiff; + } + + // 如果次版本号不同,则代数差异中等 + if (minorDiff != 0) + { + return minorDiff * 100 + buildDiff * 10 + revisionDiff; + } + + // 如果构建号不同,则代数差异较小 + if (buildDiff != 0) + { + return buildDiff * 10 + revisionDiff; + } + + // 只有修订号不同,代数差异最小 + return revisionDiff; + } + + return 0; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"DeviceIdentifier | 计算版本号代数差异失败: {ex.Message}", LogHelper.LogType.Warning); + return 0; + } + } + /// /// 根据更新类型获取紧急程度倍数(仅用于自动更新分级) /// diff --git a/Ink Canvas/Helpers/GlobalHotkeyManager.cs b/Ink Canvas/Helpers/GlobalHotkeyManager.cs index b5ba3f40..a5640467 100644 --- a/Ink Canvas/Helpers/GlobalHotkeyManager.cs +++ b/Ink Canvas/Helpers/GlobalHotkeyManager.cs @@ -402,9 +402,19 @@ namespace Ink_Canvas.Helpers { if (isMouseMode) { - // 鼠标模式下禁用快捷键,让键盘操作放行 - DisableHotkeyRegistration(); - LogHelper.WriteLogToFile("切换到鼠标模式,禁用快捷键以放行键盘操作"); + // 检查设置中是否允许在鼠标模式下启用快捷键 + if (MainWindow.Settings.Appearance.EnableHotkeysInMouseMode) + { + // 如果设置允许,则在鼠标模式下也启用快捷键 + EnableHotkeyRegistration(); + LogHelper.WriteLogToFile("切换到鼠标模式,但根据设置保持快捷键启用"); + } + else + { + // 鼠标模式下禁用快捷键,让键盘操作放行 + DisableHotkeyRegistration(); + LogHelper.WriteLogToFile("切换到鼠标模式,禁用快捷键以放行键盘操作"); + } } else { diff --git a/Ink Canvas/Helpers/InkFadeManager.cs b/Ink Canvas/Helpers/InkFadeManager.cs index 4490b721..a13d1ea6 100644 --- a/Ink Canvas/Helpers/InkFadeManager.cs +++ b/Ink Canvas/Helpers/InkFadeManager.cs @@ -6,6 +6,7 @@ using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; +using System.Windows.Media.Effects; using System.Windows.Shapes; using System.Windows.Threading; @@ -324,6 +325,13 @@ namespace Ink_Canvas.Helpers { path.StrokeThickness = Math.Max(drawingAttribs.Width * 1.5, 20); } + + // 为高亮笔添加轻微的模糊效果,使渐隐更加自然 + path.Effect = new BlurEffect + { + Radius = 0.5, // 轻微的模糊效果 + KernelType = KernelType.Gaussian + }; } // 不设置任何变换,保持墨迹原有粗细 @@ -389,6 +397,57 @@ namespace Ink_Canvas.Helpers } } + /// + /// 统一渐隐动画 - 整个墨迹作为一个整体进行渐隐,与擦除效果一致 + /// + private void StartUnifiedFadeAnimation(UIElement visual, Stroke stroke, double currentOpacity, int duration) + { + try + { + // 创建透明度动画,模拟擦除时的效果 + var fadeAnimation = new DoubleAnimation + { + From = currentOpacity, + To = 0.0, + Duration = TimeSpan.FromMilliseconds(duration), + EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut } + }; + + // 如果是高亮笔,添加轻微的缩放效果,使渐隐更加自然 + if (stroke.DrawingAttributes.IsHighlighter) + { + // 创建轻微的缩放动画,模拟墨迹"蒸发"的效果 + var scaleAnimation = new DoubleAnimation + { + From = 1.0, + To = 0.95, // 轻微缩小,增加自然感 + Duration = TimeSpan.FromMilliseconds(duration), + EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseIn } + }; + + // 创建缩放变换 + var scaleTransform = new ScaleTransform(); + visual.RenderTransform = scaleTransform; + visual.RenderTransformOrigin = new Point(0.5, 0.5); + + // 应用缩放动画 + scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnimation); + scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnimation); + } + + // 添加动画完成事件 + fadeAnimation.Completed += (sender, e) => OnAnimationCompleted(visual, stroke); + + // 应用透明度动画 + visual.BeginAnimation(UIElement.OpacityProperty, fadeAnimation); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"统一渐隐动画失败: {ex}", LogHelper.LogType.Error); + OnAnimationCompleted(visual, stroke); + } + } + /// /// 开始高亮笔的渐隐动画 /// @@ -396,7 +455,8 @@ namespace Ink_Canvas.Helpers { try { - StartProgressiveFadeAnimation(visual, stroke, currentOpacity, (int)(AnimationDuration * 1.5)); + // 高亮笔使用统一的渐隐动画,与擦除效果一致 + StartUnifiedFadeAnimation(visual, stroke, currentOpacity, (int)(AnimationDuration * 1.2)); } catch (Exception ex) { diff --git a/Ink Canvas/Helpers/PPTManager.cs b/Ink Canvas/Helpers/PPTManager.cs index d8a03bd4..48fc4c70 100644 --- a/Ink Canvas/Helpers/PPTManager.cs +++ b/Ink Canvas/Helpers/PPTManager.cs @@ -70,7 +70,32 @@ namespace Ink_Canvas.Helpers try { if (PPTApplication == null || !Marshal.IsComObject(PPTApplication)) return false; - return PPTApplication.SlideShowWindows?.Count > 0; + + // 检查是否有放映窗口 + var slideShowWindows = PPTApplication.SlideShowWindows; + if (slideShowWindows == null || slideShowWindows.Count == 0) return false; + + // 验证放映窗口是否真正有效 + try + { + var slideShowWindow = slideShowWindows[1]; + if (slideShowWindow == null) return false; + + // 尝试访问放映窗口的属性来验证其有效性 + var _ = slideShowWindow.View; + return true; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + // COM对象已失效,触发断开连接 + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"验证PPT放映窗口失败: {comEx.Message} (HR: 0x{hr:X8})", LogHelper.LogType.Warning); + return false; + } } catch (COMException comEx) { @@ -80,10 +105,12 @@ namespace Ink_Canvas.Helpers // COM对象已失效,触发断开连接 DisconnectFromPPT(); } + LogHelper.WriteLogToFile($"检查PPT放映状态失败: {comEx.Message} (HR: 0x{hr:X8})", LogHelper.LogType.Warning); return false; } - catch + catch (Exception ex) { + LogHelper.WriteLogToFile($"检查PPT放映状态时发生意外错误: {ex}", LogHelper.LogType.Warning); return false; } } @@ -452,7 +479,30 @@ namespace Ink_Canvas.Helpers { CurrentPresentation = activePresentation; CurrentSlides = CurrentPresentation.Slides; - SlidesCount = CurrentSlides.Count; + + // 验证页数读取是否成功 + try + { + var slideCount = CurrentSlides.Count; + if (slideCount > 0) + { + SlidesCount = slideCount; + LogHelper.WriteLogToFile($"成功读取PPT页数: {slideCount}", LogHelper.LogType.Trace); + } + else + { + // 页数为0,可能是空演示文稿或读取失败 + SlidesCount = 0; + LogHelper.WriteLogToFile("PPT演示文稿页数为0,可能为空演示文稿", LogHelper.LogType.Warning); + } + } + catch (COMException comEx) + { + // 页数读取失败 + var hr = (uint)comEx.HResult; + SlidesCount = 0; + LogHelper.WriteLogToFile($"读取PPT页数失败: {comEx.Message} (HR: 0x{hr:X8})", LogHelper.LogType.Warning); + } // 获取当前幻灯片 try diff --git a/Ink Canvas/Helpers/PPTUIManager.cs b/Ink Canvas/Helpers/PPTUIManager.cs index b022a29b..1e29affc 100644 --- a/Ink Canvas/Helpers/PPTUIManager.cs +++ b/Ink Canvas/Helpers/PPTUIManager.cs @@ -81,10 +81,19 @@ namespace Ink_Canvas.Helpers _mainWindow.BtnPPTSlideShow.Visibility = Visibility.Collapsed; _mainWindow.BtnPPTSlideShowEnd.Visibility = Visibility.Visible; + // 只有在页数有效时才更新页码显示 if (currentSlide > 0 && totalSlides > 0) { _mainWindow.PPTBtnPageNow.Text = currentSlide.ToString(); _mainWindow.PPTBtnPageTotal.Text = $"/ {totalSlides}"; + LogHelper.WriteLogToFile($"更新PPT页码显示: {currentSlide}/{totalSlides}", LogHelper.LogType.Trace); + } + else + { + // 页数无效时清空页码显示 + _mainWindow.PPTBtnPageNow.Text = "?"; + _mainWindow.PPTBtnPageTotal.Text = "/ ?"; + LogHelper.WriteLogToFile($"PPT页数无效,清空页码显示: 当前页={currentSlide}, 总页数={totalSlides}", LogHelper.LogType.Warning); } UpdateNavigationPanelsVisibility(); @@ -113,8 +122,20 @@ namespace Ink_Canvas.Helpers { try { - _mainWindow.PPTBtnPageNow.Text = currentSlide.ToString(); - _mainWindow.PPTBtnPageTotal.Text = $"/ {totalSlides}"; + // 只有在页数有效时才更新页码显示 + if (currentSlide > 0 && totalSlides > 0) + { + _mainWindow.PPTBtnPageNow.Text = currentSlide.ToString(); + _mainWindow.PPTBtnPageTotal.Text = $"/ {totalSlides}"; + LogHelper.WriteLogToFile($"更新PPT页码显示: {currentSlide}/{totalSlides}", LogHelper.LogType.Trace); + } + else + { + // 页数无效时清空页码显示 + _mainWindow.PPTBtnPageNow.Text = "?"; + _mainWindow.PPTBtnPageTotal.Text = "/ ?"; + LogHelper.WriteLogToFile($"PPT页数无效,清空页码显示: 当前页={currentSlide}, 总页数={totalSlides}", LogHelper.LogType.Warning); + } } catch (Exception ex) { @@ -156,17 +177,25 @@ namespace Ink_Canvas.Helpers try { // 检查是否应该显示PPT按钮 - // 不仅要检查按钮设置,还要确保确实在PPT放映模式下 + // 不仅要检查按钮设置,还要确保确实在PPT放映模式下且页数有效 + bool isInSlideShow = _mainWindow.PPTManager?.IsInSlideShow == true; + int slidesCount = _mainWindow.PPTManager?.SlidesCount ?? 0; + bool hasValidPageCount = slidesCount > 0; + bool shouldShowButtons = ShowPPTButton && _mainWindow.BtnPPTSlideShowEnd.Visibility == Visibility.Visible && - _mainWindow.PPTManager?.IsInSlideShow == true; + isInSlideShow && + hasValidPageCount; if (!shouldShowButtons) { HideAllNavigationPanels(); + LogHelper.WriteLogToFile($"隐藏PPT导航面板 - 放映状态: {isInSlideShow}, 页数: {slidesCount}, 按钮设置: {ShowPPTButton}", LogHelper.LogType.Trace); return; } + LogHelper.WriteLogToFile($"显示PPT导航面板 - 放映状态: {isInSlideShow}, 页数: {slidesCount}", LogHelper.LogType.Trace); + // 设置侧边按钮位置 _mainWindow.LeftSidePanelForPPTNavigation.Margin = new Thickness(0, 0, 0, PPTLSButtonPosition * 2); _mainWindow.RightSidePanelForPPTNavigation.Margin = new Thickness(0, 0, 0, PPTRSButtonPosition * 2); diff --git a/Ink Canvas/Helpers/Plugins/EnhancedPluginBaseV2.cs b/Ink Canvas/Helpers/Plugins/EnhancedPluginBaseV2.cs new file mode 100644 index 00000000..d4f2f96f --- /dev/null +++ b/Ink Canvas/Helpers/Plugins/EnhancedPluginBaseV2.cs @@ -0,0 +1,241 @@ +using System.Windows.Controls; + +namespace Ink_Canvas.Helpers.Plugins +{ + /// + /// 增强的插件基类 V2,提供对三个专门服务接口的访问 + /// 插件开发者可以根据需要选择性地使用这些服务 + /// + public abstract class EnhancedPluginBaseV2 : PluginBase, IEnhancedPlugin + { + /// + /// 获取服务实例 + /// + public IGetService GetService { get; private set; } + + /// + /// 窗口服务实例 + /// + public IWindowService WindowService { get; private set; } + + /// + /// 操作服务实例 + /// + public IActionService ActionService { get; private set; } + + /// + /// 插件服务实例(兼容性) + /// + public IPluginService PluginService { get; private set; } + + /// + /// 构造函数 + /// + protected EnhancedPluginBaseV2() + { + // 初始化所有服务实例 + PluginService = PluginServiceManager.Instance; + GetService = PluginServiceManager.Instance; + WindowService = PluginServiceManager.Instance; + ActionService = PluginServiceManager.Instance; + } + + /// + /// 插件启动时调用,在Initialize之后 + /// + public virtual void OnStartup() + { + LogHelper.WriteLogToFile($"插件 {Name} 已启动"); + } + + /// + /// 插件关闭时调用,在Cleanup之前 + /// + public virtual void OnShutdown() + { + LogHelper.WriteLogToFile($"插件 {Name} 正在关闭"); + } + + /// + /// 获取插件的菜单项 + /// + /// 菜单项集合 + public virtual MenuItem[] GetMenuItems() + { + return new MenuItem[0]; + } + + /// + /// 获取插件的工具栏按钮 + /// + /// 工具栏按钮集合 + public virtual Button[] GetToolbarButtons() + { + return new Button[0]; + } + + /// + /// 获取插件的状态栏信息 + /// + /// 状态栏信息 + public virtual string GetStatusBarInfo() + { + return $"{Name} v{Version} - {(IsEnabled ? "已启用" : "已禁用")}"; + } + + /// + /// 插件配置变更时调用 + /// + public virtual void OnConfigurationChanged() + { + LogHelper.WriteLogToFile($"插件 {Name} 配置已变更"); + } + + #region 便捷方法 + + /// + /// 显示通知消息 + /// + /// 消息内容 + /// 消息类型 + protected void ShowNotification(string message, NotificationType type = NotificationType.Info) + { + WindowService.ShowNotification(message, type); + } + + /// + /// 显示确认对话框 + /// + /// 消息内容 + /// 标题 + /// 用户选择结果 + protected bool ShowConfirmDialog(string message, string title = "确认") + { + return WindowService.ShowConfirmDialog(message, title); + } + + /// + /// 显示输入对话框 + /// + /// 提示消息 + /// 标题 + /// 默认值 + /// 用户输入内容 + protected string ShowInputDialog(string message, string title = "输入", string defaultValue = "") + { + return WindowService.ShowInputDialog(message, title, defaultValue); + } + + /// + /// 获取系统设置 + /// + /// 设置类型 + /// 设置键 + /// 默认值 + /// 设置值 + protected T GetSetting(string key, T defaultValue = default(T)) + { + return GetService.GetSetting(key, defaultValue); + } + + /// + /// 设置系统设置 + /// + /// 设置类型 + /// 设置键 + /// 设置值 + protected void SetSetting(string key, T value) + { + ActionService.SetSetting(key, value); + } + + /// + /// 保存设置 + /// + protected void SaveSettings() + { + ActionService.SaveSettings(); + } + + /// + /// 清除当前画布 + /// + protected void ClearCanvas() + { + ActionService.ClearCanvas(); + } + + /// + /// 撤销操作 + /// + protected void Undo() + { + ActionService.Undo(); + } + + /// + /// 重做操作 + /// + protected void Redo() + { + ActionService.Redo(); + } + + /// + /// 检查是否可以撤销 + /// + protected bool CanUndo => GetService.CanUndo; + + /// + /// 检查是否可以重做 + /// + protected bool CanRedo => GetService.CanRedo; + + /// + /// 获取当前绘制模式 + /// + protected int CurrentDrawingMode => GetService.CurrentDrawingMode; + + /// + /// 设置绘制模式 + /// + /// 绘制模式 + protected void SetDrawingMode(int mode) + { + ActionService.SetDrawingMode(mode); + } + + /// + /// 注册事件处理器 + /// + /// 事件名称 + /// 事件处理器 + protected void RegisterEventHandler(string eventName, System.EventHandler handler) + { + ActionService.RegisterEventHandler(eventName, handler); + } + + /// + /// 注销事件处理器 + /// + /// 事件名称 + /// 事件处理器 + protected void UnregisterEventHandler(string eventName, System.EventHandler handler) + { + ActionService.UnregisterEventHandler(eventName, handler); + } + + /// + /// 触发事件 + /// + /// 事件名称 + /// 事件发送者 + /// 事件参数 + protected void TriggerEvent(string eventName, object sender, System.EventArgs args) + { + ActionService.TriggerEvent(eventName, sender, args); + } + + #endregion + } +} \ No newline at end of file diff --git a/Ink Canvas/Helpers/Plugins/IActionService.cs b/Ink Canvas/Helpers/Plugins/IActionService.cs new file mode 100644 index 00000000..0101a605 --- /dev/null +++ b/Ink Canvas/Helpers/Plugins/IActionService.cs @@ -0,0 +1,296 @@ +using System; +using System.Windows.Media; + +namespace Ink_Canvas.Helpers.Plugins +{ + /// + /// 操作服务接口,统一所有执行操作相关的方法 + /// + public interface IActionService + { + #region 画布操作 + + /// + /// 清除当前画布 + /// + void ClearCanvas(); + + /// + /// 清除所有画布 + /// + void ClearAllCanvases(); + + /// + /// 添加新页面 + /// + void AddNewPage(); + + /// + /// 删除当前页面 + /// + void DeleteCurrentPage(); + + /// + /// 切换到指定页面 + /// + /// 页面索引 + void SwitchToPage(int pageIndex); + + /// + /// 切换到下一页 + /// + void NextPage(); + + /// + /// 切换到上一页 + /// + void PreviousPage(); + + #endregion + + #region 绘制操作 + + /// + /// 设置绘制模式 + /// + /// 绘制模式 + void SetDrawingMode(int mode); + + /// + /// 设置笔触宽度 + /// + /// 宽度 + void SetInkWidth(double width); + + /// + /// 设置笔触颜色 + /// + /// 颜色 + void SetInkColor(Color color); + + /// + /// 设置高亮笔宽度 + /// + /// 宽度 + void SetHighlighterWidth(double width); + + /// + /// 设置橡皮擦大小 + /// + /// 大小 + void SetEraserSize(int size); + + /// + /// 设置橡皮擦类型 + /// + /// 类型 + void SetEraserType(int type); + + /// + /// 设置橡皮擦形状 + /// + /// 形状 + void SetEraserShape(int shape); + + /// + /// 设置笔触透明度 + /// + /// 透明度 + void SetInkAlpha(double alpha); + + /// + /// 设置笔触样式 + /// + /// 样式 + void SetInkStyle(int style); + + /// + /// 设置背景颜色 + /// + /// 颜色 + void SetBackgroundColor(string color); + + #endregion + + #region 文件操作 + + /// + /// 保存画布内容 + /// + /// 文件路径 + void SaveCanvas(string filePath); + + /// + /// 加载画布内容 + /// + /// 文件路径 + void LoadCanvas(string filePath); + + /// + /// 导出为图片 + /// + /// 文件路径 + /// 图片格式 + void ExportAsImage(string filePath, string format); + + /// + /// 导出为PDF + /// + /// 文件路径 + void ExportAsPDF(string filePath); + + #endregion + + #region 撤销重做操作 + + /// + /// 撤销操作 + /// + void Undo(); + + /// + /// 重做操作 + /// + void Redo(); + + #endregion + + #region 选择操作 + + /// + /// 全选 + /// + void SelectAll(); + + /// + /// 取消选择 + /// + void DeselectAll(); + + /// + /// 删除选中内容 + /// + void DeleteSelected(); + + /// + /// 复制选中内容 + /// + void CopySelected(); + + /// + /// 剪切选中内容 + /// + void CutSelected(); + + /// + /// 粘贴内容 + /// + void Paste(); + + #endregion + + #region 系统设置操作 + + /// + /// 设置系统设置 + /// + /// 设置类型 + /// 设置键 + /// 设置值 + void SetSetting(string key, T value); + + /// + /// 保存设置到文件 + /// + void SaveSettings(); + + /// + /// 从文件加载设置 + /// + void LoadSettings(); + + /// + /// 重置设置为默认值 + /// + void ResetSettings(); + + #endregion + + #region 插件管理操作 + + /// + /// 启用插件 + /// + /// 插件名称 + void EnablePlugin(string pluginName); + + /// + /// 禁用插件 + /// + /// 插件名称 + void DisablePlugin(string pluginName); + + /// + /// 卸载插件 + /// + /// 插件名称 + void UnloadPlugin(string pluginName); + + #endregion + + #region 事件系统操作 + + /// + /// 注册事件处理器 + /// + /// 事件名称 + /// 事件处理器 + void RegisterEventHandler(string eventName, EventHandler handler); + + /// + /// 注销事件处理器 + /// + /// 事件名称 + /// 事件处理器 + void UnregisterEventHandler(string eventName, EventHandler handler); + + /// + /// 触发事件 + /// + /// 事件名称 + /// 事件发送者 + /// 事件参数 + void TriggerEvent(string eventName, object sender, EventArgs args); + + #endregion + + #region 应用程序操作 + + /// + /// 重启应用程序 + /// + void RestartApplication(); + + /// + /// 退出应用程序 + /// + void ExitApplication(); + + /// + /// 检查更新 + /// + void CheckForUpdates(); + + /// + /// 打开帮助文档 + /// + void OpenHelpDocument(); + + /// + /// 打开关于页面 + /// + void OpenAboutPage(); + + #endregion + } +} \ No newline at end of file diff --git a/Ink Canvas/Helpers/Plugins/IGetService.cs b/Ink Canvas/Helpers/Plugins/IGetService.cs new file mode 100644 index 00000000..70ff947e --- /dev/null +++ b/Ink Canvas/Helpers/Plugins/IGetService.cs @@ -0,0 +1,215 @@ +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; + +namespace Ink_Canvas.Helpers.Plugins +{ + /// + /// 获取服务接口,统一所有获取类的方法 + /// + public interface IGetService + { + #region 窗口和UI获取 + + /// + /// 获取主窗口引用 + /// + Window MainWindow { get; } + + /// + /// 获取当前画布 + /// + InkCanvas CurrentCanvas { get; } + + /// + /// 获取所有画布页面 + /// + List AllCanvasPages { get; } + + /// + /// 获取当前页面索引 + /// + int CurrentPageIndex { get; } + + /// + /// 获取当前页面数量 + /// + int TotalPageCount { get; } + + /// + /// 获取浮动工具栏 + /// + FrameworkElement FloatingToolBar { get; } + + /// + /// 获取左侧面板 + /// + FrameworkElement LeftPanel { get; } + + /// + /// 获取右侧面板 + /// + FrameworkElement RightPanel { get; } + + /// + /// 获取顶部面板 + /// + FrameworkElement TopPanel { get; } + + /// + /// 获取底部面板 + /// + FrameworkElement BottomPanel { get; } + + #endregion + + #region 绘制工具状态获取 + + /// + /// 获取当前绘制模式 + /// + int CurrentDrawingMode { get; } + + /// + /// 获取当前笔触宽度 + /// + double CurrentInkWidth { get; } + + /// + /// 获取当前笔触颜色 + /// + Color CurrentInkColor { get; } + + /// + /// 获取当前高亮笔宽度 + /// + double CurrentHighlighterWidth { get; } + + /// + /// 获取当前橡皮擦大小 + /// + int CurrentEraserSize { get; } + + /// + /// 获取当前橡皮擦类型 + /// + int CurrentEraserType { get; } + + /// + /// 获取当前橡皮擦形状 + /// + int CurrentEraserShape { get; } + + /// + /// 获取当前笔触透明度 + /// + double CurrentInkAlpha { get; } + + /// + /// 获取当前笔触样式 + /// + int CurrentInkStyle { get; } + + /// + /// 获取当前背景颜色 + /// + string CurrentBackgroundColor { get; } + + #endregion + + #region 应用状态获取 + + /// + /// 获取当前主题模式 + /// + bool IsDarkTheme { get; } + + /// + /// 获取当前是否为白板模式 + /// + bool IsWhiteboardMode { get; } + + /// + /// 获取当前是否为PPT模式 + /// + bool IsPPTMode { get; } + + /// + /// 获取当前是否为全屏模式 + /// + bool IsFullScreenMode { get; } + + /// + /// 获取当前是否为画板模式 + /// + bool IsCanvasMode { get; } + + /// + /// 获取当前是否为选择模式 + /// + bool IsSelectionMode { get; } + + /// + /// 获取当前是否为擦除模式 + /// + bool IsEraserMode { get; } + + /// + /// 获取当前是否为形状绘制模式 + /// + bool IsShapeDrawingMode { get; } + + /// + /// 获取当前是否为高亮模式 + /// + bool IsHighlighterMode { get; } + + #endregion + + #region 撤销重做状态获取 + + /// + /// 获取是否可以撤销 + /// + bool CanUndo { get; } + + /// + /// 获取是否可以重做 + /// + bool CanRedo { get; } + + #endregion + + #region 系统设置获取 + + /// + /// 获取系统设置 + /// + /// 设置类型 + /// 设置键 + /// 默认值 + /// 设置值 + T GetSetting(string key, T defaultValue = default(T)); + + #endregion + + #region 插件信息获取 + + /// + /// 获取所有已加载的插件 + /// + /// 插件列表 + List GetAllPlugins(); + + /// + /// 获取指定插件 + /// + /// 插件名称 + /// 插件实例 + IPlugin GetPlugin(string pluginName); + + #endregion + } +} \ No newline at end of file diff --git a/Ink Canvas/Helpers/Plugins/IPluginService.cs b/Ink Canvas/Helpers/Plugins/IPluginService.cs index ff24ec2b..b1923121 100644 --- a/Ink Canvas/Helpers/Plugins/IPluginService.cs +++ b/Ink Canvas/Helpers/Plugins/IPluginService.cs @@ -8,525 +8,12 @@ namespace Ink_Canvas.Helpers.Plugins { /// /// 插件服务接口,提供对软件内部功能的访问 + /// 继承自三个专门的服务接口:获取服务、窗口服务、操作服务 /// - public interface IPluginService + public interface IPluginService : IGetService, IWindowService, IActionService { - #region 窗口和UI访问 - - /// - /// 获取主窗口引用 - /// - Window MainWindow { get; } - - /// - /// 获取当前画布 - /// - InkCanvas CurrentCanvas { get; } - - /// - /// 获取所有画布页面 - /// - List AllCanvasPages { get; } - - /// - /// 获取当前页面索引 - /// - int CurrentPageIndex { get; } - - /// - /// 获取当前页面数量 - /// - int TotalPageCount { get; } - - /// - /// 获取浮动工具栏 - /// - FrameworkElement FloatingToolBar { get; } - - /// - /// 获取左侧面板 - /// - FrameworkElement LeftPanel { get; } - - /// - /// 获取右侧面板 - /// - FrameworkElement RightPanel { get; } - - /// - /// 获取顶部面板 - /// - FrameworkElement TopPanel { get; } - - /// - /// 获取底部面板 - /// - FrameworkElement BottomPanel { get; } - - #endregion - - #region 绘制工具状态 - - /// - /// 获取当前绘制模式 - /// - int CurrentDrawingMode { get; } - - /// - /// 获取当前笔触宽度 - /// - double CurrentInkWidth { get; } - - /// - /// 获取当前笔触颜色 - /// - Color CurrentInkColor { get; } - - /// - /// 获取当前高亮笔宽度 - /// - double CurrentHighlighterWidth { get; } - - /// - /// 获取当前橡皮擦大小 - /// - int CurrentEraserSize { get; } - - /// - /// 获取当前橡皮擦类型 - /// - int CurrentEraserType { get; } - - /// - /// 获取当前橡皮擦形状 - /// - int CurrentEraserShape { get; } - - /// - /// 获取当前笔触透明度 - /// - double CurrentInkAlpha { get; } - - /// - /// 获取当前笔触样式 - /// - int CurrentInkStyle { get; } - - /// - /// 获取当前背景颜色 - /// - string CurrentBackgroundColor { get; } - - #endregion - - #region 应用状态 - - /// - /// 获取当前主题模式 - /// - bool IsDarkTheme { get; } - - /// - /// 获取当前是否为白板模式 - /// - bool IsWhiteboardMode { get; } - - /// - /// 获取当前是否为PPT模式 - /// - bool IsPPTMode { get; } - - /// - /// 获取当前是否为全屏模式 - /// - bool IsFullScreenMode { get; } - - /// - /// 获取当前是否为画板模式 - /// - bool IsCanvasMode { get; } - - /// - /// 获取当前是否为选择模式 - /// - bool IsSelectionMode { get; } - - /// - /// 获取当前是否为擦除模式 - /// - bool IsEraserMode { get; } - - /// - /// 获取当前是否为形状绘制模式 - /// - bool IsShapeDrawingMode { get; } - - /// - /// 获取当前是否为高亮模式 - /// - bool IsHighlighterMode { get; } - - #endregion - - #region 画布操作 - - /// - /// 清除当前画布 - /// - void ClearCanvas(); - - /// - /// 清除所有画布 - /// - void ClearAllCanvases(); - - /// - /// 添加新页面 - /// - void AddNewPage(); - - /// - /// 删除当前页面 - /// - void DeleteCurrentPage(); - - /// - /// 切换到指定页面 - /// - /// 页面索引 - void SwitchToPage(int pageIndex); - - /// - /// 切换到下一页 - /// - void NextPage(); - - /// - /// 切换到上一页 - /// - void PreviousPage(); - - #endregion - - #region 绘制操作 - - /// - /// 设置绘制模式 - /// - /// 绘制模式 - void SetDrawingMode(int mode); - - /// - /// 设置笔触宽度 - /// - /// 宽度 - void SetInkWidth(double width); - - /// - /// 设置笔触颜色 - /// - /// 颜色 - void SetInkColor(Color color); - - /// - /// 设置高亮笔宽度 - /// - /// 宽度 - void SetHighlighterWidth(double width); - - /// - /// 设置橡皮擦大小 - /// - /// 大小 - void SetEraserSize(int size); - - /// - /// 设置橡皮擦类型 - /// - /// 类型 - void SetEraserType(int type); - - /// - /// 设置橡皮擦形状 - /// - /// 形状 - void SetEraserShape(int shape); - - /// - /// 设置笔触透明度 - /// - /// 透明度 - void SetInkAlpha(double alpha); - - /// - /// 设置笔触样式 - /// - /// 样式 - void SetInkStyle(int style); - - /// - /// 设置背景颜色 - /// - /// 颜色 - void SetBackgroundColor(string color); - - #endregion - - #region 文件操作 - - /// - /// 保存画布内容 - /// - /// 文件路径 - void SaveCanvas(string filePath); - - /// - /// 加载画布内容 - /// - /// 文件路径 - void LoadCanvas(string filePath); - - /// - /// 导出为图片 - /// - /// 文件路径 - /// 图片格式 - void ExportAsImage(string filePath, string format); - - /// - /// 导出为PDF - /// - /// 文件路径 - void ExportAsPDF(string filePath); - - #endregion - - #region 撤销重做 - - /// - /// 撤销操作 - /// - void Undo(); - - /// - /// 重做操作 - /// - void Redo(); - - /// - /// 是否可以撤销 - /// - bool CanUndo { get; } - - /// - /// 是否可以重做 - /// - bool CanRedo { get; } - - #endregion - - #region 选择操作 - - /// - /// 全选 - /// - void SelectAll(); - - /// - /// 取消选择 - /// - void DeselectAll(); - - /// - /// 删除选中内容 - /// - void DeleteSelected(); - - /// - /// 复制选中内容 - /// - void CopySelected(); - - /// - /// 剪切选中内容 - /// - void CutSelected(); - - /// - /// 粘贴内容 - /// - void Paste(); - - #endregion - - #region 窗口管理 - - /// - /// 显示设置窗口 - /// - void ShowSettingsWindow(); - - /// - /// 隐藏设置窗口 - /// - void HideSettingsWindow(); - - /// - /// 显示插件设置窗口 - /// - void ShowPluginSettingsWindow(); - - /// - /// 隐藏插件设置窗口 - /// - void HidePluginSettingsWindow(); - - /// - /// 显示帮助窗口 - /// - void ShowHelpWindow(); - - /// - /// 隐藏帮助窗口 - /// - void HideHelpWindow(); - - /// - /// 显示关于窗口 - /// - void ShowAboutWindow(); - - /// - /// 隐藏关于窗口 - /// - void HideAboutWindow(); - - #endregion - - #region 通知和消息 - - /// - /// 显示通知消息 - /// - /// 消息内容 - /// 消息类型 - void ShowNotification(string message, NotificationType type = NotificationType.Info); - - /// - /// 显示确认对话框 - /// - /// 消息内容 - /// 标题 - /// 用户选择结果 - bool ShowConfirmDialog(string message, string title = "确认"); - - /// - /// 显示输入对话框 - /// - /// 提示消息 - /// 标题 - /// 默认值 - /// 用户输入内容 - string ShowInputDialog(string message, string title = "输入", string defaultValue = ""); - - #endregion - - #region 系统功能 - - /// - /// 获取系统设置 - /// - /// 设置类型 - /// 设置键 - /// 默认值 - /// 设置值 - T GetSetting(string key, T defaultValue = default(T)); - - /// - /// 设置系统设置 - /// - /// 设置类型 - /// 设置键 - /// 设置值 - void SetSetting(string key, T value); - - /// - /// 保存设置到文件 - /// - void SaveSettings(); - - /// - /// 从文件加载设置 - /// - void LoadSettings(); - - /// - /// 重置设置为默认值 - /// - void ResetSettings(); - - #endregion - - #region 插件管理 - - /// - /// 获取所有已加载的插件 - /// - /// 插件列表 - List GetAllPlugins(); - - /// - /// 获取指定插件 - /// - /// 插件名称 - /// 插件实例 - IPlugin GetPlugin(string pluginName); - - /// - /// 启用插件 - /// - /// 插件名称 - void EnablePlugin(string pluginName); - - /// - /// 禁用插件 - /// - /// 插件名称 - void DisablePlugin(string pluginName); - - /// - /// 卸载插件 - /// - /// 插件名称 - void UnloadPlugin(string pluginName); - - #endregion - - #region 事件系统 - - /// - /// 注册事件处理器 - /// - /// 事件名称 - /// 事件处理器 - void RegisterEventHandler(string eventName, EventHandler handler); - - /// - /// 注销事件处理器 - /// - /// 事件名称 - /// 事件处理器 - void UnregisterEventHandler(string eventName, EventHandler handler); - - /// - /// 触发事件 - /// - /// 事件名称 - /// 事件发送者 - /// 事件参数 - void TriggerEvent(string eventName, object sender, EventArgs args); - - #endregion + // 这个接口现在继承自三个专门的服务接口 + // 所有方法都在子接口中定义,这里不需要重复定义 } /// diff --git a/Ink Canvas/Helpers/Plugins/IWindowService.cs b/Ink Canvas/Helpers/Plugins/IWindowService.cs new file mode 100644 index 00000000..2685046c --- /dev/null +++ b/Ink Canvas/Helpers/Plugins/IWindowService.cs @@ -0,0 +1,154 @@ +using System; + +namespace Ink_Canvas.Helpers.Plugins +{ + /// + /// 窗口服务接口,统一所有窗口操作相关的方法 + /// + public interface IWindowService + { + #region 窗口显示和隐藏 + + /// + /// 显示设置窗口 + /// + void ShowSettingsWindow(); + + /// + /// 隐藏设置窗口 + /// + void HideSettingsWindow(); + + /// + /// 显示插件设置窗口 + /// + void ShowPluginSettingsWindow(); + + /// + /// 隐藏插件设置窗口 + /// + void HidePluginSettingsWindow(); + + /// + /// 显示帮助窗口 + /// + void ShowHelpWindow(); + + /// + /// 隐藏帮助窗口 + /// + void HideHelpWindow(); + + /// + /// 显示关于窗口 + /// + void ShowAboutWindow(); + + /// + /// 隐藏关于窗口 + /// + void HideAboutWindow(); + + #endregion + + #region 对话框和通知 + + /// + /// 显示通知消息 + /// + /// 消息内容 + /// 消息类型 + void ShowNotification(string message, NotificationType type = NotificationType.Info); + + /// + /// 显示确认对话框 + /// + /// 消息内容 + /// 标题 + /// 用户选择结果 + bool ShowConfirmDialog(string message, string title = "确认"); + + /// + /// 显示输入对话框 + /// + /// 提示消息 + /// 标题 + /// 默认值 + /// 用户输入内容 + string ShowInputDialog(string message, string title = "输入", string defaultValue = ""); + + #endregion + + #region 窗口状态控制 + + /// + /// 设置窗口全屏状态 + /// + /// 是否全屏 + void SetFullScreen(bool isFullScreen); + + /// + /// 设置窗口置顶状态 + /// + /// 是否置顶 + void SetTopMost(bool isTopMost); + + /// + /// 设置窗口可见性 + /// + /// 是否可见 + void SetWindowVisibility(bool isVisible); + + /// + /// 最小化窗口 + /// + void MinimizeWindow(); + + /// + /// 最大化窗口 + /// + void MaximizeWindow(); + + /// + /// 恢复窗口 + /// + void RestoreWindow(); + + /// + /// 关闭窗口 + /// + void CloseWindow(); + + #endregion + + #region 窗口位置和大小 + + /// + /// 设置窗口位置 + /// + /// X坐标 + /// Y坐标 + void SetWindowPosition(double x, double y); + + /// + /// 设置窗口大小 + /// + /// 宽度 + /// 高度 + void SetWindowSize(double width, double height); + + /// + /// 获取窗口位置 + /// + /// 窗口位置 + (double x, double y) GetWindowPosition(); + + /// + /// 获取窗口大小 + /// + /// 窗口大小 + (double width, double height) GetWindowSize(); + + #endregion + } +} \ No newline at end of file diff --git a/Ink Canvas/Helpers/Plugins/PluginServiceManager.cs b/Ink Canvas/Helpers/Plugins/PluginServiceManager.cs index 3f7ec460..9975998e 100644 --- a/Ink Canvas/Helpers/Plugins/PluginServiceManager.cs +++ b/Ink Canvas/Helpers/Plugins/PluginServiceManager.cs @@ -115,7 +115,149 @@ namespace Ink_Canvas.Helpers.Plugins #endregion - #region 画布操作 + #region IGetService 实现 + + public bool CanUndo => false; // 暂时返回默认值 + + public bool CanRedo => false; // 暂时返回默认值 + + public T GetSetting(string key, T defaultValue = default(T)) + { + // 暂时不实现,避免访问权限问题 + return defaultValue; + } + + public List GetAllPlugins() + { + return new List(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() { @@ -152,10 +294,6 @@ namespace Ink_Canvas.Helpers.Plugins // 暂时不实现,避免访问权限问题 } - #endregion - - #region 绘制操作 - public void SetDrawingMode(int mode) { // 暂时不实现,避免访问权限问题 @@ -206,10 +344,6 @@ namespace Ink_Canvas.Helpers.Plugins // 暂时不实现,避免访问权限问题 } - #endregion - - #region 文件操作 - public void SaveCanvas(string filePath) { // 暂时不实现,避免访问权限问题 @@ -230,10 +364,6 @@ namespace Ink_Canvas.Helpers.Plugins // 暂时不实现,避免访问权限问题 } - #endregion - - #region 撤销重做 - public void Undo() { // 暂时不实现,避免访问权限问题 @@ -244,14 +374,6 @@ namespace Ink_Canvas.Helpers.Plugins // 暂时不实现,避免访问权限问题 } - public bool CanUndo => false; // 暂时返回默认值 - - public bool CanRedo => false; // 暂时返回默认值 - - #endregion - - #region 选择操作 - public void SelectAll() { // 暂时不实现,避免访问权限问题 @@ -282,81 +404,6 @@ namespace Ink_Canvas.Helpers.Plugins // 暂时不实现,避免访问权限问题 } - #endregion - - #region 窗口管理 - - public void ShowSettingsWindow() - { - // 暂时不实现,避免访问权限问题 - } - - public void HideSettingsWindow() - { - // 暂时不实现,避免访问权限问题 - } - - public void ShowPluginSettingsWindow() - { - // 暂时不实现,避免访问权限问题 - } - - public void HidePluginSettingsWindow() - { - // 暂时不实现,避免访问权限问题 - } - - public void ShowHelpWindow() - { - // 暂时不实现,避免访问权限问题 - } - - public void HideHelpWindow() - { - // 暂时不实现,避免访问权限问题 - } - - public void ShowAboutWindow() - { - // 暂时不实现,避免访问权限问题 - } - - public void HideAboutWindow() - { - // 暂时不实现,避免访问权限问题 - } - - #endregion - - #region 通知和消息 - - 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; - } - - #endregion - - #region 系统功能 - - public T GetSetting(string key, T defaultValue = default(T)) - { - // 暂时不实现,避免访问权限问题 - return defaultValue; - } - public void SetSetting(string key, T value) { // 暂时不实现,避免访问权限问题 @@ -377,20 +424,6 @@ namespace Ink_Canvas.Helpers.Plugins // 暂时不实现,避免访问权限问题 } - #endregion - - #region 插件管理 - - public List GetAllPlugins() - { - return new List(PluginManager.Instance.Plugins); - } - - public IPlugin GetPlugin(string pluginName) - { - return PluginManager.Instance.Plugins.FirstOrDefault(p => p.Name == pluginName); - } - public void EnablePlugin(string pluginName) { var plugin = GetPlugin(pluginName); @@ -418,10 +451,6 @@ namespace Ink_Canvas.Helpers.Plugins } } - #endregion - - #region 事件系统 - public void RegisterEventHandler(string eventName, EventHandler handler) { if (!_eventHandlers.ContainsKey(eventName)) @@ -450,6 +479,31 @@ namespace Ink_Canvas.Helpers.Plugins } } + public void RestartApplication() + { + // 暂时不实现,避免访问权限问题 + } + + public void ExitApplication() + { + // 暂时不实现,避免访问权限问题 + } + + public void CheckForUpdates() + { + // 暂时不实现,避免访问权限问题 + } + + public void OpenHelpDocument() + { + // 暂时不实现,避免访问权限问题 + } + + public void OpenAboutPage() + { + // 暂时不实现,避免访问权限问题 + } + #endregion } } \ No newline at end of file diff --git a/Ink Canvas/Helpers/WindowZOrderManager.cs b/Ink Canvas/Helpers/WindowZOrderManager.cs new file mode 100644 index 00000000..2ce292c8 --- /dev/null +++ b/Ink Canvas/Helpers/WindowZOrderManager.cs @@ -0,0 +1,263 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Interop; + +namespace Ink_Canvas.Helpers +{ + /// + /// 窗口Z-Order管理器,用于管理窗口的层级顺序 + /// 在无焦点模式下,确保后打开的窗口能够置顶于先打开的窗口 + /// + public static class WindowZOrderManager + { + #region Win32 API 声明 + private const int GWL_EXSTYLE = -20; + private const int WS_EX_TOPMOST = 0x00000008; + private const int WS_EX_NOACTIVATE = 0x08000000; + private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); + private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2); + private const uint SWP_NOMOVE = 0x0002; + private const uint SWP_NOSIZE = 0x0001; + private const uint SWP_NOACTIVATE = 0x0010; + private const uint SWP_SHOWWINDOW = 0x0040; + private const uint SWP_NOOWNERZORDER = 0x0200; + + [DllImport("user32.dll")] + private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + + [DllImport("user32.dll")] + private static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + [DllImport("user32.dll")] + private static extern bool IsWindow(IntPtr hWnd); + + [DllImport("user32.dll")] + private static extern bool IsWindowVisible(IntPtr hWnd); + + [DllImport("user32.dll")] + private static extern bool IsIconic(IntPtr hWnd); + + [DllImport("user32.dll")] + private static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll")] + private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + + [DllImport("kernel32.dll")] + private static extern uint GetCurrentProcessId(); + #endregion + + // 窗口层级管理 + private static readonly List _windowStack = new List(); + private static readonly object _lockObject = new object(); + + /// + /// 窗口信息类 + /// + private class WindowInfo + { + public IntPtr Handle { get; set; } + public Window Window { get; set; } + public DateTime CreatedTime { get; set; } + public bool IsTopmost { get; set; } + public bool IsNoFocusMode { get; set; } + } + + /// + /// 注册窗口到Z-Order管理器 + /// + /// 要注册的窗口 + /// 是否置顶 + /// 是否无焦点模式 + public static void RegisterWindow(Window window, bool isTopmost = false, bool isNoFocusMode = false) + { + if (window == null) return; + + lock (_lockObject) + { + var hwnd = new WindowInteropHelper(window).Handle; + if (hwnd == IntPtr.Zero) return; + + // 移除已存在的记录 + _windowStack.RemoveAll(w => w.Handle == hwnd); + + // 添加新记录 + var windowInfo = new WindowInfo + { + Handle = hwnd, + Window = window, + CreatedTime = DateTime.Now, + IsTopmost = isTopmost, + IsNoFocusMode = isNoFocusMode + }; + + _windowStack.Add(windowInfo); + + // 应用Z-Order + ApplyZOrder(); + } + } + + /// + /// 从Z-Order管理器中移除窗口 + /// + /// 要移除的窗口 + public static void UnregisterWindow(Window window) + { + if (window == null) return; + + lock (_lockObject) + { + var hwnd = new WindowInteropHelper(window).Handle; + _windowStack.RemoveAll(w => w.Handle == hwnd); + ApplyZOrder(); + } + } + + /// + /// 设置窗口为置顶状态 + /// + /// 要置顶的窗口 + /// 是否置顶 + public static void SetWindowTopmost(Window window, bool isTopmost) + { + if (window == null) return; + + lock (_lockObject) + { + var windowInfo = _windowStack.FirstOrDefault(w => w.Window == window); + if (windowInfo != null) + { + windowInfo.IsTopmost = isTopmost; + ApplyZOrder(); + } + } + } + + /// + /// 将窗口移到最顶层 + /// + /// 要移到最顶层的窗口 + public static void BringToTop(Window window) + { + if (window == null) return; + + try + { + var hwnd = new WindowInteropHelper(window).Handle; + if (hwnd == IntPtr.Zero) return; + + // 使用更直接的方法:先激活窗口,再置顶 + window.Activate(); + window.Focus(); + + // 设置窗口为置顶 + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER); + + // 确保窗口样式正确 + int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE); + SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST); + + // 再次确保置顶 + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"BringToTop失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 应用Z-Order排序 + /// + private static void ApplyZOrder() + { + // 简化逻辑:直接设置所有窗口为置顶,让Windows系统自然处理层级 + foreach (var windowInfo in _windowStack.ToList()) + { + if (windowInfo.IsTopmost && IsWindow(windowInfo.Handle) && IsWindowVisible(windowInfo.Handle) && !IsIconic(windowInfo.Handle)) + { + // 设置窗口为置顶 + SetWindowPos(windowInfo.Handle, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER); + + // 确保窗口样式正确 + int exStyle = GetWindowLong(windowInfo.Handle, GWL_EXSTYLE); + if ((exStyle & WS_EX_TOPMOST) == 0) + { + SetWindowLong(windowInfo.Handle, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST); + } + } + } + } + + /// + /// 检查是否有子窗口在前景 + /// + /// 如果有子窗口在前景返回true + public static bool HasChildWindowInForeground() + { + lock (_lockObject) + { + var foregroundWindow = GetForegroundWindow(); + if (foregroundWindow == IntPtr.Zero) return false; + + return _windowStack.Any(w => w.Handle == foregroundWindow); + } + } + + /// + /// 清理无效的窗口记录 + /// + public static void CleanupInvalidWindows() + { + lock (_lockObject) + { + _windowStack.RemoveAll(w => !IsWindow(w.Handle) || !IsWindowVisible(w.Handle)); + } + } + + /// + /// 获取当前注册的窗口数量 + /// + /// 窗口数量 + public static int GetWindowCount() + { + lock (_lockObject) + { + return _windowStack.Count; + } + } + + /// + /// 强制刷新所有窗口的置顶状态 + /// + public static void ForceRefreshAllWindows() + { + lock (_lockObject) + { + foreach (var windowInfo in _windowStack.ToList()) + { + if (windowInfo.IsTopmost && IsWindow(windowInfo.Handle)) + { + // 强制设置窗口为置顶 + SetWindowPos(windowInfo.Handle, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER); + + // 确保窗口样式正确 + int exStyle = GetWindowLong(windowInfo.Handle, GWL_EXSTYLE); + SetWindowLong(windowInfo.Handle, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST); + } + } + } + } + } +} \ No newline at end of file diff --git a/Ink Canvas/InkCanvasForClass.csproj b/Ink Canvas/InkCanvasForClass.csproj index 4c53cecb..2bf113a3 100644 --- a/Ink Canvas/InkCanvasForClass.csproj +++ b/Ink Canvas/InkCanvasForClass.csproj @@ -24,12 +24,8 @@ false False true - Debug;Release;x86 Debug - - - embedded - bin\$(Configuration)\ - True + Release;x86 Debug;Debug + AnyCPU;x86 embedded @@ -128,6 +124,7 @@ + diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index 42bc95ac..d918730f 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -531,6 +531,42 @@ + + + + + + + 选择软件运行模式。仅PPT模式下,软件将完全隐藏,仅在PPT放映时出现。 + + + + + + + + + + + + + + + 打开新的设置窗口,提供更丰富的设置选项和更好的用户体验。(开发中) + + private void CheckEnableTwoFingerGestureBtnVisibility(bool isVisible) { - // 在PPT模式下始终隐藏手势按钮 + // 在PPT模式下根据设置决定是否显示手势按钮 if (currentMode == 0 || BtnPPTSlideShowEnd.Visibility == Visibility.Visible) { - EnableTwoFingerGestureBorder.Visibility = Visibility.Collapsed; + // 如果启用了PPT放映模式显示手势按钮,则显示手势按钮(在PPT模式下不依赖手势功能是否启用) + if (Settings.PowerPointSettings.ShowGestureButtonInSlideShow && isVisible) + { + EnableTwoFingerGestureBorder.Visibility = Visibility.Visible; + } + else + { + EnableTwoFingerGestureBorder.Visibility = Visibility.Collapsed; + } return; } @@ -346,6 +358,11 @@ namespace Ink_Canvas { BorderSettings.Visibility = Visibility.Collapsed; isOpeningOrHidingSettingsPane = false; + // 在设置面板完全关闭后,根据当前设置恢复无焦点模式 + if (Settings.Advanced.IsNoFocusMode) + { + ApplyNoFocusMode(); + } }; BorderSettings.Visibility = Visibility.Visible; @@ -650,7 +667,11 @@ namespace Ink_Canvas //关闭黑板 HideSubPanelsImmediately(); - if (StackPanelPPTControls.Visibility == Visibility.Visible) + // 只有在PPT放映模式下且页数有效时才显示翻页按钮 + if (StackPanelPPTControls.Visibility == Visibility.Visible && + BtnPPTSlideShowEnd.Visibility == Visibility.Visible && + PPTManager?.IsInSlideShow == true && + PPTManager?.SlidesCount > 0) { var dops = Settings.PowerPointSettings.PPTButtonsDisplayOption.ToString(); var dopsc = dops.ToCharArray(); @@ -658,15 +679,23 @@ namespace Ink_Canvas if (dopsc[1] == '2' && !isDisplayingOrHidingBlackboard) AnimationsHelper.ShowWithFadeIn(RightBottomPanelForPPTNavigation); if (dopsc[2] == '2' && !isDisplayingOrHidingBlackboard) AnimationsHelper.ShowWithFadeIn(LeftSidePanelForPPTNavigation); if (dopsc[3] == '2' && !isDisplayingOrHidingBlackboard) AnimationsHelper.ShowWithFadeIn(RightSidePanelForPPTNavigation); + LogHelper.WriteLogToFile($"显示PPT翻页按钮 - 放映状态: {PPTManager?.IsInSlideShow}, 页数: {PPTManager?.SlidesCount}", LogHelper.LogType.Trace); } - // 修复PPT放映时点击白板按钮后翻页按钮不显示的问题 - if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) + else { - // 强制显示PPT翻页按钮 - LeftBottomPanelForPPTNavigation.Visibility = Visibility.Visible; - RightBottomPanelForPPTNavigation.Visibility = Visibility.Visible; - LeftSidePanelForPPTNavigation.Visibility = Visibility.Visible; - RightSidePanelForPPTNavigation.Visibility = Visibility.Visible; + // 如果不在放映模式或页数无效,隐藏所有翻页按钮 + LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; + RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; + LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; + RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; + LogHelper.WriteLogToFile($"隐藏PPT翻页按钮 - 放映状态: {PPTManager?.IsInSlideShow}, 页数: {PPTManager?.SlidesCount}", LogHelper.LogType.Trace); + } + + // 使用PPT UI管理器来正确更新翻页按钮显示状态,确保遵循用户设置 + if (_pptUIManager != null) + { + _pptUIManager.UpdateNavigationPanelsVisibility(); + LogHelper.WriteLogToFile($"使用PPT UI管理器更新翻页按钮显示状态", LogHelper.LogType.Trace); } if (Settings.Automation.IsAutoSaveStrokesAtClear && @@ -798,6 +827,10 @@ namespace Ink_Canvas if (sender == SymbolIconSelect && lastBorderMouseDownObject != SymbolIconSelect) return; BtnSelect_Click(null, null); + + // 更新模式缓存 + UpdateCurrentToolMode("select"); + HideSubPanels("select"); } @@ -932,7 +965,47 @@ namespace Ink_Canvas AnimationsHelper.HideWithSlideAndFade(BoardBorderTools); AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel); - new RandWindow(Settings).Show(); + var randWindow = new RandWindow(Settings); + randWindow.Show(); + + // 使用延迟确保窗口完全显示后再强制置顶 + randWindow.Dispatcher.BeginInvoke(new Action(() => + { + try + { + // 强制激活窗口 + randWindow.Activate(); + randWindow.Focus(); + + // 设置置顶 + randWindow.Topmost = true; + + // 使用Win32 API强制置顶 + var hwnd = new WindowInteropHelper(randWindow).Handle; + if (hwnd != IntPtr.Zero) + { + const int WS_EX_TOPMOST = 0x00000008; + const int GWL_EXSTYLE = -20; + const int SWP_NOMOVE = 0x0002; + const int SWP_NOSIZE = 0x0001; + const int SWP_SHOWWINDOW = 0x0040; + const int SWP_NOOWNERZORDER = 0x0200; + var HWND_TOPMOST = new IntPtr(-1); + + // 设置窗口样式为置顶 + int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE); + SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST); + + // 强制置顶 + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"强制置顶RandWindow失败: {ex.Message}", LogHelper.LogType.Error); + } + }), DispatcherPriority.Loaded); } public void CheckEraserTypeTab() @@ -1329,7 +1402,31 @@ namespace Ink_Canvas var toolbarHeight = ForegroundWindowInfo.GetTaskbarHeight(screen, dpiScaleY); // 计算浮动栏位置,考虑快捷调色盘的显示状态 - double floatingBarWidth = ViewboxFloatingBar.ActualWidth * ViewboxFloatingBarScaleTransform.ScaleX; + // 使用更可靠的方法获取浮动栏宽度 + double baseWidth = ViewboxFloatingBar.ActualWidth; + + // 如果ActualWidth为0,尝试使用DesiredSize + if (baseWidth <= 0) + { + baseWidth = ViewboxFloatingBar.DesiredSize.Width; + } + + // 如果仍然为0,使用RenderSize + if (baseWidth <= 0) + { + baseWidth = ViewboxFloatingBar.RenderSize.Width; + } + + // 如果所有方法都失败,使用一个基于内容的估算值 + if (baseWidth <= 0) + { + // 根据浮动栏内容估算宽度 + baseWidth = 200; // 最小宽度 + LogHelper.WriteLogToFile($"浮动栏宽度无法获取,使用估算值: {baseWidth}"); + } + + double floatingBarWidth = baseWidth * ViewboxFloatingBarScaleTransform.ScaleX; + // 如果快捷调色盘显示,确保有足够空间 if ((QuickColorPalettePanel != null && QuickColorPalettePanel.Visibility == Visibility.Visible) || @@ -1450,7 +1547,31 @@ namespace Ink_Canvas var toolbarHeight = ForegroundWindowInfo.GetTaskbarHeight(screen, dpiScaleY); // 计算浮动栏位置,考虑快捷调色盘的显示状态 - double floatingBarWidth = ViewboxFloatingBar.ActualWidth * ViewboxFloatingBarScaleTransform.ScaleX; + // 使用更可靠的方法获取浮动栏宽度 + double baseWidth = ViewboxFloatingBar.ActualWidth; + + // 如果ActualWidth为0,尝试使用DesiredSize + if (baseWidth <= 0) + { + baseWidth = ViewboxFloatingBar.DesiredSize.Width; + } + + // 如果仍然为0,使用RenderSize + if (baseWidth <= 0) + { + baseWidth = ViewboxFloatingBar.RenderSize.Width; + } + + // 如果所有方法都失败,使用一个基于内容的估算值 + if (baseWidth <= 0) + { + // 根据浮动栏内容估算宽度 + baseWidth = 200; // 最小宽度 + LogHelper.WriteLogToFile($"浮动栏宽度无法获取,使用估算值: {baseWidth}"); + } + + double floatingBarWidth = baseWidth * ViewboxFloatingBarScaleTransform.ScaleX; + // 如果快捷调色盘显示,确保有足够空间 if ((QuickColorPalettePanel != null && QuickColorPalettePanel.Visibility == Visibility.Visible) || @@ -1532,7 +1653,31 @@ namespace Ink_Canvas var toolbarHeight = ForegroundWindowInfo.GetTaskbarHeight(screen, dpiScaleY); // 计算浮动栏位置,考虑快捷调色盘的显示状态 - double floatingBarWidth = ViewboxFloatingBar.ActualWidth * ViewboxFloatingBarScaleTransform.ScaleX; + // 使用更可靠的方法获取浮动栏宽度 + double baseWidth = ViewboxFloatingBar.ActualWidth; + + // 如果ActualWidth为0,尝试使用DesiredSize + if (baseWidth <= 0) + { + baseWidth = ViewboxFloatingBar.DesiredSize.Width; + } + + // 如果仍然为0,使用RenderSize + if (baseWidth <= 0) + { + baseWidth = ViewboxFloatingBar.RenderSize.Width; + } + + // 如果所有方法都失败,使用一个基于内容的估算值 + if (baseWidth <= 0) + { + // 根据浮动栏内容估算宽度 + baseWidth = 200; // 最小宽度 + LogHelper.WriteLogToFile($"浮动栏宽度无法获取,使用估算值: {baseWidth}"); + } + + double floatingBarWidth = baseWidth * ViewboxFloatingBarScaleTransform.ScaleX; + // 如果快捷调色盘显示,确保有足够空间 if ((QuickColorPalettePanel != null && QuickColorPalettePanel.Visibility == Visibility.Visible) || @@ -1587,12 +1732,16 @@ namespace Ink_Canvas // 禁用高级橡皮擦系统 DisableAdvancedEraserSystem(); - // 隱藏高亮 - HideFloatingBarHighlight(); - // 使用集中化的工具模式切换方法,确保快捷键状态正确更新 // 鼠标模式下应该禁用快捷键以放行键盘操作 SetCurrentToolMode(InkCanvasEditingMode.None); + + // 更新模式缓存,确保后续的模式检测正确 + UpdateCurrentToolMode("cursor"); + + // 修复:在浮动栏收起状态下,仍然需要设置按钮高亮状态 + // 这样在浮动栏展开时能正确显示高光 + SetFloatingBarHighlightPosition("cursor"); // 切换前自动截图保存墨迹 if (inkCanvas.Strokes.Count > 0 && @@ -1718,6 +1867,9 @@ namespace Ink_Canvas { // 使用集中化的工具模式切换方法 SetCurrentToolMode(InkCanvasEditingMode.Ink); + + // 更新模式缓存 + UpdateCurrentToolMode("pen"); GridTransparencyFakeBackground.Opacity = 1; GridTransparencyFakeBackground.Background = new SolidColorBrush(StringToColor("#01FFFFFF")); @@ -1752,6 +1904,9 @@ namespace Ink_Canvas CheckEnableTwoFingerGestureBtnVisibility(true); // 使用集中化的工具模式切换方法 SetCurrentToolMode(InkCanvasEditingMode.Ink); + + // 更新模式缓存 + UpdateCurrentToolMode("pen"); // 在批注模式下显示快捷调色盘(如果设置中启用了) if (Settings.Appearance.IsShowQuickColorPalette && QuickColorPalettePanel != null && QuickColorPaletteSingleRowPanel != null) @@ -1874,6 +2029,9 @@ namespace Ink_Canvas } // 使用集中化的工具模式切换方法 SetCurrentToolMode(InkCanvasEditingMode.Ink); + + // 更新模式缓存 + UpdateCurrentToolMode("pen"); // 修复:从线擦切换到批注时,保持之前的笔类型状态 forceEraser = false; @@ -1935,6 +2093,10 @@ namespace Ink_Canvas // 使用新的高级橡皮擦系统 // 使用集中化的工具模式切换方法 SetCurrentToolMode(InkCanvasEditingMode.EraseByPoint); + + // 更新模式缓存 + UpdateCurrentToolMode("eraser"); + ApplyAdvancedEraserShape(); // 使用新的橡皮擦形状应用方法 SetCursorBasedOnEditingMode(inkCanvas); HideSubPanels("eraser"); // 高亮橡皮按钮 @@ -1975,6 +2137,10 @@ namespace Ink_Canvas // 使用新的高级橡皮擦系统 // 使用集中化的工具模式切换方法 SetCurrentToolMode(InkCanvasEditingMode.EraseByPoint); + + // 更新模式缓存 + UpdateCurrentToolMode("eraser"); + ApplyAdvancedEraserShape(); // 使用新的橡皮擦形状应用方法 SetCursorBasedOnEditingMode(inkCanvas); HideSubPanels("eraser"); // 高亮橡皮按钮 @@ -2013,6 +2179,10 @@ namespace Ink_Canvas inkCanvas.EraserShape = new EllipseStylusShape(5, 5); // 使用集中化的工具模式切换方法 SetCurrentToolMode(InkCanvasEditingMode.EraseByStroke); + + // 更新模式缓存 + UpdateCurrentToolMode("eraserByStrokes"); + drawingShapeMode = 0; // 修复:切换到线擦时,保存当前的笔类型状态,而不是强制重置 @@ -2417,6 +2587,7 @@ namespace Ink_Canvas } private bool isOpeningOrHidingSettingsPane; + private bool wasNoFocusModeBeforeSettings; private void BtnSettings_Click(object sender, RoutedEventArgs e) { @@ -2426,6 +2597,14 @@ namespace Ink_Canvas } else { + // 临时禁用无焦点模式以避免下拉选项被遮挡 + wasNoFocusModeBeforeSettings = Settings.Advanced.IsNoFocusMode; + if (wasNoFocusModeBeforeSettings) + { + Settings.Advanced.IsNoFocusMode = false; + ApplyNoFocusMode(); + } + // 设置蒙版为可点击,并添加半透明背景 BorderSettingsMask.IsHitTestVisible = true; BorderSettingsMask.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0)); @@ -3132,6 +3311,15 @@ namespace Ink_Canvas { if (FloatingbarSelectionBG == null) return; + // 检查浮动栏是否处于收起状态 + if (isFloatingBarFolded || (BorderFloatingBarMainControls != null && BorderFloatingBarMainControls.Visibility == Visibility.Collapsed)) + { + // 在收起状态下,仍然需要设置高光位置,但可能需要调整计算方式 + // 这里先隐藏高光,等浮动栏展开时再显示 + FloatingbarSelectionBG.Visibility = Visibility.Hidden; + return; + } + double position = 0; double buttonWidth = 28; // 每个按钮的默认宽度 double highlightWidth = 28; // 高光的默认宽度 @@ -3262,7 +3450,13 @@ namespace Ink_Canvas { try { - // 检查当前编辑模式 + // 优先使用缓存的模式,避免在浮动栏刷新时返回过时的模式信息 + if (!string.IsNullOrEmpty(_currentToolMode)) + { + return _currentToolMode; + } + + // 如果缓存为空,则从inkCanvas状态推断模式 if (inkCanvas.EditingMode == InkCanvasEditingMode.Select) { return "select"; @@ -3306,7 +3500,17 @@ namespace Ink_Canvas LogHelper.WriteLogToFile($"获取当前选中模式失败: {ex.Message}", LogHelper.LogType.Error); } - return string.Empty; + return "cursor"; // 默认返回鼠标模式 + } + + /// + /// 更新当前工具模式缓存 + /// + /// 模式名称 + private void UpdateCurrentToolMode(string mode) + { + _currentToolMode = mode; + LogHelper.WriteLogToFile($"更新工具模式缓存: {mode}", LogHelper.LogType.Trace); } #endregion diff --git a/Ink Canvas/MainWindow_cs/MW_Hotkeys.cs b/Ink Canvas/MainWindow_cs/MW_Hotkeys.cs index 61fbaaf7..5a880df1 100644 --- a/Ink Canvas/MainWindow_cs/MW_Hotkeys.cs +++ b/Ink Canvas/MainWindow_cs/MW_Hotkeys.cs @@ -7,7 +7,11 @@ namespace Ink_Canvas { private void Window_MouseWheel(object sender, MouseWheelEventArgs e) { - if (StackPanelPPTControls.Visibility != Visibility.Visible || currentMode != 0) return; + // 只有在PPT放映模式下才响应鼠标滚轮翻页 + if (StackPanelPPTControls.Visibility != Visibility.Visible || + currentMode != 0 || + BtnPPTSlideShowEnd.Visibility != Visibility.Visible || + PPTManager?.IsInSlideShow != true) return; // 直接发送翻页请求到PPT放映软件,不通过软件处理 if (e.Delta >= 120) @@ -24,7 +28,11 @@ namespace Ink_Canvas private void Main_Grid_PreviewKeyDown(object sender, KeyEventArgs e) { - if (StackPanelPPTControls.Visibility != Visibility.Visible || currentMode != 0) return; + // 只有在PPT放映模式下才响应键盘翻页快捷键 + if (StackPanelPPTControls.Visibility != Visibility.Visible || + currentMode != 0 || + BtnPPTSlideShowEnd.Visibility != Visibility.Visible || + PPTManager?.IsInSlideShow != true) return; // 直接发送翻页请求到PPT放映软件,不通过软件处理 if (e.Key == Key.Down || e.Key == Key.PageDown || e.Key == Key.Right || e.Key == Key.N || diff --git a/Ink Canvas/MainWindow_cs/MW_PPT.cs b/Ink Canvas/MainWindow_cs/MW_PPT.cs index ce8bdd48..7a9c77c0 100644 --- a/Ink Canvas/MainWindow_cs/MW_PPT.cs +++ b/Ink Canvas/MainWindow_cs/MW_PPT.cs @@ -3,6 +3,7 @@ using iNKORE.UI.WPF.Modern; using Microsoft.Office.Core; using Microsoft.Office.Interop.PowerPoint; using System; +using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Security.Cryptography; @@ -82,8 +83,12 @@ namespace Ink_Canvas // 长按翻页相关字段 private DispatcherTimer _longPressTimer; private bool _isLongPressNext = true; // true为下一页,false为上一页 - private const int LongPressDelay = 15; // 长按延迟时间(毫秒) - private const int LongPressInterval = 15; // 长按翻页间隔(毫秒) + private const int LongPressDelay = 500; // 长按延迟时间(毫秒) + private const int LongPressInterval = 50; // 长按翻页间隔(毫秒) + + // PowerPoint应用程序守护相关字段 + private DispatcherTimer _powerPointProcessMonitorTimer; + private const int ProcessMonitorInterval = 5000; // 应用程序监控间隔(毫秒) #endregion #region PPT Managers @@ -159,6 +164,246 @@ namespace Ink_Canvas LogHelper.WriteLogToFile("PPT监控已停止", LogHelper.LogType.Event); } + #region PowerPoint Application Management + /// + /// 启动PowerPoint应用程序守护 + /// + private void StartPowerPointProcessMonitoring() + { + try + { + if (!Settings.PowerPointSettings.EnablePowerPointEnhancement) return; + + // 创建PowerPoint应用程序实例 + CreatePowerPointApplication(); + + // 启动应用程序监控定时器 + if (_powerPointProcessMonitorTimer == null) + { + _powerPointProcessMonitorTimer = new DispatcherTimer(); + _powerPointProcessMonitorTimer.Interval = TimeSpan.FromMilliseconds(ProcessMonitorInterval); + _powerPointProcessMonitorTimer.Tick += OnPowerPointApplicationMonitorTick; + } + _powerPointProcessMonitorTimer.Start(); + + LogHelper.WriteLogToFile("PowerPoint应用程序守护已启动", LogHelper.LogType.Event); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"启动PowerPoint应用程序守护失败: {ex}", LogHelper.LogType.Error); + } + } + + /// + /// 停止PowerPoint应用程序守护 + /// + private void StopPowerPointProcessMonitoring() + { + try + { + // 停止应用程序监控定时器 + _powerPointProcessMonitorTimer?.Stop(); + + // 关闭PowerPoint应用程序 + ClosePowerPointApplication(); + + LogHelper.WriteLogToFile("PowerPoint应用程序守护已停止", LogHelper.LogType.Event); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"停止PowerPoint应用程序守护失败: {ex}", LogHelper.LogType.Error); + } + } + + /// + /// 创建PowerPoint应用程序实例 + /// + private void CreatePowerPointApplication() + { + try + { + // 如果应用程序已存在且有效,则不重复创建 + if (pptApplication != null && IsPowerPointApplicationValid()) + { + return; + } + + // 创建新的PowerPoint应用程序实例 + pptApplication = new Microsoft.Office.Interop.PowerPoint.Application(); + + // 设置为不可见,作为后台进程 + pptApplication.Visible = Microsoft.Office.Core.MsoTriState.msoFalse; + + // 设置应用程序属性 + pptApplication.WindowState = Microsoft.Office.Interop.PowerPoint.PpWindowState.ppWindowMinimized; + + // 直接设置PPTManager的PPTApplication属性,绕过COM注册问题 + Task.Delay(1000).ContinueWith(_ => + { + Dispatcher.Invoke(() => + { + try + { + // 直接设置PPTManager的PowerPoint应用程序实例 + if (_pptManager != null) + { + // 使用反射或直接访问来设置PPTManager的PPTApplication + SetPPTManagerApplication(pptApplication); + LogHelper.WriteLogToFile("已直接设置PPTManager的PowerPoint应用程序实例", LogHelper.LogType.Event); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"设置PPTManager的PowerPoint应用程序实例失败: {ex}", LogHelper.LogType.Error); + } + }); + }); + + LogHelper.WriteLogToFile("PowerPoint应用程序实例已创建", LogHelper.LogType.Event); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"创建PowerPoint应用程序实例失败: {ex}", LogHelper.LogType.Error); + } + } + + /// + /// 设置PPTManager的PowerPoint应用程序实例 + /// + private void SetPPTManagerApplication(Microsoft.Office.Interop.PowerPoint.Application app) + { + try + { + if (_pptManager == null) return; + + // 使用反射调用PPTManager的ConnectToPPT方法 + var pptManagerType = _pptManager.GetType(); + var connectMethod = pptManagerType.GetMethod("ConnectToPPT", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + + if (connectMethod != null) + { + connectMethod.Invoke(_pptManager, new object[] { app }); + LogHelper.WriteLogToFile("通过ConnectToPPT方法设置PowerPoint应用程序实例", LogHelper.LogType.Event); + } + else + { + // 如果无法通过反射调用,尝试直接设置属性 + var pptApplicationProperty = pptManagerType.GetProperty("PPTApplication", + System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + + if (pptApplicationProperty != null && pptApplicationProperty.CanWrite) + { + pptApplicationProperty.SetValue(_pptManager, app); + LogHelper.WriteLogToFile("通过属性设置PPTManager的PowerPoint应用程序实例", LogHelper.LogType.Event); + } + else + { + LogHelper.WriteLogToFile("无法设置PPTManager的PowerPoint应用程序实例", LogHelper.LogType.Warning); + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"设置PPTManager的PowerPoint应用程序实例失败: {ex}", LogHelper.LogType.Error); + } + } + + /// + /// 检查PowerPoint应用程序是否有效 + /// + private bool IsPowerPointApplicationValid() + { + try + { + if (pptApplication == null) return false; + if (!Marshal.IsComObject(pptApplication)) return false; + + // 尝试访问一个简单的属性来验证连接是否有效 + var _ = pptApplication.Name; + return true; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + // 如果COM对象已失效,返回false + if (hr == 0x8001010E || hr == 0x80004005 || hr == 0x800706B5) + { + return false; + } + return false; + } + catch + { + return false; + } + } + + /// + /// 关闭PowerPoint应用程序 + /// + private void ClosePowerPointApplication() + { + try + { + if (pptApplication != null) + { + // 关闭所有打开的演示文稿 + if (pptApplication.Presentations.Count > 0) + { + for (int i = pptApplication.Presentations.Count; i >= 1; i--) + { + try + { + pptApplication.Presentations[i].Close(); + } + catch { } + } + } + + // 退出PowerPoint应用程序 + pptApplication.Quit(); + + // 释放COM对象 + Marshal.ReleaseComObject(pptApplication); + pptApplication = null; + } + + LogHelper.WriteLogToFile("PowerPoint应用程序已关闭", LogHelper.LogType.Event); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"关闭PowerPoint应用程序失败: {ex}", LogHelper.LogType.Error); + } + } + + /// + /// PowerPoint应用程序监控定时器事件 + /// + private void OnPowerPointApplicationMonitorTick(object sender, EventArgs e) + { + try + { + if (!Settings.PowerPointSettings.EnablePowerPointEnhancement) + { + StopPowerPointProcessMonitoring(); + return; + } + + // 检查应用程序是否还在运行 + if (!IsPowerPointApplicationValid()) + { + LogHelper.WriteLogToFile("检测到PowerPoint应用程序已失效,重新创建", LogHelper.LogType.Event); + CreatePowerPointApplication(); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"PowerPoint应用程序监控异常: {ex}", LogHelper.LogType.Error); + } + } + #endregion + private void DisposePPTManagers() { try @@ -170,6 +415,11 @@ namespace Ink_Canvas _pptManager = null; _pptInkManager = null; _pptUIManager = null; + + // 清理PowerPoint进程守护 + StopPowerPointProcessMonitoring(); + _powerPointProcessMonitorTimer = null; + LogHelper.WriteLogToFile("PPT管理器已释放", LogHelper.LogType.Event); } catch (Exception ex) @@ -335,6 +585,9 @@ namespace Ink_Canvas { LogHelper.WriteLogToFile("PPT放映状态变化:退出放映模式", LogHelper.LogType.Trace); } + + // 检查主窗口可见性(用于仅PPT模式) + CheckMainWindowVisibility(); }); } catch (Exception ex) @@ -393,10 +646,24 @@ namespace Ink_Canvas BorderFloatingBarMainControls.Visibility = Visibility.Visible; - // 在PPT模式下隐藏手势面板和手势按钮 + // 在PPT模式下根据设置决定是否隐藏手势面板和手势按钮 AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder); AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder); - EnableTwoFingerGestureBorder.Visibility = Visibility.Collapsed; + + // 根据设置决定是否在PPT放映模式下显示手势按钮 + if (Settings.PowerPointSettings.ShowGestureButtonInSlideShow) + { + // 如果启用了PPT放映模式显示手势按钮,则显示手势按钮 + if (Settings.Gesture.IsEnableTwoFingerGesture) + { + CheckEnableTwoFingerGestureBtnVisibility(true); + } + } + else + { + // 如果禁用了PPT放映模式显示手势按钮,则隐藏手势按钮 + EnableTwoFingerGestureBorder.Visibility = Visibility.Collapsed; + } if (Settings.PowerPointSettings.IsShowCanvasAtNewSlideShow && !Settings.Automation.IsAutoFoldInPPTSlideShow) @@ -795,11 +1062,51 @@ namespace Ink_Canvas } } + private void ToggleSwitchPowerPointEnhancement_Toggled(object sender, RoutedEventArgs e) + { + if (!isLoaded) return; + + Settings.PowerPointSettings.EnablePowerPointEnhancement = ToggleSwitchPowerPointEnhancement.IsOn; + + // 与WPS支持互斥 + if (Settings.PowerPointSettings.EnablePowerPointEnhancement) + { + Settings.PowerPointSettings.IsSupportWPS = false; + ToggleSwitchSupportWPS.IsOn = false; + + // 更新PPT管理器的WPS支持设置 + if (_pptManager != null) + { + _pptManager.IsSupportWPS = false; + } + } + + SaveSettingsToFile(); + + // 启动或停止PowerPoint进程守护 + if (Settings.PowerPointSettings.EnablePowerPointEnhancement) + { + StartPowerPointProcessMonitoring(); + } + else + { + StopPowerPointProcessMonitoring(); + } + } + private void ToggleSwitchSupportWPS_Toggled(object sender, RoutedEventArgs e) { if (!isLoaded) return; Settings.PowerPointSettings.IsSupportWPS = ToggleSwitchSupportWPS.IsOn; + + // 与PowerPoint联动增强互斥 + if (Settings.PowerPointSettings.IsSupportWPS) + { + Settings.PowerPointSettings.EnablePowerPointEnhancement = false; + ToggleSwitchPowerPointEnhancement.IsOn = false; + StopPowerPointProcessMonitoring(); + } // 更新PPT管理器的WPS支持设置 if (_pptManager != null) diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs index 1afb5732..381745de 100644 --- a/Ink Canvas/MainWindow_cs/MW_Settings.cs +++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs @@ -197,14 +197,32 @@ namespace Ink_Canvas val > 0.5 && val < 1.25 ? val : val <= 0.5 ? 0.5 : val >= 1.25 ? 1.25 : 1; ViewboxFloatingBarScaleTransform.ScaleY = val > 0.5 && val < 1.25 ? val : val <= 0.5 ? 0.5 : val >= 1.25 ? 1.25 : 1; - // auto align - 新增:只在屏幕模式下重新计算浮动栏位置 - if (currentMode == 0) + + // 等待UI更新后再重新计算浮动栏位置,确保居中计算准确 + Dispatcher.BeginInvoke(new Action(async () => { - if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) - ViewboxFloatingBarMarginAnimation(60); - else - ViewboxFloatingBarMarginAnimation(100, true); - } + // 强制更新布局以确保ActualWidth正确 + ViewboxFloatingBar.UpdateLayout(); + + // 等待一小段时间让布局完全更新 + await Task.Delay(100); + + // 再次强制更新布局 + ViewboxFloatingBar.UpdateLayout(); + + // 强制重新测量和排列 + ViewboxFloatingBar.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + ViewboxFloatingBar.Arrange(new Rect(ViewboxFloatingBar.DesiredSize)); + + // auto align - 新增:只在屏幕模式下重新计算浮动栏位置 + if (currentMode == 0) + { + if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) + ViewboxFloatingBarMarginAnimation(60); + else + ViewboxFloatingBarMarginAnimation(100, true); + } + }), DispatcherPriority.Render); } private void ViewboxFloatingBarOpacityValueSlider_ValueChanged(object sender, RoutedEventArgs e) @@ -2697,7 +2715,8 @@ namespace Ink_Canvas } // 重新计算浮动栏位置,因为按钮可见性变化会影响浮动栏宽度 - if (!isFloatingBarFolded && currentMode == 0) // 新增:只在屏幕模式下重新计算浮动栏位置 + // 修复:移除浮动栏收起状态检查,确保在收起状态下也能正确修正位置 + if (currentMode == 0) // 只在屏幕模式下重新计算浮动栏位置 { if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) { diff --git a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs index e4c5c6b1..2ce687d5 100644 --- a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs +++ b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs @@ -256,6 +256,10 @@ namespace Ink_Canvas _taskbar.Visibility = Settings.Appearance.EnableTrayIcon ? Visibility.Visible : Visibility.Collapsed; ViewboxFloatingBar.Opacity = Settings.Appearance.ViewboxFloatingBarOpacityValue; + + // 初始化浮动栏透明度滑块值 + ViewboxFloatingBarOpacityValueSlider.Value = Settings.Appearance.ViewboxFloatingBarOpacityValue; + ViewboxFloatingBarOpacityInPPTValueSlider.Value = Settings.Appearance.ViewboxFloatingBarOpacityInPPTValue; if (Settings.Appearance.EnableViewboxBlackBoardScaleTransform) // 画板 UI 缩放 80% { @@ -455,6 +459,8 @@ namespace Ink_Canvas ToggleSwitchSupportWPS.IsOn = Settings.PowerPointSettings.IsSupportWPS; + ToggleSwitchPowerPointEnhancement.IsOn = Settings.PowerPointSettings.EnablePowerPointEnhancement; + ToggleSwitchAutoSaveScreenShotInPowerPoint.IsOn = Settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint; ToggleSwitchEnableWppProcessKill.IsOn = Settings.PowerPointSettings.EnableWppProcessKill; @@ -780,6 +786,17 @@ namespace Ink_Canvas ToggleSwitchDirectCallCiRand.IsOn = Settings.RandSettings.DirectCallCiRand; } + // ModeSettings + if (Settings.ModeSettings != null) + { + ToggleSwitchMode.IsOn = Settings.ModeSettings.IsPPTOnlyMode; + } + else + { + Settings.ModeSettings = new ModeSettings(); + ToggleSwitchMode.IsOn = false; + } + // Automation if (Settings.Automation != null) { diff --git a/Ink Canvas/MainWindow_cs/MW_ShapeDrawing.cs b/Ink Canvas/MainWindow_cs/MW_ShapeDrawing.cs index 0644f141..9cc3731b 100644 --- a/Ink Canvas/MainWindow_cs/MW_ShapeDrawing.cs +++ b/Ink Canvas/MainWindow_cs/MW_ShapeDrawing.cs @@ -107,6 +107,10 @@ namespace Ink_Canvas else if (sender == ImageDrawArrow || sender == BoardImageDrawArrow) drawingShapeMode = 2; else if (sender == ImageDrawParallelLine || sender == BoardImageDrawParallelLine) drawingShapeMode = 15; + + // 更新模式缓存 + UpdateCurrentToolMode("shape"); + isLongPressSelected = true; if (isSingleFingerDragMode) BtnFingerDragMode_Click(BtnFingerDragMode, null); } diff --git a/Ink Canvas/MainWindow_cs/MW_Timer.cs b/Ink Canvas/MainWindow_cs/MW_Timer.cs index 3c9e0e4b..e9a32ba2 100644 --- a/Ink Canvas/MainWindow_cs/MW_Timer.cs +++ b/Ink Canvas/MainWindow_cs/MW_Timer.cs @@ -122,14 +122,34 @@ namespace Ink_Canvas nowTimeVM.nowTime = DateTime.Now.ToString("tt hh'时'mm'分'ss'秒'"); } - // 修改TimerDisplayTime_ElapsedAsync方法中的时间格式 + // 修改TimerDisplayTime_ElapsedAsync方法中的时间格式,实现校验制 private async Task TimerDisplayTime_ElapsedAsync() { - DateTime now = await GetNetworkTimeAsync(); + DateTime localTime = DateTime.Now; + DateTime displayTime = localTime; // 默认使用本地时间 + + try + { + DateTime networkTime = await GetNetworkTimeAsync(); + + // 计算时间差 + TimeSpan timeDifference = networkTime - localTime; + double timeDifferenceMinutes = Math.Abs(timeDifference.TotalMinutes); + + // 如果网络时间与本地时间相差不超过1分钟,则使用本地时间 + // 否则使用网络时间 + displayTime = timeDifferenceMinutes <= 1.0 ? localTime : networkTime; + } + catch + { + // 网络时间获取失败时,使用本地时间 + displayTime = localTime; + } + // 只更新时间,日期由原有逻辑定时更新即可 Dispatcher.Invoke(() => { - nowTimeVM.nowTime = now.ToString("tt hh'时'mm'分'ss'秒'"); + nowTimeVM.nowTime = displayTime.ToString("tt hh'时'mm'分'ss'秒'"); }); } @@ -274,6 +294,17 @@ namespace Ink_Canvas private bool foldFloatingBarByUser, // 保持收纳操作不受自动收纳的控制 unfoldFloatingBarByUser; // 允许用户在希沃软件内进行展开操作 + /// + /// 检测是否为批注窗口(窗口标题为空且高度小于500像素) + /// + /// 如果是批注窗口返回true,否则返回false + private bool IsAnnotationWindow() + { + var windowTitle = ForegroundWindowInfo.WindowTitle(); + var windowRect = ForegroundWindowInfo.WindowRect(); + return windowTitle.Length == 0 && windowRect.Height < 500; + } + private void timerCheckAutoFold_Elapsed(object sender, ElapsedEventArgs e) { if (isFloatingBarChangingHideMode) return; @@ -316,7 +347,17 @@ namespace Ink_Canvas ForegroundWindowInfo.WindowRect().Height >= SystemParameters.WorkArea.Height - 16 && ForegroundWindowInfo.WindowRect().Width >= SystemParameters.WorkArea.Width - 16) { - if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null); + // 检测到批注窗口时保持收纳状态 + if (IsAnnotationWindow()) + { + // 批注窗口打开时,如果当前是展开状态则收纳 + if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null); + } + else + { + // 非批注窗口时正常处理 + if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null); + } // EasiNote5C } else if (Settings.Automation.IsAutoFoldInEasiNote5C && windowProcessName == "EasiNote5C" && @@ -335,14 +376,34 @@ namespace Ink_Canvas ForegroundWindowInfo.WindowRect().Height >= SystemParameters.WorkArea.Height - 16 && ForegroundWindowInfo.WindowRect().Width >= SystemParameters.WorkArea.Width - 16) { - if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null); + // 检测到批注窗口时保持收纳状态 + if (IsAnnotationWindow()) + { + // 批注窗口打开时,如果当前是展开状态则收纳 + if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null); + } + else + { + // 非批注窗口时正常处理 + if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null); + } // HiteTouchPro } else if (Settings.Automation.IsAutoFoldInHiteTouchPro && windowProcessName == "HiteTouchPro" && ForegroundWindowInfo.WindowRect().Height >= SystemParameters.WorkArea.Height - 16 && ForegroundWindowInfo.WindowRect().Width >= SystemParameters.WorkArea.Width - 16) { - if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null); + // 检测到批注窗口时保持收纳状态 + if (IsAnnotationWindow()) + { + // 批注窗口打开时,如果当前是展开状态则收纳 + if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null); + } + else + { + // 非批注窗口时正常处理 + if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null); + } // WxBoardMain } else if (Settings.Automation.IsAutoFoldInWxBoardMain && windowProcessName == "WxBoardMain" && @@ -369,7 +430,17 @@ namespace Ink_Canvas ForegroundWindowInfo.WindowRect().Height >= SystemParameters.WorkArea.Height - 16 && ForegroundWindowInfo.WindowRect().Width >= SystemParameters.WorkArea.Width - 16) { - if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null); + // 检测到批注窗口时保持收纳状态 + if (IsAnnotationWindow()) + { + // 批注窗口打开时,如果当前是展开状态则收纳 + if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null); + } + else + { + // 非批注窗口时正常处理 + if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null); + } // AdmoxWhiteboard } else if (Settings.Automation.IsAutoFoldInAdmoxWhiteboard && windowProcessName == "Amdox.WhiteBoard" && diff --git a/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs b/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs index bfba3b73..a5f6af9c 100644 --- a/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs +++ b/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs @@ -468,45 +468,55 @@ namespace Ink_Canvas BlackboardUIGridForInkReplay.IsHitTestVisible = false; dec.Add(e.TouchDevice.Id); - // Palm Eraser 逻辑 - 优化:改进手掌判定条件,提高精度 + // Palm Eraser 逻辑 - 优化:改进手掌判定条件,使用设备提供的触摸面积信息 if (Settings.Canvas.EnablePalmEraser && dec.Count >= 2 && !isPalmEraserActive && !palmEraserTouchDownHandled) { - var bounds = e.GetTouchPoint(inkCanvas).Bounds; + touchPoint = e.GetTouchPoint(inkCanvas); + var size = touchPoint.Size; // 使用设备提供的触摸面积信息 + var bounds = touchPoint.Bounds; // 保留bounds用于宽高比计算 // 根据敏感度设置调整判定参数 - double palmThreshold; + double palmAreaThreshold; // 改为面积阈值 double aspectRatioThreshold; int minTouchPoints; switch (Settings.Canvas.PalmEraserSensitivity) { case 0: // 低敏感度 - 更严格的判定 - palmThreshold = 80; + palmAreaThreshold = 6400; // 80*80的面积 aspectRatioThreshold = 0.4; minTouchPoints = 4; break; case 1: // 中敏感度 - 平衡的判定 - palmThreshold = 60; + palmAreaThreshold = 3600; // 60*60的面积 aspectRatioThreshold = 0.3; minTouchPoints = 3; break; case 2: // 高敏感度 - 较宽松的判定 default: - palmThreshold = 50; + palmAreaThreshold = 2500; // 50*50的面积 aspectRatioThreshold = 0.25; minTouchPoints = 2; break; } - // 计算宽高比 + // 计算触摸面积(使用设备提供的Size) + double touchArea = size.Width * size.Height; + + // 计算宽高比(使用Bounds确保准确性) double aspectRatio = Math.Min(bounds.Width, bounds.Height) / Math.Max(bounds.Width, bounds.Height); - // 更严格的手掌判定条件 - bool isLargeTouch = bounds.Width >= palmThreshold && bounds.Height >= palmThreshold; + // 改进的手掌判定条件:使用面积而不是单独的宽高 + bool isLargeTouch = touchArea >= palmAreaThreshold; bool isPalmLikeShape = aspectRatio >= aspectRatioThreshold; bool hasMultipleTouchPoints = dec.Count >= minTouchPoints; + + // 新增:额外的判定条件提高准确性 + bool isReasonableSize = size.Width >= 20 && size.Height >= 20 && size.Width <= 200 && size.Height <= 200; // 合理的触摸尺寸范围 + bool isNotTooElongated = aspectRatio >= 0.2; // 避免过于细长的触摸(可能是手指) + bool hasEnoughArea = touchArea >= 400; // 最小面积要求,避免小面积误判 - if (isLargeTouch && isPalmLikeShape && hasMultipleTouchPoints) + if (isLargeTouch && isPalmLikeShape && hasMultipleTouchPoints && isReasonableSize && isNotTooElongated && hasEnoughArea) { // 记录当前编辑模式和高光状态 palmEraserLastEditingMode = inkCanvas.EditingMode; @@ -529,7 +539,7 @@ namespace Ink_Canvas StartPalmEraserRecoveryTimer(); // 记录日志 - LogHelper.WriteLogToFile($"Palm eraser activated - Sensitivity: {Settings.Canvas.PalmEraserSensitivity}, Touch bounds: {bounds.Width}x{bounds.Height}, Aspect ratio: {aspectRatio:F2}, Touch points: {dec.Count}"); + LogHelper.WriteLogToFile($"Palm eraser activated - Sensitivity: {Settings.Canvas.PalmEraserSensitivity}, Touch area: {touchArea:F0}, Size: {size.Width}x{size.Height}, Bounds: {bounds.Width}x{bounds.Height}, Aspect ratio: {aspectRatio:F2}, Touch points: {dec.Count}, Reasonable size: {isReasonableSize}, Not elongated: {isNotTooElongated}, Enough area: {hasEnoughArea}"); } } @@ -602,6 +612,8 @@ namespace Ink_Canvas // 当所有手掌擦触摸点都抬起时,恢复原编辑模式 if (isPalmEraserActive && palmEraserTouchIds.Count == 0) { + LogHelper.WriteLogToFile($"Palm eraser recovery triggered - Touch points remaining: {palmEraserTouchIds.Count}, dec.Count: {dec.Count}"); + // 恢复高光状态 drawingAttributes.IsHighlighter = palmEraserLastIsHighlighter; @@ -764,6 +776,37 @@ namespace Ink_Canvas // 修复:确保手掌擦除后触摸事件能正常响应 if (isPalmEraserActive) { + LogHelper.WriteLogToFile("Palm eraser force recovery - all touch points cleared"); + + // 恢复高光状态 + drawingAttributes.IsHighlighter = palmEraserLastIsHighlighter; + + // 恢复编辑模式 + try + { + if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) + { + switch (palmEraserLastEditingMode) + { + case InkCanvasEditingMode.Ink: + PenIcon_Click(null, null); + break; + case InkCanvasEditingMode.Select: + SymbolIconSelect_MouseUp(null, null); + break; + default: + inkCanvas.EditingMode = palmEraserLastEditingMode; + break; + } + LogHelper.WriteLogToFile($"Palm eraser force recovered to mode: {palmEraserLastEditingMode}"); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"Palm eraser force recovery failed: {ex.Message}, forcing to Ink mode", LogHelper.LogType.Error); + inkCanvas.EditingMode = InkCanvasEditingMode.Ink; + } + // 如果手掌擦还在激活状态但触摸点已清空,强制重置状态 isPalmEraserActive = false; palmEraserTouchDownHandled = false; @@ -773,6 +816,11 @@ namespace Ink_Canvas ViewboxFloatingBar.IsHitTestVisible = true; BlackboardUIGridForInkReplay.IsHitTestVisible = true; + + // 停止恢复定时器 + StopPalmEraserRecoveryTimer(); + + LogHelper.WriteLogToFile("Palm eraser force recovery completed"); } } } diff --git a/Ink Canvas/MainWindow_cs/MW_TrayIcon.cs b/Ink Canvas/MainWindow_cs/MW_TrayIcon.cs index 96796023..21a9de2b 100644 --- a/Ink Canvas/MainWindow_cs/MW_TrayIcon.cs +++ b/Ink Canvas/MainWindow_cs/MW_TrayIcon.cs @@ -30,6 +30,12 @@ namespace Ink_Canvas var mainWin = (MainWindow)Current.MainWindow; if (mainWin.IsLoaded) { + // 在无焦点模式下,暂时取消主窗口置顶,让系统菜单能够正常显示 + if (Ink_Canvas.MainWindow.Settings.Advanced.IsAlwaysOnTop && Ink_Canvas.MainWindow.Settings.Advanced.IsNoFocusMode) + { + mainWin.Topmost = false; + } + // 判斷是否在收納模式中 if (mainWin.isFloatingBarFolded) { @@ -57,6 +63,19 @@ namespace Ink_Canvas } } + private void SysTrayMenu_Closed(object sender, RoutedEventArgs e) + { + var mainWin = (MainWindow)Current.MainWindow; + if (mainWin.IsLoaded) + { + // 菜单关闭后,恢复主窗口的置顶状态 + if (Ink_Canvas.MainWindow.Settings.Advanced.IsAlwaysOnTop && Ink_Canvas.MainWindow.Settings.Advanced.IsNoFocusMode) + { + mainWin.Topmost = true; + } + } + } + private void CloseAppTrayIconMenuItem_Clicked(object sender, RoutedEventArgs e) { var mainWin = (MainWindow)Current.MainWindow; diff --git a/Ink Canvas/Properties/AssemblyInfo.cs b/Ink Canvas/Properties/AssemblyInfo.cs index 7e0bc215..dff64f25 100644 --- a/Ink Canvas/Properties/AssemblyInfo.cs +++ b/Ink Canvas/Properties/AssemblyInfo.cs @@ -49,5 +49,5 @@ using System.Windows; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.7.9.1")] -[assembly: AssemblyFileVersion("1.7.9.1")] +[assembly: AssemblyVersion("1.7.10.0")] +[assembly: AssemblyFileVersion("1.7.10.0")] diff --git a/Ink Canvas/Resources/Cursors/close-hand-cursor.cur b/Ink Canvas/Resources/Cursors/close-hand-cursor.cur new file mode 100644 index 00000000..f8085925 Binary files /dev/null and b/Ink Canvas/Resources/Cursors/close-hand-cursor.cur differ diff --git a/Ink Canvas/Resources/Cursors/cursor-move.cur b/Ink Canvas/Resources/Cursors/cursor-move.cur new file mode 100644 index 00000000..168593ea Binary files /dev/null and b/Ink Canvas/Resources/Cursors/cursor-move.cur differ diff --git a/Ink Canvas/Resources/Cursors/cursor-resize-lr.cur b/Ink Canvas/Resources/Cursors/cursor-resize-lr.cur new file mode 100644 index 00000000..a29801b8 Binary files /dev/null and b/Ink Canvas/Resources/Cursors/cursor-resize-lr.cur differ diff --git a/Ink Canvas/Resources/Cursors/cursor-resize-lt-rb.cur b/Ink Canvas/Resources/Cursors/cursor-resize-lt-rb.cur new file mode 100644 index 00000000..01c3e194 Binary files /dev/null and b/Ink Canvas/Resources/Cursors/cursor-resize-lt-rb.cur differ diff --git a/Ink Canvas/Resources/Cursors/cursor-resize-rt-lb.cur b/Ink Canvas/Resources/Cursors/cursor-resize-rt-lb.cur new file mode 100644 index 00000000..a0e31268 Binary files /dev/null and b/Ink Canvas/Resources/Cursors/cursor-resize-rt-lb.cur differ diff --git a/Ink Canvas/Resources/Cursors/cursor-resize-tb.cur b/Ink Canvas/Resources/Cursors/cursor-resize-tb.cur new file mode 100644 index 00000000..b0649957 Binary files /dev/null and b/Ink Canvas/Resources/Cursors/cursor-resize-tb.cur differ diff --git a/Ink Canvas/Resources/Cursors/open-hand-cursor.cur b/Ink Canvas/Resources/Cursors/open-hand-cursor.cur new file mode 100644 index 00000000..baa77795 Binary files /dev/null and b/Ink Canvas/Resources/Cursors/open-hand-cursor.cur differ diff --git a/Ink Canvas/Resources/GeometryIcons.xaml b/Ink Canvas/Resources/GeometryIcons.xaml new file mode 100644 index 00000000..919eb517 --- /dev/null +++ b/Ink Canvas/Resources/GeometryIcons.xaml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ink Canvas/Resources/ICCConfiguration.cs b/Ink Canvas/Resources/ICCConfiguration.cs new file mode 100644 index 00000000..5a866e0d --- /dev/null +++ b/Ink Canvas/Resources/ICCConfiguration.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Media; + +namespace Ink_Canvas.Resources.ICCConfiguration { + public enum InitialPositionTypes { + TopLeft, TopRight, BottomLeft, BottomRight, TopCenter, BottomCenter, Custom + } + public enum ElementCornerRadiusTypes { + SuperEllipse, Circle, Custom, None + } + public class NearSnapAreaSize { + public double[] TopLeft { get; set; } = {24,24}; + public double[] TopRight { get; set; } = {24,24}; + public double[] BottomLeft { get; set; } = {24,24}; + public double[] BottomRight { get; set; } = {24,24}; + public double TopCenter { get; set; } = 24; + public double BottomCenter { get; set; } = 24; + } + public class ICCFloatingBarConfiguration { + public bool SemiTransparent { get; set; } = false; + public bool NearSnap { get; set; } = true; + public InitialPositionTypes InitialPosition { get; set; } = InitialPositionTypes.BottomCenter; + public Point InitialPositionPoint { get; set; } = new Point(0, 0); + public double ElementCornerRadiusValue = 0; + public ElementCornerRadiusTypes ElementCornerRadiusType { get; set; } = ElementCornerRadiusTypes.SuperEllipse; + + public bool ParallaxEffect { get; set; } = true; + public bool MiniMode { get; set; } = false; + public Color ClearButtonColor { get; set; } = Color.FromRgb(224, 27, 36); + public Color ClearButtonPressColor { get; set; } = Color.FromRgb(254, 226, 226); + public Color ToolButtonSelectedBgColor { get; set; } = Color.FromRgb(37, 99, 235); + public double MovingLimitationNoSnap { get; set; } = 12; + public double MovingLimitationSnapped { get; set; } = 24; + + public NearSnapAreaSize NearSnapAreaSize { get; set; } = new NearSnapAreaSize() { + TopLeft = new double[] { 24, 24 }, + TopRight = new double[] { 24, 24 }, + BottomLeft = new double[] { 24, 24 }, + BottomRight = new double[] { 24, 24 }, + }; + + public string[] ToolBarItemsInCursorMode { get; set; } = new string[] { + "Cursor", "Pen", "Clear", "Separator", "Whiteboard", "Gesture", "Menu", "Fold" + }; + public string[] ToolBarItemsInMiniMode { get; set; } = new string[] { + "Cursor", "Pen", "Clear" + }; + public string[] ToolBarItemsInAnnotationMode { get; set; } = new string[] { + "Cursor", "Pen", "Clear", "Separator", "Eraser", "ShapeDrawing", "Select", "Separator", "Undo", "Redo", "Separator", "Whiteboard", "Gesture", "Menu", "Fold" + }; + } + + public class ICCConfiguration { + public ICCFloatingBarConfiguration FloatingBar { get; set; } = new ICCFloatingBarConfiguration(); + } +} diff --git a/Ink Canvas/Resources/Icons-png/classic-icons/desktop-folder.png b/Ink Canvas/Resources/Icons-png/classic-icons/desktop-folder.png new file mode 100644 index 00000000..66291766 Binary files /dev/null and b/Ink Canvas/Resources/Icons-png/classic-icons/desktop-folder.png differ diff --git a/Ink Canvas/Resources/Icons-png/classic-icons/desktop-small-icon.png b/Ink Canvas/Resources/Icons-png/classic-icons/desktop-small-icon.png new file mode 100644 index 00000000..4f6235ef Binary files /dev/null and b/Ink Canvas/Resources/Icons-png/classic-icons/desktop-small-icon.png differ diff --git a/Ink Canvas/Resources/Icons-png/classic-icons/disk-drive.png b/Ink Canvas/Resources/Icons-png/classic-icons/disk-drive.png new file mode 100644 index 00000000..7ab6fd20 Binary files /dev/null and b/Ink Canvas/Resources/Icons-png/classic-icons/disk-drive.png differ diff --git a/Ink Canvas/Resources/Icons-png/classic-icons/documents-folder.png b/Ink Canvas/Resources/Icons-png/classic-icons/documents-folder.png new file mode 100644 index 00000000..8b18500d Binary files /dev/null and b/Ink Canvas/Resources/Icons-png/classic-icons/documents-folder.png differ diff --git a/Ink Canvas/Resources/Icons-png/classic-icons/folder.png b/Ink Canvas/Resources/Icons-png/classic-icons/folder.png new file mode 100644 index 00000000..32706ec6 Binary files /dev/null and b/Ink Canvas/Resources/Icons-png/classic-icons/folder.png differ diff --git a/Ink Canvas/Resources/Icons-png/classic-icons/photo-small-icon.png b/Ink Canvas/Resources/Icons-png/classic-icons/photo-small-icon.png new file mode 100644 index 00000000..eb1b6ceb Binary files /dev/null and b/Ink Canvas/Resources/Icons-png/classic-icons/photo-small-icon.png differ diff --git a/Ink Canvas/Resources/Icons-png/classic-icons/program-icon.png b/Ink Canvas/Resources/Icons-png/classic-icons/program-icon.png new file mode 100644 index 00000000..1c61bf41 Binary files /dev/null and b/Ink Canvas/Resources/Icons-png/classic-icons/program-icon.png differ diff --git a/Ink Canvas/Resources/Icons-png/classic-icons/user-folder.png b/Ink Canvas/Resources/Icons-png/classic-icons/user-folder.png new file mode 100644 index 00000000..851d5c68 Binary files /dev/null and b/Ink Canvas/Resources/Icons-png/classic-icons/user-folder.png differ diff --git a/Ink Canvas/Resources/Icons-png/icc-toolbar-v2.png b/Ink Canvas/Resources/Icons-png/icc-toolbar-v2.png new file mode 100644 index 00000000..b64fa6e4 Binary files /dev/null and b/Ink Canvas/Resources/Icons-png/icc-toolbar-v2.png differ diff --git a/Ink Canvas/Resources/Icons-png/uac_icon.png b/Ink Canvas/Resources/Icons-png/uac_icon.png new file mode 100644 index 00000000..9c90eba8 Binary files /dev/null and b/Ink Canvas/Resources/Icons-png/uac_icon.png differ diff --git a/Ink Canvas/Resources/Icons-png/windows-ink.png b/Ink Canvas/Resources/Icons-png/windows-ink.png new file mode 100644 index 00000000..1026b335 Binary files /dev/null and b/Ink Canvas/Resources/Icons-png/windows-ink.png differ diff --git a/Ink Canvas/Resources/Illustrations/settingsv2-powerpoint-pagebtns-bottom.png b/Ink Canvas/Resources/Illustrations/settingsv2-powerpoint-pagebtns-bottom.png new file mode 100644 index 00000000..c947e78c Binary files /dev/null and b/Ink Canvas/Resources/Illustrations/settingsv2-powerpoint-pagebtns-bottom.png differ diff --git a/Ink Canvas/Resources/Illustrations/settingsv2-powerpoint-pagebtns-side.png b/Ink Canvas/Resources/Illustrations/settingsv2-powerpoint-pagebtns-side.png new file mode 100644 index 00000000..b53e586e Binary files /dev/null and b/Ink Canvas/Resources/Illustrations/settingsv2-powerpoint-pagebtns-side.png differ diff --git a/Ink Canvas/Resources/Illustrations/wait-a-moment.png b/Ink Canvas/Resources/Illustrations/wait-a-moment.png new file mode 100644 index 00000000..0448f36c Binary files /dev/null and b/Ink Canvas/Resources/Illustrations/wait-a-moment.png differ diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs index 2216016d..9b6c8b5c 100644 --- a/Ink Canvas/Resources/Settings.cs +++ b/Ink Canvas/Resources/Settings.cs @@ -25,6 +25,8 @@ namespace Ink_Canvas public Startup Startup { get; set; } = new Startup(); [JsonProperty("randSettings")] public RandSettings RandSettings { get; set; } = new RandSettings(); + [JsonProperty("modeSettings")] + public ModeSettings ModeSettings { get; set; } = new ModeSettings(); } public class Canvas @@ -89,7 +91,7 @@ namespace Ink_Canvas [JsonProperty("enablePalmEraser")] public bool EnablePalmEraser { get; set; } = true; [JsonProperty("palmEraserSensitivity")] - public int PalmEraserSensitivity { get; set; } = 2; // 0-低敏感度, 1-中敏感度, 2-高敏感度 + public int PalmEraserSensitivity { get; set; } = 0; // 0-低敏感度, 1-中敏感度, 2-高敏感度 [JsonProperty("clearCanvasAlsoClearImages")] public bool ClearCanvasAlsoClearImages { get; set; } = true; [JsonProperty("showCircleCenter")] @@ -233,6 +235,9 @@ namespace Ink_Canvas [JsonProperty("quickColorPaletteDisplayMode")] public int QuickColorPaletteDisplayMode { get; set; } = 1; + [JsonProperty("enableHotkeysInMouseMode")] + public bool EnableHotkeysInMouseMode { get; set; } = false; + } public class PowerPointSettings @@ -304,6 +309,10 @@ namespace Ink_Canvas public bool EnableWppProcessKill { get; set; } = true; [JsonProperty("isAlwaysGoToFirstPageOnReenter")] public bool IsAlwaysGoToFirstPageOnReenter { get; set; } + [JsonProperty("enablePowerPointEnhancement")] + public bool EnablePowerPointEnhancement { get; set; } = false; + [JsonProperty("showGestureButtonInSlideShow")] + public bool ShowGestureButtonInSlideShow { get; set; } = false; } public class Automation @@ -577,4 +586,10 @@ namespace Ink_Canvas // 用于JSON序列化 public CustomFloatingBarIcon() { } } + + public class ModeSettings + { + [JsonProperty("isPPTOnlyMode")] + public bool IsPPTOnlyMode { get; set; } = false; // 是否为仅PPT模式,默认为false(正常模式) + } } diff --git a/Ink Canvas/Resources/contributors.png b/Ink Canvas/Resources/contributors.png new file mode 100644 index 00000000..989ba357 Binary files /dev/null and b/Ink Canvas/Resources/contributors.png differ diff --git a/Ink Canvas/Resources/qrcodes.png b/Ink Canvas/Resources/qrcodes.png new file mode 100644 index 00000000..918dd1df Binary files /dev/null and b/Ink Canvas/Resources/qrcodes.png differ diff --git a/Ink Canvas/Windows/CountdownTimerWindow.xaml b/Ink Canvas/Windows/CountdownTimerWindow.xaml index 8c785503..1a8bfe8e 100644 --- a/Ink Canvas/Windows/CountdownTimerWindow.xaml +++ b/Ink Canvas/Windows/CountdownTimerWindow.xaml @@ -179,12 +179,12 @@ - + - + @@ -194,12 +194,12 @@ - + - + @@ -215,7 +215,7 @@ - + - + - + diff --git a/Ink Canvas/Windows/CountdownTimerWindow.xaml.cs b/Ink Canvas/Windows/CountdownTimerWindow.xaml.cs index 03e38cc6..31e190ce 100644 --- a/Ink Canvas/Windows/CountdownTimerWindow.xaml.cs +++ b/Ink Canvas/Windows/CountdownTimerWindow.xaml.cs @@ -1,16 +1,11 @@ using Ink_Canvas.Helpers; using System; -using System.ComponentModel; using System.Media; using System.Timers; using System.Windows; -using System.Windows.Forms; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; -using Application = System.Windows.Application; -using MouseEventArgs = System.Windows.Input.MouseEventArgs; -using Timer = System.Timers.Timer; namespace Ink_Canvas { @@ -56,7 +51,7 @@ namespace Ink_Canvas TextBlockSecond.Text = "00"; timer.Stop(); isTimerRunning = false; - FontIconStart.Glyph = ""; + SymbolIconStart.Symbol = iNKORE.UI.WPF.Modern.Controls.Symbol.Play; BtnStartCover.Visibility = Visibility.Visible; TextBlockHour.Foreground = new SolidColorBrush(StringToColor("#FF5B5D5F")); BorderStopTime.Visibility = Visibility.Collapsed; @@ -75,23 +70,23 @@ namespace Ink_Canvas SoundPlayer player = new SoundPlayer(); - int hour; + int hour = 0; int minute = 1; - int second; + int second = 0; int totalSeconds = 60; DateTime startTime = DateTime.Now; DateTime pauseTime = DateTime.Now; - bool isTimerRunning; - bool isPaused; + bool isTimerRunning = false; + bool isPaused = false; Timer timer = new Timer(); private void Grid_MouseUp(object sender, MouseButtonEventArgs e) { if (isTimerRunning) return; - if (ProcessBarTime.Visibility == Visibility.Visible && !isTimerRunning) + if (ProcessBarTime.Visibility == Visibility.Visible && isTimerRunning == false) { ProcessBarTime.Visibility = Visibility.Collapsed; GridAdjustHour.Visibility = Visibility.Visible; @@ -207,12 +202,12 @@ namespace Ink_Canvas if (WindowState == WindowState.Normal) { WindowState = WindowState.Maximized; - FontIconFullscreen.Glyph = ""; + SymbolIconFullscreen.Symbol = iNKORE.UI.WPF.Modern.Controls.Symbol.BackToWindow; } else { WindowState = WindowState.Normal; - FontIconFullscreen.Glyph = ""; + SymbolIconFullscreen.Symbol = iNKORE.UI.WPF.Modern.Controls.Symbol.FullScreen; } } @@ -227,6 +222,7 @@ namespace Ink_Canvas BtnStartCover.Visibility = Visibility.Collapsed; BorderStopTime.Visibility = Visibility.Collapsed; TextBlockHour.Foreground = new SolidColorBrush(StringToColor("#FF5B5D5F")); + return; } else if (isTimerRunning && isPaused) { @@ -237,7 +233,7 @@ namespace Ink_Canvas BtnStartCover.Visibility = Visibility.Collapsed; BorderStopTime.Visibility = Visibility.Collapsed; TextBlockHour.Foreground = new SolidColorBrush(StringToColor("#FF5B5D5F")); - FontIconStart.Glyph = ""; + SymbolIconStart.Symbol = iNKORE.UI.WPF.Modern.Controls.Symbol.Play; isTimerRunning = false; timer.Stop(); isPaused = false; @@ -287,7 +283,7 @@ namespace Ink_Canvas startTime += DateTime.Now - pauseTime; ProcessBarTime.IsPaused = false; TextBlockHour.Foreground = Brushes.Black; - FontIconStart.Glyph = ""; + SymbolIconStart.Symbol = iNKORE.UI.WPF.Modern.Controls.Symbol.Pause; isPaused = false; timer.Start(); UpdateStopTime(); @@ -299,7 +295,7 @@ namespace Ink_Canvas pauseTime = DateTime.Now; ProcessBarTime.IsPaused = true; TextBlockHour.Foreground = new SolidColorBrush(StringToColor("#FF5B5D5F")); - FontIconStart.Glyph = ""; + SymbolIconStart.Symbol = iNKORE.UI.WPF.Modern.Controls.Symbol.Play; BorderStopTime.Visibility = Visibility.Collapsed; isPaused = true; timer.Stop(); @@ -311,7 +307,7 @@ namespace Ink_Canvas totalSeconds = ((hour * 60) + minute) * 60 + second; ProcessBarTime.IsPaused = false; TextBlockHour.Foreground = Brushes.Black; - FontIconStart.Glyph = ""; + SymbolIconStart.Symbol = iNKORE.UI.WPF.Modern.Controls.Symbol.Pause; BtnResetCover.Visibility = Visibility.Collapsed; if (totalSeconds <= 10) @@ -339,7 +335,7 @@ namespace Ink_Canvas } } - private void Window_Closing(object sender, CancelEventArgs e) + private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { isTimerRunning = false; } @@ -349,7 +345,7 @@ namespace Ink_Canvas Close(); } - private bool _isInCompact; + private bool _isInCompact = false; private void BtnMinimal_OnMouseUp(object sender, MouseButtonEventArgs e) { @@ -363,13 +359,12 @@ namespace Ink_Canvas // Set to center double dpiScaleX = 1, dpiScaleY = 1; PresentationSource source = PresentationSource.FromVisual(this); - if (source != null) - { + if (source != null) { dpiScaleX = source.CompositionTarget.TransformToDevice.M11; dpiScaleY = source.CompositionTarget.TransformToDevice.M22; } IntPtr windowHandle = new WindowInteropHelper(this).Handle; - Screen screen = Screen.FromHandle(windowHandle); + System.Windows.Forms.Screen screen = System.Windows.Forms.Screen.FromHandle(windowHandle); double screenWidth = screen.Bounds.Width / dpiScaleX, screenHeight = screen.Bounds.Height / dpiScaleY; Left = (screenWidth / 2) - (Width / 2); Top = (screenHeight / 2) - (Height / 2); diff --git a/Ink Canvas/Windows/HotkeySettingsWindow.xaml b/Ink Canvas/Windows/HotkeySettingsWindow.xaml index 8b008b07..a61d785c 100644 --- a/Ink Canvas/Windows/HotkeySettingsWindow.xaml +++ b/Ink Canvas/Windows/HotkeySettingsWindow.xaml @@ -6,170 +6,194 @@ xmlns:local="clr-namespace:Ink_Canvas.Windows" xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" ui:ThemeManager.RequestedTheme="Light" - Topmost="True" - Background="Transparent" + Background="#F9F9F9" AllowsTransparency="True" mc:Ignorable="d" WindowStyle="None" WindowStartupLocation="CenterScreen" + ResizeMode="CanResize" Title="快捷键设置" Height="600" Width="800"> - - - - - + + + + + + + + + + + -