根据 https://github.com/doudou0720/ICC-CE/pull/21 中的建议修改代码
This commit is contained in:
@@ -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
|
||||
<a href="icc://fold">立即收纳 Ink Canvas</a>
|
||||
```
|
||||
|
||||
### 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` (无需管理员权限)。
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2417,8 +2417,10 @@
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchIsSpecialScreen"
|
||||
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchIsSpecialScreen_OnToggled" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchIsEnableUriScheme"
|
||||
<ui:ToggleSwitch OnContent="开" OffContent="关" Name="ToggleSwitchIsEnableUriScheme"
|
||||
Visibility="Collapsed" IsOn="False"
|
||||
AutomationProperties.Name="外部协议调用 (icc://)"
|
||||
ToolTip="通过 icc:// 协议从外部控制软件"
|
||||
Toggled="ToggleSwitchIsEnableUriScheme_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<StackPanel Orientation="Vertical">
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<double> e)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -417,8 +417,8 @@
|
||||
</Grid>
|
||||
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,8,0,0">
|
||||
<Button x:Name="BtnManualBackup" Content="手动备份" Padding="12,6" Margin="0,0,12,0" Background="#2563eb" Foreground="White"/>
|
||||
<Button x:Name="BtnRestoreBackup" Content="还原备份" Padding="12,6" Background="#2563eb" Foreground="White"/>
|
||||
<Button x:Name="BtnManualBackup" Tag="manual_backup" Content="手动备份" Padding="12,6" Margin="0,0,12,0" Background="#2563eb" Foreground="White" Click="Button_Click"/>
|
||||
<Button x:Name="BtnRestoreBackup" Tag="restore_backup" Content="还原备份" Padding="12,6" Background="#2563eb" Foreground="White" Click="Button_Click"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
@@ -430,9 +430,9 @@
|
||||
<TextBlock Text="管理.icstk文件的关联设置,双击.icstk文件可直接在Ink Canvas中打开"
|
||||
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11" Margin="0,0,0,12"/>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,8,0,0">
|
||||
<Button x:Name="BtnUnregisterFileAssociation" Content="取消文件关联" Padding="15,5" Margin="0,0,8,0" Background="#2563eb" Foreground="White" Click="Button_Click"/>
|
||||
<Button x:Name="BtnCheckFileAssociation" Content="检查关联状态" Padding="15,5" Margin="0,0,8,0" Background="#2563eb" Foreground="White" Click="Button_Click"/>
|
||||
<Button x:Name="BtnRegisterFileAssociation" Content="重新注册关联" Padding="15,5" Background="#2563eb" Foreground="White" Click="Button_Click"/>
|
||||
<Button x:Name="BtnUnregisterFileAssociation" Tag="unregister" Content="取消文件关联" Padding="15,5" Margin="0,0,8,0" Background="#2563eb" Foreground="White" Click="Button_Click"/>
|
||||
<Button x:Name="BtnCheckFileAssociation" Tag="check" Content="检查关联状态" Padding="15,5" Margin="0,0,8,0" Background="#2563eb" Foreground="White" Click="Button_Click"/>
|
||||
<Button x:Name="BtnRegisterFileAssociation" Tag="register" Content="重新注册关联" Padding="15,5" Background="#2563eb" Foreground="White" Click="Button_Click"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
@@ -448,7 +448,10 @@
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="启用外部协议 (icc://)" HorizontalAlignment="Left"/>
|
||||
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后可通过 icc://fold (收纳) 或 icc://unfold (展开) 等命令控制" HorizontalAlignment="Left"/>
|
||||
</StackPanel>
|
||||
<Border x:Name="ToggleSwitchIsEnableUriScheme" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="IsEnableUriScheme" MouseLeftButtonDown="ToggleSwitch_Click">
|
||||
<Border x:Name="ToggleSwitchIsEnableUriScheme" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="IsEnableUriScheme" MouseLeftButtonDown="ToggleSwitch_Click"
|
||||
Focusable="True" IsTabStop="True" KeyDown="ToggleSwitch_KeyDown"
|
||||
AutomationProperties.Name="启用外部协议 (icc://)"
|
||||
AutomationProperties.HelpText="开启后可通过 icc://fold (收纳) 或 icc://unfold (展开) 等命令控制">
|
||||
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
|
||||
@@ -658,7 +661,7 @@
|
||||
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
|
||||
<StackPanel Orientation="Vertical" Margin="18,18,18,18">
|
||||
<TextBlock Text="Dlass设置管理" FontWeight="Bold" Foreground="#2e3436" FontSize="18" Margin="0,0,0,12"/>
|
||||
<Button x:Name="BtnDlassSettingsManage" Content="Dlass设置管理" Padding="15,5" HorizontalAlignment="Left" Background="#2563eb" Foreground="White"/>
|
||||
<Button x:Name="BtnDlassSettingsManage" Tag="dlass_settings" Content="Dlass设置管理" Padding="15,5" HorizontalAlignment="Left" Background="#2563eb" Foreground="White" Click="Button_Click"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
@@ -330,6 +330,18 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ToggleSwitch键盘事件处理
|
||||
/// </summary>
|
||||
private void ToggleSwitch_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == System.Windows.Input.Key.Space || e.Key == System.Windows.Input.Key.Enter)
|
||||
{
|
||||
ToggleSwitch_Click(sender, new RoutedEventArgs());
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slider值变化事件处理
|
||||
/// </summary>
|
||||
@@ -466,31 +478,39 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
var button = sender as Button;
|
||||
if (button == null) return;
|
||||
|
||||
string name = button.Name;
|
||||
string action = button.Tag?.ToString();
|
||||
if (string.IsNullOrEmpty(action)) action = button.Name;
|
||||
|
||||
// 这些按钮的功能可能需要调用 MainWindow 中的方法
|
||||
// 暂时先留空,后续可以根据需要实现
|
||||
switch (name)
|
||||
switch (action)
|
||||
{
|
||||
case "manual_backup":
|
||||
case "BtnManualBackup":
|
||||
// TODO: 调用 MainWindow 的备份方法
|
||||
break;
|
||||
|
||||
case "restore_backup":
|
||||
case "BtnRestoreBackup":
|
||||
// TODO: 调用 MainWindow 的还原方法
|
||||
break;
|
||||
|
||||
case "unregister":
|
||||
case "BtnUnregisterFileAssociation":
|
||||
// TODO: 调用 MainWindow 的取消文件关联方法
|
||||
break;
|
||||
|
||||
case "check":
|
||||
case "BtnCheckFileAssociation":
|
||||
// TODO: 调用 MainWindow 的检查文件关联方法
|
||||
// TODO: 调用 MainWindow 的检查文件关联状态方法
|
||||
break;
|
||||
|
||||
case "register":
|
||||
case "BtnRegisterFileAssociation":
|
||||
// TODO: 调用 MainWindow 的注册文件关联方法
|
||||
break;
|
||||
|
||||
case "dlass_settings":
|
||||
case "BtnDlassSettingsManage":
|
||||
// TODO: 调用 MainWindow 的 Dlass 设置管理方法
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user