From 0b0714c1667e65b9ecb03e0cc2eee07347b619aa Mon Sep 17 00:00:00 2001 From: PANDA-JSR Date: Wed, 4 Feb 2026 14:30:27 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=20https://github.com/dou?= =?UTF-8?q?dou0720/ICC-CE/pull/21=20=E4=B8=AD=E7=9A=84=E5=BB=BA=E8=AE=AE?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/Docs/ExternalProtocol.md | 77 +++++++++++++++++++ Ink Canvas/Helpers/UriSchemeHelper.cs | 34 +++++++- Ink Canvas/MainWindow.xaml | 4 +- Ink Canvas/MainWindow_cs/MW_AutoFold.cs | 18 +++++ Ink Canvas/MainWindow_cs/MW_Settings.cs | 51 +++++++++--- Ink Canvas/MainWindow_cs/MW_UriHandler.cs | 64 ++++++++++++++- Ink Canvas/Resources/Settings.cs | 3 + .../SettingsViews/AdvancedPanel.xaml | 17 ++-- .../SettingsViews/AdvancedPanel.xaml.cs | 26 ++++++- 9 files changed, 269 insertions(+), 25 deletions(-) create mode 100644 Ink Canvas/Docs/ExternalProtocol.md diff --git a/Ink Canvas/Docs/ExternalProtocol.md b/Ink Canvas/Docs/ExternalProtocol.md new file mode 100644 index 00000000..d68569b7 --- /dev/null +++ b/Ink Canvas/Docs/ExternalProtocol.md @@ -0,0 +1,77 @@ +# Ink Canvas 外部协议 (URI Scheme) 说明文档 + +Ink Canvas 支持通过自定义协议 `icc://` 进行外部调用。通过此功能,其他应用程序、网页脚本或系统快捷方式可以远程控制 Ink Canvas 的运行状态。 + +## 启用方法 + +在使用外部协议之前,必须先在软件设置中启用: +1. 打开 **软件设置**。 +2. 进入 **高级选项** 面板。 +3. 找到 **外部协议调用** 区域。 +4. 开启 **“启用外部协议 (icc://)”** 开关。 + +> **注意**:此操作会自动在系统注册表中为当前用户注册协议。如果手动关闭该功能,协议将被注销。 + +--- + +## 命令列表 + +### 1. 基础控制命令 + +| 命令 | 完整 URI | 作用 | +| :--- | :--- | :--- | +| **Fold** | `icc://fold` | 进入**收纳模式**。如果当前处于展开状态,将清空墨迹并折叠到侧边栏。 | +| **Unfold** | `icc://unfold` | 退出**收纳模式**。如果当前已折叠,将展开浮动工具栏。 | +| **Toggle** | `icc://toggle` | **切换**状态。已展开则收起,已收起则展开。 | +| **Show** | `icc://show` | 与 `unfold` 作用相同,用于兼容旧版指令。 | + +### 2. 侧边栏工具命令 + +以下命令对应收纳模式下侧边栏提供的快速工具。 + +| 命令 | 完整 URI | 作用 | +| :--- | :--- | :--- | +| **单次抽** | `icc://randone` | 打开随机点名窗口并执行**单次抽选**。 | +| **随机抽** | `icc://rand` | 打开随机点名窗口并执行**随机抽选**。 | +| **计时器** | `icc://timer` | 打开**计时器/倒计时**工具。 | +| **白板** | `icc://whiteboard` | 切换到**白板模式**(也可使用 `icc://board`)。 | + +### 3. 进阶功能命令 (隐藏功能) + +以下功能专门用于解决与第三方侧边栏或悬浮窗程序的兼容性问题,未在常规设置界面显示。 + +| 命令 | 完整 URI | 作用 | +| :--- | :--- | :--- | +| **ThoroughHideOn** | `icc://thoroughHideOn` | **开启**“收起时彻底隐藏”功能。开启后,进入收纳模式时主窗口将完全不可见。 | +| **ThoroughHideOff** | `icc://thoroughHideOff` | **关闭**“收起时彻底隐藏”功能。恢复默认的侧边栏边缘留痕模式。 | +| **ThoroughHideToggle** | `icc://thoroughhidetoggle` | **切换**“收起时彻底隐藏”功能的开启/关闭状态。 | + +--- + +## 使用示例 + +### A. 在浏览器中调用 +可以直接在浏览器地址栏输入并回车,或在 HTML 中使用超链接: +```html +立即收纳 Ink Canvas +``` + +### B. 在 Windows “运行”对话框中使用 +按下 `Win + R`,输入 `icc://toggle` 并回车。 + +### C. 在批处理或命令行中使用 +```cmd +start icc://unfold +``` + +--- + +## 开发者说明 + +### 运行机制 +1. **唤醒启动**:如果 Ink Canvas 尚未运行,调用 URI 会直接启动程序并执行命令。 +2. **进程间通信 (IPC)**:如果程序已经在运行,外部调用会启动一个临时的指令传递进程,通过系统事件和临时文件将指令发送给已运行的实例,实现无缝控制。 + +### 兼容性 +* 支持 Windows 7 及更高版本。 +* 注册表位置:`HKEY_CURRENT_USER\Software\Classes\icc` (无需管理员权限)。 diff --git a/Ink Canvas/Helpers/UriSchemeHelper.cs b/Ink Canvas/Helpers/UriSchemeHelper.cs index 70a31b25..1e957402 100644 --- a/Ink Canvas/Helpers/UriSchemeHelper.cs +++ b/Ink Canvas/Helpers/UriSchemeHelper.cs @@ -75,8 +75,38 @@ namespace Ink_Canvas.Helpers { if (shellKey == null) return false; string command = shellKey.GetValue("") as string; - string exePath = Process.GetCurrentProcess().MainModule.FileName; - return !string.IsNullOrEmpty(command) && command.Contains(exePath); + if (string.IsNullOrEmpty(command)) return false; + + // 提取第一个标记作为可执行文件路径(处理带引号的情况) + string registeredExePath = ""; + if (command.StartsWith("\"")) + { + int nextQuote = command.IndexOf("\"", 1); + if (nextQuote > 1) + { + registeredExePath = command.Substring(1, nextQuote - 1); + } + } + else + { + int firstSpace = command.IndexOf(" "); + registeredExePath = firstSpace > 0 ? command.Substring(0, firstSpace) : command; + } + + if (string.IsNullOrEmpty(registeredExePath)) return false; + + string currentExePath = Process.GetCurrentProcess().MainModule.FileName; + + try + { + string normalizedRegisteredPath = System.IO.Path.GetFullPath(registeredExePath); + string normalizedCurrentPath = System.IO.Path.GetFullPath(currentExePath); + return string.Equals(normalizedRegisteredPath, normalizedCurrentPath, StringComparison.OrdinalIgnoreCase); + } + catch + { + return string.Equals(registeredExePath, currentExePath, StringComparison.OrdinalIgnoreCase); + } } } } diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index 2ad47907..95032d06 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -2417,8 +2417,10 @@ - diff --git a/Ink Canvas/MainWindow_cs/MW_AutoFold.cs b/Ink Canvas/MainWindow_cs/MW_AutoFold.cs index 8c3298b7..4b2afe4a 100644 --- a/Ink Canvas/MainWindow_cs/MW_AutoFold.cs +++ b/Ink Canvas/MainWindow_cs/MW_AutoFold.cs @@ -105,6 +105,15 @@ namespace Ink_Canvas HideSubPanels("cursor"); SidePannelMarginAnimation(-10); }); + + // 新增:如果开启了彻底隐藏,则隐藏主窗口 + if (Settings.Automation.ThoroughlyHideWhenFolded) + { + await Dispatcher.InvokeAsync(() => + { + this.Visibility = Visibility.Hidden; + }); + } } private async void LeftUnFoldButtonDisplayQuickPanel_MouseUp(object sender, MouseButtonEventArgs e) @@ -230,6 +239,15 @@ namespace Ink_Canvas public async Task UnFoldFloatingBar(object sender) { + // 新增:如果之前彻底隐藏了,先恢复显示 + if (this.Visibility != Visibility.Visible) + { + await Dispatcher.InvokeAsync(() => + { + this.Visibility = Visibility.Visible; + }); + } + await Dispatcher.InvokeAsync(() => { LeftUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed; diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs index a9bbc7f0..c696af39 100644 --- a/Ink Canvas/MainWindow_cs/MW_Settings.cs +++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs @@ -2856,24 +2856,55 @@ namespace Ink_Canvas private void ToggleSwitchIsEnableUriScheme_Toggled(object sender, RoutedEventArgs e) { if (!isLoaded) return; - Settings.Advanced.IsEnableUriScheme = ToggleSwitchIsEnableUriScheme.IsOn; - if (Settings.Advanced.IsEnableUriScheme) + bool newState = ToggleSwitchIsEnableUriScheme.IsOn; + bool success = false; + + try { - if (!UriSchemeHelper.IsUriSchemeRegistered()) + if (newState) { - UriSchemeHelper.RegisterUriScheme(); + if (!UriSchemeHelper.IsUriSchemeRegistered()) + { + success = UriSchemeHelper.RegisterUriScheme(); + } + else + { + success = true; + } } + else + { + if (UriSchemeHelper.IsUriSchemeRegistered()) + { + success = UriSchemeHelper.UnregisterUriScheme(); + } + else + { + success = true; + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"切换URI Scheme状态失败: {ex.Message}", LogHelper.LogType.Error); + success = false; + } + + if (success) + { + Settings.Advanced.IsEnableUriScheme = newState; + SaveSettingsToFile(); } else { - if (UriSchemeHelper.IsUriSchemeRegistered()) - { - UriSchemeHelper.UnregisterUriScheme(); - } - } + // 回滚 UI 状态 + isLoaded = false; + ToggleSwitchIsEnableUriScheme.IsOn = !newState; + isLoaded = true; - SaveSettingsToFile(); + ShowNotification("设置外部协议失败,请检查权限或日志"); + } } private void TouchMultiplierSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) diff --git a/Ink Canvas/MainWindow_cs/MW_UriHandler.cs b/Ink Canvas/MainWindow_cs/MW_UriHandler.cs index 0b124ed9..59013ddd 100644 --- a/Ink Canvas/MainWindow_cs/MW_UriHandler.cs +++ b/Ink Canvas/MainWindow_cs/MW_UriHandler.cs @@ -12,18 +12,32 @@ namespace Ink_Canvas { if (string.IsNullOrEmpty(uri)) return; + // 检查是否启用了外部协议 + if (!Settings.Advanced.IsEnableUriScheme) + { + LogHelper.WriteLogToFile($"URI协议已禁用,忽略请求: {uri}", LogHelper.LogType.Warning); + return; + } + LogHelper.WriteLogToFile($"正在处理URI命令: {uri}", LogHelper.LogType.Event); // 解析URI // 格式: icc://command?param=value // 如果URI以icc:开头但不是标准URI格式,尝试手动解析 string command = ""; - + if (Uri.TryCreate(uri, UriKind.Absolute, out Uri uriObj)) { command = uriObj.Host.ToLower(); + // 处理像 icc:fold 这样 Host 可能为空的情况 + if (string.IsNullOrEmpty(command)) + { + command = uriObj.AbsolutePath.Trim('/').ToLower(); + } } - else if (uri.StartsWith("icc:", StringComparison.OrdinalIgnoreCase)) + + // 如果解析失败且是 icc: 协议,则手动处理 + if (string.IsNullOrEmpty(command) && uri.StartsWith("icc:", StringComparison.OrdinalIgnoreCase)) { // 简单的手动解析: icc:fold string path = uri.Substring(4); @@ -63,6 +77,52 @@ namespace Ink_Canvas } break; + case "thoroughhideon": + Settings.Automation.ThoroughlyHideWhenFolded = true; + SaveSettingsToFile(); + ShowNotification("已开启:收起时彻底隐藏"); + // 如果当前已经是在收纳模式,立即隐藏 + if (isFloatingBarFolded) + { + this.Visibility = Visibility.Hidden; + } + break; + + case "thoroughhideoff": + Settings.Automation.ThoroughlyHideWhenFolded = false; + SaveSettingsToFile(); + ShowNotification("已关闭:收起时彻底隐藏"); + // 确保窗口可见 + this.Visibility = Visibility.Visible; + break; + + case "thoroughhidetoggle": + Settings.Automation.ThoroughlyHideWhenFolded = !Settings.Automation.ThoroughlyHideWhenFolded; + SaveSettingsToFile(); + ShowNotification(Settings.Automation.ThoroughlyHideWhenFolded ? "已开启:收起时彻底隐藏" : "已关闭:收起时彻底隐藏"); + if (isFloatingBarFolded) + { + this.Visibility = Settings.Automation.ThoroughlyHideWhenFolded ? Visibility.Hidden : Visibility.Visible; + } + break; + + case "randone": + SymbolIconRandOne_MouseUp(null, null); + break; + + case "rand": + SymbolIconRand_MouseUp(null, null); + break; + + case "timer": + ImageCountdownTimer_MouseUp(null, null); + break; + + case "whiteboard": + case "board": + ImageBlackboard_MouseUp(null, null); + break; + default: LogHelper.WriteLogToFile($"未知的URI命令: {command}", LogHelper.LogType.Warning); break; diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs index bb3dac6d..2fbedc70 100644 --- a/Ink Canvas/Resources/Settings.cs +++ b/Ink Canvas/Resources/Settings.cs @@ -496,6 +496,9 @@ namespace Ink_Canvas [JsonProperty("autoSaveStrokesIntervalMinutes")] public int AutoSaveStrokesIntervalMinutes { get; set; } = 5; + [JsonProperty("thoroughlyHideWhenFolded")] + public bool ThoroughlyHideWhenFolded { get; set; } = false; + [JsonProperty("floatingWindowInterceptor")] public FloatingWindowInterceptorSettings FloatingWindowInterceptor { get; set; } = new FloatingWindowInterceptorSettings(); } diff --git a/Ink Canvas/Windows/SettingsViews/SettingsViews/AdvancedPanel.xaml b/Ink Canvas/Windows/SettingsViews/SettingsViews/AdvancedPanel.xaml index 6b9576c3..aa07890a 100644 --- a/Ink Canvas/Windows/SettingsViews/SettingsViews/AdvancedPanel.xaml +++ b/Ink Canvas/Windows/SettingsViews/SettingsViews/AdvancedPanel.xaml @@ -417,8 +417,8 @@ -