improve:展台

新增将展台替换为希沃展台快捷启动功能
This commit is contained in:
2026-04-05 09:38:16 +08:00
parent dfc23b4428
commit f5a657d5c3
11 changed files with 188 additions and 49 deletions
+125 -46
View File
@@ -1,72 +1,151 @@
using Microsoft.Win32;
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
namespace Ink_Canvas.Helpers
{
internal class SoftwareLauncher
internal static class SoftwareLauncher
{
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
/// <summary>与 ICA 一致:在「程序和功能」卸载列表中按 DisplayName 匹配后启动 sweclauncher.exe。</summary>
public static void LaunchEasiCamera(string softwareName)
{
string executablePath = FindEasiCameraExecutablePath(softwareName);
if (!string.IsNullOrEmpty(executablePath))
if (string.IsNullOrEmpty(executablePath))
{
try
{
Process.Start(executablePath);
//Console.WriteLine(softwareName + " 启动成功!");
}
catch (Exception ex)
{
Console.WriteLine("启动失败: " + ex.Message);
//MessageBox.Show("启动失败: " + ex.Message);
}
MessageBox.Show(
"未找到希沃视频展台安装信息(已扫描 64 位与 32 位卸载注册表)。请确认已通过官方安装包安装「希沃视频展台」。",
"Ink Canvas",
MessageBoxButton.OK,
MessageBoxImage.Information);
return;
}
try
{
var directory = Path.GetDirectoryName(executablePath);
var psi = new ProcessStartInfo
{
FileName = executablePath,
UseShellExecute = true,
WorkingDirectory = string.IsNullOrEmpty(directory) ? Environment.SystemDirectory : directory
};
Process.Start(psi);
}
catch (Exception ex)
{
MessageBox.Show(
"无法启动希沃视频展台:" + ex.Message,
"Ink Canvas",
MessageBoxButton.OK,
MessageBoxImage.Warning);
}
//Console.WriteLine(softwareName + " 未找到可执行文件路径。");
}
private static string FindEasiCameraExecutablePath(string softwareName)
{
string executablePath = null;
if (string.IsNullOrWhiteSpace(softwareName))
return null;
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall"))
// 64 位进程默认只枚举 64 位注册表视图;32 位希沃常写在 WOW6432Node 下,需一并扫描。
string[] uninstallRoots =
{
foreach (string subkeyName in key.GetSubKeyNames())
{
using (RegistryKey subkey = key.OpenSubKey(subkeyName))
{
string displayName = subkey.GetValue("DisplayName") as string;
string installLocation = subkey.GetValue("InstallLocation") as string;
string uninstallString = subkey.GetValue("UninstallString") as string;
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall",
};
if (!string.IsNullOrEmpty(displayName) && displayName.Contains(softwareName))
{
if (!string.IsNullOrEmpty(installLocation))
{
executablePath = Path.Combine(installLocation, "sweclauncher.exe");
}
else if (!string.IsNullOrEmpty(uninstallString))
{
int lastSlashIndex = uninstallString.LastIndexOf("\\");
if (lastSlashIndex >= 0)
{
string folderPath = uninstallString.Substring(0, lastSlashIndex);
executablePath = Path.Combine(folderPath, "sweclauncher", "sweclauncher.exe");
}
}
break;
}
}
foreach (string root in uninstallRoots)
{
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(root))
{
if (key == null) continue;
string found = FindInUninstallKey(key, softwareName);
if (!string.IsNullOrEmpty(found))
return found;
}
}
return executablePath;
return null;
}
private static string FindInUninstallKey(RegistryKey uninstallKey, string softwareName)
{
foreach (string subkeyName in uninstallKey.GetSubKeyNames())
{
using (RegistryKey subkey = uninstallKey.OpenSubKey(subkeyName))
{
if (subkey == null) continue;
string displayName = subkey.GetValue("DisplayName") as string;
if (string.IsNullOrEmpty(displayName) || !displayName.Contains(softwareName))
continue;
string installLocation = subkey.GetValue("InstallLocation") as string;
string uninstallString = subkey.GetValue("UninstallString") as string;
string resolved = TryResolveSweclauncher(installLocation, uninstallString);
if (!string.IsNullOrEmpty(resolved) && File.Exists(resolved))
return resolved;
}
}
return null;
}
private static string TryResolveSweclauncher(string installLocation, string uninstallString)
{
if (!string.IsNullOrWhiteSpace(installLocation))
{
string fromLoc = ResolveSweclauncherUnderInstallRoot(installLocation.Trim().TrimEnd('\\'));
if (!string.IsNullOrEmpty(fromLoc))
return fromLoc;
}
if (!string.IsNullOrWhiteSpace(uninstallString))
{
// 常见:"...\uninstall.exe" 或带引号路径
string trimmed = uninstallString.Trim();
if (trimmed.Length >= 2 && trimmed[0] == '"')
{
int end = trimmed.IndexOf('"', 1);
if (end > 1)
trimmed = trimmed.Substring(1, end - 1);
}
int lastSlash = trimmed.LastIndexOf('\\');
if (lastSlash < 0)
return null;
string folderPath = trimmed.Substring(0, lastSlash);
string candidate = Path.Combine(folderPath, "sweclauncher", "sweclauncher.exe");
if (File.Exists(candidate))
return candidate;
candidate = Path.Combine(folderPath, "sweclauncher.exe");
if (File.Exists(candidate))
return candidate;
}
return null;
}
private static string ResolveSweclauncherUnderInstallRoot(string installRoot)
{
string[] candidates =
{
Path.Combine(installRoot, "sweclauncher.exe"),
Path.Combine(installRoot, "sweclauncher", "sweclauncher.exe"),
};
foreach (string p in candidates)
{
if (File.Exists(p))
return p;
}
return null;
}
}
}
+9
View File
@@ -903,6 +903,15 @@
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchCompressPicturesUploaded_Toggled" />
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="{i18n:I18n Key=Canvas_LaunchSeewoVideoShowcaseForWhiteboardBooth}" VerticalAlignment="Center"
FontSize="14" Margin="0,0,8,0" Width="320"
Style="{StaticResource AutoFitSettingsOptionLabel14}" />
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth"
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth_Toggled" />
</ikw:SimpleStackPanel>
<TextBlock Text="{i18n:I18n Key=Canvas_LaunchSeewoVideoShowcaseForWhiteboardBoothHint}" TextWrapping="Wrap" Foreground="#a1a1aa" />
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
StrokeThickness="1" Margin="0,4,0,4" />
<ikw:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
+9
View File
@@ -2705,6 +2705,15 @@ namespace Ink_Canvas
SaveSettingsToFile();
}
private void ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
Settings.Canvas.LaunchSeewoVideoShowcaseForWhiteboardBooth =
ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth.IsOn;
SaveSettingsToFile();
}
private void ToggleSwitchAutoStraightenLine_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
@@ -852,6 +852,9 @@ namespace Ink_Canvas
ToggleSwitchDisablePressure.IsOn = Settings.Canvas.DisablePressure;
inkCanvas.DefaultDrawingAttributes.IgnorePressure = Settings.Canvas.DisablePressure;
ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth.IsOn =
Settings.Canvas.LaunchSeewoVideoShowcaseForWhiteboardBooth;
if (Settings.Canvas.EnableVelocityBrushTip)
{
Settings.Canvas.InkStyle = 3;
@@ -874,8 +877,6 @@ namespace Ink_Canvas
ToggleSwitchClearCanvasAlsoClearImages.IsOn = Settings.Canvas.ClearCanvasAlsoClearImages;
ToggleSwitchShowCircleCenter.IsOn = Settings.Canvas.ShowCircleCenter;
ApplyWhiteboardBoothToolbarFromSettings();
switch (Settings.Canvas.EraserShapeType)
{
case 0:
@@ -51,6 +51,14 @@ namespace Ink_Canvas
/// <param name="e">鼠标按钮事件的参数。</param>
private void BtnToggleVideoPresenter_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (Settings?.Canvas?.LaunchSeewoVideoShowcaseForWhiteboardBooth == true)
{
// 与主窗口「希沃视频展台」入口(BoardLaunchEasiCamera_MouseUp)一致:先走黑板/白板入口逻辑再启动
ImageBlackboard_MouseUp(null, null);
SoftwareLauncher.LaunchEasiCamera("希沃视频展台");
return;
}
ToggleVideoPresenterSidebar();
}
+2
View File
@@ -179,6 +179,8 @@
<data name="AskEachTime" xml:space="preserve"><value>Ask each time</value></data>
<data name="Canvas_AsymptoteHint" xml:space="preserve"><value># Disabling may cause undo bugs.</value></data>
<data name="Canvas_ShowCircleCenter" xml:space="preserve"><value>Show circle center</value></data>
<data name="Canvas_LaunchSeewoVideoShowcaseForWhiteboardBooth" xml:space="preserve"><value>Whiteboard booth opens Seewo Video Showcase</value></data>
<data name="Canvas_LaunchSeewoVideoShowcaseForWhiteboardBoothHint" xml:space="preserve"><value>When enabled, the whiteboard toolbar Booth button launches Seewo Video Showcase (must be installed). When disabled, the built-in booth is used.</value></data>
<data name="Canvas_WPFBezier" xml:space="preserve"><value>WPF default Bezier smoothing</value></data>
<data name="Canvas_AdvancedSmoothing" xml:space="preserve"><value>Advanced curve smoothing (recommended)</value></data>
<data name="Canvas_InkFade" xml:space="preserve"><value>Ink fade</value></data>
+2
View File
@@ -194,6 +194,8 @@
<data name="AskEachTime" xml:space="preserve"><value>每次询问</value></data>
<data name="Canvas_AsymptoteHint" xml:space="preserve"><value># 请注意,若不保留双曲线渐近线可能会有遇到撤回相关的 BUG…</value></data>
<data name="Canvas_ShowCircleCenter" xml:space="preserve"><value>绘制圆时显示圆心位置</value></data>
<data name="Canvas_LaunchSeewoVideoShowcaseForWhiteboardBooth" xml:space="preserve"><value>白板展台按钮启动希沃视频展台</value></data>
<data name="Canvas_LaunchSeewoVideoShowcaseForWhiteboardBoothHint" xml:space="preserve"><value>开启后,点击白板工具栏「展台」将打开希沃视频展台(需已安装);关闭则使用内置展台。</value></data>
<data name="Canvas_WPFBezier" xml:space="preserve"><value>使用WPF默认贝塞尔曲线平滑</value></data>
<data name="Canvas_AdvancedSmoothing" xml:space="preserve"><value>使用高级曲线平滑(推荐)</value></data>
<data name="Canvas_InkFade" xml:space="preserve"><value>启用墨迹渐隐功能</value></data>
+4
View File
@@ -151,6 +151,10 @@ namespace Ink_Canvas
[JsonProperty("enableVelocityBrushTip")]
public bool EnableVelocityBrushTip { get; set; }
/// <summary>为 true 时,白板工具栏「展台」按钮启动希沃视频展台(sweclauncher),否则使用内置展台。</summary>
[JsonProperty("launchSeewoVideoShowcaseForWhiteboardBooth")]
public bool LaunchSeewoVideoShowcaseForWhiteboardBooth { get; set; } = false;
}
public enum OptionalOperation
@@ -63,7 +63,7 @@
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="屏蔽压感" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后,将忽略所有设备的压感信息,使所有笔画具有统一的粗细。与压感触屏模式互斥" HorizontalAlignment="Left"/>
</StackPanel>
<Border x:Name="ToggleSwitchDisablePressure" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="DisablePressure" MouseLeftButtonDown="ToggleSwitch_Click">
<Border x:Name="ToggleSwitchDisablePressure" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="DisablePressure" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
@@ -155,6 +155,20 @@
</Border>
</Border>
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="白板展台按钮启动希沃视频展台" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后,点击白板工具栏「展台」将打开希沃视频展台(需已安装);关闭则使用内置展台" HorizontalAlignment="Left" TextWrapping="Wrap" MaxWidth="420"/>
</StackPanel>
<Border x:Name="ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1" Tag="LaunchSeewoVideoShowcaseForWhiteboardBooth" MouseLeftButtonDown="ToggleSwitch_Click">
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
</Border.Effect>
</Border>
</Border>
</Grid>
</StackPanel>
</Border>
@@ -101,6 +101,9 @@ namespace Ink_Canvas.Windows.SettingsViews
// 插入图片时自动压缩
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchCompressPicturesUploaded"), canvas.IsCompressPicturesUploaded);
// 白板展台按钮启动希沃视频展台
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth"), canvas.LaunchSeewoVideoShowcaseForWhiteboardBooth);
// 保留双曲线渐近线
SetOptionButtonState("HyperbolaAsymptote", (int)canvas.HyperbolaAsymptoteOption);
@@ -247,6 +250,8 @@ namespace Ink_Canvas.Windows.SettingsViews
return canvas.EnablePressureTouchMode;
case "DisablePressure":
return canvas.DisablePressure;
case "LaunchSeewoVideoShowcaseForWhiteboardBooth":
return canvas.LaunchSeewoVideoShowcaseForWhiteboardBooth;
case "HideStrokeWhenSelecting":
return canvas.HideStrokeWhenSelecting;
case "ClearCanvasAndClearTimeMachine":
@@ -342,6 +347,11 @@ namespace Ink_Canvas.Windows.SettingsViews
}
break;
case "LaunchSeewoVideoShowcaseForWhiteboardBooth":
MainWindowSettingsHelper.InvokeToggleSwitchToggled(
"ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth", newState);
break;
case "HideStrokeWhenSelecting":
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchHideStrokeWhenSelecting", newState);
@@ -419,6 +419,7 @@ namespace Ink_Canvas.Windows.SettingsViews
{ "ToggleSwitchShowCursor", "CanvasAndInkPanel" },
{ "ToggleSwitchDisablePressure", "CanvasAndInkPanel" },
{ "ToggleSwitchEnablePressureTouchMode", "CanvasAndInkPanel" },
{ "ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth", "CanvasAndInkPanel" },
{ "ComboBoxEraserSize", "CanvasAndInkPanel" },
{ "ComboBoxHyperbolaAsymptoteOption", "CanvasAndInkPanel" },
{ "ComboBoxAutoSaveStrokesInterval", "CanvasAndInkPanel" },