@@ -1 +1 @@
|
||||
1.7.0.0
|
||||
1.7.1.0
|
||||
|
||||
+286
-2
@@ -5,7 +5,7 @@ using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using Microsoft.Win32;
|
||||
using System.Security; // 添加SecurityException所需命名空间
|
||||
using System.Security;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -15,6 +15,7 @@ using static System.Windows.Forms.VisualStyles.VisualStyleElement;
|
||||
using MessageBox = System.Windows.MessageBox;
|
||||
using Window = System.Windows.Window;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
@@ -34,6 +35,16 @@ namespace Ink_Canvas
|
||||
public static bool IsAppExitByUser = false;
|
||||
// 新增:退出信号文件路径
|
||||
private static string watchdogExitSignalFile = Path.Combine(Path.GetTempPath(), "icc_watchdog_exit_" + System.Diagnostics.Process.GetCurrentProcess().Id + ".flag");
|
||||
// 新增:崩溃日志文件路径
|
||||
private static string crashLogFile = Path.Combine(Environment.GetEnvironmentVariable("APPDATA"), "Ink Canvas", "crash_logs");
|
||||
// 新增:进程ID
|
||||
private static int currentProcessId = Process.GetCurrentProcess().Id;
|
||||
// 新增:应用启动时间
|
||||
private static DateTime appStartTime = DateTime.Now;
|
||||
// 新增:最后一次错误信息
|
||||
private static string lastErrorMessage = string.Empty;
|
||||
// 新增:是否已初始化崩溃监听器
|
||||
private static bool crashListenersInitialized = false;
|
||||
|
||||
public App()
|
||||
{
|
||||
@@ -53,6 +64,9 @@ namespace Ink_Canvas
|
||||
this.DispatcherUnhandledException += App_DispatcherUnhandledException;
|
||||
StartHeartbeatMonitor();
|
||||
|
||||
// 新增:初始化全局异常和进程结束处理
|
||||
InitializeCrashListeners();
|
||||
|
||||
// 仅在崩溃后操作为静默重启时才启动看门狗
|
||||
if (CrashAction == CrashActionType.SilentRestart)
|
||||
{
|
||||
@@ -61,6 +75,259 @@ namespace Ink_Canvas
|
||||
this.Exit += App_Exit; // 注册退出事件
|
||||
}
|
||||
|
||||
// 新增:初始化崩溃监听器
|
||||
private void InitializeCrashListeners()
|
||||
{
|
||||
if (crashListenersInitialized) return;
|
||||
|
||||
try
|
||||
{
|
||||
// 确保崩溃日志目录存在
|
||||
if (!Directory.Exists(crashLogFile))
|
||||
{
|
||||
Directory.CreateDirectory(crashLogFile);
|
||||
}
|
||||
|
||||
// 注册非UI线程未处理异常处理程序
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
|
||||
// 注册控制台Ctrl+C等终止信号处理
|
||||
Console.CancelKeyPress += Console_CancelKeyPress;
|
||||
|
||||
// 注册系统会话结束事件(关机、注销等)
|
||||
SystemEvents.SessionEnding += SystemEvents_SessionEnding;
|
||||
|
||||
// 注册进程退出处理程序
|
||||
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
|
||||
|
||||
// 尝试注册Windows关闭消息监听
|
||||
SetConsoleCtrlHandler(ConsoleCtrlHandler, true);
|
||||
|
||||
// 如果系统支持,添加Windows Management Instrumentation监听器
|
||||
try
|
||||
{
|
||||
// 使用反射动态加载和调用WMI
|
||||
TrySetupWmiMonitoring();
|
||||
}
|
||||
catch (Exception wmiEx)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"设置WMI进程监控失败: {wmiEx.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
|
||||
crashListenersInitialized = true;
|
||||
LogHelper.WriteLogToFile("已初始化崩溃监听器", LogHelper.LogType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"初始化崩溃监听器失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:动态加载WMI监控(避免直接引用System.Management)
|
||||
private void TrySetupWmiMonitoring()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查System.Management程序集是否可用
|
||||
var assemblyName = "System.Management, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
|
||||
var assembly = Assembly.Load(assemblyName);
|
||||
if (assembly == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("未找到System.Management程序集,跳过WMI监控", LogHelper.LogType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用反射创建WMI查询
|
||||
var watcherType = assembly.GetType("System.Management.ManagementEventWatcher");
|
||||
if (watcherType == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("未找到ManagementEventWatcher类型,跳过WMI监控", LogHelper.LogType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建WMI查询字符串
|
||||
string queryString = $"SELECT * FROM __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.ProcessId = {currentProcessId}";
|
||||
|
||||
// 创建ManagementEventWatcher实例
|
||||
object watcher = Activator.CreateInstance(watcherType, queryString);
|
||||
|
||||
// 获取EventArrived事件信息
|
||||
var eventInfo = watcherType.GetEvent("EventArrived");
|
||||
if (eventInfo == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("未找到EventArrived事件,跳过WMI监控", LogHelper.LogType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建委托并订阅事件
|
||||
Type delegateType = eventInfo.EventHandlerType;
|
||||
var handler = Delegate.CreateDelegate(delegateType, this, GetType().GetMethod("WmiEventHandler", BindingFlags.NonPublic | BindingFlags.Instance));
|
||||
eventInfo.AddEventHandler(watcher, handler);
|
||||
|
||||
// 启动监听
|
||||
var startMethod = watcherType.GetMethod("Start");
|
||||
startMethod.Invoke(watcher, null);
|
||||
|
||||
LogHelper.WriteLogToFile("已成功启动WMI进程监控", LogHelper.LogType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"动态加载WMI监控失败: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
// WMI事件处理方法(通过反射调用)
|
||||
private void WmiEventHandler(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 尝试从事件参数中提取信息
|
||||
dynamic eventArgs = e;
|
||||
dynamic newEvent = eventArgs.NewEvent;
|
||||
if (newEvent != null)
|
||||
{
|
||||
dynamic targetInstance = newEvent["TargetInstance"];
|
||||
if (targetInstance != null)
|
||||
{
|
||||
string processName = targetInstance["Name"]?.ToString() ?? "未知进程";
|
||||
WriteCrashLog($"WMI检测到进程{processName}(ID:{currentProcessId})已终止");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"处理WMI事件时出错: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:Windows控制台控制处理程序
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate handler, bool add);
|
||||
|
||||
private delegate bool ConsoleCtrlDelegate(int ctrlType);
|
||||
|
||||
private static bool ConsoleCtrlHandler(int ctrlType)
|
||||
{
|
||||
string eventType = "未知控制类型";
|
||||
|
||||
// 使用传统switch语句替代switch表达式
|
||||
switch (ctrlType)
|
||||
{
|
||||
case 0:
|
||||
eventType = "CTRL_C_EVENT";
|
||||
break;
|
||||
case 1:
|
||||
eventType = "CTRL_BREAK_EVENT";
|
||||
break;
|
||||
case 2:
|
||||
eventType = "CTRL_CLOSE_EVENT";
|
||||
break;
|
||||
case 5:
|
||||
eventType = "CTRL_LOGOFF_EVENT";
|
||||
break;
|
||||
case 6:
|
||||
eventType = "CTRL_SHUTDOWN_EVENT";
|
||||
break;
|
||||
default:
|
||||
eventType = $"未知控制类型({ctrlType})";
|
||||
break;
|
||||
}
|
||||
|
||||
WriteCrashLog($"接收到系统控制信号: {eventType}");
|
||||
|
||||
// 返回true表示已处理该事件
|
||||
return false;
|
||||
}
|
||||
|
||||
// 新增:系统会话结束事件处理
|
||||
private void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
|
||||
{
|
||||
string reason = e.Reason == SessionEndReasons.Logoff ? "用户注销" : "系统关机";
|
||||
WriteCrashLog($"系统会话即将结束: {reason}");
|
||||
}
|
||||
|
||||
// 新增:控制台取消事件处理
|
||||
private void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
|
||||
{
|
||||
WriteCrashLog($"接收到控制台中断信号: {e.SpecialKey}");
|
||||
e.Cancel = true; // 取消默认处理
|
||||
}
|
||||
|
||||
// 新增:处理非UI线程的未处理异常
|
||||
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var exception = e.ExceptionObject as Exception;
|
||||
string errorMessage = exception?.ToString() ?? "未知异常";
|
||||
lastErrorMessage = errorMessage;
|
||||
|
||||
WriteCrashLog($"捕获到未处理的异常: {errorMessage}");
|
||||
|
||||
if (e.IsTerminating)
|
||||
{
|
||||
WriteCrashLog("应用程序即将终止");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 尝试在最后时刻记录错误
|
||||
try
|
||||
{
|
||||
File.AppendAllText(
|
||||
Path.Combine(crashLogFile, $"critical_error_{DateTime.Now:yyyyMMdd_HHmmss}.log"),
|
||||
$"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 记录未处理异常时发生错误: {ex.Message}\r\n"
|
||||
);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:处理进程退出事件
|
||||
private void CurrentDomain_ProcessExit(object sender, EventArgs e)
|
||||
{
|
||||
TimeSpan runDuration = DateTime.Now - appStartTime;
|
||||
WriteCrashLog($"应用程序退出,运行时长: {runDuration}");
|
||||
|
||||
// 如果有最后错误消息,记录到日志
|
||||
if (!string.IsNullOrEmpty(lastErrorMessage))
|
||||
{
|
||||
WriteCrashLog($"最后错误信息: {lastErrorMessage}");
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:记录崩溃日志
|
||||
private static void WriteCrashLog(string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 确保目录存在
|
||||
if (!Directory.Exists(crashLogFile))
|
||||
{
|
||||
Directory.CreateDirectory(crashLogFile);
|
||||
}
|
||||
|
||||
string logFileName = Path.Combine(crashLogFile, $"crash_{DateTime.Now:yyyyMMdd}.log");
|
||||
|
||||
// 收集系统状态信息
|
||||
string memoryUsage = (Process.GetCurrentProcess().WorkingSet64 / (1024 * 1024)).ToString() + " MB";
|
||||
string cpuTime = Process.GetCurrentProcess().TotalProcessorTime.ToString();
|
||||
string processUptime = (DateTime.Now - Process.GetCurrentProcess().StartTime).ToString();
|
||||
|
||||
string statusInfo = $"[内存: {memoryUsage}, CPU时间: {cpuTime}, 运行时长: {processUptime}]";
|
||||
|
||||
// 写入日志
|
||||
File.AppendAllText(
|
||||
logFileName,
|
||||
$"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [PID:{currentProcessId}] {message}\r\n{statusInfo}\r\n\r\n"
|
||||
);
|
||||
|
||||
// 同时记录到主日志
|
||||
LogHelper.WriteLogToFile(message, LogHelper.LogType.Error);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// 增加字段保存崩溃后操作设置
|
||||
public static CrashActionType CrashAction = CrashActionType.SilentRestart;
|
||||
|
||||
@@ -92,6 +359,11 @@ namespace Ink_Canvas
|
||||
{
|
||||
Ink_Canvas.MainWindow.ShowNewMessage("抱歉,出现未预期的异常,可能导致 InkCanvasForClass 运行不稳定。\n建议保存墨迹后重启应用。", true);
|
||||
LogHelper.NewLog(e.Exception.ToString());
|
||||
|
||||
// 新增:记录到崩溃日志
|
||||
lastErrorMessage = e.Exception.ToString();
|
||||
WriteCrashLog($"UI线程未处理异常: {e.Exception}");
|
||||
|
||||
e.Handled = true;
|
||||
|
||||
SyncCrashActionFromSettings(); // 新增:崩溃时同步最新设置
|
||||
@@ -398,6 +670,10 @@ namespace Ink_Canvas
|
||||
// 仅在软件内主动退出时关闭看门狗,并写入退出信号
|
||||
try
|
||||
{
|
||||
// 新增:记录应用退出状态
|
||||
string exitType = IsAppExitByUser ? "用户主动退出" : "应用程序退出";
|
||||
WriteCrashLog($"{exitType},退出代码: {e.ApplicationExitCode}");
|
||||
|
||||
if (IsAppExitByUser)
|
||||
{
|
||||
// 写入退出信号文件,通知看门狗正常退出
|
||||
@@ -409,7 +685,15 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 尝试记录最后的错误
|
||||
try
|
||||
{
|
||||
LogHelper.WriteLogToFile($"退出处理时发生错误: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Reflection;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
|
||||
@@ -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.0.4")]
|
||||
[assembly: AssemblyFileVersion("1.7.0.4")]
|
||||
[assembly: AssemblyVersion("1.7.1.0")]
|
||||
[assembly: AssemblyFileVersion("1.7.1.0")]
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Ink_Canvas.Helpers
|
||||
if (channel == UpdateChannel.Release)
|
||||
{
|
||||
// Release通道版本信息地址
|
||||
primaryUrl = "https://github.com/InkCanvasForClass/community/raw/refs/heads/beta/AutomaticUpdateVersionControl.txt";
|
||||
primaryUrl = "https://github.com/InkCanvasForClass/community/raw/refs/heads/main/AutomaticUpdateVersionControl.txt";
|
||||
fallbackUrl = "https://bgithub.xyz/InkCanvasForClass/community/raw/refs/heads/main/AutomaticUpdateVersionControl.txt";
|
||||
}
|
||||
else
|
||||
@@ -622,6 +622,13 @@ namespace Ink_Canvas.Helpers
|
||||
VersionUrl = "https://bgithub.xyz/InkCanvasForClass/community/raw/refs/heads/main/AutomaticUpdateVersionControl.txt",
|
||||
DownloadUrlFormat = "https://bgithub.xyz/InkCanvasForClass/community/releases/download/{0}/InkCanvasForClass.CE.{0}.zip",
|
||||
LogUrl = "https://bgithub.xyz/InkCanvasForClass/community/raw/refs/heads/main/UpdateLog.md"
|
||||
},
|
||||
new UpdateLineGroup
|
||||
{
|
||||
GroupName = "kkgithub线路",
|
||||
VersionUrl = "https://kkgithub.com/InkCanvasForClass/community/raw/refs/heads/beta/AutomaticUpdateVersionControl.txt",
|
||||
DownloadUrlFormat = "https://kkgithub.com/InkCanvasForClass/community/releases/download/{0}/InkCanvasForClass.CE.{0}.zip",
|
||||
LogUrl = "https://kkgithub.com/InkCanvasForClass/community/raw/refs/heads/beta/UpdateLog.md"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -640,6 +647,13 @@ namespace Ink_Canvas.Helpers
|
||||
VersionUrl = "https://bgithub.xyz/InkCanvasForClass/community-beta/raw/refs/heads/main/AutomaticUpdateVersionControl.txt",
|
||||
DownloadUrlFormat = "https://bgithub.xyz/InkCanvasForClass/community-beta/releases/download/{0}/InkCanvasForClass.CE.{0}.zip",
|
||||
LogUrl = "https://bgithub.xyz/InkCanvasForClass/community-beta/raw/refs/heads/main/UpdateLog.md"
|
||||
},
|
||||
new UpdateLineGroup
|
||||
{
|
||||
GroupName = "kkgithub线路",
|
||||
VersionUrl = "https://kkgithub.com/InkCanvasForClass/community-beta/raw/refs/heads/main/AutomaticUpdateVersionControl.txt",
|
||||
DownloadUrlFormat = "https://kkgithub.com/InkCanvasForClass/community-beta/releases/download/{0}/InkCanvasForClass.CE.{0}.zip",
|
||||
LogUrl = "https://kkgithub.com/InkCanvasForClass/community-beta/raw/refs/heads/main/UpdateLog.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
using iNKORE.UI.WPF.Modern.Controls;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动台按钮控件
|
||||
/// </summary>
|
||||
public class LauncherButton
|
||||
{
|
||||
/// <summary>
|
||||
/// 父插件
|
||||
/// </summary>
|
||||
private readonly SuperLauncherPlugin _plugin;
|
||||
|
||||
/// <summary>
|
||||
/// 实际按钮控件
|
||||
/// </summary>
|
||||
private readonly SimpleStackPanel _panel;
|
||||
|
||||
/// <summary>
|
||||
/// 获取按钮UI元素
|
||||
/// </summary>
|
||||
public UIElement Element => _panel;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="plugin">父插件</param>
|
||||
public LauncherButton(SuperLauncherPlugin plugin)
|
||||
{
|
||||
try
|
||||
{
|
||||
_plugin = plugin;
|
||||
LogHelper.WriteLogToFile("开始创建启动台按钮", LogHelper.LogType.Info);
|
||||
|
||||
// 创建SimpleStackPanel
|
||||
_panel = new SimpleStackPanel
|
||||
{
|
||||
Name = "Launcher_Icon",
|
||||
Orientation = Orientation.Vertical,
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
Width = 28,
|
||||
Margin = new Thickness(0, -2, 0, 0),
|
||||
Background = Brushes.Transparent
|
||||
};
|
||||
|
||||
LogHelper.WriteLogToFile("创建SimpleStackPanel完成", LogHelper.LogType.Info);
|
||||
|
||||
// 添加图标
|
||||
var image = CreateIconImage();
|
||||
_panel.Children.Add(image);
|
||||
|
||||
// 添加文本
|
||||
TextBlock textBlock = new TextBlock
|
||||
{
|
||||
Text = "启动台",
|
||||
Foreground = Brushes.Black,
|
||||
FontSize = 8,
|
||||
Margin = new Thickness(0, 1, 0, 0),
|
||||
TextAlignment = TextAlignment.Center
|
||||
};
|
||||
_panel.Children.Add(textBlock);
|
||||
|
||||
// 设置鼠标事件
|
||||
_panel.MouseDown += Panel_MouseDown;
|
||||
_panel.MouseUp += Panel_MouseUp;
|
||||
_panel.MouseLeave += Panel_MouseLeave;
|
||||
|
||||
// 右键菜单支持
|
||||
_panel.ContextMenu = CreateContextMenu();
|
||||
|
||||
// 设置工具提示
|
||||
_panel.ToolTip = "启动台";
|
||||
|
||||
LogHelper.WriteLogToFile("启动台按钮创建完成", LogHelper.LogType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"创建启动台按钮时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建右键菜单
|
||||
/// </summary>
|
||||
private ContextMenu CreateContextMenu()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建菜单
|
||||
ContextMenu menu = new ContextMenu();
|
||||
|
||||
// 创建位置切换菜单项
|
||||
MenuItem positionMenuItem = new MenuItem();
|
||||
positionMenuItem.Header = _plugin.Config.ButtonPosition == LauncherButtonPosition.Left ?
|
||||
"移至右侧" : "移至左侧";
|
||||
positionMenuItem.Click += (s, e) =>
|
||||
{
|
||||
// 切换位置
|
||||
_plugin.Config.ButtonPosition = _plugin.Config.ButtonPosition == LauncherButtonPosition.Left ?
|
||||
LauncherButtonPosition.Right : LauncherButtonPosition.Left;
|
||||
|
||||
// 更新按钮位置
|
||||
_plugin.UpdateButtonPosition();
|
||||
|
||||
// 保存配置
|
||||
_plugin.SaveConfig();
|
||||
|
||||
LogHelper.WriteLogToFile($"通过右键菜单切换启动台按钮位置为: {_plugin.Config.ButtonPosition}",
|
||||
LogHelper.LogType.Info);
|
||||
};
|
||||
menu.Items.Add(positionMenuItem);
|
||||
|
||||
// 添加设置菜单项
|
||||
MenuItem settingsMenuItem = new MenuItem();
|
||||
settingsMenuItem.Header = "打开设置";
|
||||
settingsMenuItem.Click += (s, e) =>
|
||||
{
|
||||
// 打开插件设置窗口
|
||||
var mainWindow = Application.Current.MainWindow;
|
||||
if (mainWindow != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用反射调用主窗口的ShowPluginSettings方法
|
||||
var method = mainWindow.GetType().GetMethod("ShowPluginSettings");
|
||||
if (method != null)
|
||||
{
|
||||
method.Invoke(mainWindow, null);
|
||||
LogHelper.WriteLogToFile("已打开插件设置窗口", LogHelper.LogType.Info);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"打开插件设置窗口失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
};
|
||||
menu.Items.Add(settingsMenuItem);
|
||||
|
||||
return menu;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"创建右键菜单时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取实际的UI元素
|
||||
/// </summary>
|
||||
[Obsolete("使用Element属性代替")]
|
||||
public UIElement GetUIElement()
|
||||
{
|
||||
return _panel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建图标图像
|
||||
/// </summary>
|
||||
private Image CreateIconImage()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建图像
|
||||
Image image = new Image
|
||||
{
|
||||
Height = 17,
|
||||
Margin = new Thickness(0, 3, 0, 0)
|
||||
};
|
||||
|
||||
// 设置位图缩放模式
|
||||
RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.HighQuality);
|
||||
|
||||
// 创建绘图图像
|
||||
DrawingImage drawingImage = new DrawingImage();
|
||||
DrawingGroup drawingGroup = new DrawingGroup();
|
||||
drawingGroup.ClipGeometry = Geometry.Parse("M0,0 V24 H24 V0 H0 Z");
|
||||
|
||||
// 使用提供的应用网格图标
|
||||
GeometryDrawing geometryDrawing = new GeometryDrawing
|
||||
{
|
||||
Brush = new SolidColorBrush(Color.FromRgb(0x1B, 0x1B, 0x1B)),
|
||||
Geometry = Geometry.Parse("F0 M24,24z M0,0z M4.41721,4.29873C4.35178,4.29873,4.29873,4.35178,4.29873,4.41721L4.29873,9.15646C4.29873,9.22189,4.35178,9.27494,4.41721,9.27494L9.15646,9.27494C9.22189,9.27494,9.27494,9.22189,9.27494,9.15646L9.27494,4.41721C9.27494,4.35178,9.22189,4.29873,9.15646,4.29873L4.41721,4.29873z M2.64,4.41721C2.64,3.43569,3.43569,2.64,4.41721,2.64L9.15646,2.64C10.138,2.64,10.9337,3.43569,10.9337,4.41721L10.9337,9.15646C10.9337,10.138,10.138,10.9337,9.15646,10.9337L4.41721,10.9337C3.43569,10.9337,2.64,10.138,2.64,9.15646L2.64,4.41721z M14.8435,4.29873C14.7781,4.29873,14.7251,4.35178,14.7251,4.41721L14.7251,9.15646C14.7251,9.22189,14.7781,9.27494,14.8435,9.27494L19.5828,9.27494C19.6482,9.27494,19.7013,9.22189,19.7013,9.15646L19.7013,4.41721C19.7013,4.35178,19.6482,4.29873,19.5828,4.29873L14.8435,4.29873z M13.0663,4.41721C13.0663,3.43569,13.862,2.64,14.8435,2.64L19.5828,2.64C20.5643,2.64,21.36,3.43569,21.36,4.41721L21.36,9.15646C21.36,10.138,20.5643,10.9337,19.5828,10.9337L14.8435,10.9337C13.862,10.9337,13.0663,10.138,13.0663,9.15646L13.0663,4.41721z M14.8435,14.7251C14.7781,14.7251,14.7251,14.7781,14.7251,14.8435L14.7251,19.5828C14.7251,19.6482,14.7781,19.7013,14.8435,19.7013L19.5828,19.7013C19.6482,19.7013,19.7013,19.6482,19.7013,19.5828L19.7013,14.8435C19.7013,14.7781,19.6482,14.7251,19.5828,14.7251L14.8435,14.7251z M13.0663,14.8435C13.0663,13.862,13.862,13.0663,14.8435,13.0663L19.5828,13.0663C20.5643,13.0663,21.36,13.862,21.36,14.8435L21.36,19.5828C21.36,20.5643,20.5643,21.36,19.5828,21.36L14.8435,21.36C13.862,21.36,13.0663,20.5643,13.0663,19.5828L13.0663,14.8435z M4.41721,14.7251C4.35178,14.7251,4.29873,14.7781,4.29873,14.8435L4.29873,19.5828C4.29873,19.6482,4.35178,19.7013,4.41721,19.7013L9.15646,19.7013C9.22189,19.7013,9.27494,19.6482,9.27494,19.5828L9.27494,14.8435C9.27494,14.7781,9.22189,14.7251,9.15646,14.7251L4.41721,14.7251z M2.64,14.8435C2.64,13.862,3.43569,13.0663,4.41721,13.0663L9.15646,13.0663C10.138,13.0663,10.9337,13.862,10.9337,14.8435L10.9337,19.5828C10.9337,20.5643,10.138,21.36,9.15646,21.36L4.41721,21.36C3.43569,21.36,2.64,20.5643,2.64,19.5828L2.64,14.8435z")
|
||||
};
|
||||
|
||||
drawingGroup.Children.Add(geometryDrawing);
|
||||
|
||||
// 设置图像源
|
||||
drawingImage.Drawing = drawingGroup;
|
||||
image.Source = drawingImage;
|
||||
|
||||
return image;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"创建图标图像时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
|
||||
// 返回一个空图像
|
||||
return new Image();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 鼠标按下事件
|
||||
/// </summary>
|
||||
private void Panel_MouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 提供反馈
|
||||
_panel.Background = new SolidColorBrush(Color.FromArgb(40, 0, 0, 0));
|
||||
LogHelper.WriteLogToFile("启动台按钮鼠标按下", LogHelper.LogType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"启动台按钮鼠标按下事件出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 鼠标抬起事件
|
||||
/// </summary>
|
||||
private void Panel_MouseUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 只有左键点击才显示启动台窗口
|
||||
if (e.ChangedButton != MouseButton.Left)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 恢复背景
|
||||
_panel.Background = Brushes.Transparent;
|
||||
LogHelper.WriteLogToFile("启动台按钮鼠标抬起,准备显示启动台窗口", LogHelper.LogType.Info);
|
||||
|
||||
// 获取按钮在屏幕上的位置
|
||||
Point buttonPosition = _panel.PointToScreen(new Point(_panel.ActualWidth / 2, 0));
|
||||
|
||||
// 显示启动台窗口
|
||||
_plugin.ShowLauncherWindow(buttonPosition);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"启动台按钮鼠标抬起事件出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 鼠标离开事件
|
||||
/// </summary>
|
||||
private void Panel_MouseLeave(object sender, MouseEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 恢复背景
|
||||
_panel.Background = Brushes.Transparent;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"启动台按钮鼠标离开事件出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动台按钮位置
|
||||
/// </summary>
|
||||
public enum LauncherButtonPosition
|
||||
{
|
||||
/// <summary>
|
||||
/// 左侧
|
||||
/// </summary>
|
||||
Left,
|
||||
|
||||
/// <summary>
|
||||
/// 右侧
|
||||
/// </summary>
|
||||
Right
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动台配置
|
||||
/// </summary>
|
||||
public class LauncherConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动台按钮位置
|
||||
/// </summary>
|
||||
public LauncherButtonPosition ButtonPosition { get; set; } = LauncherButtonPosition.Right;
|
||||
|
||||
/// <summary>
|
||||
/// 启动台应用程序列表
|
||||
/// </summary>
|
||||
public List<LauncherItem> Items { get; set; } = new List<LauncherItem>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动台应用项
|
||||
/// </summary>
|
||||
public class LauncherItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用程序名称
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用程序路径
|
||||
/// </summary>
|
||||
public string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否可见
|
||||
/// </summary>
|
||||
public bool IsVisible { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 在启动台中的位置(0-39)
|
||||
/// </summary>
|
||||
public int Position { get; set; } = -1;
|
||||
|
||||
/// <summary>
|
||||
/// 是否已固定位置
|
||||
/// </summary>
|
||||
public bool IsPositionFixed { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 图标缓存
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
private ImageSource _iconCache;
|
||||
|
||||
/// <summary>
|
||||
/// 获取应用程序图标
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
public ImageSource Icon
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_iconCache != null)
|
||||
{
|
||||
return _iconCache;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (File.Exists(Path))
|
||||
{
|
||||
// 从文件中获取图标
|
||||
Icon icon = System.Drawing.Icon.ExtractAssociatedIcon(Path);
|
||||
if (icon != null)
|
||||
{
|
||||
_iconCache = Imaging.CreateBitmapSourceFromHIcon(
|
||||
icon.Handle,
|
||||
Int32Rect.Empty,
|
||||
BitmapSizeOptions.FromEmptyOptions());
|
||||
|
||||
icon.Dispose();
|
||||
return _iconCache;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 从注册表中获取文件类型关联图标
|
||||
string extension = System.IO.Path.GetExtension(Path);
|
||||
if (!string.IsNullOrEmpty(extension))
|
||||
{
|
||||
string fileType = Registry.ClassesRoot.OpenSubKey(extension)?.GetValue(string.Empty) as string;
|
||||
if (!string.IsNullOrEmpty(fileType))
|
||||
{
|
||||
string iconPath = Registry.ClassesRoot.OpenSubKey(fileType + "\\DefaultIcon")?.GetValue(string.Empty) as string;
|
||||
if (!string.IsNullOrEmpty(iconPath))
|
||||
{
|
||||
string[] parts = iconPath.Split(',');
|
||||
string iconFile = parts[0].Trim('"');
|
||||
int iconIndex = parts.Length > 1 ? Convert.ToInt32(parts[1]) : 0;
|
||||
|
||||
if (File.Exists(iconFile))
|
||||
{
|
||||
Icon icon = IconExtractor.Extract(iconFile, iconIndex, true);
|
||||
if (icon != null)
|
||||
{
|
||||
_iconCache = Imaging.CreateBitmapSourceFromHIcon(
|
||||
icon.Handle,
|
||||
Int32Rect.Empty,
|
||||
BitmapSizeOptions.FromEmptyOptions());
|
||||
|
||||
icon.Dispose();
|
||||
return _iconCache;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"获取应用图标时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
// 返回默认图标
|
||||
return GetDefaultIcon();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取默认图标
|
||||
/// </summary>
|
||||
private ImageSource GetDefaultIcon()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 对于资源管理器,使用特定图标
|
||||
if (Path.EndsWith("explorer.exe", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
try
|
||||
{
|
||||
// 直接从C:\Windows\explorer.exe获取图标
|
||||
string explorerPath = @"C:\Windows\explorer.exe";
|
||||
if (File.Exists(explorerPath))
|
||||
{
|
||||
Icon icon = System.Drawing.Icon.ExtractAssociatedIcon(explorerPath);
|
||||
if (icon != null)
|
||||
{
|
||||
_iconCache = Imaging.CreateBitmapSourceFromHIcon(
|
||||
icon.Handle,
|
||||
Int32Rect.Empty,
|
||||
BitmapSizeOptions.FromEmptyOptions());
|
||||
|
||||
icon.Dispose();
|
||||
return _iconCache;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"获取资源管理器图标时出错: {ex.Message}", LogHelper.LogType.Warning);
|
||||
// 如果获取Windows图标失败,回退到默认图标
|
||||
}
|
||||
|
||||
// 回退到备用图标
|
||||
string explorerIconPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "Icons-Fluent", "ic_fluent_folder_24_regular.png");
|
||||
if (File.Exists(explorerIconPath))
|
||||
{
|
||||
Uri uri = new Uri(explorerIconPath);
|
||||
BitmapImage image = new BitmapImage(uri);
|
||||
_iconCache = image;
|
||||
return _iconCache;
|
||||
}
|
||||
}
|
||||
|
||||
// 返回一个简单的默认图标
|
||||
string iconPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "Icons-png", "icc.png");
|
||||
if (File.Exists(iconPath))
|
||||
{
|
||||
Uri uri = new Uri(iconPath);
|
||||
BitmapImage image = new BitmapImage(uri);
|
||||
_iconCache = image;
|
||||
return _iconCache;
|
||||
}
|
||||
|
||||
// 如果还是没有找到,尝试使用应用程序图标
|
||||
string appIconPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "Icons-Fluent", "ic_fluent_apps_24_regular.png");
|
||||
if (File.Exists(appIconPath))
|
||||
{
|
||||
Uri uri = new Uri(appIconPath);
|
||||
BitmapImage image = new BitmapImage(uri);
|
||||
_iconCache = image;
|
||||
return _iconCache;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"获取默认图标时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动应用程序
|
||||
/// </summary>
|
||||
public void Launch()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(Path))
|
||||
{
|
||||
LogHelper.WriteLogToFile("无法启动应用程序:路径为空", LogHelper.LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查文件是否存在
|
||||
if (!System.IO.File.Exists(Path) && !Path.Contains(":\\"))
|
||||
{
|
||||
// 可能是系统命令,如explorer.exe
|
||||
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = Path,
|
||||
UseShellExecute = true
|
||||
};
|
||||
System.Diagnostics.Process.Start(psi);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 使用Process.Start启动应用程序
|
||||
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = Path,
|
||||
UseShellExecute = true
|
||||
};
|
||||
System.Diagnostics.Process.Start(psi);
|
||||
}
|
||||
|
||||
LogHelper.WriteLogToFile($"已启动应用程序: {Path}", LogHelper.LogType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"启动应用程序时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"启动应用程序时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 图标提取工具类
|
||||
/// </summary>
|
||||
public static class IconExtractor
|
||||
{
|
||||
/// <summary>
|
||||
/// 从文件中提取图标
|
||||
/// </summary>
|
||||
/// <param name="file">文件路径</param>
|
||||
/// <param name="index">图标索引</param>
|
||||
/// <param name="largeIcon">是否提取大图标</param>
|
||||
/// <returns>提取的图标</returns>
|
||||
public static Icon Extract(string file, int index, bool largeIcon)
|
||||
{
|
||||
try
|
||||
{
|
||||
IntPtr large;
|
||||
IntPtr small;
|
||||
ExtractIconEx(file, index, out large, out small, 1);
|
||||
|
||||
try
|
||||
{
|
||||
return Icon.FromHandle(largeIcon ? large : small);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (large != IntPtr.Zero)
|
||||
DestroyIcon(large);
|
||||
|
||||
if (small != IntPtr.Zero)
|
||||
DestroyIcon(small);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[System.Runtime.InteropServices.DllImport("Shell32.dll", EntryPoint = "ExtractIconEx")]
|
||||
private static extern int ExtractIconEx(
|
||||
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPStr)] string lpszFile,
|
||||
int nIconIndex,
|
||||
out IntPtr phiconLarge,
|
||||
out IntPtr phiconSmall,
|
||||
int nIcons);
|
||||
|
||||
[System.Runtime.InteropServices.DllImport("User32.dll")]
|
||||
private static extern int DestroyIcon(IntPtr hIcon);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<UserControl x:Class="Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher.LauncherSettingsControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="500" d:DesignWidth="600">
|
||||
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 标题 -->
|
||||
<TextBlock Grid.Row="0" Text="超级启动台设置" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<!-- 基本设置 -->
|
||||
<StackPanel Grid.Row="1" Margin="0,0,0,15">
|
||||
<TextBlock Text="基本设置" FontSize="14" FontWeight="SemiBold" Margin="0,0,0,10"/>
|
||||
|
||||
<Grid Margin="10,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="120"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 按钮位置 -->
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="按钮位置:" VerticalAlignment="Center"/>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" Margin="0,5">
|
||||
<RadioButton x:Name="RbtnLeft" Content="浮动栏左侧" Margin="0,0,20,0" Checked="RbtnPosition_Checked"/>
|
||||
<RadioButton x:Name="RbtnRight" Content="浮动栏右侧" IsChecked="True" Checked="RbtnPosition_Checked"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 应用管理 -->
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Text="应用管理" FontSize="14" FontWeight="SemiBold" Margin="0,0,0,10"/>
|
||||
|
||||
<Border Grid.Row="1" BorderThickness="1" BorderBrush="#CCCCCC" CornerRadius="5">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 应用列表 -->
|
||||
<DataGrid Grid.Row="0" x:Name="DgApps" AutoGenerateColumns="False" Margin="5"
|
||||
CanUserAddRows="False" CanUserDeleteRows="False"
|
||||
HeadersVisibility="Column" SelectionMode="Single"
|
||||
SelectionChanged="DgApps_SelectionChanged">
|
||||
<DataGrid.Columns>
|
||||
<DataGridCheckBoxColumn Header="显示" Binding="{Binding IsVisible}" Width="50"/>
|
||||
<DataGridTextColumn Header="名称" Binding="{Binding Name}" Width="150"/>
|
||||
<DataGridTextColumn Header="路径" Binding="{Binding Path}" Width="*"/>
|
||||
<DataGridTextColumn Header="位置" Binding="{Binding Position}" Width="50"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="5">
|
||||
<Button x:Name="BtnAdd" Content="添加应用" Padding="10,5" Margin="0,5,5,5" Click="BtnAdd_Click"/>
|
||||
<Button x:Name="BtnEdit" Content="编辑" Padding="10,5" Margin="5" Click="BtnEdit_Click"/>
|
||||
<Button x:Name="BtnDelete" Content="删除" Padding="10,5" Margin="5" Click="BtnDelete_Click"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,15,0,0">
|
||||
<Button x:Name="BtnSave" Content="保存设置" Padding="15,5" Click="BtnSave_Click"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,395 @@
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using Ink_Canvas.Windows;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
|
||||
{
|
||||
/// <summary>
|
||||
/// LauncherSettingsControl.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class LauncherSettingsControl : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// 父插件
|
||||
/// </summary>
|
||||
private readonly SuperLauncherPlugin _plugin;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="plugin">父插件</param>
|
||||
public LauncherSettingsControl(SuperLauncherPlugin plugin)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_plugin = plugin;
|
||||
|
||||
// 设置按钮位置
|
||||
RbtnLeft.IsChecked = _plugin.Config.ButtonPosition == LauncherButtonPosition.Left;
|
||||
RbtnRight.IsChecked = _plugin.Config.ButtonPosition == LauncherButtonPosition.Right;
|
||||
|
||||
// 绑定应用列表
|
||||
DgApps.ItemsSource = _plugin.LauncherItems;
|
||||
|
||||
// 初始化按钮状态
|
||||
UpdateButtonStates();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新按钮状态
|
||||
/// </summary>
|
||||
private void UpdateButtonStates()
|
||||
{
|
||||
bool hasSelection = DgApps.SelectedItem != null;
|
||||
BtnEdit.IsEnabled = hasSelection;
|
||||
BtnDelete.IsEnabled = hasSelection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 位置单选按钮选择事件
|
||||
/// </summary>
|
||||
private void RbtnPosition_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!IsLoaded) return;
|
||||
|
||||
LauncherButtonPosition oldPosition = _plugin.Config.ButtonPosition;
|
||||
|
||||
if (sender == RbtnLeft)
|
||||
{
|
||||
_plugin.Config.ButtonPosition = LauncherButtonPosition.Left;
|
||||
}
|
||||
else if (sender == RbtnRight)
|
||||
{
|
||||
_plugin.Config.ButtonPosition = LauncherButtonPosition.Right;
|
||||
}
|
||||
|
||||
// 如果位置发生变化,更新按钮位置
|
||||
if (oldPosition != _plugin.Config.ButtonPosition)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 更新按钮位置
|
||||
_plugin.UpdateButtonPosition();
|
||||
|
||||
// 保存配置
|
||||
_plugin.SaveConfig();
|
||||
|
||||
LogHelper.WriteLogToFile($"启动台按钮位置已更改为: {_plugin.Config.ButtonPosition}", LogHelper.LogType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"更新启动台按钮位置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"更新启动台按钮位置时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加应用按钮点击事件
|
||||
/// </summary>
|
||||
private void BtnAdd_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 弹出文件选择对话框
|
||||
OpenFileDialog dialog = new OpenFileDialog
|
||||
{
|
||||
Title = "选择应用程序",
|
||||
Filter = "应用程序 (*.exe)|*.exe|所有文件 (*.*)|*.*",
|
||||
Multiselect = false
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
// 创建新的启动项
|
||||
LauncherItem item = new LauncherItem
|
||||
{
|
||||
Name = System.IO.Path.GetFileNameWithoutExtension(dialog.FileName),
|
||||
Path = dialog.FileName,
|
||||
IsVisible = true,
|
||||
Position = -1 // 让插件管理器分配位置
|
||||
};
|
||||
|
||||
// 显示编辑对话框
|
||||
EditLauncherItem(item, true);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"添加启动项时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"添加启动项时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑应用按钮点击事件
|
||||
/// </summary>
|
||||
private void BtnEdit_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DgApps.SelectedItem is LauncherItem item)
|
||||
{
|
||||
EditLauncherItem(item, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除应用按钮点击事件
|
||||
/// </summary>
|
||||
private void BtnDelete_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DgApps.SelectedItem is LauncherItem item)
|
||||
{
|
||||
// 确认删除
|
||||
MessageBoxResult result = MessageBox.Show(
|
||||
$"确定要删除 {item.Name} 吗?",
|
||||
"删除确认",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Question);
|
||||
|
||||
if (result == MessageBoxResult.Yes)
|
||||
{
|
||||
// 从集合中移除
|
||||
_plugin.LauncherItems.Remove(item);
|
||||
|
||||
// 保存配置
|
||||
_plugin.SaveConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存设置按钮点击事件
|
||||
/// </summary>
|
||||
private void BtnSave_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 保存配置
|
||||
_plugin.SaveConfig();
|
||||
|
||||
// 如果插件已启用,重新加载启动台按钮
|
||||
if (_plugin.IsEnabled)
|
||||
{
|
||||
_plugin.Disable();
|
||||
_plugin.Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果插件未启用,则启用它
|
||||
_plugin.Enable();
|
||||
|
||||
// 通知PluginSettingsWindow刷新插件列表
|
||||
var window = Window.GetWindow(this);
|
||||
if (window is PluginSettingsWindow pluginSettingsWindow)
|
||||
{
|
||||
// 触发刷新
|
||||
pluginSettingsWindow.RefreshPluginList();
|
||||
}
|
||||
}
|
||||
|
||||
MessageBox.Show("设置已保存并应用!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"保存设置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"保存设置时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用项选择变更事件
|
||||
/// </summary>
|
||||
private void DgApps_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
UpdateButtonStates();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑启动项
|
||||
/// </summary>
|
||||
/// <param name="item">启动项</param>
|
||||
/// <param name="isNew">是否为新建</param>
|
||||
private void EditLauncherItem(LauncherItem item, bool isNew)
|
||||
{
|
||||
// 创建简单的编辑窗口
|
||||
Window editWindow = new Window
|
||||
{
|
||||
Title = isNew ? "添加应用" : "编辑应用",
|
||||
Width = 400,
|
||||
Height = 200,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterScreen,
|
||||
ResizeMode = ResizeMode.NoResize
|
||||
};
|
||||
|
||||
// 创建编辑表单
|
||||
Grid grid = new Grid
|
||||
{
|
||||
Margin = new Thickness(20)
|
||||
};
|
||||
|
||||
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
|
||||
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
|
||||
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
|
||||
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(80) });
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||
|
||||
// 名称输入框
|
||||
TextBlock nameLabel = new TextBlock
|
||||
{
|
||||
Text = "名称:",
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
TextBox nameTextBox = new TextBox
|
||||
{
|
||||
Text = item.Name,
|
||||
Margin = new Thickness(0, 5, 0, 5)
|
||||
};
|
||||
|
||||
Grid.SetRow(nameLabel, 0);
|
||||
Grid.SetColumn(nameLabel, 0);
|
||||
Grid.SetRow(nameTextBox, 0);
|
||||
Grid.SetColumn(nameTextBox, 1);
|
||||
|
||||
grid.Children.Add(nameLabel);
|
||||
grid.Children.Add(nameTextBox);
|
||||
|
||||
// 路径输入框
|
||||
TextBlock pathLabel = new TextBlock
|
||||
{
|
||||
Text = "路径:",
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
Grid pathGrid = new Grid();
|
||||
pathGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||
pathGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength() });
|
||||
|
||||
TextBox pathTextBox = new TextBox
|
||||
{
|
||||
Text = item.Path,
|
||||
Margin = new Thickness(0, 5, 5, 5)
|
||||
};
|
||||
Button browseButton = new Button
|
||||
{
|
||||
Content = "浏览",
|
||||
Padding = new Thickness(5, 0, 5, 0),
|
||||
Margin = new Thickness(0, 5, 0, 5)
|
||||
};
|
||||
|
||||
browseButton.Click += (s, e) =>
|
||||
{
|
||||
OpenFileDialog dialog = new OpenFileDialog
|
||||
{
|
||||
Title = "选择应用程序",
|
||||
Filter = "应用程序 (*.exe)|*.exe|所有文件 (*.*)|*.*",
|
||||
Multiselect = false,
|
||||
FileName = pathTextBox.Text
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
pathTextBox.Text = dialog.FileName;
|
||||
}
|
||||
};
|
||||
|
||||
Grid.SetColumn(pathTextBox, 0);
|
||||
Grid.SetColumn(browseButton, 1);
|
||||
pathGrid.Children.Add(pathTextBox);
|
||||
pathGrid.Children.Add(browseButton);
|
||||
|
||||
Grid.SetRow(pathLabel, 1);
|
||||
Grid.SetColumn(pathLabel, 0);
|
||||
Grid.SetRow(pathGrid, 1);
|
||||
Grid.SetColumn(pathGrid, 1);
|
||||
|
||||
grid.Children.Add(pathLabel);
|
||||
grid.Children.Add(pathGrid);
|
||||
|
||||
// 确认和取消按钮
|
||||
StackPanel buttonPanel = new StackPanel
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
HorizontalAlignment = HorizontalAlignment.Right,
|
||||
Margin = new Thickness(0, 10, 0, 0)
|
||||
};
|
||||
|
||||
Button okButton = new Button
|
||||
{
|
||||
Content = "确定",
|
||||
Padding = new Thickness(15, 5, 15, 5),
|
||||
Margin = new Thickness(0, 0, 10, 0),
|
||||
IsDefault = true
|
||||
};
|
||||
|
||||
Button cancelButton = new Button
|
||||
{
|
||||
Content = "取消",
|
||||
Padding = new Thickness(15, 5, 15, 5),
|
||||
IsCancel = true
|
||||
};
|
||||
|
||||
okButton.Click += (s, e) =>
|
||||
{
|
||||
// 验证输入
|
||||
if (string.IsNullOrWhiteSpace(nameTextBox.Text))
|
||||
{
|
||||
MessageBox.Show("请输入应用名称!", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(pathTextBox.Text))
|
||||
{
|
||||
MessageBox.Show("请输入应用路径!", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新项目
|
||||
item.Name = nameTextBox.Text;
|
||||
item.Path = pathTextBox.Text;
|
||||
|
||||
// 如果是新建,添加到集合
|
||||
if (isNew)
|
||||
{
|
||||
_plugin.AddLauncherItem(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 触发属性变更通知,刷新DataGrid
|
||||
if (DgApps.ItemsSource is ICollectionView view)
|
||||
{
|
||||
view.Refresh();
|
||||
}
|
||||
|
||||
// 保存配置
|
||||
_plugin.SaveConfig();
|
||||
}
|
||||
|
||||
editWindow.DialogResult = true;
|
||||
editWindow.Close();
|
||||
};
|
||||
|
||||
cancelButton.Click += (s, e) =>
|
||||
{
|
||||
editWindow.DialogResult = false;
|
||||
editWindow.Close();
|
||||
};
|
||||
|
||||
buttonPanel.Children.Add(okButton);
|
||||
buttonPanel.Children.Add(cancelButton);
|
||||
|
||||
Grid.SetRow(buttonPanel, 2);
|
||||
Grid.SetColumnSpan(buttonPanel, 2);
|
||||
|
||||
grid.Children.Add(buttonPanel);
|
||||
|
||||
// 设置窗口内容
|
||||
editWindow.Content = grid;
|
||||
|
||||
// 显示窗口
|
||||
editWindow.ShowDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<Window x:Class="Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher.LauncherWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher"
|
||||
mc:Ignorable="d"
|
||||
Title="启动台"
|
||||
Width="400"
|
||||
Height="300"
|
||||
WindowStyle="None"
|
||||
AllowsTransparency="True"
|
||||
Background="#80000000"
|
||||
ResizeMode="NoResize"
|
||||
Topmost="True"
|
||||
Deactivated="Window_Deactivated"
|
||||
ShowInTaskbar="False">
|
||||
|
||||
<Window.Resources>
|
||||
<!-- 应用项样式 -->
|
||||
<Style x:Key="LauncherItemStyle" TargetType="Button">
|
||||
<Setter Property="Width" Value="80"/>
|
||||
<Setter Property="Height" Value="80"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="Background" Value="#40FFFFFF"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border x:Name="border" Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="8">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Image Grid.Row="0" Source="{Binding Icon}" Width="32" Height="32" Margin="0,10,0,5"/>
|
||||
<TextBlock Grid.Row="1" Text="{Binding Name}" TextWrapping="Wrap" TextAlignment="Center"
|
||||
Margin="2,0,2,8" FontSize="11" Foreground="White"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#80FFFFFF"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background" Value="#C0FFFFFF"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Border CornerRadius="15" Background="#80000000" BorderThickness="1" BorderBrush="#40FFFFFF">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 标题栏 -->
|
||||
<Grid Grid.Row="0" Height="40">
|
||||
<TextBlock Text="启动台" Foreground="White" FontSize="18" FontWeight="Bold"
|
||||
VerticalAlignment="Center" Margin="15,0,0,0"/>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,10,0">
|
||||
<Button x:Name="BtnFixMode" Click="BtnFixMode_Click" Width="30" Height="30"
|
||||
Margin="5,0" Background="Transparent" BorderThickness="0"
|
||||
ToolTip="切换固定模式">
|
||||
<Path x:Name="FixModeIcon" Data="M7,2V13H10V22L17,10H13L17,2H7Z" Fill="White" Stretch="Uniform" Width="16" Height="16"/>
|
||||
</Button>
|
||||
<Button x:Name="BtnClose" Click="BtnClose_Click" Width="30" Height="30"
|
||||
Background="Transparent" BorderThickness="0"
|
||||
ToolTip="关闭">
|
||||
<Path Data="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
|
||||
Fill="White" Stretch="Uniform" Width="16" Height="16"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- 应用网格 -->
|
||||
<ScrollViewer Grid.Row="1" Margin="10" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<WrapPanel x:Name="AppPanel" Orientation="Horizontal" HorizontalAlignment="Center"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Window>
|
||||
@@ -0,0 +1,460 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
|
||||
{
|
||||
/// <summary>
|
||||
/// LauncherWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class LauncherWindow : Window
|
||||
{
|
||||
/// <summary>
|
||||
/// 父插件
|
||||
/// </summary>
|
||||
private readonly SuperLauncherPlugin _plugin;
|
||||
|
||||
/// <summary>
|
||||
/// 是否处于固定模式
|
||||
/// </summary>
|
||||
private bool _isFixMode = false;
|
||||
|
||||
/// <summary>
|
||||
/// 应用项按钮列表
|
||||
/// </summary>
|
||||
private readonly Dictionary<Button, LauncherItem> _appButtons = new Dictionary<Button, LauncherItem>();
|
||||
|
||||
/// <summary>
|
||||
/// 拖拽中的按钮
|
||||
/// </summary>
|
||||
private Button _draggingButton;
|
||||
|
||||
/// <summary>
|
||||
/// 拖拽开始位置
|
||||
/// </summary>
|
||||
private Point _dragStartPoint;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public LauncherWindow(SuperLauncherPlugin plugin)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_plugin = plugin;
|
||||
|
||||
// 加载应用项
|
||||
LoadLauncherItems();
|
||||
|
||||
// 添加鼠标按下事件(用于拖动窗口)
|
||||
this.MouseDown += (s, e) =>
|
||||
{
|
||||
if (e.ChangedButton == MouseButton.Left && e.ButtonState == MouseButtonState.Pressed)
|
||||
{
|
||||
this.DragMove();
|
||||
}
|
||||
};
|
||||
|
||||
// 根据应用数量调整窗口大小
|
||||
AdjustWindowSize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载启动台应用项
|
||||
/// </summary>
|
||||
private void LoadLauncherItems()
|
||||
{
|
||||
// 清空现有应用项
|
||||
AppPanel.Children.Clear();
|
||||
_appButtons.Clear();
|
||||
|
||||
// 获取显示的应用项
|
||||
var visibleItems = _plugin.LauncherItems
|
||||
.Where(item => item.IsVisible)
|
||||
.OrderBy(item => item.Position)
|
||||
.ToList();
|
||||
|
||||
foreach (var item in visibleItems)
|
||||
{
|
||||
// 创建应用按钮
|
||||
Button appButton = new Button
|
||||
{
|
||||
Style = (Style)FindResource("LauncherItemStyle"),
|
||||
DataContext = item,
|
||||
Tag = item.Position
|
||||
};
|
||||
|
||||
// 添加点击事件
|
||||
appButton.Click += AppButton_Click;
|
||||
|
||||
// 在固定模式下,添加拖拽事件
|
||||
appButton.PreviewMouseDown += AppButton_PreviewMouseDown;
|
||||
appButton.PreviewMouseMove += AppButton_PreviewMouseMove;
|
||||
appButton.PreviewMouseUp += AppButton_PreviewMouseUp;
|
||||
|
||||
// 记录按钮和项目的对应关系
|
||||
_appButtons.Add(appButton, item);
|
||||
|
||||
// 添加到面板
|
||||
AppPanel.Children.Add(appButton);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据应用数量调整窗口大小
|
||||
/// </summary>
|
||||
private void AdjustWindowSize()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 每行最多显示4个应用
|
||||
const int appsPerRow = 4;
|
||||
|
||||
// 计算行数
|
||||
int visibleCount = _appButtons.Count;
|
||||
int rowCount = (int)Math.Ceiling(visibleCount / (double)appsPerRow);
|
||||
|
||||
// 设置窗口宽度(每个应用90像素宽 = 80 + 5*2)
|
||||
Width = Math.Min(appsPerRow * 90 + 40, 400); // 最大宽度400
|
||||
|
||||
// 设置窗口高度(每个应用90像素高 = 80 + 5*2)
|
||||
Height = Math.Min(rowCount * 90 + 60, 600); // 最大高度600,标题栏40 + 边距20
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"调整启动台窗口大小时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用按钮点击事件
|
||||
/// </summary>
|
||||
private void AppButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_isFixMode) return; // 在固定模式下,不响应点击事件
|
||||
|
||||
if (sender is Button button && _appButtons.TryGetValue(button, out LauncherItem item))
|
||||
{
|
||||
// 获取应用路径和名称,用于后续启动
|
||||
string appPath = item.Path;
|
||||
string appName = item.Name;
|
||||
|
||||
LogHelper.WriteLogToFile($"点击启动应用: {appName}, 路径: {appPath}", LogHelper.LogType.Info);
|
||||
|
||||
// 首先标记窗口正在关闭
|
||||
IsClosing = true;
|
||||
|
||||
// 创建一个应用启动任务
|
||||
var launchTask = new System.Threading.Tasks.Task(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 等待一段时间,确保窗口关闭流程已经开始
|
||||
System.Threading.Thread.Sleep(200);
|
||||
|
||||
// 使用UI线程启动应用
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查应用路径是否存在
|
||||
if (System.IO.File.Exists(appPath) || !appPath.Contains(":\\"))
|
||||
{
|
||||
// 创建进程启动信息
|
||||
var psi = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = appPath,
|
||||
UseShellExecute = true,
|
||||
};
|
||||
|
||||
// 启动应用程序
|
||||
var process = System.Diagnostics.Process.Start(psi);
|
||||
LogHelper.WriteLogToFile($"应用程序 {appName} 已启动", LogHelper.LogType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile($"应用路径不存在: {appPath}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"找不到应用程序: {appPath}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"启动应用程序失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"启动应用程序失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"应用启动任务出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
});
|
||||
|
||||
// 关闭窗口
|
||||
try
|
||||
{
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
try { Close(); } catch { }
|
||||
|
||||
// 启动应用程序任务
|
||||
launchTask.Start();
|
||||
}), System.Windows.Threading.DispatcherPriority.Background);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"关闭窗口或启动任务时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
// 如果无法通过UI关闭窗口,直接启动任务
|
||||
launchTask.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"应用按钮点击事件出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
try { IsClosing = true; Close(); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
#region 固定模式拖拽事件
|
||||
|
||||
/// <summary>
|
||||
/// 应用按钮鼠标按下事件
|
||||
/// </summary>
|
||||
private void AppButton_PreviewMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (!_isFixMode) return;
|
||||
|
||||
if (e.ChangedButton == MouseButton.Left && sender is Button button)
|
||||
{
|
||||
_draggingButton = button;
|
||||
_dragStartPoint = e.GetPosition(AppPanel);
|
||||
button.CaptureMouse();
|
||||
button.Opacity = 0.7;
|
||||
|
||||
// 阻止事件冒泡,以避免触发按钮点击
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用按钮鼠标移动事件
|
||||
/// </summary>
|
||||
private void AppButton_PreviewMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (!_isFixMode || _draggingButton == null) return;
|
||||
|
||||
if (e.LeftButton == MouseButtonState.Pressed)
|
||||
{
|
||||
Point currentPosition = e.GetPosition(AppPanel);
|
||||
|
||||
// 移动按钮
|
||||
System.Windows.Controls.Canvas.SetLeft(_draggingButton, currentPosition.X - _draggingButton.ActualWidth / 2);
|
||||
System.Windows.Controls.Canvas.SetTop(_draggingButton, currentPosition.Y - _draggingButton.ActualHeight / 2);
|
||||
|
||||
// 将按钮移到最上层
|
||||
Panel.SetZIndex(_draggingButton, 100);
|
||||
|
||||
// 阻止事件冒泡
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用按钮鼠标释放事件
|
||||
/// </summary>
|
||||
private void AppButton_PreviewMouseUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (!_isFixMode || _draggingButton == null) return;
|
||||
|
||||
// 释放鼠标捕获
|
||||
_draggingButton.ReleaseMouseCapture();
|
||||
|
||||
// 计算新位置
|
||||
Point releasePoint = e.GetPosition(AppPanel);
|
||||
int newPosition = CalculateGridPosition(releasePoint);
|
||||
|
||||
// 获取当前项目
|
||||
LauncherItem currentItem = _appButtons[_draggingButton];
|
||||
|
||||
// 重新排序
|
||||
ReorderItems(currentItem, newPosition);
|
||||
|
||||
// 重新加载应用项
|
||||
LoadLauncherItems();
|
||||
|
||||
// 保存配置
|
||||
_plugin.SaveConfig();
|
||||
|
||||
// 清除拖拽状态
|
||||
_draggingButton.Opacity = 1;
|
||||
Panel.SetZIndex(_draggingButton, 0);
|
||||
_draggingButton = null;
|
||||
|
||||
// 阻止事件冒泡
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算网格位置
|
||||
/// </summary>
|
||||
private int CalculateGridPosition(Point point)
|
||||
{
|
||||
// 计算行和列
|
||||
int columnCount = 4; // 每行最多4个应用
|
||||
int columnWidth = 90; // 应用宽度(包括边距)
|
||||
int rowHeight = 90; // 应用高度(包括边距)
|
||||
|
||||
int column = (int)(point.X / columnWidth);
|
||||
int row = (int)(point.Y / rowHeight);
|
||||
|
||||
// 确保在有效范围内
|
||||
column = Math.Max(0, Math.Min(column, columnCount - 1));
|
||||
row = Math.Max(0, row);
|
||||
|
||||
// 计算位置索引
|
||||
return row * columnCount + column;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重新排序应用项
|
||||
/// </summary>
|
||||
private void ReorderItems(LauncherItem item, int newPosition)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 设置项目为固定位置
|
||||
item.IsPositionFixed = true;
|
||||
|
||||
// 如果位置相同,无需调整
|
||||
if (item.Position == newPosition)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取所有可见项目
|
||||
var visibleItems = _plugin.LauncherItems
|
||||
.Where(i => i.IsVisible)
|
||||
.OrderBy(i => i.Position)
|
||||
.ToList();
|
||||
|
||||
// 移除当前项目
|
||||
visibleItems.Remove(item);
|
||||
|
||||
// 查找插入位置
|
||||
int insertIndex = 0;
|
||||
for (int i = 0; i < visibleItems.Count; i++)
|
||||
{
|
||||
if (visibleItems[i].Position >= newPosition)
|
||||
{
|
||||
insertIndex = i;
|
||||
break;
|
||||
}
|
||||
insertIndex = i + 1;
|
||||
}
|
||||
|
||||
// 插入项目
|
||||
visibleItems.Insert(insertIndex, item);
|
||||
|
||||
// 重新分配位置
|
||||
for (int i = 0; i < visibleItems.Count; i++)
|
||||
{
|
||||
visibleItems[i].Position = i;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"重新排序应用项时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 窗口事件处理
|
||||
|
||||
/// <summary>
|
||||
/// 窗口失去焦点事件
|
||||
/// </summary>
|
||||
private void Window_Deactivated(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 只有在非固定模式、窗口已加载、未处于关闭状态且IsLoaded=true时关闭窗口
|
||||
if (!_isFixMode && IsLoaded && !IsClosing)
|
||||
{
|
||||
// 标记为正在关闭
|
||||
IsClosing = true;
|
||||
|
||||
// 使用Dispatcher.BeginInvoke而不是直接调用Close,避免冲突
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 再次检查窗口状态
|
||||
if (IsLoaded && !IsClosing)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"延迟关闭窗口时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}), System.Windows.Threading.DispatcherPriority.Background);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"窗口失去焦点关闭时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 窗口是否正在关闭
|
||||
/// </summary>
|
||||
private bool IsClosing { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 重写OnClosing方法,标记窗口正在关闭
|
||||
/// </summary>
|
||||
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
IsClosing = true;
|
||||
base.OnClosing(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭按钮点击事件
|
||||
/// </summary>
|
||||
private void BtnClose_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 固定模式按钮点击事件
|
||||
/// </summary>
|
||||
private void BtnFixMode_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// 切换固定模式
|
||||
_isFixMode = !_isFixMode;
|
||||
|
||||
// 更新固定模式按钮图标颜色
|
||||
FixModeIcon.Fill = _isFixMode ? Brushes.Yellow : Brushes.White;
|
||||
|
||||
// 显示提示
|
||||
if (_isFixMode)
|
||||
{
|
||||
MessageBox.Show("已进入固定模式,您可以拖动应用图标调整位置。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,589 @@
|
||||
using Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins.BuiltIn
|
||||
{
|
||||
/// <summary>
|
||||
/// 超级启动台插件
|
||||
/// </summary>
|
||||
public class SuperLauncherPlugin : PluginBase
|
||||
{
|
||||
#region 插件基本信息
|
||||
|
||||
public override string Name => "超级启动台";
|
||||
|
||||
public override string Description => "在浮动栏添加一个启动台按钮,可快速启动常用应用程序。";
|
||||
|
||||
public override Version Version => new Version(1, 0, 0);
|
||||
|
||||
public override string Author => "ICC CE 团队";
|
||||
|
||||
public override bool IsBuiltIn => true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 插件属性和字段
|
||||
|
||||
/// <summary>
|
||||
/// 启动台配置
|
||||
/// </summary>
|
||||
public LauncherConfig Config { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 启动台应用程序列表
|
||||
/// </summary>
|
||||
public ObservableCollection<LauncherItem> LauncherItems { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 启动台按钮
|
||||
/// </summary>
|
||||
private LauncherButton _launcherButton;
|
||||
|
||||
/// <summary>
|
||||
/// 启动台窗口
|
||||
/// </summary>
|
||||
private LauncherWindow _launcherWindow;
|
||||
|
||||
/// <summary>
|
||||
/// 配置文件路径
|
||||
/// </summary>
|
||||
private readonly string _configPath = Path.Combine(App.RootPath, "PluginConfigs", "SuperLauncher.json");
|
||||
|
||||
/// <summary>
|
||||
/// 标记是否已添加到浮动栏
|
||||
/// </summary>
|
||||
private bool _isAddedToFloatingBar = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 插件生命周期
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
// 创建配置目录
|
||||
string configDir = Path.Combine(App.RootPath, "PluginConfigs");
|
||||
if (!Directory.Exists(configDir))
|
||||
{
|
||||
Directory.CreateDirectory(configDir);
|
||||
}
|
||||
|
||||
// 加载配置
|
||||
LoadConfig();
|
||||
|
||||
LogHelper.WriteLogToFile("超级启动台插件已初始化", LogHelper.LogType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"初始化超级启动台插件时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Enable()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsEnabled) return; // 防止重复启用
|
||||
|
||||
// 创建启动台按钮
|
||||
if (_launcherButton == null)
|
||||
{
|
||||
_launcherButton = new LauncherButton(this);
|
||||
LogHelper.WriteLogToFile("超级启动台按钮已创建", LogHelper.LogType.Info);
|
||||
}
|
||||
|
||||
// 添加启动台按钮到浮动栏
|
||||
AddLauncherButtonToFloatingBar();
|
||||
|
||||
// 设置启用状态
|
||||
base.Enable();
|
||||
|
||||
// 保存插件配置
|
||||
SavePluginSettings();
|
||||
|
||||
LogHelper.WriteLogToFile("超级启动台插件已启用", LogHelper.LogType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"启用超级启动台插件时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Disable()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsEnabled) return; // 防止重复禁用
|
||||
|
||||
// 从浮动栏移除启动台按钮
|
||||
RemoveLauncherButtonFromFloatingBar();
|
||||
|
||||
// 如果启动台窗口打开,则关闭
|
||||
if (_launcherWindow != null && _launcherWindow.IsVisible)
|
||||
{
|
||||
_launcherWindow.Close();
|
||||
_launcherWindow = null;
|
||||
}
|
||||
|
||||
// 设置禁用状态
|
||||
base.Disable();
|
||||
|
||||
// 保存插件配置
|
||||
SavePluginSettings();
|
||||
|
||||
LogHelper.WriteLogToFile("超级启动台插件已禁用", LogHelper.LogType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"禁用超级启动台插件时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public override UserControl GetSettingsView()
|
||||
{
|
||||
return new LauncherSettingsControl(this);
|
||||
}
|
||||
|
||||
public override void Cleanup()
|
||||
{
|
||||
// 保存配置
|
||||
SaveConfig();
|
||||
|
||||
// 从浮动栏移除启动台按钮
|
||||
RemoveLauncherButtonFromFloatingBar();
|
||||
|
||||
// 如果启动台窗口打开,则关闭
|
||||
if (_launcherWindow != null && _launcherWindow.IsVisible)
|
||||
{
|
||||
_launcherWindow.Close();
|
||||
_launcherWindow = null;
|
||||
}
|
||||
|
||||
base.Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存插件设置
|
||||
/// </summary>
|
||||
public override void SavePluginSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 确保配置已加载
|
||||
if (Config == null)
|
||||
{
|
||||
LoadConfig();
|
||||
}
|
||||
|
||||
// 更新其他设置,但不更改插件启用状态
|
||||
|
||||
// 保存配置
|
||||
SaveConfig();
|
||||
|
||||
LogHelper.WriteLogToFile($"超级启动台插件设置已保存", LogHelper.LogType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"保存超级启动台插件设置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 配置管理
|
||||
|
||||
/// <summary>
|
||||
/// 加载配置
|
||||
/// </summary>
|
||||
private void LoadConfig()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(_configPath))
|
||||
{
|
||||
string json = File.ReadAllText(_configPath);
|
||||
Config = JsonConvert.DeserializeObject<LauncherConfig>(json) ?? CreateDefaultConfig();
|
||||
LauncherItems = new ObservableCollection<LauncherItem>(Config.Items ?? new List<LauncherItem>());
|
||||
|
||||
// 注意:不再根据配置更改插件启用状态
|
||||
// 插件状态由PluginManager统一管理
|
||||
}
|
||||
else
|
||||
{
|
||||
Config = CreateDefaultConfig();
|
||||
LauncherItems = new ObservableCollection<LauncherItem>(Config.Items);
|
||||
SaveConfig();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"加载超级启动台配置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
Config = CreateDefaultConfig();
|
||||
LauncherItems = new ObservableCollection<LauncherItem>(Config.Items);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存配置
|
||||
/// </summary>
|
||||
public void SaveConfig()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 同步LauncherItems到Config
|
||||
Config.Items = new List<LauncherItem>(LauncherItems);
|
||||
|
||||
// 序列化并保存配置
|
||||
string json = JsonConvert.SerializeObject(Config, Formatting.Indented);
|
||||
File.WriteAllText(_configPath, json);
|
||||
|
||||
LogHelper.WriteLogToFile("超级启动台配置已保存", LogHelper.LogType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"保存超级启动台配置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建默认配置
|
||||
/// </summary>
|
||||
private LauncherConfig CreateDefaultConfig()
|
||||
{
|
||||
var config = new LauncherConfig
|
||||
{
|
||||
ButtonPosition = LauncherButtonPosition.Right,
|
||||
// 不再使用IsEnabled,插件状态由PluginManager管理
|
||||
Items = new List<LauncherItem>
|
||||
{
|
||||
new LauncherItem
|
||||
{
|
||||
Name = "资源管理器",
|
||||
Path = @"C:\Windows\explorer.exe",
|
||||
IsVisible = true,
|
||||
Position = 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 启动台按钮管理
|
||||
|
||||
/// <summary>
|
||||
/// 将启动台按钮添加到浮动栏
|
||||
/// </summary>
|
||||
private void AddLauncherButtonToFloatingBar()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 如果已经添加,先移除
|
||||
if (_isAddedToFloatingBar)
|
||||
{
|
||||
RemoveLauncherButtonFromFloatingBar();
|
||||
_isAddedToFloatingBar = false;
|
||||
}
|
||||
|
||||
// 获取主窗口实例
|
||||
var mainWindow = Application.Current.MainWindow;
|
||||
if (mainWindow == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("未找到主窗口实例,无法添加启动台按钮", LogHelper.LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建启动台按钮
|
||||
_launcherButton = new LauncherButton(this);
|
||||
var buttonElement = _launcherButton.Element;
|
||||
|
||||
// 查找浮动栏
|
||||
var floatingBar = mainWindow.FindName("StackPanelFloatingBar") as Panel;
|
||||
if (floatingBar == null)
|
||||
{
|
||||
// 如果直接查找失败,则尝试遍历可视树查找
|
||||
Panel floatingBarPanelFromTree = null;
|
||||
FindStackPanelFloatingBar(mainWindow, ref floatingBarPanelFromTree);
|
||||
floatingBar = floatingBarPanelFromTree;
|
||||
}
|
||||
|
||||
if (floatingBar == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("未找到浮动栏,无法添加启动台按钮", LogHelper.LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加启动台按钮到浮动栏
|
||||
if (Config.ButtonPosition == LauncherButtonPosition.Left)
|
||||
{
|
||||
floatingBar.Children.Insert(0, buttonElement);
|
||||
LogHelper.WriteLogToFile("启动台按钮已添加到浮动栏左侧", LogHelper.LogType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
floatingBar.Children.Add(buttonElement);
|
||||
LogHelper.WriteLogToFile("启动台按钮已添加到浮动栏右侧", LogHelper.LogType.Info);
|
||||
}
|
||||
|
||||
_isAddedToFloatingBar = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"添加启动台按钮到浮动栏时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 递归查找StackPanelFloatingBar
|
||||
/// </summary>
|
||||
private void FindStackPanelFloatingBar(DependencyObject parent, ref Panel result)
|
||||
{
|
||||
if (parent == null || result != null) return;
|
||||
|
||||
try
|
||||
{
|
||||
// 检查当前对象是否为我们要找的面板
|
||||
if (parent is Panel panel && panel.Name == "StackPanelFloatingBar")
|
||||
{
|
||||
result = panel;
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取子元素数量
|
||||
int childCount = VisualTreeHelper.GetChildrenCount(parent);
|
||||
|
||||
// 遍历所有子元素
|
||||
for (int i = 0; i < childCount; i++)
|
||||
{
|
||||
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
|
||||
FindStackPanelFloatingBar(child, ref result);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"查找StackPanelFloatingBar时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从浮动栏移除启动台按钮
|
||||
/// </summary>
|
||||
private void RemoveLauncherButtonFromFloatingBar()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_isAddedToFloatingBar || _launcherButton == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取主窗口实例
|
||||
var mainWindow = Application.Current.MainWindow;
|
||||
if (mainWindow == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("未找到主窗口实例,无法移除启动台按钮", LogHelper.LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取按钮元素
|
||||
var buttonElement = _launcherButton.Element;
|
||||
|
||||
// 查找浮动栏
|
||||
var floatingBar = mainWindow.FindName("StackPanelFloatingBar") as Panel;
|
||||
if (floatingBar == null)
|
||||
{
|
||||
// 如果直接查找失败,则尝试遍历可视树查找
|
||||
Panel floatingBarPanelFromTree = null;
|
||||
FindStackPanelFloatingBar(mainWindow, ref floatingBarPanelFromTree);
|
||||
floatingBar = floatingBarPanelFromTree;
|
||||
}
|
||||
|
||||
if (floatingBar == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("未找到浮动栏,无法移除启动台按钮", LogHelper.LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 从浮动栏移除启动台按钮
|
||||
if (floatingBar.Children.Contains(buttonElement))
|
||||
{
|
||||
floatingBar.Children.Remove(buttonElement);
|
||||
LogHelper.WriteLogToFile("启动台按钮已从浮动栏移除", LogHelper.LogType.Info);
|
||||
}
|
||||
|
||||
_isAddedToFloatingBar = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"移除启动台按钮时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新启动台按钮位置
|
||||
/// </summary>
|
||||
public void UpdateButtonPosition()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 如果按钮已添加到浮动栏,重新添加以更新位置
|
||||
if (_isAddedToFloatingBar)
|
||||
{
|
||||
RemoveLauncherButtonFromFloatingBar();
|
||||
AddLauncherButtonToFloatingBar();
|
||||
LogHelper.WriteLogToFile($"启动台按钮位置已更新为: {Config.ButtonPosition}", LogHelper.LogType.Info);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"更新启动台按钮位置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 启动台功能
|
||||
|
||||
/// <summary>
|
||||
/// 显示启动台窗口
|
||||
/// </summary>
|
||||
/// <param name="buttonPosition">按钮在屏幕上的位置</param>
|
||||
public void ShowLauncherWindow(Point buttonPosition)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 如果窗口已存在,关闭它
|
||||
if (_launcherWindow != null && _launcherWindow.IsVisible)
|
||||
{
|
||||
_launcherWindow.Close();
|
||||
_launcherWindow = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建新的启动台窗口
|
||||
_launcherWindow = new LauncherWindow(this);
|
||||
|
||||
// 计算窗口位置,使其位于按钮上方
|
||||
PositionLauncherWindow(_launcherWindow, buttonPosition);
|
||||
|
||||
// 显示窗口
|
||||
_launcherWindow.Show();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"显示启动台窗口时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置启动台窗口位置
|
||||
/// </summary>
|
||||
/// <param name="window">启动台窗口</param>
|
||||
/// <param name="buttonPosition">按钮在屏幕上的位置</param>
|
||||
private void PositionLauncherWindow(LauncherWindow window, Point buttonPosition)
|
||||
{
|
||||
// 确保窗口已加载
|
||||
if (window.ActualWidth == 0 || window.ActualHeight == 0)
|
||||
{
|
||||
window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||
|
||||
// 设置窗口加载完成后的位置
|
||||
window.Loaded += (s, e) =>
|
||||
{
|
||||
// 窗口位于按钮上方居中
|
||||
double left = buttonPosition.X - (window.ActualWidth / 2);
|
||||
double top = buttonPosition.Y - window.ActualHeight - 10; // 在按钮上方留出一些间距
|
||||
|
||||
// 确保窗口在屏幕内
|
||||
left = Math.Max(0, Math.Min(left, SystemParameters.WorkArea.Width - window.ActualWidth));
|
||||
top = Math.Max(0, Math.Min(top, SystemParameters.WorkArea.Height - window.ActualHeight));
|
||||
|
||||
window.Left = left;
|
||||
window.Top = top;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// 窗口位于按钮上方居中
|
||||
double left = buttonPosition.X - (window.ActualWidth / 2);
|
||||
double top = buttonPosition.Y - window.ActualHeight - 10; // 在按钮上方留出一些间距
|
||||
|
||||
// 确保窗口在屏幕内
|
||||
left = Math.Max(0, Math.Min(left, SystemParameters.WorkArea.Width - window.ActualWidth));
|
||||
top = Math.Max(0, Math.Min(top, SystemParameters.WorkArea.Height - window.ActualHeight));
|
||||
|
||||
window.Left = left;
|
||||
window.Top = top;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加应用到启动台
|
||||
/// </summary>
|
||||
/// <param name="item">启动台项</param>
|
||||
public void AddLauncherItem(LauncherItem item)
|
||||
{
|
||||
// 如果项目数量已达上限,则不添加
|
||||
if (LauncherItems.Count >= 40)
|
||||
{
|
||||
MessageBox.Show("启动台项目数量已达上限(40个)!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
// 寻找合适的位置
|
||||
if (item.Position < 0)
|
||||
{
|
||||
item.Position = FindNextAvailablePosition();
|
||||
}
|
||||
|
||||
// 添加项目并保存配置
|
||||
LauncherItems.Add(item);
|
||||
SaveConfig();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找下一个可用位置
|
||||
/// </summary>
|
||||
private int FindNextAvailablePosition()
|
||||
{
|
||||
// 获取已使用的位置列表
|
||||
var usedPositions = new HashSet<int>();
|
||||
foreach (var item in LauncherItems)
|
||||
{
|
||||
usedPositions.Add(item.Position);
|
||||
}
|
||||
|
||||
// 查找第一个可用位置
|
||||
for (int i = 0; i < 40; i++)
|
||||
{
|
||||
if (!usedPositions.Contains(i))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果所有位置都已使用,则返回0
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// 定义插件的基本接口
|
||||
/// </summary>
|
||||
public interface IPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// 插件名称
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件描述
|
||||
/// </summary>
|
||||
string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件版本
|
||||
/// </summary>
|
||||
Version Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件作者
|
||||
/// </summary>
|
||||
string Author { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为内置插件
|
||||
/// </summary>
|
||||
bool IsBuiltIn { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 初始化插件
|
||||
/// 此方法在插件加载时被调用,用于执行一些初始化工作
|
||||
/// </summary>
|
||||
void Initialize();
|
||||
|
||||
/// <summary>
|
||||
/// 启用插件
|
||||
/// 此方法在插件被用户或系统启用时调用,激活插件功能
|
||||
/// </summary>
|
||||
void Enable();
|
||||
|
||||
/// <summary>
|
||||
/// 禁用插件
|
||||
/// 此方法在插件被用户或系统禁用时调用,停用插件功能
|
||||
/// </summary>
|
||||
void Disable();
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件设置界面
|
||||
/// 此方法返回插件的设置界面控件,用于展示在设置窗口
|
||||
/// </summary>
|
||||
/// <returns>插件设置界面</returns>
|
||||
UserControl GetSettingsView();
|
||||
|
||||
/// <summary>
|
||||
/// 插件卸载时的清理工作
|
||||
/// 此方法在插件被卸载前调用,用于释放资源和执行清理
|
||||
/// </summary>
|
||||
void Cleanup();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
using System;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// 插件基类,提供基本实现
|
||||
/// </summary>
|
||||
public abstract class PluginBase : IPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// 插件状态(私有字段)
|
||||
/// </summary>
|
||||
private bool _isEnabled = false;
|
||||
|
||||
/// <summary>
|
||||
/// 插件状态(公共属性)
|
||||
/// </summary>
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => _isEnabled;
|
||||
protected set
|
||||
{
|
||||
if (_isEnabled != value)
|
||||
{
|
||||
_isEnabled = value;
|
||||
OnEnabledStateChanged(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件ID
|
||||
/// </summary>
|
||||
public string Id { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件路径
|
||||
/// </summary>
|
||||
public string PluginPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件名称
|
||||
/// </summary>
|
||||
public abstract string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件描述
|
||||
/// </summary>
|
||||
public abstract string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件版本
|
||||
/// </summary>
|
||||
public abstract Version Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件作者
|
||||
/// </summary>
|
||||
public abstract string Author { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为内置插件
|
||||
/// </summary>
|
||||
public virtual bool IsBuiltIn => false;
|
||||
|
||||
/// <summary>
|
||||
/// 状态变更事件
|
||||
/// </summary>
|
||||
public event EventHandler<bool> EnabledStateChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化插件
|
||||
/// </summary>
|
||||
public virtual void Initialize()
|
||||
{
|
||||
Id = GetType().FullName;
|
||||
|
||||
// 添加日志,记录插件名称
|
||||
try
|
||||
{
|
||||
string name = Name;
|
||||
LogHelper.WriteLogToFile($"初始化插件: ID={Id}, 名称={name ?? "未命名"}", LogHelper.LogType.Info);
|
||||
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
LogHelper.WriteLogToFile($"警告: 插件 {Id} 的名称为空", LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"获取插件名称时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 已初始化", LogHelper.LogType.Info);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启用插件
|
||||
/// </summary>
|
||||
public virtual void Enable()
|
||||
{
|
||||
if (!IsEnabled)
|
||||
{
|
||||
IsEnabled = true;
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 已启用", LogHelper.LogType.Info);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 禁用插件
|
||||
/// </summary>
|
||||
public virtual void Disable()
|
||||
{
|
||||
if (IsEnabled)
|
||||
{
|
||||
IsEnabled = false;
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 已禁用", LogHelper.LogType.Info);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件设置界面
|
||||
/// </summary>
|
||||
/// <returns>插件设置界面</returns>
|
||||
public virtual UserControl GetSettingsView()
|
||||
{
|
||||
// 默认返回空设置页面
|
||||
return new UserControl();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件卸载时的清理工作
|
||||
/// </summary>
|
||||
public virtual void Cleanup()
|
||||
{
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 已卸载", LogHelper.LogType.Info);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存插件自身的设置
|
||||
/// 注意:此方法仅用于保存插件的特定设置,不应影响插件启用/禁用状态
|
||||
/// 插件启用状态由PluginManager统一管理
|
||||
/// </summary>
|
||||
public virtual void SavePluginSettings()
|
||||
{
|
||||
// 默认实现不做任何事情
|
||||
// 子类可以重写此方法,将自身设置保存到配置文件中
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 设置已保存", LogHelper.LogType.Event);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发状态变更事件
|
||||
/// </summary>
|
||||
/// <param name="isEnabled">是否启用</param>
|
||||
protected virtual void OnEnabledStateChanged(bool isEnabled)
|
||||
{
|
||||
EnabledStateChanged?.Invoke(this, isEnabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,276 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// 插件模板,用于开发者参考
|
||||
/// 注意:实际开发时,请将此类移到单独的程序集中
|
||||
/// </summary>
|
||||
public class PluginTemplate : PluginBase
|
||||
{
|
||||
#region 插件基本信息
|
||||
|
||||
/// <summary>
|
||||
/// 插件名称
|
||||
/// </summary>
|
||||
public override string Name => "插件模板";
|
||||
|
||||
/// <summary>
|
||||
/// 插件描述
|
||||
/// </summary>
|
||||
public override string Description => "这是一个插件开发模板,用于开发者参考。";
|
||||
|
||||
/// <summary>
|
||||
/// 插件版本
|
||||
/// </summary>
|
||||
public override Version Version => new Version(1, 0, 0);
|
||||
|
||||
/// <summary>
|
||||
/// 插件作者
|
||||
/// </summary>
|
||||
public override string Author => "Your Name";
|
||||
|
||||
/// <summary>
|
||||
/// 是否为内置插件(外部插件请返回false)
|
||||
/// </summary>
|
||||
public override bool IsBuiltIn => false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 插件生命周期
|
||||
|
||||
/// <summary>
|
||||
/// 插件初始化
|
||||
/// 在这里进行插件的初始化工作,如加载配置、注册事件等
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
// 先调用基类方法,这样会设置插件ID和记录日志
|
||||
base.Initialize();
|
||||
|
||||
// TODO: 在这里进行插件初始化工作
|
||||
|
||||
// 示例:记录初始化信息
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 开始初始化", LogHelper.LogType.Info);
|
||||
|
||||
// 示例:加载配置
|
||||
LoadConfig();
|
||||
|
||||
// 示例:注册自定义事件
|
||||
// MainWindow.Instance.SomeEvent += OnSomeEvent;
|
||||
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 初始化完成", LogHelper.LogType.Info);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启用插件
|
||||
/// 在这里激活插件功能
|
||||
/// </summary>
|
||||
public override void Enable()
|
||||
{
|
||||
// 先调用基类方法,这样会设置插件状态和记录日志
|
||||
base.Enable();
|
||||
|
||||
// TODO: 在这里启用插件功能
|
||||
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 已启用", LogHelper.LogType.Info);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 禁用插件
|
||||
/// 在这里停用插件功能
|
||||
/// </summary>
|
||||
public override void Disable()
|
||||
{
|
||||
// 先调用基类方法,这样会设置插件状态和记录日志
|
||||
base.Disable();
|
||||
|
||||
// TODO: 在这里禁用插件功能
|
||||
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 已禁用", LogHelper.LogType.Info);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理资源
|
||||
/// 在插件卸载时调用,清理资源
|
||||
/// </summary>
|
||||
public override void Cleanup()
|
||||
{
|
||||
// TODO: 在这里清理插件资源
|
||||
|
||||
// 示例:取消注册事件
|
||||
// MainWindow.Instance.SomeEvent -= OnSomeEvent;
|
||||
|
||||
// 示例:保存配置
|
||||
SaveConfig();
|
||||
|
||||
// 最后调用基类方法
|
||||
base.Cleanup();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 插件配置
|
||||
|
||||
/// <summary>
|
||||
/// 加载插件配置
|
||||
/// </summary>
|
||||
private void LoadConfig()
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: 从文件或其他位置加载配置
|
||||
// 示例:
|
||||
// string configPath = Path.Combine(App.RootPath, "PluginConfigs", "YourPluginName.json");
|
||||
// if (File.Exists(configPath))
|
||||
// {
|
||||
// string json = File.ReadAllText(configPath);
|
||||
// YourConfig = Newtonsoft.Json.JsonConvert.DeserializeObject<YourConfigClass>(json);
|
||||
// }
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"加载插件配置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存插件配置
|
||||
/// </summary>
|
||||
private void SaveConfig()
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: 保存配置到文件或其他位置
|
||||
// 示例:
|
||||
// string configDir = Path.Combine(App.RootPath, "PluginConfigs");
|
||||
// if (!Directory.Exists(configDir))
|
||||
// {
|
||||
// Directory.CreateDirectory(configDir);
|
||||
// }
|
||||
// string configPath = Path.Combine(configDir, "YourPluginName.json");
|
||||
// string json = Newtonsoft.Json.JsonConvert.SerializeObject(YourConfig, Newtonsoft.Json.Formatting.Indented);
|
||||
// File.WriteAllText(configPath, json);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"保存插件配置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 插件设置界面
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件设置界面
|
||||
/// </summary>
|
||||
/// <returns>插件设置界面</returns>
|
||||
public override UserControl GetSettingsView()
|
||||
{
|
||||
// 创建插件设置界面
|
||||
return new PluginTemplateSettingsControl();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 插件功能方法
|
||||
|
||||
// TODO: 在这里添加插件的具体功能方法
|
||||
|
||||
/// <summary>
|
||||
/// 示例方法:执行一些功能
|
||||
/// </summary>
|
||||
public void DoSomething()
|
||||
{
|
||||
if (!IsEnabled) return;
|
||||
|
||||
try
|
||||
{
|
||||
// TODO: 实现你的功能
|
||||
MessageBox.Show("插件功能执行示例", "插件模板", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"执行插件功能时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件设置控件
|
||||
/// </summary>
|
||||
public class PluginTemplateSettingsControl : UserControl
|
||||
{
|
||||
public PluginTemplateSettingsControl()
|
||||
{
|
||||
// 创建设置界面布局
|
||||
var panel = new StackPanel
|
||||
{
|
||||
Margin = new Thickness(10)
|
||||
};
|
||||
|
||||
// 添加标题
|
||||
panel.Children.Add(new TextBlock
|
||||
{
|
||||
Text = "插件模板设置",
|
||||
FontSize = 16,
|
||||
FontWeight = FontWeights.Bold,
|
||||
Margin = new Thickness(0, 0, 0, 10)
|
||||
});
|
||||
|
||||
// 添加说明文字
|
||||
panel.Children.Add(new TextBlock
|
||||
{
|
||||
Text = "这是一个示例设置界面,你可以在这里添加自己的设置控件。",
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
Margin = new Thickness(0, 0, 0, 15)
|
||||
});
|
||||
|
||||
// 添加示例设置选项
|
||||
var checkBox = new CheckBox
|
||||
{
|
||||
Content = "启用某项功能",
|
||||
Margin = new Thickness(0, 0, 0, 10)
|
||||
};
|
||||
panel.Children.Add(checkBox);
|
||||
|
||||
// 添加文本输入框
|
||||
panel.Children.Add(new TextBlock
|
||||
{
|
||||
Text = "设置项:",
|
||||
Margin = new Thickness(0, 5, 0, 5)
|
||||
});
|
||||
|
||||
panel.Children.Add(new TextBox
|
||||
{
|
||||
Margin = new Thickness(0, 0, 0, 10),
|
||||
Width = 200,
|
||||
HorizontalAlignment = HorizontalAlignment.Left
|
||||
});
|
||||
|
||||
// 添加按钮
|
||||
var button = new Button
|
||||
{
|
||||
Content = "保存设置",
|
||||
Padding = new Thickness(10, 5, 10, 5),
|
||||
Margin = new Thickness(0, 10, 0, 0),
|
||||
HorizontalAlignment = HorizontalAlignment.Left
|
||||
};
|
||||
|
||||
button.Click += (sender, e) =>
|
||||
{
|
||||
MessageBox.Show("设置已保存!", "插件模板", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
};
|
||||
|
||||
panel.Children.Add(button);
|
||||
|
||||
// 设置控件内容
|
||||
this.Content = panel;
|
||||
}
|
||||
}
|
||||
}
|
||||
+86
-74
@@ -113,6 +113,9 @@
|
||||
<DataTrigger Binding="{Binding Tag}" Value="about">
|
||||
<Setter Property="Background" Value="#3b82f6"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Tag}" Value="plugins">
|
||||
<Setter Property="Background" Value="#3b82f6"/>
|
||||
</DataTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
@@ -197,6 +200,11 @@
|
||||
<!-- 主要导航按钮 -->
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<StackPanel>
|
||||
<!-- Plugins -->
|
||||
<Button Width="40" Height="40" Margin="0,5,0,0" Style="{StaticResource NavButton}"
|
||||
Click="NavPlugins_Click" Tag="plugins" ToolTip="插件">
|
||||
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="18"/>
|
||||
</Button>
|
||||
<!-- Startup -->
|
||||
<Button Width="40" Height="40" Margin="0,10,0,0" Style="{StaticResource NavButton}"
|
||||
Click="NavStartup_Click" Tag="startup" ToolTip="启动设置">
|
||||
@@ -572,80 +580,21 @@
|
||||
</ui:SimpleStackPanel>
|
||||
</ui:SimpleStackPanel>
|
||||
</Border>
|
||||
<Border Margin="0,0,0,10" Height="100" CornerRadius="5" BorderBrush="#a1a1aa"
|
||||
BorderThickness="1">
|
||||
<ui:SimpleStackPanel VerticalAlignment="Center">
|
||||
<TextBlock Foreground="#fafafa" HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center" FontSize="15" Margin="0,0,0,10"
|
||||
Text="开发中...请不要点击,可能会导致ICC异常崩溃" />
|
||||
<ui:SimpleStackPanel Spacing="5">
|
||||
<ui:SimpleStackPanel Spacing="5" Orientation="Horizontal"
|
||||
HorizontalAlignment="Center">
|
||||
<Button Width="116" Height="45" FontFamily="Microsoft YaHei UI"
|
||||
Click="BtnRestart_Click">
|
||||
<Button.Resources>
|
||||
</Button.Resources>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center" Spacing="0">
|
||||
<Image RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Margin="0,0,6,0" Height="20" Width="20">
|
||||
<Image.Source>
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="F1 M24,24z M0,0z M6.34315,6.34315C7.84299,4.8433 9.87707,4.0005 11.9981,4 14.2527,4.00897 16.4167,4.88785 18.039,6.45324L18.5858,7 16,7C15.4477,7 15,7.44772 15,8 15,8.55228 15.4477,9 16,9L21,9C21.1356,9 21.2649,8.97301 21.3828,8.92412 21.5007,8.87532 21.6112,8.80298 21.7071,8.70711 21.8902,8.52405 21.9874,8.28768 21.9989,8.04797 21.9996,8.03199 22,8.016 22,8L22,3C22,2.44772 21.5523,2 21,2 20.4477,2 20,2.44772 20,3L20,5.58579 19.4471,5.03289 19.435,5.02103C17.4405,3.09289,14.7779,2.01044,12.0038,2L12,2C9.34784,2 6.8043,3.05357 4.92893,4.92893 3.05357,6.8043 2,9.34784 2,12 2,12.5523 2.44772,13 3,13 3.55228,13 4,12.5523 4,12 4,9.87827 4.84285,7.84344 6.34315,6.34315z" />
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="F1 M24,24z M0,0z M22,12C22,14.6522 20.9464,17.1957 19.0711,19.0711 17.1957,20.9464 14.6522,22 12,22L11.9962,22C9.22213,21.9896,6.55946,20.9071,4.56496,18.979L4.55289,18.9671 4,18.4142 4,21C4,21.5523 3.55228,22 3,22 2.44772,22 2,21.5523 2,21L2,16.0002C2,15.8646 2.02699,15.7351 2.07588,15.6172 2.12432,15.5001 2.19595,15.3904 2.29078,15.295 2.29219,15.2936 2.2936,15.2922 2.29502,15.2908 2.48924,15.0977 2.74301,15.0008 2.997,15 2.998,15 2.999,15 3,15L8,15C8.55228,15 9,15.4477 9,16 9,16.5523 8.55228,17 8,17L5.41421,17 5.96095,17.5467C7.5833,19.1122 9.74736,19.9911 12.002,20 14.123,19.9995 16.157,19.1567 17.6569,17.6569 19.1571,16.1566 20,14.1217 20,12 20,11.4477 20.4477,11 21,11 21.5523,11 22,11.4477 22,12z" />
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
</Image>
|
||||
<Label FontSize="16" Foreground="#fafafa"
|
||||
VerticalAlignment="Center" FontFamily="Microsoft YaHei UI"
|
||||
FontWeight="Bold">
|
||||
测试
|
||||
</Label>
|
||||
</ui:SimpleStackPanel>
|
||||
</Button>
|
||||
<Button Width="116" Height="45" FontFamily="Microsoft YaHei UI"
|
||||
Click="BtnResetToSuggestion_Click"
|
||||
Margin="0,0,0,0">
|
||||
<ui:SimpleStackPanel Orientation="Horizontal"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center" Spacing="0">
|
||||
<Image
|
||||
Margin="0,0,4,0" RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Height="20" Width="20">
|
||||
<Image.Source>
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="F1 M24,24z M0,0z M2,6C2,5.44772,2.44772,5,3,5L21,5C21.5523,5 22,5.44772 22,6 22,6.55228 21.5523,7 21,7L3,7C2.44772,7,2,6.55228,2,6z" />
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="F1 M24,24z M0,0z M2,12C2,11.4477,2.44772,11,3,11L7,11C7.55228,11 8,11.4477 8,12 8,12.5523 7.55228,13 7,13L3,13C2.44772,13,2,12.5523,2,12z" />
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="F1 M24,24z M0,0z M3,17C2.44772,17 2,17.4477 2,18 2,18.5523 2.44772,19 3,19L7,19C7.55228,19 8,18.5523 8,18 8,17.4477 7.55228,17 7,17L3,17z" />
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="F1 M24,24z M0,0z M12.3829,11.2029C13.4335,10.1522 14.8952,9.5 16.5,9.5 17.9587,9.5 19.3576,10.0795 20.3891,11.1109 21.4205,12.1424 22,13.5413 22,15 22,16.2593 21.6038,17.4867 20.8675,18.5083 20.1311,19.5299 19.092,20.2939 17.8974,20.6921 16.7027,21.0903 15.413,21.1026 14.211,20.7271 13.009,20.3516 11.9556,19.6074 11.2,18.6 10.8686,18.1582 10.9582,17.5314 11.4,17.2 11.8418,16.8686 12.4686,16.9582 12.8,17.4 13.3037,18.0716 14.006,18.5677 14.8073,18.8181 15.6087,19.0684 16.4685,19.0602 17.2649,18.7947 18.0614,18.5292 18.7541,18.0199 19.245,17.3388 19.7359,16.6578 20,15.8395 20,15 20,14.0717 19.6313,13.1815 18.9749,12.5251 18.3185,11.8687 17.4283,11.5 16.5,11.5 15.4448,11.5 14.4865,11.9278 13.7971,12.6171L13.4142,13 15,13C15.5523,13 16,13.4477 16,14 16,14.5523 15.5523,15 15,15L11.0007,15C10.9997,15 10.998,15 10.997,15 10.8625,14.9996 10.7343,14.9727 10.6172,14.9241 10.5001,14.8757 10.3904,14.804 10.295,14.7092 10.2936,14.7078 10.2922,14.7064 10.2908,14.705 10.196,14.6096 10.1243,14.4999 10.0759,14.3828 10.027,14.2649 10,14.1356 10,14L10,10C10,9.44772 10.4477,9 11,9 11.5523,9 12,9.44772 12,10L12,11.5858 12.3829,11.2029z" />
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
</Image>
|
||||
<Label Margin="2,0,0,0" FontSize="16" VerticalAlignment="Center"
|
||||
FontFamily="Microsoft YaHei UI">
|
||||
测试
|
||||
</Label>
|
||||
</ui:SimpleStackPanel>
|
||||
</Button>
|
||||
</ui:SimpleStackPanel>
|
||||
</ui:SimpleStackPanel>
|
||||
<GroupBox Name="GroupBoxPlugins">
|
||||
<GroupBox.Header>
|
||||
<TextBlock Margin="0,12,0,0" Text="插件管理" FontWeight="Bold" Foreground="#fafafa"
|
||||
FontSize="26" />
|
||||
</GroupBox.Header>
|
||||
<ui:SimpleStackPanel Spacing="6" Margin="0,10,0,0">
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,10" Foreground="#fafafa">
|
||||
通过插件扩展InkCanvas的功能。您可以启用或禁用插件,或加载自定义插件。
|
||||
</TextBlock>
|
||||
<Button x:Name="BtnOpenPluginManager" Content="打开插件管理器"
|
||||
HorizontalAlignment="Left" Click="BtnOpenPluginManager_Click"
|
||||
Padding="15,5" Margin="0,10,0,0"/>
|
||||
</ui:SimpleStackPanel>
|
||||
</Border>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox>
|
||||
<GroupBox.Header>
|
||||
<TextBlock Margin="0,12,0,0" Text="启动" FontWeight="Bold" Foreground="#fafafa"
|
||||
@@ -1023,6 +972,15 @@
|
||||
<ComboBoxItem Content="贴吧滑稽" FontFamily="Microsoft YaHei UI" />
|
||||
</ComboBox>
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,6,0,0">
|
||||
<TextBlock Foreground="#fafafa" Text="自定义浮动栏图标" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<Button Name="ButtonAddCustomIcon" Content="上传" FontFamily="Microsoft YaHei UI"
|
||||
Click="ButtonAddCustomIcon_Click" Padding="10,3"/>
|
||||
<Button Name="ButtonManageCustomIcons" Content="管理" FontFamily="Microsoft YaHei UI"
|
||||
Click="ButtonManageCustomIcons_Click" Padding="10,3" Margin="5,0,0,0"/>
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="浮动工具栏缩放" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
@@ -2326,6 +2284,17 @@
|
||||
FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchAutoSaveStrokesAtScreenshot_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="墨迹全页面保存" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent=""
|
||||
Name="ToggleSwitchSaveFullPageStrokes" IsOn="False"
|
||||
FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchSaveFullPageStrokes_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<TextBlock Text="# 开启后自动保存和手动保存墨迹时将以全屏模式保存而非单独保存每条墨迹" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
|
||||
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
|
||||
StrokeThickness="1" Margin="0,4,0,4" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
@@ -2399,6 +2368,23 @@
|
||||
<TextBlock Foreground="#fafafa" Text="天" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="8,0,0,0" />
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
|
||||
StrokeThickness="1" Margin="0,8,0,8" />
|
||||
|
||||
<TextBlock Margin="0,0,0,8" Text="收纳模式" FontWeight="Bold" Foreground="#fafafa"
|
||||
FontSize="20" />
|
||||
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="退出收纳模式时自动切换至批注模式"
|
||||
VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent=""
|
||||
Name="ToggleSwitchAutoEnterAnnotationModeWhenExitFoldMode"
|
||||
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchAutoEnterAnnotationModeWhenExitFoldMode_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
<TextBlock Text="# 开启后,退出收纳模式时将自动切换至批注模式,便于快速批注" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
</ui:SimpleStackPanel>
|
||||
</GroupBox>
|
||||
<GroupBox Name="GroupBoxRandWindow">
|
||||
@@ -2406,7 +2392,7 @@
|
||||
<TextBlock Margin="0,12,0,0" Text="随机点名" FontWeight="Bold" Foreground="#fafafa"
|
||||
FontSize="26" />
|
||||
</GroupBox.Header>
|
||||
<ui:SimpleStackPanel Spacing="6">
|
||||
<ui:SimpleStackPanel Spacing="12">
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="显示修改随机点名名单的按钮"
|
||||
VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
|
||||
@@ -2416,6 +2402,29 @@
|
||||
FontWeight="Bold"
|
||||
Toggled="ToggleSwitchDisplayRandWindowNamesInputBtn_OnToggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
<TextBlock Foreground="#fafafa" Text="点名窗口背景设置"
|
||||
FontSize="16" FontWeight="Bold" Margin="0,10,0,5" />
|
||||
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,5,0,0">
|
||||
<TextBlock Foreground="#fafafa" Text="背景选择:" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ComboBox Name="ComboBoxPickNameBackground" FontFamily="Microsoft YaHei UI"
|
||||
SelectedIndex="0" Width="180"
|
||||
SelectionChanged="ComboBoxPickNameBackground_SelectionChanged">
|
||||
<ComboBoxItem Content="默认背景" FontFamily="Microsoft YaHei UI" />
|
||||
<!-- 自定义背景会在代码中动态添加 -->
|
||||
</ComboBox>
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,5,0,0">
|
||||
<TextBlock Foreground="#fafafa" Text="自定义背景:" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<Button Name="ButtonAddCustomBackground" Content="上传" FontFamily="Microsoft YaHei UI"
|
||||
Click="ButtonAddCustomBackground_Click" Padding="10,3"/>
|
||||
<Button Name="ButtonManageBackgrounds" Content="管理" FontFamily="Microsoft YaHei UI"
|
||||
Click="ButtonManageBackgrounds_Click" Padding="10,3" Margin="5,0,0,0"/>
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="启用随机抽和单次抽按钮"
|
||||
VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
|
||||
@@ -6986,6 +6995,9 @@
|
||||
</ui:SimpleStackPanel>
|
||||
</ui:SimpleStackPanel>
|
||||
<Grid Width="0">
|
||||
<Grid x:Name="BackgroundPaletteGrid" Margin="-203,-128,83,37">
|
||||
<!-- 背景面板将在代码中动态添加 -->
|
||||
</Grid>
|
||||
<Border Visibility="Visible" ClipToBounds="True" Name="EraserSizePanel"
|
||||
Margin="-203,-128,83,37" CornerRadius="5" Background="#fafafa" Opacity="1"
|
||||
BorderBrush="#2563eb" BorderThickness="1">
|
||||
|
||||
+334
-40
@@ -22,6 +22,8 @@ using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Reflection;
|
||||
using Brushes = System.Windows.Media.Brushes;
|
||||
using Point = System.Windows.Point;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public partial class MainWindow : Window {
|
||||
@@ -177,6 +179,16 @@ namespace Ink_Canvas {
|
||||
loadPenCanvas();
|
||||
//加载设置
|
||||
LoadSettings(true);
|
||||
|
||||
// 加载自定义背景颜色
|
||||
LoadCustomBackgroundColor();
|
||||
|
||||
// 注册设置面板滚动事件
|
||||
if (SettingsPanelScrollViewer != null)
|
||||
{
|
||||
SettingsPanelScrollViewer.ScrollChanged += SettingsPanelScrollViewer_ScrollChanged;
|
||||
}
|
||||
|
||||
// HasNewUpdateWindow hasNewUpdateWindow = new HasNewUpdateWindow();
|
||||
if (Environment.Is64BitProcess) GroupBoxInkRecognition.Visibility = Visibility.Collapsed;
|
||||
|
||||
@@ -198,6 +210,9 @@ namespace Ink_Canvas {
|
||||
new SolidColorBrush(System.Windows.Media.Color.FromArgb(127, 24, 24, 27));
|
||||
BtnRightWhiteBoardSwitchPreviousLabel.Opacity = 0.5;
|
||||
|
||||
// 应用颜色主题,这将考虑自定义背景色
|
||||
CheckColorTheme(true);
|
||||
|
||||
BtnWhiteBoardSwitchPrevious.IsEnabled = CurrentWhiteboardIndex != 1;
|
||||
BorderInkReplayToolBox.Visibility = Visibility.Collapsed;
|
||||
|
||||
@@ -219,11 +234,41 @@ namespace Ink_Canvas {
|
||||
RadioCrashSilentRestart.IsChecked = true;
|
||||
else
|
||||
RadioCrashNoAction.IsChecked = true;
|
||||
|
||||
// 注册系统关机事件处理
|
||||
RegisterShutdownHandler();
|
||||
|
||||
// 设置默认为黑板模式
|
||||
Settings.Canvas.UsingWhiteboard = false;
|
||||
Settings.Canvas.CustomBackgroundColor = "#162924"; // 黑板默认颜色 RGB(22, 41, 36)
|
||||
SaveSettingsToFile();
|
||||
|
||||
// 如果当前不是黑板模式,则切换到黑板模式
|
||||
if (currentMode == 0)
|
||||
{
|
||||
// 延迟执行,确保UI已完全加载
|
||||
Dispatcher.BeginInvoke(new Action(() => {
|
||||
// 重新加载自定义背景颜色
|
||||
LoadCustomBackgroundColor();
|
||||
|
||||
// 模拟点击切换按钮进入黑板模式
|
||||
if (GridTransparencyFakeBackground.Background != Brushes.Transparent)
|
||||
{
|
||||
BtnSwitch_Click(BtnSwitch, null);
|
||||
}
|
||||
|
||||
// 确保背景颜色正确设置为黑板颜色
|
||||
CheckColorTheme(true);
|
||||
}), System.Windows.Threading.DispatcherPriority.Loaded);
|
||||
}
|
||||
|
||||
// 初始化插件系统
|
||||
InitializePluginSystem();
|
||||
}
|
||||
|
||||
private void SystemEventsOnDisplaySettingsChanged(object sender, EventArgs e) {
|
||||
if (!Settings.Advanced.IsEnableResolutionChangeDetection) return;
|
||||
ShowNotification($"检测到显示器信息变化,变为{System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width}x{System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height}");
|
||||
ShowNotification($"检测到显示器信息变化,变为{System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width}x{System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height})");
|
||||
new Thread(() => {
|
||||
var isFloatingBarOutsideScreen = false;
|
||||
var isInPPTPresentationMode = false;
|
||||
@@ -703,7 +748,7 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
// 辅助方法:显示指定的设置部分
|
||||
private void ShowSettingsSection(string sectionTag)
|
||||
private async void ShowSettingsSection(string sectionTag)
|
||||
{
|
||||
// 显示设置面板
|
||||
BorderSettings.Visibility = Visibility.Visible;
|
||||
@@ -715,81 +760,275 @@ namespace Ink_Canvas {
|
||||
var stackPanel = SettingsPanelScrollViewer.Content as StackPanel;
|
||||
if (stackPanel == null) return;
|
||||
|
||||
// 首先隐藏所有GroupBox
|
||||
// 确保所有GroupBox都是可见的
|
||||
foreach (var child in stackPanel.Children)
|
||||
{
|
||||
if (child is GroupBox groupBox)
|
||||
{
|
||||
groupBox.Visibility = Visibility.Collapsed;
|
||||
groupBox.Visibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
// 根据传入的sectionTag显示相应的设置部分
|
||||
// 确保UI完全更新
|
||||
await Dispatcher.InvokeAsync(() => {}, System.Windows.Threading.DispatcherPriority.Render);
|
||||
|
||||
// 根据传入的sectionTag滚动到相应的设置部分
|
||||
GroupBox targetGroupBox = null;
|
||||
|
||||
switch (sectionTag.ToLower())
|
||||
{
|
||||
case "startup":
|
||||
// 显示启动设置
|
||||
ShowGroupBoxByHeader(stackPanel, "启动");
|
||||
targetGroupBox = FindGroupBoxByHeader(stackPanel, "启动");
|
||||
break;
|
||||
case "canvas":
|
||||
// 显示画板和墨迹设置
|
||||
ShowGroupBoxByHeader(stackPanel, "画板和墨迹");
|
||||
targetGroupBox = FindGroupBoxByHeader(stackPanel, "画板和墨迹");
|
||||
break;
|
||||
case "gesture":
|
||||
// 显示手势设置
|
||||
ShowGroupBoxByHeader(stackPanel, "手势");
|
||||
targetGroupBox = FindGroupBoxByHeader(stackPanel, "手势");
|
||||
break;
|
||||
case "inkrecognition":
|
||||
// 显示墨迹纠正设置
|
||||
ShowGroupBoxByHeader(stackPanel, "墨迹纠正");
|
||||
if (GroupBoxInkRecognition != null)
|
||||
GroupBoxInkRecognition.Visibility = Visibility.Visible;
|
||||
targetGroupBox = GroupBoxInkRecognition;
|
||||
break;
|
||||
case "crashaction":
|
||||
// 显示崩溃后操作设置
|
||||
ShowGroupBoxByHeader(stackPanel, "崩溃后操作");
|
||||
targetGroupBox = FindGroupBoxByHeader(stackPanel, "崩溃后操作");
|
||||
break;
|
||||
case "ppt":
|
||||
// 显示PPT联动设置
|
||||
ShowGroupBoxByHeader(stackPanel, "PPT联动");
|
||||
targetGroupBox = FindGroupBoxByHeader(stackPanel, "PPT联动");
|
||||
break;
|
||||
case "advanced":
|
||||
// 显示高级设置
|
||||
// 这里可能需要根据实际情况调整
|
||||
targetGroupBox = FindGroupBoxByHeader(stackPanel, "高级设置");
|
||||
break;
|
||||
case "automation":
|
||||
// 显示自动化设置
|
||||
// 这里可能需要根据实际情况调整
|
||||
targetGroupBox = FindGroupBoxByHeader(stackPanel, "自动化");
|
||||
break;
|
||||
case "randomwindow":
|
||||
// 显示随机窗口设置
|
||||
if (GroupBoxRandWindow != null)
|
||||
GroupBoxRandWindow.Visibility = Visibility.Visible;
|
||||
targetGroupBox = GroupBoxRandWindow;
|
||||
break;
|
||||
case "theme":
|
||||
// 显示主题设置
|
||||
if (GroupBoxAppearanceNewUI != null)
|
||||
GroupBoxAppearanceNewUI.Visibility = Visibility.Visible;
|
||||
targetGroupBox = GroupBoxAppearanceNewUI;
|
||||
break;
|
||||
case "shortcuts":
|
||||
// 显示快捷键设置
|
||||
// 快捷键设置部分可能尚未实现
|
||||
targetGroupBox = FindGroupBoxByHeader(stackPanel, "快捷键");
|
||||
break;
|
||||
case "about":
|
||||
// 显示关于页面
|
||||
ShowGroupBoxByHeader(stackPanel, "关于");
|
||||
targetGroupBox = FindGroupBoxByHeader(stackPanel, "关于");
|
||||
break;
|
||||
case "plugins":
|
||||
targetGroupBox = GroupBoxPlugins;
|
||||
break;
|
||||
default:
|
||||
// 默认显示第一个GroupBox
|
||||
if (stackPanel.Children.Count > 0 && stackPanel.Children[0] is GroupBox firstGroupBox)
|
||||
{
|
||||
firstGroupBox.Visibility = Visibility.Visible;
|
||||
}
|
||||
break;
|
||||
// 默认滚动到顶部
|
||||
SettingsPanelScrollViewer.ScrollToTop();
|
||||
return;
|
||||
}
|
||||
|
||||
// 滚动到顶部
|
||||
SettingsPanelScrollViewer.ScrollToTop();
|
||||
// 如果找到目标GroupBox,则滚动到它的位置
|
||||
if (targetGroupBox != null)
|
||||
{
|
||||
// 使用动画平滑滚动到目标位置
|
||||
ScrollToElement(targetGroupBox);
|
||||
|
||||
// 高亮显示当前选中的导航项
|
||||
UpdateNavigationButtonState(sectionTag);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有找到目标GroupBox,则滚动到顶部
|
||||
SettingsPanelScrollViewer.ScrollToTop();
|
||||
}
|
||||
}
|
||||
|
||||
// 根据Header文本查找GroupBox
|
||||
private GroupBox FindGroupBoxByHeader(StackPanel parent, string headerText)
|
||||
{
|
||||
foreach (var child in parent.Children)
|
||||
{
|
||||
if (child is GroupBox groupBox)
|
||||
{
|
||||
// 查找GroupBox的Header
|
||||
if (groupBox.Header is TextBlock headerTextBlock &&
|
||||
headerTextBlock.Text != null &&
|
||||
headerTextBlock.Text.Contains(headerText))
|
||||
{
|
||||
return groupBox;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 平滑滚动到指定元素
|
||||
private async void ScrollToElement(FrameworkElement element)
|
||||
{
|
||||
if (element == null || SettingsPanelScrollViewer == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
// 暂时禁用滚动事件处理
|
||||
SettingsPanelScrollViewer.ScrollChanged -= SettingsPanelScrollViewer_ScrollChanged;
|
||||
|
||||
// 记录当前滚动位置
|
||||
double originalOffset = SettingsPanelScrollViewer.VerticalOffset;
|
||||
|
||||
// 将ScrollViewer内部的位置信息重置到顶部(不会触发视觉更新)
|
||||
SettingsPanelScrollViewer.ScrollToHome();
|
||||
|
||||
// 使用Dispatcher进行延迟处理,确保布局更新
|
||||
await Dispatcher.InvokeAsync(() => {
|
||||
try
|
||||
{
|
||||
// 强制更新布局
|
||||
SettingsPanelScrollViewer.UpdateLayout();
|
||||
|
||||
// 获取元素相对于顶部的准确位置
|
||||
Point elementPosition = element.TransformToAncestor(SettingsPanelScrollViewer).Transform(new Point(0, 0));
|
||||
|
||||
// 计算目标位置,减去一些偏移,使元素不会贴在顶部
|
||||
double targetPosition = elementPosition.Y - 20;
|
||||
|
||||
// 确保目标位置不小于0
|
||||
targetPosition = Math.Max(0, targetPosition);
|
||||
|
||||
// 直接设置滚动位置,不使用动画
|
||||
SettingsPanelScrollViewer.ScrollToVerticalOffset(targetPosition);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 如果出现异常,恢复到原来的滚动位置
|
||||
SettingsPanelScrollViewer.ScrollToVerticalOffset(originalOffset);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 重新启用滚动事件处理
|
||||
SettingsPanelScrollViewer.ScrollChanged += SettingsPanelScrollViewer_ScrollChanged;
|
||||
}
|
||||
}, System.Windows.Threading.DispatcherPriority.Render);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// 确保在异常情况下也重新启用滚动事件处理
|
||||
SettingsPanelScrollViewer.ScrollChanged += SettingsPanelScrollViewer_ScrollChanged;
|
||||
}
|
||||
}
|
||||
|
||||
// 滚动条变化事件处理
|
||||
private void SettingsPanelScrollViewer_ScrollChanged(object sender, System.Windows.Controls.ScrollChangedEventArgs e)
|
||||
{
|
||||
// 可以在这里添加滚动事件的处理逻辑,如果需要的话
|
||||
}
|
||||
|
||||
// 更新导航按钮状态
|
||||
private void UpdateNavigationButtonState(string activeTag)
|
||||
{
|
||||
// 清除所有导航按钮的Tag属性
|
||||
ClearAllNavButtonTags();
|
||||
|
||||
// 设置当前活动按钮的Tag属性
|
||||
switch (activeTag.ToLower())
|
||||
{
|
||||
case "startup":
|
||||
SetNavButtonTag("startup");
|
||||
break;
|
||||
case "canvas":
|
||||
SetNavButtonTag("canvas");
|
||||
break;
|
||||
case "gesture":
|
||||
SetNavButtonTag("gesture");
|
||||
break;
|
||||
case "inkrecognition":
|
||||
SetNavButtonTag("inkrecognition");
|
||||
break;
|
||||
case "crashaction":
|
||||
SetNavButtonTag("crashaction");
|
||||
break;
|
||||
case "ppt":
|
||||
SetNavButtonTag("ppt");
|
||||
break;
|
||||
case "advanced":
|
||||
SetNavButtonTag("advanced");
|
||||
break;
|
||||
case "automation":
|
||||
SetNavButtonTag("automation");
|
||||
break;
|
||||
case "randomwindow":
|
||||
SetNavButtonTag("randomwindow");
|
||||
break;
|
||||
case "theme":
|
||||
SetNavButtonTag("theme");
|
||||
break;
|
||||
case "shortcuts":
|
||||
SetNavButtonTag("shortcuts");
|
||||
break;
|
||||
case "about":
|
||||
SetNavButtonTag("about");
|
||||
break;
|
||||
case "plugins":
|
||||
SetNavButtonTag("plugins");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 清除所有导航按钮的Tag属性
|
||||
private void ClearAllNavButtonTags()
|
||||
{
|
||||
var grid = BorderSettings.Child as Grid;
|
||||
if (grid == null) return;
|
||||
|
||||
var navSidebar = grid.Children[0] as Border;
|
||||
if (navSidebar == null) return;
|
||||
|
||||
var navGrid = navSidebar.Child as Grid;
|
||||
if (navGrid == null) return;
|
||||
|
||||
var scrollViewer = navGrid.Children[1] as ScrollViewer;
|
||||
if (scrollViewer == null) return;
|
||||
|
||||
var stackPanel = scrollViewer.Content as StackPanel;
|
||||
if (stackPanel == null) return;
|
||||
|
||||
foreach (var child in stackPanel.Children)
|
||||
{
|
||||
if (child is Button button)
|
||||
{
|
||||
button.Tag = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置导航按钮的Tag属性
|
||||
private void SetNavButtonTag(string tag)
|
||||
{
|
||||
var grid = BorderSettings.Child as Grid;
|
||||
if (grid == null) return;
|
||||
|
||||
var navSidebar = grid.Children[0] as Border;
|
||||
if (navSidebar == null) return;
|
||||
|
||||
var navGrid = navSidebar.Child as Grid;
|
||||
if (navGrid == null) return;
|
||||
|
||||
var scrollViewer = navGrid.Children[1] as ScrollViewer;
|
||||
if (scrollViewer == null) return;
|
||||
|
||||
var stackPanel = scrollViewer.Content as StackPanel;
|
||||
if (stackPanel == null) return;
|
||||
|
||||
foreach (var child in stackPanel.Children)
|
||||
{
|
||||
if (child is Button button)
|
||||
{
|
||||
// 检查按钮的ToolTip属性,根据tag设置对应的按钮
|
||||
string buttonTag = button.Tag as string;
|
||||
|
||||
// 如果按钮的Tag与要设置的tag匹配,则设置Tag
|
||||
if (buttonTag != null && buttonTag.ToLower() == tag.ToLower())
|
||||
{
|
||||
button.Tag = tag;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 根据Header文本查找并显示GroupBox
|
||||
@@ -812,5 +1051,60 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
#endregion Navigation Sidebar Methods
|
||||
|
||||
// 添加插件系统初始化方法
|
||||
private void InitializePluginSystem()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 初始化插件管理器
|
||||
Helpers.Plugins.PluginManager.Instance.Initialize();
|
||||
LogHelper.WriteLogToFile("插件系统已初始化", LogHelper.LogType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"初始化插件系统时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加插件管理导航点击事件处理
|
||||
private void NavPlugins_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ShowSettingsSection("plugins");
|
||||
}
|
||||
|
||||
// 添加打开插件管理器按钮点击事件
|
||||
private void BtnOpenPluginManager_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 暂时隐藏设置面板
|
||||
BorderSettings.Visibility = Visibility.Hidden;
|
||||
BorderSettingsMask.Visibility = Visibility.Hidden;
|
||||
|
||||
// 创建并显示插件设置窗口
|
||||
Windows.PluginSettingsWindow pluginSettingsWindow = new Windows.PluginSettingsWindow();
|
||||
|
||||
// 设置窗口关闭事件,用于在插件管理窗口关闭后恢复设置面板
|
||||
pluginSettingsWindow.Closed += (s, args) =>
|
||||
{
|
||||
// 恢复设置面板显示
|
||||
BorderSettings.Visibility = Visibility.Visible;
|
||||
BorderSettingsMask.Visibility = Visibility.Visible;
|
||||
};
|
||||
|
||||
// 显示插件设置窗口
|
||||
pluginSettingsWindow.ShowDialog();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 确保在发生错误时也恢复设置面板显示
|
||||
BorderSettings.Visibility = Visibility.Visible;
|
||||
BorderSettingsMask.Visibility = Visibility.Visible;
|
||||
|
||||
LogHelper.WriteLogToFile($"打开插件管理器时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"打开插件管理器时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,6 +218,13 @@ namespace Ink_Canvas {
|
||||
await Task.Delay(0);
|
||||
|
||||
await Dispatcher.InvokeAsync(() => {
|
||||
// 根据设置决定是否自动切换至批注模式
|
||||
if (Settings.Automation.IsAutoEnterAnnotationModeWhenExitFoldMode && currentMode == 0)
|
||||
{
|
||||
// 切换至批注模式
|
||||
PenIcon_Click(null, null);
|
||||
}
|
||||
|
||||
if (StackPanelPPTControls.Visibility == Visibility.Visible)
|
||||
{
|
||||
var dops = Settings.PowerPointSettings.PPTButtonsDisplayOption.ToString();
|
||||
|
||||
@@ -1,30 +1,693 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Controls.Primitives;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public partial class MainWindow : Window {
|
||||
private void BoardChangeBackgroundColorBtn_MouseUp(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
|
||||
// 创建背景选项面板(如果不存在)
|
||||
if (BackgroundPalette == null)
|
||||
{
|
||||
CreateBackgroundPalette();
|
||||
}
|
||||
|
||||
// 显示或隐藏背景选项面板
|
||||
if (BackgroundPalette != null)
|
||||
{
|
||||
if (BackgroundPalette.Visibility == Visibility.Visible)
|
||||
{
|
||||
// 如果面板已经显示,则隐藏它
|
||||
AnimationsHelper.HideWithSlideAndFade(BackgroundPalette);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 隐藏其他可能显示的面板
|
||||
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(PenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
|
||||
|
||||
// 显示背景选项面板
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(BackgroundPalette);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 原有的背景切换代码
|
||||
Settings.Canvas.UsingWhiteboard = !Settings.Canvas.UsingWhiteboard;
|
||||
SaveSettingsToFile();
|
||||
if (Settings.Canvas.UsingWhiteboard) {
|
||||
if (inkColor == 5) lastBoardInkColor = 0;
|
||||
ICCWaterMarkDark.Visibility = Visibility.Visible;
|
||||
ICCWaterMarkWhite.Visibility = Visibility.Collapsed;
|
||||
|
||||
// 设置为白板默认背景色
|
||||
Color defaultWhiteboardColor = Color.FromRgb(234, 235, 237);
|
||||
|
||||
if (currentMode == 1) // 白板模式
|
||||
{
|
||||
// 设置背景为默认白板背景色
|
||||
GridBackgroundCover.Background = new SolidColorBrush(defaultWhiteboardColor);
|
||||
|
||||
// 更新RGB滑块的值为默认白板背景色
|
||||
if (BackgroundPalette != null && BackgroundPalette.Visibility == Visibility.Visible)
|
||||
{
|
||||
UpdateRGBSliders(defaultWhiteboardColor);
|
||||
}
|
||||
|
||||
// 更新自定义背景色为默认白板背景色
|
||||
CustomBackgroundColor = defaultWhiteboardColor;
|
||||
|
||||
// 保存到设置
|
||||
string colorHex = $"#{defaultWhiteboardColor.R:X2}{defaultWhiteboardColor.G:X2}{defaultWhiteboardColor.B:X2}";
|
||||
Settings.Canvas.CustomBackgroundColor = colorHex;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
// 设置墨迹颜色为黑色
|
||||
CheckLastColor(0);
|
||||
forceEraser = false;
|
||||
}
|
||||
else {
|
||||
if (inkColor == 0) lastBoardInkColor = 5;
|
||||
ICCWaterMarkWhite.Visibility = Visibility.Visible;
|
||||
ICCWaterMarkDark.Visibility = Visibility.Collapsed;
|
||||
|
||||
// 设置为黑板默认背景色
|
||||
Color defaultBlackboardColor = Color.FromRgb(22, 41, 36);
|
||||
|
||||
if (currentMode == 1) // 黑板模式
|
||||
{
|
||||
// 设置背景为默认黑板背景色
|
||||
GridBackgroundCover.Background = new SolidColorBrush(defaultBlackboardColor);
|
||||
|
||||
// 更新RGB滑块的值为默认黑板背景色
|
||||
if (BackgroundPalette != null && BackgroundPalette.Visibility == Visibility.Visible)
|
||||
{
|
||||
UpdateRGBSliders(defaultBlackboardColor);
|
||||
}
|
||||
|
||||
// 更新自定义背景色为默认黑板背景色
|
||||
CustomBackgroundColor = defaultBlackboardColor;
|
||||
|
||||
// 保存到设置
|
||||
string colorHex = $"#{defaultBlackboardColor.R:X2}{defaultBlackboardColor.G:X2}{defaultBlackboardColor.B:X2}";
|
||||
Settings.Canvas.CustomBackgroundColor = colorHex;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
// 设置墨迹颜色为白色
|
||||
CheckLastColor(5);
|
||||
forceEraser = false;
|
||||
}
|
||||
|
||||
CheckColorTheme(true);
|
||||
}
|
||||
|
||||
// 创建背景选项面板
|
||||
private void CreateBackgroundPalette()
|
||||
{
|
||||
// 确保加载自定义背景色
|
||||
LoadCustomBackgroundColor();
|
||||
|
||||
// 创建一个类似于PenPalette的面板
|
||||
BackgroundPalette = new Border
|
||||
{
|
||||
Name = "BackgroundPalette",
|
||||
Visibility = Visibility.Collapsed,
|
||||
Background = new SolidColorBrush(Colors.White),
|
||||
Opacity = 1,
|
||||
BorderBrush = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)),
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(8),
|
||||
Width = 300,
|
||||
MaxHeight = 400
|
||||
};
|
||||
|
||||
// 确保面板显示在顶层
|
||||
System.Windows.Controls.Panel.SetZIndex(BackgroundPalette, 1000);
|
||||
|
||||
// 创建面板内容
|
||||
var stackPanel = new StackPanel();
|
||||
|
||||
// 创建标题栏
|
||||
var titleBorder = new Border
|
||||
{
|
||||
BorderBrush = new SolidColorBrush(Color.FromRgb(0x1e, 0x3a, 0x8a)),
|
||||
Height = 32,
|
||||
BorderThickness = new Thickness(0, 0, 0, 1),
|
||||
CornerRadius = new CornerRadius(8, 8, 0, 0),
|
||||
Background = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)),
|
||||
Margin = new Thickness(-1, -1, -1, 0),
|
||||
Padding = new Thickness(1, 1, 1, 0)
|
||||
};
|
||||
|
||||
var titleCanvas = new System.Windows.Controls.Canvas { Height = 24, ClipToBounds = true };
|
||||
var titleText = new TextBlock
|
||||
{
|
||||
Text = "背景设置",
|
||||
Foreground = new SolidColorBrush(Colors.White),
|
||||
Padding = new Thickness(0, 5, 0, 0),
|
||||
FontSize = 11,
|
||||
FontWeight = FontWeights.Bold,
|
||||
TextAlignment = TextAlignment.Center
|
||||
};
|
||||
System.Windows.Controls.Canvas.SetLeft(titleText, 8);
|
||||
titleCanvas.Children.Add(titleText);
|
||||
|
||||
// 关闭按钮
|
||||
var closeImage = new System.Windows.Controls.Image
|
||||
{
|
||||
Source = new System.Windows.Media.Imaging.BitmapImage(new Uri("/Resources/new-icons/close-white.png", UriKind.Relative)),
|
||||
Height = 16,
|
||||
Width = 16
|
||||
};
|
||||
RenderOptions.SetBitmapScalingMode(closeImage, BitmapScalingMode.HighQuality);
|
||||
closeImage.MouseUp += CloseBordertools_MouseUp;
|
||||
System.Windows.Controls.Canvas.SetRight(closeImage, 8);
|
||||
System.Windows.Controls.Canvas.SetTop(closeImage, 4);
|
||||
titleCanvas.Children.Add(closeImage);
|
||||
|
||||
titleBorder.Child = titleCanvas;
|
||||
stackPanel.Children.Add(titleBorder);
|
||||
|
||||
// 创建背景选项内容区域
|
||||
var contentPanel = new StackPanel { Margin = new Thickness(8) };
|
||||
|
||||
// 黑板/白板选择
|
||||
var modeTitle = new TextBlock
|
||||
{
|
||||
Text = "白板模式",
|
||||
Foreground = new SolidColorBrush(Color.FromRgb(0x17, 0x25, 0x54)),
|
||||
FontSize = 10,
|
||||
FontWeight = FontWeights.Bold,
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
Margin = new Thickness(0, 4, 0, 8)
|
||||
};
|
||||
contentPanel.Children.Add(modeTitle);
|
||||
|
||||
var modePanel = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Center };
|
||||
|
||||
// 白板按钮
|
||||
var whiteboardButton = new Border
|
||||
{
|
||||
Width = 60,
|
||||
Height = 30,
|
||||
Background = Settings.Canvas.UsingWhiteboard ? new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)) : new SolidColorBrush(Colors.LightGray),
|
||||
CornerRadius = new CornerRadius(4),
|
||||
Margin = new Thickness(0, 0, 8, 0)
|
||||
};
|
||||
var whiteboardText = new TextBlock
|
||||
{
|
||||
Text = "白板",
|
||||
Foreground = Settings.Canvas.UsingWhiteboard ? new SolidColorBrush(Colors.White) : new SolidColorBrush(Colors.Black),
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
whiteboardButton.Child = whiteboardText;
|
||||
whiteboardButton.MouseUp += (s, args) => {
|
||||
Settings.Canvas.UsingWhiteboard = true;
|
||||
SaveSettingsToFile();
|
||||
ICCWaterMarkDark.Visibility = Visibility.Visible;
|
||||
ICCWaterMarkWhite.Visibility = Visibility.Collapsed;
|
||||
|
||||
// 设置为白板默认背景色
|
||||
Color defaultWhiteboardColor = Color.FromRgb(234, 235, 237);
|
||||
|
||||
if (currentMode == 1) // 白板模式
|
||||
{
|
||||
// 设置背景为默认白板背景色
|
||||
GridBackgroundCover.Background = new SolidColorBrush(defaultWhiteboardColor);
|
||||
|
||||
// 更新RGB滑块的值为默认白板背景色
|
||||
UpdateRGBSliders(defaultWhiteboardColor);
|
||||
|
||||
// 更新自定义背景色为默认白板背景色
|
||||
CustomBackgroundColor = defaultWhiteboardColor;
|
||||
|
||||
// 保存到设置
|
||||
string colorHex = $"#{defaultWhiteboardColor.R:X2}{defaultWhiteboardColor.G:X2}{defaultWhiteboardColor.B:X2}";
|
||||
Settings.Canvas.CustomBackgroundColor = colorHex;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
// 设置墨迹颜色为黑色
|
||||
CheckLastColor(0);
|
||||
forceEraser = false;
|
||||
|
||||
CheckColorTheme(true);
|
||||
UpdateBackgroundButtonsState();
|
||||
};
|
||||
modePanel.Children.Add(whiteboardButton);
|
||||
|
||||
// 黑板按钮
|
||||
var blackboardButton = new Border
|
||||
{
|
||||
Width = 60,
|
||||
Height = 30,
|
||||
Background = !Settings.Canvas.UsingWhiteboard ? new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)) : new SolidColorBrush(Colors.LightGray),
|
||||
CornerRadius = new CornerRadius(4)
|
||||
};
|
||||
var blackboardText = new TextBlock
|
||||
{
|
||||
Text = "黑板",
|
||||
Foreground = !Settings.Canvas.UsingWhiteboard ? new SolidColorBrush(Colors.White) : new SolidColorBrush(Colors.Black),
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
blackboardButton.Child = blackboardText;
|
||||
blackboardButton.MouseUp += (s, args) => {
|
||||
Settings.Canvas.UsingWhiteboard = false;
|
||||
SaveSettingsToFile();
|
||||
ICCWaterMarkWhite.Visibility = Visibility.Visible;
|
||||
ICCWaterMarkDark.Visibility = Visibility.Collapsed;
|
||||
|
||||
// 设置为黑板默认背景色
|
||||
Color defaultBlackboardColor = Color.FromRgb(22, 41, 36);
|
||||
|
||||
if (currentMode == 1) // 黑板模式
|
||||
{
|
||||
// 设置背景为默认黑板背景色
|
||||
GridBackgroundCover.Background = new SolidColorBrush(defaultBlackboardColor);
|
||||
|
||||
// 更新RGB滑块的值为默认黑板背景色
|
||||
UpdateRGBSliders(defaultBlackboardColor);
|
||||
|
||||
// 更新自定义背景色为默认黑板背景色
|
||||
CustomBackgroundColor = defaultBlackboardColor;
|
||||
|
||||
// 保存到设置
|
||||
string colorHex = $"#{defaultBlackboardColor.R:X2}{defaultBlackboardColor.G:X2}{defaultBlackboardColor.B:X2}";
|
||||
Settings.Canvas.CustomBackgroundColor = colorHex;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
// 设置墨迹颜色为白色
|
||||
CheckLastColor(5);
|
||||
forceEraser = false;
|
||||
|
||||
CheckColorTheme(true);
|
||||
UpdateBackgroundButtonsState();
|
||||
};
|
||||
modePanel.Children.Add(blackboardButton);
|
||||
|
||||
contentPanel.Children.Add(modePanel);
|
||||
|
||||
// 添加一条分隔线
|
||||
var separator = new Border
|
||||
{
|
||||
Height = 1,
|
||||
Background = new SolidColorBrush(Color.FromRgb(0xd4, 0xd4, 0xd8)),
|
||||
Margin = new Thickness(0, 12, 0, 12)
|
||||
};
|
||||
contentPanel.Children.Add(separator);
|
||||
|
||||
// 添加RGB颜色选择器部分
|
||||
var colorTitle = new TextBlock
|
||||
{
|
||||
Text = "背景颜色",
|
||||
Foreground = new SolidColorBrush(Color.FromRgb(0x17, 0x25, 0x54)),
|
||||
FontSize = 10,
|
||||
FontWeight = FontWeights.Bold,
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
Margin = new Thickness(0, 4, 0, 8)
|
||||
};
|
||||
contentPanel.Children.Add(colorTitle);
|
||||
|
||||
// 创建颜色预览
|
||||
Border colorPreview = new Border
|
||||
{
|
||||
Width = 100,
|
||||
Height = 40,
|
||||
BorderThickness = new Thickness(1),
|
||||
BorderBrush = new SolidColorBrush(Color.FromRgb(0xd4, 0xd4, 0xd8)),
|
||||
Background = new SolidColorBrush(Colors.White),
|
||||
CornerRadius = new CornerRadius(4),
|
||||
Margin = new Thickness(0, 0, 0, 10),
|
||||
HorizontalAlignment = HorizontalAlignment.Center
|
||||
};
|
||||
contentPanel.Children.Add(colorPreview);
|
||||
|
||||
// 获取当前背景颜色
|
||||
Color currentBackgroundColor;
|
||||
if (currentMode == 1) // 白板或黑板模式
|
||||
{
|
||||
if (GridBackgroundCover.Background is SolidColorBrush brush)
|
||||
{
|
||||
currentBackgroundColor = brush.Color;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 默认颜色
|
||||
currentBackgroundColor = Settings.Canvas.UsingWhiteboard ?
|
||||
Color.FromRgb(234, 235, 237) : // 白板默认颜色
|
||||
Color.FromRgb(22, 41, 36); // 黑板默认颜色
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 默认白色
|
||||
currentBackgroundColor = Colors.White;
|
||||
}
|
||||
|
||||
// 更新颜色预览
|
||||
colorPreview.Background = new SolidColorBrush(currentBackgroundColor);
|
||||
|
||||
// 先创建所有滑块控件
|
||||
// R滑块和文本框
|
||||
var rPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(10, 0, 10, 5) };
|
||||
var rLabel = new TextBlock { Text = "R:", Width = 20, VerticalAlignment = VerticalAlignment.Center };
|
||||
var rSlider = new System.Windows.Controls.Slider
|
||||
{
|
||||
Minimum = 0,
|
||||
Maximum = 255,
|
||||
Value = currentBackgroundColor.R,
|
||||
Width = 150,
|
||||
Margin = new Thickness(5, 0, 5, 0),
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
var rValueText = new TextBlock
|
||||
{
|
||||
Text = currentBackgroundColor.R.ToString(),
|
||||
Width = 30,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
TextAlignment = TextAlignment.Right
|
||||
};
|
||||
|
||||
// G滑块和文本框
|
||||
var gPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(10, 0, 10, 5) };
|
||||
var gLabel = new TextBlock { Text = "G:", Width = 20, VerticalAlignment = VerticalAlignment.Center };
|
||||
var gSlider = new System.Windows.Controls.Slider
|
||||
{
|
||||
Minimum = 0,
|
||||
Maximum = 255,
|
||||
Value = currentBackgroundColor.G,
|
||||
Width = 150,
|
||||
Margin = new Thickness(5, 0, 5, 0),
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
var gValueText = new TextBlock
|
||||
{
|
||||
Text = currentBackgroundColor.G.ToString(),
|
||||
Width = 30,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
TextAlignment = TextAlignment.Right
|
||||
};
|
||||
|
||||
// B滑块和文本框
|
||||
var bPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(10, 0, 10, 5) };
|
||||
var bLabel = new TextBlock { Text = "B:", Width = 20, VerticalAlignment = VerticalAlignment.Center };
|
||||
var bSlider = new System.Windows.Controls.Slider
|
||||
{
|
||||
Minimum = 0,
|
||||
Maximum = 255,
|
||||
Value = currentBackgroundColor.B,
|
||||
Width = 150,
|
||||
Margin = new Thickness(5, 0, 5, 0),
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
var bValueText = new TextBlock
|
||||
{
|
||||
Text = currentBackgroundColor.B.ToString(),
|
||||
Width = 30,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
TextAlignment = TextAlignment.Right
|
||||
};
|
||||
|
||||
// 现在添加事件处理程序
|
||||
rSlider.ValueChanged += (s, e) => {
|
||||
int value = (int)e.NewValue;
|
||||
rValueText.Text = value.ToString();
|
||||
UpdateColorPreview(colorPreview, rSlider, gSlider, bSlider);
|
||||
};
|
||||
|
||||
gSlider.ValueChanged += (s, e) => {
|
||||
int value = (int)e.NewValue;
|
||||
gValueText.Text = value.ToString();
|
||||
UpdateColorPreview(colorPreview, rSlider, gSlider, bSlider);
|
||||
};
|
||||
|
||||
bSlider.ValueChanged += (s, e) => {
|
||||
int value = (int)e.NewValue;
|
||||
bValueText.Text = value.ToString();
|
||||
UpdateColorPreview(colorPreview, rSlider, gSlider, bSlider);
|
||||
};
|
||||
|
||||
// 添加控件到面板
|
||||
rPanel.Children.Add(rLabel);
|
||||
rPanel.Children.Add(rSlider);
|
||||
rPanel.Children.Add(rValueText);
|
||||
contentPanel.Children.Add(rPanel);
|
||||
|
||||
gPanel.Children.Add(gLabel);
|
||||
gPanel.Children.Add(gSlider);
|
||||
gPanel.Children.Add(gValueText);
|
||||
contentPanel.Children.Add(gPanel);
|
||||
|
||||
bPanel.Children.Add(bLabel);
|
||||
bPanel.Children.Add(bSlider);
|
||||
bPanel.Children.Add(bValueText);
|
||||
contentPanel.Children.Add(bPanel);
|
||||
|
||||
// 应用按钮
|
||||
var applyButton = new Button
|
||||
{
|
||||
Content = "应用颜色",
|
||||
Margin = new Thickness(0, 10, 0, 0),
|
||||
Padding = new Thickness(10, 5, 10, 5),
|
||||
Background = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)),
|
||||
Foreground = new SolidColorBrush(Colors.White),
|
||||
BorderThickness = new Thickness(0),
|
||||
HorizontalAlignment = HorizontalAlignment.Center
|
||||
};
|
||||
|
||||
applyButton.Click += (s, e) => {
|
||||
Color selectedColor = Color.FromRgb(
|
||||
(byte)rSlider.Value,
|
||||
(byte)gSlider.Value,
|
||||
(byte)bSlider.Value
|
||||
);
|
||||
ApplyCustomBackgroundColor(selectedColor);
|
||||
};
|
||||
|
||||
contentPanel.Children.Add(applyButton);
|
||||
|
||||
stackPanel.Children.Add(contentPanel);
|
||||
|
||||
// 将面板添加到父容器
|
||||
BackgroundPalette.Child = stackPanel;
|
||||
|
||||
// 获取主窗口中的根网格,确保面板添加到顶层
|
||||
Grid mainGrid = FindName("Main_Grid") as Grid;
|
||||
if (mainGrid != null)
|
||||
{
|
||||
// 删除可能已存在的BackgroundPalette
|
||||
foreach (UIElement element in mainGrid.Children)
|
||||
{
|
||||
if (element is Border border && border.Name == "BackgroundPalette")
|
||||
{
|
||||
mainGrid.Children.Remove(border);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 重新定位面板
|
||||
BackgroundPalette.HorizontalAlignment = HorizontalAlignment.Center;
|
||||
BackgroundPalette.VerticalAlignment = VerticalAlignment.Center;
|
||||
BackgroundPalette.Margin = new Thickness(0, 0, 0, 0);
|
||||
|
||||
// 添加到主网格
|
||||
mainGrid.Children.Add(BackgroundPalette);
|
||||
|
||||
// 设置面板位置
|
||||
var clickElement = FindName("BoardChangeBackgroundColorBtn") as FrameworkElement;
|
||||
if (clickElement != null)
|
||||
{
|
||||
Point position = clickElement.TranslatePoint(new Point(0, 0), mainGrid);
|
||||
BackgroundPalette.Margin = new Thickness(
|
||||
position.X - 150,
|
||||
position.Y + clickElement.ActualHeight + 5,
|
||||
0, 0);
|
||||
BackgroundPalette.HorizontalAlignment = HorizontalAlignment.Left;
|
||||
BackgroundPalette.VerticalAlignment = VerticalAlignment.Top;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新背景按钮状态
|
||||
private void UpdateBackgroundButtonsState()
|
||||
{
|
||||
if (BackgroundPalette != null && BackgroundPalette.Child is StackPanel stackPanel)
|
||||
{
|
||||
if (stackPanel.Children.Count > 1 && stackPanel.Children[1] is StackPanel contentPanel)
|
||||
{
|
||||
if (contentPanel.Children.Count > 1 && contentPanel.Children[1] is StackPanel modePanel)
|
||||
{
|
||||
if (modePanel.Children.Count > 1)
|
||||
{
|
||||
var whiteboardButton = modePanel.Children[0] as Border;
|
||||
var blackboardButton = modePanel.Children[1] as Border;
|
||||
|
||||
if (whiteboardButton != null && whiteboardButton.Child is TextBlock whiteboardText)
|
||||
{
|
||||
whiteboardButton.Background = Settings.Canvas.UsingWhiteboard ?
|
||||
new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)) :
|
||||
new SolidColorBrush(Colors.LightGray);
|
||||
whiteboardText.Foreground = Settings.Canvas.UsingWhiteboard ?
|
||||
new SolidColorBrush(Colors.White) :
|
||||
new SolidColorBrush(Colors.Black);
|
||||
}
|
||||
|
||||
if (blackboardButton != null && blackboardButton.Child is TextBlock blackboardText)
|
||||
{
|
||||
blackboardButton.Background = !Settings.Canvas.UsingWhiteboard ?
|
||||
new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)) :
|
||||
new SolidColorBrush(Colors.LightGray);
|
||||
blackboardText.Foreground = !Settings.Canvas.UsingWhiteboard ?
|
||||
new SolidColorBrush(Colors.White) :
|
||||
new SolidColorBrush(Colors.Black);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加成员变量保存背景面板引用
|
||||
private Border BackgroundPalette { get; set; }
|
||||
|
||||
// 添加成员变量保存当前自定义背景色
|
||||
private Color? CustomBackgroundColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新颜色预览框的颜色
|
||||
/// </summary>
|
||||
private void UpdateColorPreview(Border colorPreview, System.Windows.Controls.Slider rSlider, System.Windows.Controls.Slider gSlider, System.Windows.Controls.Slider bSlider)
|
||||
{
|
||||
Color previewColor = Color.FromRgb(
|
||||
(byte)rSlider.Value,
|
||||
(byte)gSlider.Value,
|
||||
(byte)bSlider.Value
|
||||
);
|
||||
colorPreview.Background = new SolidColorBrush(previewColor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用自定义背景颜色
|
||||
/// </summary>
|
||||
private void ApplyCustomBackgroundColor(Color color)
|
||||
{
|
||||
// 保存当前选择的颜色
|
||||
CustomBackgroundColor = color;
|
||||
|
||||
// 将颜色转换为十六进制字符串并保存到设置中
|
||||
string colorHex = $"#{color.R:X2}{color.G:X2}{color.B:X2}";
|
||||
Settings.Canvas.CustomBackgroundColor = colorHex;
|
||||
|
||||
// 只在白板或黑板模式下应用自定义背景色
|
||||
if (currentMode == 1) // 白板或黑板模式
|
||||
{
|
||||
// 设置白板/黑板模式下的背景
|
||||
GridBackgroundCover.Background = new SolidColorBrush(color);
|
||||
}
|
||||
|
||||
// 保存设置
|
||||
SaveSettingsToFile();
|
||||
|
||||
// 立即更新界面
|
||||
if (BackgroundPalette != null)
|
||||
{
|
||||
UpdateBackgroundButtonsState();
|
||||
UpdateRGBSliders(color); // 更新RGB滑块的值
|
||||
}
|
||||
|
||||
// 显示提示信息
|
||||
ShowNotification($"已应用自定义背景色: {colorHex}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从设置中加载自定义背景色
|
||||
/// </summary>
|
||||
private void LoadCustomBackgroundColor()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Settings.Canvas.CustomBackgroundColor))
|
||||
{
|
||||
try
|
||||
{
|
||||
// 解析颜色字符串
|
||||
string colorHex = Settings.Canvas.CustomBackgroundColor;
|
||||
if (colorHex.StartsWith("#") && colorHex.Length == 7) // #RRGGBB 格式
|
||||
{
|
||||
byte r = Convert.ToByte(colorHex.Substring(1, 2), 16);
|
||||
byte g = Convert.ToByte(colorHex.Substring(3, 2), 16);
|
||||
byte b = Convert.ToByte(colorHex.Substring(5, 2), 16);
|
||||
|
||||
// 保存到内存中
|
||||
CustomBackgroundColor = Color.FromRgb(r, g, b);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 解析失败,根据当前模式设置默认颜色
|
||||
if (!Settings.Canvas.UsingWhiteboard)
|
||||
{
|
||||
// 黑板模式默认颜色
|
||||
CustomBackgroundColor = Color.FromRgb(22, 41, 36);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 白板模式默认颜色
|
||||
CustomBackgroundColor = Color.FromRgb(234, 235, 237);
|
||||
}
|
||||
|
||||
// 可以在这里记录日志
|
||||
Console.WriteLine($"解析自定义背景色失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有设置自定义背景色,根据当前模式设置默认颜色
|
||||
if (!Settings.Canvas.UsingWhiteboard)
|
||||
{
|
||||
// 黑板模式默认颜色
|
||||
CustomBackgroundColor = Color.FromRgb(22, 41, 36);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 白板模式默认颜色
|
||||
CustomBackgroundColor = Color.FromRgb(234, 235, 237);
|
||||
}
|
||||
}
|
||||
|
||||
// 只在白板或黑板模式下应用自定义背景色
|
||||
if (currentMode == 1 && CustomBackgroundColor.HasValue) // 白板或黑板模式
|
||||
{
|
||||
// 设置白板/黑板模式下的背景
|
||||
GridBackgroundCover.Background = new SolidColorBrush(CustomBackgroundColor.Value);
|
||||
|
||||
// 更新RGB滑块的值(如果调色板已经创建)
|
||||
if (BackgroundPalette != null && BackgroundPalette.Visibility == Visibility.Visible)
|
||||
{
|
||||
UpdateRGBSliders(CustomBackgroundColor.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BoardEraserIcon_Click(object sender, RoutedEventArgs e) {
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint ||
|
||||
inkCanvas.EditingMode == InkCanvasEditingMode.EraseByStroke) {
|
||||
@@ -90,5 +753,59 @@ namespace Ink_Canvas {
|
||||
ImageBlackboard_MouseUp(null, null);
|
||||
Process.Start("https://www.desmos.com/calculator?lang=zh-CN");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据当前背景颜色更新RGB滑块的值
|
||||
/// </summary>
|
||||
private void UpdateRGBSliders(Color color)
|
||||
{
|
||||
if (BackgroundPalette != null && BackgroundPalette.Child is StackPanel stackPanel)
|
||||
{
|
||||
if (stackPanel.Children.Count > 1 && stackPanel.Children[1] is StackPanel contentPanel)
|
||||
{
|
||||
// 查找RGB滑块
|
||||
System.Windows.Controls.Slider rSlider = null;
|
||||
System.Windows.Controls.Slider gSlider = null;
|
||||
System.Windows.Controls.Slider bSlider = null;
|
||||
|
||||
// 遍历面板查找RGB滑块
|
||||
foreach (var child in contentPanel.Children)
|
||||
{
|
||||
if (child is StackPanel panel && panel.Orientation == Orientation.Horizontal)
|
||||
{
|
||||
foreach (var panelChild in panel.Children)
|
||||
{
|
||||
if (panelChild is System.Windows.Controls.Slider slider)
|
||||
{
|
||||
if (panel.Children.Count > 0 && panel.Children[0] is TextBlock label)
|
||||
{
|
||||
if (label.Text == "R:")
|
||||
{
|
||||
rSlider = slider;
|
||||
}
|
||||
else if (label.Text == "G:")
|
||||
{
|
||||
gSlider = slider;
|
||||
}
|
||||
else if (label.Text == "B:")
|
||||
{
|
||||
bSlider = slider;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新滑块值
|
||||
if (rSlider != null && gSlider != null && bSlider != null)
|
||||
{
|
||||
rSlider.Value = color.R;
|
||||
gSlider.Value = color.G;
|
||||
bSlider.Value = color.B;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,14 +70,24 @@ namespace Ink_Canvas {
|
||||
if (changeColorTheme)
|
||||
if (currentMode != 0) {
|
||||
if (Settings.Canvas.UsingWhiteboard) {
|
||||
GridBackgroundCover.Background = new SolidColorBrush(Color.FromRgb(234, 235, 237));
|
||||
// 检查是否有自定义背景色,如果有则使用自定义背景色
|
||||
if (CustomBackgroundColor.HasValue) {
|
||||
GridBackgroundCover.Background = new SolidColorBrush(CustomBackgroundColor.Value);
|
||||
} else {
|
||||
GridBackgroundCover.Background = new SolidColorBrush(Color.FromRgb(234, 235, 237));
|
||||
}
|
||||
WaterMarkTime.Foreground = new SolidColorBrush(Color.FromRgb(22, 41, 36));
|
||||
WaterMarkDate.Foreground = new SolidColorBrush(Color.FromRgb(22, 41, 36));
|
||||
BlackBoardWaterMark.Foreground = new SolidColorBrush(Color.FromRgb(22, 41, 36));
|
||||
isUselightThemeColor = false;
|
||||
}
|
||||
else {
|
||||
GridBackgroundCover.Background = new SolidColorBrush(Color.FromRgb(22, 41, 36));
|
||||
// 黑板模式下,检查是否有自定义背景色
|
||||
if (CustomBackgroundColor.HasValue) {
|
||||
GridBackgroundCover.Background = new SolidColorBrush(CustomBackgroundColor.Value);
|
||||
} else {
|
||||
GridBackgroundCover.Background = new SolidColorBrush(Color.FromRgb(22, 41, 36));
|
||||
}
|
||||
WaterMarkTime.Foreground = new SolidColorBrush(Color.FromRgb(234, 235, 237));
|
||||
WaterMarkDate.Foreground = new SolidColorBrush(Color.FromRgb(234, 235, 237));
|
||||
BlackBoardWaterMark.Foreground = new SolidColorBrush(Color.FromRgb(234, 235, 237));
|
||||
|
||||
@@ -281,6 +281,13 @@ namespace Ink_Canvas {
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderLeftPageListView);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderRightPageListView);
|
||||
|
||||
// 隐藏背景设置面板
|
||||
var bgPalette = LogicalTreeHelper.FindLogicalNode(this, "BackgroundPalette") as Border;
|
||||
if (bgPalette != null)
|
||||
{
|
||||
AnimationsHelper.HideWithSlideAndFade(bgPalette);
|
||||
}
|
||||
|
||||
if (BorderSettings.Visibility == Visibility.Visible) {
|
||||
// 设置蒙版为不可点击,并移除背景
|
||||
@@ -1850,11 +1857,22 @@ namespace Ink_Canvas {
|
||||
|
||||
if (Settings.Canvas.UsingWhiteboard)
|
||||
{
|
||||
BtnColorBlack_Click(null, null);
|
||||
// 如果有自定义背景色并且是白板模式,应用自定义背景色
|
||||
if (CustomBackgroundColor.HasValue)
|
||||
{
|
||||
GridBackgroundCover.Background = new SolidColorBrush(CustomBackgroundColor.Value);
|
||||
}
|
||||
// 白板模式下设置墨迹颜色为黑色
|
||||
CheckLastColor(0);
|
||||
forceEraser = false;
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
else
|
||||
{
|
||||
BtnColorWhite_Click(null, null);
|
||||
// 黑板模式下设置墨迹颜色为白色
|
||||
CheckLastColor(5);
|
||||
forceEraser = false;
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
|
||||
StackPanelPPTButtons.Visibility = Visibility.Collapsed;
|
||||
|
||||
@@ -1226,5 +1226,90 @@ namespace Ink_Canvas {
|
||||
private void ImagePPTControlEnd_MouseUp(object sender, MouseButtonEventArgs e) {
|
||||
BtnPPTSlideShowEnd_Click(BtnPPTSlideShowEnd, null);
|
||||
}
|
||||
|
||||
// 添加关机事件注册方法
|
||||
private void RegisterShutdownHandler()
|
||||
{
|
||||
try
|
||||
{
|
||||
SystemEvents.SessionEnding += SystemEvents_SessionEnding;
|
||||
LogHelper.WriteLogToFile("已注册系统关机事件处理", LogHelper.LogType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"注册系统关机事件处理失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
// 系统关机事件处理
|
||||
private void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
|
||||
{
|
||||
LogHelper.WriteLogToFile("检测到系统关机事件,正在清理PowerPoint进程", LogHelper.LogType.Info);
|
||||
|
||||
// 终止PowerPoint进程守护
|
||||
try
|
||||
{
|
||||
// 停止计时器以终止进程守护
|
||||
timerCheckPPT.Stop();
|
||||
|
||||
// 清理COM对象
|
||||
ResetPresentationObjects();
|
||||
|
||||
// 强制结束所有PowerPoint进程
|
||||
foreach (var process in Process.GetProcessesByName("POWERPNT"))
|
||||
{
|
||||
try
|
||||
{
|
||||
process.Kill();
|
||||
LogHelper.WriteLogToFile($"已终止PowerPoint进程: {process.Id}", LogHelper.LogType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"终止PowerPoint进程失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
// 强制结束所有WPS进程
|
||||
foreach (var processName in GetPossibleWPSProcessNames())
|
||||
{
|
||||
foreach (var process in Process.GetProcessesByName(processName))
|
||||
{
|
||||
try
|
||||
{
|
||||
process.Kill();
|
||||
LogHelper.WriteLogToFile($"已终止WPS进程: {process.ProcessName}({process.Id})", LogHelper.LogType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"终止WPS进程失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 强制GC回收
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"系统关机清理过程中出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
// 在主窗口初始化方法中添加以下调用
|
||||
// 在适当的初始化方法中调用 RegisterShutdownHandler();
|
||||
|
||||
// 在主窗口关闭时取消注册关机事件
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 取消注册系统关机事件
|
||||
SystemEvents.SessionEnding -= SystemEvents_SessionEnding;
|
||||
}
|
||||
catch { }
|
||||
|
||||
base.OnClosed(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@ using System.Windows;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using File = System.IO.File;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Forms;
|
||||
using OpenFileDialog = Microsoft.Win32.OpenFileDialog;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public partial class MainWindow : Window {
|
||||
@@ -33,9 +37,77 @@ namespace Ink_Canvas {
|
||||
else
|
||||
//savePathWithName = savePath + @"\" + DateTime.Now.ToString("u").Replace(':', '-') + ".icstk";
|
||||
savePathWithName = savePath + @"\" + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss-fff") + ".icstk";
|
||||
|
||||
var fs = new FileStream(savePathWithName, FileMode.Create);
|
||||
inkCanvas.Strokes.Save(fs);
|
||||
if (newNotice) ShowNotification("墨迹成功保存至 " + savePathWithName);
|
||||
|
||||
if (Settings.Automation.IsSaveFullPageStrokes)
|
||||
{
|
||||
// 全页面保存模式 - 保存整个墨迹页面的图像
|
||||
var bitmap = new System.Drawing.Bitmap(
|
||||
(int)System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width,
|
||||
(int)System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height);
|
||||
|
||||
using (var g = System.Drawing.Graphics.FromImage(bitmap))
|
||||
{
|
||||
// 创建黑色或透明背景
|
||||
System.Drawing.Color bgColor = Settings.Canvas.UsingWhiteboard
|
||||
? System.Drawing.Color.White
|
||||
: System.Drawing.Color.FromArgb(22, 41, 36); // 黑板背景色
|
||||
g.Clear(bgColor);
|
||||
|
||||
// 将InkCanvas墨迹渲染到Visual
|
||||
var visual = new DrawingVisual();
|
||||
using (var dc = visual.RenderOpen())
|
||||
{
|
||||
// 创建一个VisualBrush,使用inkCanvas作为源
|
||||
var visualBrush = new VisualBrush(inkCanvas);
|
||||
// 绘制矩形并填充为inkCanvas的内容
|
||||
dc.DrawRectangle(visualBrush, null, new Rect(0, 0, inkCanvas.ActualWidth, inkCanvas.ActualHeight));
|
||||
}
|
||||
|
||||
// 创建适合墨迹画布尺寸的渲染位图
|
||||
var rtb = new RenderTargetBitmap(
|
||||
(int)inkCanvas.ActualWidth, (int)inkCanvas.ActualHeight,
|
||||
96, 96,
|
||||
PixelFormats.Pbgra32);
|
||||
rtb.Render(visual);
|
||||
|
||||
// 转换为GDI+ Bitmap并保存
|
||||
var encoder = new PngBitmapEncoder();
|
||||
encoder.Frames.Add(BitmapFrame.Create(rtb));
|
||||
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
encoder.Save(ms);
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
var imgBitmap = new System.Drawing.Bitmap(ms);
|
||||
|
||||
// 将生成的墨迹图像绘制到屏幕截图上
|
||||
// 居中绘制,确保墨迹位于屏幕中央
|
||||
int x = (bitmap.Width - imgBitmap.Width) / 2;
|
||||
int y = (bitmap.Height - imgBitmap.Height) / 2;
|
||||
g.DrawImage(imgBitmap, x, y);
|
||||
|
||||
// 保存为PNG
|
||||
string imagePathWithName = Path.ChangeExtension(savePathWithName, "png");
|
||||
bitmap.Save(imagePathWithName, System.Drawing.Imaging.ImageFormat.Png);
|
||||
|
||||
// 仍然保存墨迹文件以兼容旧版本
|
||||
inkCanvas.Strokes.Save(fs);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示提示
|
||||
if (newNotice) ShowNotification("墨迹成功全页面保存至 " + Path.ChangeExtension(savePathWithName, "png"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 常规保存模式 - 仅保存墨迹对象
|
||||
inkCanvas.Strokes.Save(fs);
|
||||
if (newNotice) ShowNotification("墨迹成功保存至 " + savePathWithName);
|
||||
}
|
||||
|
||||
fs.Close();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ShowNotification("墨迹保存失败");
|
||||
|
||||
@@ -243,41 +243,97 @@ namespace Ink_Canvas {
|
||||
public void ComboBoxFloatingBarImg_SelectionChanged(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Appearance.FloatingBarImg = ComboBoxFloatingBarImg.SelectedIndex;
|
||||
if (ComboBoxFloatingBarImg.SelectedIndex == 0) {
|
||||
UpdateFloatingBarIcon();
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
public void UpdateFloatingBarIcon()
|
||||
{
|
||||
int index = Settings.Appearance.FloatingBarImg;
|
||||
|
||||
if (index == 0) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/icc.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(0.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 1) {
|
||||
} else if (index == 1) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(
|
||||
new Uri("pack://application:,,,/Resources/Icons-png/icc-transparent-dark-small.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(1.2);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 2) {
|
||||
} else if (index == 2) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuandoujiyanhuaji.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 3) {
|
||||
} else if (index == 3) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuanshounvhuaji.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 4) {
|
||||
} else if (index == 4) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuanciya.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 5) {
|
||||
} else if (index == 5) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuanneikuhuaji.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 6) {
|
||||
} else if (index == 6) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuandogeyuanliangwo.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 7) {
|
||||
} else if (index == 7) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/tiebahuaji.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1);
|
||||
} else if (index >= 8 && index - 8 < Settings.Appearance.CustomFloatingBarImgs.Count) {
|
||||
// 使用自定义图标
|
||||
var customIcon = Settings.Appearance.CustomFloatingBarImgs[index - 8];
|
||||
try {
|
||||
FloatingbarHeadIconImg.Source = new BitmapImage(new Uri(customIcon.FilePath));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2);
|
||||
} catch {
|
||||
// 如果加载失败,使用默认图标
|
||||
FloatingbarHeadIconImg.Source = new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/icc.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(0.5);
|
||||
}
|
||||
}
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
public void UpdateCustomIconsInComboBox()
|
||||
{
|
||||
// 保留前8个内置图标选项
|
||||
while (ComboBoxFloatingBarImg.Items.Count > 8)
|
||||
{
|
||||
ComboBoxFloatingBarImg.Items.RemoveAt(ComboBoxFloatingBarImg.Items.Count - 1);
|
||||
}
|
||||
|
||||
// 添加自定义图标选项
|
||||
foreach (var customIcon in Settings.Appearance.CustomFloatingBarImgs)
|
||||
{
|
||||
ComboBoxItem item = new ComboBoxItem();
|
||||
item.Content = customIcon.Name;
|
||||
item.FontFamily = new System.Windows.Media.FontFamily("Microsoft YaHei UI");
|
||||
ComboBoxFloatingBarImg.Items.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void ButtonAddCustomIcon_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AddCustomIconWindow dialog = new AddCustomIconWindow(this);
|
||||
dialog.Owner = this;
|
||||
dialog.ShowDialog();
|
||||
|
||||
if (dialog.IsSuccess)
|
||||
{
|
||||
// 自动选中新添加的图标
|
||||
ComboBoxFloatingBarImg.SelectedIndex = ComboBoxFloatingBarImg.Items.Count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
private void ButtonManageCustomIcons_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CustomIconWindow dialog = new CustomIconWindow(this);
|
||||
dialog.Owner = this;
|
||||
dialog.ShowDialog();
|
||||
}
|
||||
|
||||
private void ToggleSwitchEnableTimeDisplayInWhiteboardMode_Toggled(object sender, RoutedEventArgs e) {
|
||||
@@ -1179,6 +1235,13 @@ namespace Ink_Canvas {
|
||||
else
|
||||
timerKillProcess.Stop();
|
||||
}
|
||||
|
||||
private void ToggleSwitchAutoEnterAnnotationModeWhenExitFoldMode_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
Settings.Automation.IsAutoEnterAnnotationModeWhenExitFoldMode = ToggleSwitchAutoEnterAnnotationModeWhenExitFoldMode.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchSaveScreenshotsInDateFolders_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
@@ -1293,6 +1356,12 @@ namespace Ink_Canvas {
|
||||
ToggleSwitchAutoSaveScreenShotInPowerPoint.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchSaveFullPageStrokes_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Automation.IsSaveFullPageStrokes = ToggleSwitchSaveFullPageStrokes.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1878,5 +1947,65 @@ namespace Ink_Canvas {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义点名背景相关方法
|
||||
public void UpdatePickNameBackgroundsInComboBox()
|
||||
{
|
||||
// 清除现有的自定义背景选项
|
||||
if (ComboBoxPickNameBackground != null)
|
||||
{
|
||||
// 保留第一个默认选项
|
||||
while (ComboBoxPickNameBackground.Items.Count > 1)
|
||||
{
|
||||
ComboBoxPickNameBackground.Items.RemoveAt(ComboBoxPickNameBackground.Items.Count - 1);
|
||||
}
|
||||
|
||||
// 添加自定义背景选项
|
||||
foreach (var background in Settings.RandSettings.CustomPickNameBackgrounds)
|
||||
{
|
||||
ComboBoxItem item = new ComboBoxItem();
|
||||
item.Content = background.Name;
|
||||
item.FontFamily = new System.Windows.Media.FontFamily("Microsoft YaHei UI");
|
||||
ComboBoxPickNameBackground.Items.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdatePickNameBackgroundDisplay()
|
||||
{
|
||||
// 此方法主要用于在外部窗口更改背景后更新UI
|
||||
if (ComboBoxPickNameBackground != null)
|
||||
{
|
||||
ComboBoxPickNameBackground.SelectedIndex = Settings.RandSettings.SelectedBackgroundIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private void ComboBoxPickNameBackground_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
|
||||
Settings.RandSettings.SelectedBackgroundIndex = ComboBoxPickNameBackground.SelectedIndex;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ButtonAddCustomBackground_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AddPickNameBackgroundWindow dialog = new AddPickNameBackgroundWindow(this);
|
||||
dialog.Owner = this;
|
||||
dialog.ShowDialog();
|
||||
|
||||
if (dialog.IsSuccess)
|
||||
{
|
||||
// 自动选中新添加的背景
|
||||
ComboBoxPickNameBackground.SelectedIndex = ComboBoxPickNameBackground.Items.Count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
private void ButtonManageBackgrounds_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ManagePickNameBackgroundsWindow dialog = new ManagePickNameBackgroundsWindow(this);
|
||||
dialog.Owner = this;
|
||||
dialog.ShowDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,41 +247,20 @@ namespace Ink_Canvas {
|
||||
new SolidColorBrush(StringToColor("#FF555555"));
|
||||
}
|
||||
|
||||
ComboBoxFloatingBarImg.SelectedIndex = Settings.Appearance.FloatingBarImg;
|
||||
if (ComboBoxFloatingBarImg.SelectedIndex == 0) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/icc.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(0.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 1) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(
|
||||
new Uri("pack://application:,,,/Resources/Icons-png/icc-transparent-dark-small.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(1.2);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 2) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuandoujiyanhuaji.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 3) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuanshounvhuaji.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 4) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuanciya.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 5) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuanneikuhuaji.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 6) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuandogeyuanliangwo.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 7) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/tiebahuaji.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1);
|
||||
// 更新自定义图标下拉列表
|
||||
UpdateCustomIconsInComboBox();
|
||||
|
||||
// 设置选中的图标索引
|
||||
// 如果索引超出范围(自定义图标可能已删除),使用默认图标
|
||||
if (Settings.Appearance.FloatingBarImg >= ComboBoxFloatingBarImg.Items.Count)
|
||||
{
|
||||
Settings.Appearance.FloatingBarImg = 0;
|
||||
}
|
||||
|
||||
ComboBoxFloatingBarImg.SelectedIndex = Settings.Appearance.FloatingBarImg;
|
||||
|
||||
// 更新浮动栏图标
|
||||
UpdateFloatingBarIcon();
|
||||
|
||||
ToggleSwitchEnableTimeDisplayInWhiteboardMode.IsOn =
|
||||
Settings.Appearance.EnableTimeDisplayInWhiteboardMode;
|
||||
@@ -623,6 +602,16 @@ namespace Ink_Canvas {
|
||||
ToggleSwitchDirectCallCiRand.IsOn = Settings.RandSettings.DirectCallCiRand;
|
||||
RandomDrawPanel.Visibility = Settings.RandSettings.ShowRandomAndSingleDraw ? Visibility.Visible : Visibility.Collapsed;
|
||||
SingleDrawPanel.Visibility = Settings.RandSettings.ShowRandomAndSingleDraw ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
// 加载自定义点名背景
|
||||
UpdatePickNameBackgroundsInComboBox();
|
||||
|
||||
// 设置选择的背景索引
|
||||
if (Settings.RandSettings.SelectedBackgroundIndex >= ComboBoxPickNameBackground.Items.Count)
|
||||
{
|
||||
Settings.RandSettings.SelectedBackgroundIndex = 0;
|
||||
}
|
||||
ComboBoxPickNameBackground.SelectedIndex = Settings.RandSettings.SelectedBackgroundIndex;
|
||||
} else {
|
||||
Settings.RandSettings = new RandSettings();
|
||||
ToggleSwitchDisplayRandWindowNamesInputBtn.IsOn = Settings.RandSettings.DisplayRandWindowNamesInputBtn;
|
||||
@@ -710,6 +699,8 @@ namespace Ink_Canvas {
|
||||
ToggleSwitchSaveScreenshotsInDateFolders.IsOn = Settings.Automation.IsSaveScreenshotsInDateFolders;
|
||||
|
||||
ToggleSwitchAutoSaveStrokesAtScreenshot.IsOn = Settings.Automation.IsAutoSaveStrokesAtScreenshot;
|
||||
|
||||
ToggleSwitchSaveFullPageStrokes.IsOn = Settings.Automation.IsSaveFullPageStrokes;
|
||||
|
||||
SideControlMinimumAutomationSlider.Value = Settings.Automation.MinimumAutomationStrokeNumber;
|
||||
|
||||
@@ -717,6 +708,9 @@ namespace Ink_Canvas {
|
||||
ToggleSwitchAutoDelSavedFiles.IsOn = Settings.Automation.AutoDelSavedFiles;
|
||||
ComboBoxAutoDelSavedFilesDaysThreshold.Text =
|
||||
Settings.Automation.AutoDelSavedFilesDaysThreshold.ToString();
|
||||
|
||||
// 加载退出收纳模式自动切换至批注模式设置
|
||||
ToggleSwitchAutoEnterAnnotationModeWhenExitFoldMode.IsOn = Settings.Automation.IsAutoEnterAnnotationModeWhenExitFoldMode;
|
||||
} else {
|
||||
Settings.Automation = new Automation();
|
||||
}
|
||||
|
||||
@@ -493,77 +493,11 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
// 触摸移动时保持自定义光标显示
|
||||
if (Settings.Canvas.IsShowCursor) {
|
||||
inkCanvas.ForceCursor = true;
|
||||
inkCanvas.UseCustomCursor = true; // 确保使用自定义光标
|
||||
System.Windows.Forms.Cursor.Show();
|
||||
}
|
||||
if (inkCanvas.EditingMode != InkCanvasEditingMode.None)
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
|
||||
|
||||
if (NeedUpdateIniP()) iniP = e.GetTouchPoint(inkCanvas).Position;
|
||||
if (drawingShapeMode == 9 && isFirstTouchCuboid == false) MouseTouchMove(iniP);
|
||||
inkCanvas.Opacity = 1;
|
||||
double boundsWidth = GetTouchBoundWidth(e), eraserMultiplier = 1.0;
|
||||
if (!Settings.Advanced.EraserBindTouchMultiplier && Settings.Advanced.IsSpecialScreen)
|
||||
eraserMultiplier = 1 / Settings.Advanced.TouchMultiplier;
|
||||
if (boundsWidth > BoundsWidth) {
|
||||
isLastTouchEraser = true;
|
||||
if (drawingShapeMode == 0 && forceEraser) return;
|
||||
if (boundsWidth > BoundsWidth * 2.5) {
|
||||
double k = 1;
|
||||
switch (Settings.Canvas.EraserSize) {
|
||||
case 0:
|
||||
k = 0.5;
|
||||
break;
|
||||
case 1:
|
||||
k = 0.8;
|
||||
break;
|
||||
case 3:
|
||||
k = 1.25;
|
||||
break;
|
||||
case 4:
|
||||
k = 1.5;
|
||||
break;
|
||||
}
|
||||
|
||||
inkCanvas.EraserShape = new EllipseStylusShape(boundsWidth * k * eraserMultiplier,
|
||||
boundsWidth * k * eraserMultiplier);
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
|
||||
|
||||
// 立即应用光标设置
|
||||
if (Settings.Canvas.IsShowCursor) {
|
||||
inkCanvas.Cursor = Cursors.Cross;
|
||||
System.Windows.Forms.Cursor.Show();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (StackPanelPPTControls.Visibility == Visibility.Visible && inkCanvas.Strokes.Count == 0 &&
|
||||
Settings.PowerPointSettings.IsEnableFingerGestureSlideShowControl) {
|
||||
isLastTouchEraser = false;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.GestureOnly;
|
||||
inkCanvas.Opacity = 0.1;
|
||||
}
|
||||
else {
|
||||
inkCanvas.EraserShape = new EllipseStylusShape(5, 5);
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByStroke;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
isLastTouchEraser = false;
|
||||
// 修复面积擦时不显示橡皮形状:无论 forcePointEraser 状态,均显示 50x50 橡皮
|
||||
inkCanvas.EraserShape = new EllipseStylusShape(50, 50);
|
||||
if (forceEraser) return;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
}
|
||||
|
||||
if (inkCanvas.EditingMode != InkCanvasEditingMode.None)
|
||||
return;
|
||||
|
||||
if (e.TouchDevice == null) {
|
||||
System.Windows.Forms.Cursor.Show();
|
||||
} else {
|
||||
System.Windows.Forms.Cursor.Hide();
|
||||
}
|
||||
MouseTouchMove(e.GetTouchPoint(inkCanvas).Position);
|
||||
}
|
||||
|
||||
private int drawMultiStepShapeCurrentStep = 0; //多笔完成的图形 当前所处在的笔画
|
||||
|
||||
@@ -125,37 +125,31 @@ namespace Ink_Canvas {
|
||||
Point startPoint = e.Stroke.StylusPoints[0].ToPoint();
|
||||
Point endPoint = e.Stroke.StylusPoints[e.Stroke.StylusPoints.Count - 1].ToPoint();
|
||||
|
||||
// 记录是否需要拉直线条,默认不拉直
|
||||
bool shouldStraighten = false;
|
||||
bool snapped = false;
|
||||
|
||||
// 首先检查是否应该拉直线条(使用灵敏度设置),这是主要判断条件
|
||||
// 先完成所有直线判定,再考虑端点吸附
|
||||
// 读取实际的灵敏度设置值
|
||||
double sensitivity = Settings.InkToShape.LineStraightenSensitivity;
|
||||
System.Diagnostics.Debug.WriteLine($"当前灵敏度值: {sensitivity}");
|
||||
|
||||
// 将灵敏度值传递给判断函数
|
||||
shouldStraighten = ShouldStraightenLine(e.Stroke);
|
||||
// 判断是否应该拉直线条
|
||||
bool shouldStraighten = ShouldStraightenLine(e.Stroke);
|
||||
|
||||
// 输出一些调试信息,帮助理解灵敏度设置的效果
|
||||
System.Diagnostics.Debug.WriteLine($"LineStraightenSensitivity: {Settings.InkToShape.LineStraightenSensitivity}, ShouldStraighten: {shouldStraighten}");
|
||||
|
||||
// 再检查端点吸附功能,这是独立的可选功能
|
||||
if (Settings.Canvas.LineEndpointSnapping) {
|
||||
// 只有当确定要拉直线条时,才检查端点吸附
|
||||
if (shouldStraighten && Settings.Canvas.LineEndpointSnapping) {
|
||||
// 只有在启用了形状识别(矩形或三角形)时才执行端点吸附
|
||||
if (Settings.InkToShape.IsInkToShapeRectangle || Settings.InkToShape.IsInkToShapeTriangle) {
|
||||
Point[] snappedPoints = GetSnappedEndpoints(startPoint, endPoint);
|
||||
if (snappedPoints != null) {
|
||||
startPoint = snappedPoints[0];
|
||||
endPoint = snappedPoints[1];
|
||||
snapped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果满足任一条件(需要拉直或成功吸附),则创建直线
|
||||
// 这确保灵敏度设置独立于端点吸附功能发挥作用
|
||||
if (shouldStraighten || snapped) {
|
||||
// 如果确定要拉直,则创建直线
|
||||
if (shouldStraighten) {
|
||||
StylusPointCollection straightLinePoints = CreateStraightLine(startPoint, endPoint);
|
||||
Stroke straightStroke = new Stroke(straightLinePoints) {
|
||||
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
|
||||
|
||||
@@ -26,8 +26,18 @@ namespace Ink_Canvas {
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
inkCanvas.Children.Clear();
|
||||
isInMultiTouchMode = false;
|
||||
|
||||
// 退出多指书写模式后,恢复手掌擦功能
|
||||
// 这里不需要特别操作,因为设置了isInMultiTouchMode = false后,
|
||||
// 下次触发Main_Grid_TouchDown时会自动判断并启用手掌擦功能
|
||||
}
|
||||
else {
|
||||
// 进入多指书写模式前,如果当前处于手掌擦状态,先关闭手掌擦
|
||||
if (isLastTouchEraser) {
|
||||
isLastTouchEraser = false;
|
||||
currentPalmEraserShape = null;
|
||||
}
|
||||
|
||||
inkCanvas.StylusDown += MainWindow_StylusDown;
|
||||
inkCanvas.StylusMove += MainWindow_StylusMove;
|
||||
inkCanvas.StylusUp += MainWindow_StylusUp;
|
||||
@@ -242,6 +252,13 @@ namespace Ink_Canvas {
|
||||
if (drawingShapeMode == 9 && isFirstTouchCuboid == false) MouseTouchMove(iniP);
|
||||
inkCanvas.Opacity = 1;
|
||||
|
||||
// 如果已处于多指书写模式,禁用手掌擦功能
|
||||
if (isInMultiTouchMode) {
|
||||
isLastTouchEraser = false;
|
||||
currentPalmEraserShape = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果已经处于手掌擦状态,保持状态不变
|
||||
if (isLastTouchEraser && currentPalmEraserShape != null) {
|
||||
inkCanvas.EraserShape = currentPalmEraserShape;
|
||||
@@ -252,7 +269,16 @@ namespace Ink_Canvas {
|
||||
double boundsWidth = GetTouchBoundWidth(e), eraserMultiplier = 1.0;
|
||||
if (!Settings.Advanced.EraserBindTouchMultiplier && Settings.Advanced.IsSpecialScreen)
|
||||
eraserMultiplier = 1 / Settings.Advanced.TouchMultiplier;
|
||||
if (boundsWidth > BoundsWidth) {
|
||||
|
||||
// 检查触控点数量,只有大于等于三个触控点时才激活手掌擦功能
|
||||
if (dec.Count >= 3 && boundsWidth > BoundsWidth) {
|
||||
// 保存当前的编辑模式,以便恢复
|
||||
if (!isLastTouchEraser) {
|
||||
prePalmEraserEditingMode = inkCanvas.EditingMode;
|
||||
// 模拟点击橡皮选项卡
|
||||
EraserIcon_Click(null, null);
|
||||
}
|
||||
|
||||
isLastTouchEraser = true;
|
||||
if (drawingShapeMode == 0 && forceEraser) return;
|
||||
if (boundsWidth > BoundsWidth * 2.5) {
|
||||
@@ -305,6 +331,9 @@ namespace Ink_Canvas {
|
||||
private InkCanvasEditingMode lastInkCanvasEditingMode = InkCanvasEditingMode.Ink;
|
||||
private bool isSingleFingerDragMode = false;
|
||||
|
||||
// 保存触发手掌擦前的编辑模式,用于手掌擦结束后恢复
|
||||
private InkCanvasEditingMode prePalmEraserEditingMode = InkCanvasEditingMode.Ink;
|
||||
|
||||
private void inkCanvas_PreviewTouchDown(object sender, TouchEventArgs e) {
|
||||
|
||||
inkCanvas.CaptureTouch(e.TouchDevice);
|
||||
@@ -346,9 +375,25 @@ namespace Ink_Canvas {
|
||||
if (isLastTouchEraser && dec.Count == 0) {
|
||||
isLastTouchEraser = false;
|
||||
currentPalmEraserShape = null; // 清除保存的手掌擦形状
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint && forcePointEraser) {
|
||||
// 重新应用当前设置的橡皮擦形状
|
||||
ApplyCurrentEraserShape();
|
||||
|
||||
// 当手掌擦消失时,恢复到之前的编辑模式
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
// 根据之前的编辑模式模拟点击相应的选项卡
|
||||
if (prePalmEraserEditingMode == InkCanvasEditingMode.Ink) {
|
||||
// 模拟点击批注选项卡
|
||||
PenIcon_Click(null, null);
|
||||
} else if (prePalmEraserEditingMode == InkCanvasEditingMode.None ||
|
||||
prePalmEraserEditingMode == InkCanvasEditingMode.Select) {
|
||||
// 模拟点击光标选项卡
|
||||
CursorIcon_Click(null, null);
|
||||
} else {
|
||||
// 其他编辑模式时恢复之前的模式
|
||||
inkCanvas.EditingMode = prePalmEraserEditingMode;
|
||||
if (forcePointEraser) {
|
||||
// 重新应用当前设置的橡皮擦形状
|
||||
ApplyCurrentEraserShape();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.0.4")]
|
||||
[assembly: AssemblyFileVersion("1.7.0.4")]
|
||||
[assembly: AssemblyVersion("1.7.1.0")]
|
||||
[assembly: AssemblyFileVersion("1.7.1.0")]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
@@ -65,7 +66,10 @@ namespace Ink_Canvas
|
||||
public int LineEndpointSnappingThreshold { get; set; } = 15; // 直线端点吸附的距离阈值(像素)
|
||||
|
||||
[JsonProperty("usingWhiteboard")]
|
||||
public bool UsingWhiteboard { get; set; }
|
||||
public bool UsingWhiteboard { get; set; } = false;
|
||||
|
||||
[JsonProperty("customBackgroundColor")]
|
||||
public string CustomBackgroundColor { get; set; } = "#162924";
|
||||
|
||||
[JsonProperty("hyperbolaAsymptoteOption")]
|
||||
public OptionalOperation HyperbolaAsymptoteOption { get; set; } = OptionalOperation.Ask;
|
||||
@@ -139,6 +143,8 @@ namespace Ink_Canvas
|
||||
public double ViewboxFloatingBarScaleTransformValue { get; set; } = 1.0;
|
||||
[JsonProperty("floatingBarImg")]
|
||||
public int FloatingBarImg { get; set; } = 0;
|
||||
[JsonProperty("customFloatingBarImgs")]
|
||||
public List<CustomFloatingBarIcon> CustomFloatingBarImgs { get; set; } = new List<CustomFloatingBarIcon>();
|
||||
[JsonProperty("viewboxFloatingBarOpacityValue")]
|
||||
public double ViewboxFloatingBarOpacityValue { get; set; } = 1.0;
|
||||
[JsonProperty("enableTrayIcon")]
|
||||
@@ -250,6 +256,9 @@ namespace Ink_Canvas
|
||||
|| IsAutoFoldInYiYunVisualPresenter
|
||||
|| IsAutoFoldInMaxHubWhiteboard;
|
||||
|
||||
[JsonProperty("isAutoEnterAnnotationModeWhenExitFoldMode")]
|
||||
public bool IsAutoEnterAnnotationModeWhenExitFoldMode { get; set; } = false;
|
||||
|
||||
[JsonProperty("isAutoFoldInEasiNote")]
|
||||
public bool IsAutoFoldInEasiNote { get; set; } = false;
|
||||
|
||||
@@ -355,6 +364,9 @@ namespace Ink_Canvas
|
||||
|
||||
[JsonProperty("autoDelSavedFilesDaysThreshold")]
|
||||
public int AutoDelSavedFilesDaysThreshold = 15;
|
||||
|
||||
[JsonProperty("isSaveFullPageStrokes")]
|
||||
public bool IsSaveFullPageStrokes = false;
|
||||
}
|
||||
|
||||
public class Advanced
|
||||
@@ -437,5 +449,45 @@ namespace Ink_Canvas
|
||||
public bool ShowRandomAndSingleDraw { get; set; } = true;
|
||||
[JsonProperty("directCallCiRand")]
|
||||
public bool DirectCallCiRand { get; set; } = false;
|
||||
[JsonProperty("selectedBackgroundIndex")]
|
||||
public int SelectedBackgroundIndex { get; set; } = 0;
|
||||
[JsonProperty("customPickNameBackgrounds")]
|
||||
public List<CustomPickNameBackground> CustomPickNameBackgrounds { get; set; } = new List<CustomPickNameBackground>();
|
||||
}
|
||||
|
||||
public class CustomPickNameBackground
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("filePath")]
|
||||
public string FilePath { get; set; }
|
||||
|
||||
public CustomPickNameBackground(string name, string filePath)
|
||||
{
|
||||
Name = name;
|
||||
FilePath = filePath;
|
||||
}
|
||||
|
||||
// 用于JSON序列化
|
||||
public CustomPickNameBackground() { }
|
||||
}
|
||||
|
||||
public class CustomFloatingBarIcon
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("filePath")]
|
||||
public string FilePath { get; set; }
|
||||
|
||||
public CustomFloatingBarIcon(string name, string filePath)
|
||||
{
|
||||
Name = name;
|
||||
FilePath = filePath;
|
||||
}
|
||||
|
||||
// 用于JSON序列化
|
||||
public CustomFloatingBarIcon() { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<Window x:Class="Ink_Canvas.AddCustomIconWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
|
||||
xmlns:local="clr-namespace:Ink_Canvas"
|
||||
mc:Ignorable="d"
|
||||
Title="添加自定义图标" Height="500" Width="750" WindowStartupLocation="CenterScreen" ResizeMode="NoResize">
|
||||
<Grid Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="添加自定义浮动栏图标" FontSize="18" FontWeight="Bold" Margin="0,0,0,15" Grid.Row="0"/>
|
||||
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,20">
|
||||
<TextBlock Text="选择图标文件:" VerticalAlignment="Center" FontSize="14"/>
|
||||
<TextBox Name="IconPathTextBox" Width="220" IsReadOnly="True" Margin="10,0" Height="25"/>
|
||||
<Button Content="浏览..." Click="BrowseButton_Click" Width="80" Height="45"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,0,0,20">
|
||||
<TextBlock Text="图标名称:" VerticalAlignment="Center" FontSize="14"/>
|
||||
<TextBox Name="IconNameTextBox" Width="280" Margin="28,0,0,0" Height="25"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Grid.Row="3" Text="预览:" Margin="0,0,0,8" FontSize="14"/>
|
||||
|
||||
<Border Grid.Row="4" BorderBrush="#CCCCCC" BorderThickness="1" Padding="8" HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<Image Name="IconPreviewImage" Width="72" Height="72" Stretch="Uniform"/>
|
||||
</Border>
|
||||
|
||||
<StackPanel Grid.Row="5" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,25,0,0">
|
||||
<Button Content="取消" Width="100" Height="30" Click="CancelButton_Click" Margin="0,0,15,0"/>
|
||||
<Button Name="SaveButton" Content="保存" Width="100" Height="30" Click="SaveButton_Click" IsEnabled="False"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
/// <summary>
|
||||
/// AddCustomIconWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class AddCustomIconWindow : Window
|
||||
{
|
||||
private MainWindow mainWindow;
|
||||
private string selectedFilePath;
|
||||
public bool IsSuccess { get; private set; }
|
||||
|
||||
public AddCustomIconWindow(MainWindow owner)
|
||||
{
|
||||
InitializeComponent();
|
||||
mainWindow = owner;
|
||||
IsSuccess = false;
|
||||
|
||||
// 添加TextBox内容变化事件以检查是否可以保存
|
||||
IconNameTextBox.TextChanged += (s, e) => ValidateSaveButton();
|
||||
}
|
||||
|
||||
private void BrowseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
OpenFileDialog openFileDialog = new OpenFileDialog
|
||||
{
|
||||
Filter = "图像文件|*.png;*.jpg;*.jpeg;*.bmp;*.gif;*.ico",
|
||||
Title = "选择一个图标文件"
|
||||
};
|
||||
|
||||
if (openFileDialog.ShowDialog() == true)
|
||||
{
|
||||
selectedFilePath = openFileDialog.FileName;
|
||||
IconPathTextBox.Text = selectedFilePath;
|
||||
|
||||
// 显示预览
|
||||
try
|
||||
{
|
||||
BitmapImage bitmap = new BitmapImage();
|
||||
bitmap.BeginInit();
|
||||
bitmap.UriSource = new Uri(selectedFilePath);
|
||||
bitmap.EndInit();
|
||||
IconPreviewImage.Source = bitmap;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"无法加载图像: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
|
||||
// 自动填充名称建议(文件名,不包括扩展名)
|
||||
string suggestedName = Path.GetFileNameWithoutExtension(selectedFilePath);
|
||||
IconNameTextBox.Text = suggestedName;
|
||||
|
||||
ValidateSaveButton();
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateSaveButton()
|
||||
{
|
||||
SaveButton.IsEnabled = !string.IsNullOrWhiteSpace(IconNameTextBox.Text) && !string.IsNullOrEmpty(selectedFilePath);
|
||||
}
|
||||
|
||||
private void CancelButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private void SaveButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建pictures/icons文件夹结构(如果不存在)
|
||||
string picturesFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "pictures");
|
||||
string iconsFolder = Path.Combine(picturesFolder, "icons");
|
||||
|
||||
if (!Directory.Exists(picturesFolder))
|
||||
{
|
||||
Directory.CreateDirectory(picturesFolder);
|
||||
}
|
||||
|
||||
if (!Directory.Exists(iconsFolder))
|
||||
{
|
||||
Directory.CreateDirectory(iconsFolder);
|
||||
}
|
||||
|
||||
// 生成一个唯一的文件名(使用GUID)
|
||||
string extension = Path.GetExtension(selectedFilePath);
|
||||
string newFileName = $"{Guid.NewGuid()}{extension}";
|
||||
string destPath = Path.Combine(iconsFolder, newFileName);
|
||||
|
||||
// 复制文件到pictures/icons文件夹
|
||||
File.Copy(selectedFilePath, destPath);
|
||||
|
||||
// 创建新的自定义图标对象
|
||||
var customIcon = new CustomFloatingBarIcon(IconNameTextBox.Text, destPath);
|
||||
|
||||
// 添加到主窗口的设置中
|
||||
MainWindow.Settings.Appearance.CustomFloatingBarImgs.Add(customIcon);
|
||||
|
||||
// 更新ComboBox
|
||||
mainWindow.UpdateCustomIconsInComboBox();
|
||||
|
||||
// 保存设置
|
||||
MainWindow.SaveSettingsToFile();
|
||||
|
||||
IsSuccess = true;
|
||||
this.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"保存图标时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<Window x:Class="Ink_Canvas.AddPickNameBackgroundWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
|
||||
xmlns:local="clr-namespace:Ink_Canvas"
|
||||
mc:Ignorable="d"
|
||||
Title="添加自定义点名背景" Height="550" Width="800" WindowStartupLocation="CenterScreen" ResizeMode="NoResize">
|
||||
<Grid Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="添加自定义点名背景" FontSize="20" FontWeight="Bold" Margin="0,0,0,20" Grid.Row="0"/>
|
||||
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,20">
|
||||
<TextBlock Text="选择背景图片:" VerticalAlignment="Center" FontSize="14"/>
|
||||
<TextBox Name="BackgroundPathTextBox" Width="400" IsReadOnly="True" Margin="10,0" Height="25"/>
|
||||
<Button Content="浏览..." Click="BrowseButton_Click" Width="100" Height="45"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,0,0,20">
|
||||
<TextBlock Text="背景名称:" VerticalAlignment="Center" FontSize="14"/>
|
||||
<TextBox Name="BackgroundNameTextBox" Width="400" Margin="28,0,0,0" Height="25"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Grid.Row="3" Text="预览:" Margin="0,0,0,10" FontSize="14"/>
|
||||
|
||||
<Border Grid.Row="4" BorderBrush="#CCCCCC" BorderThickness="1" Padding="8">
|
||||
<Grid>
|
||||
<Image Name="BackgroundPreviewImage" Stretch="Uniform" MaxHeight="250"/>
|
||||
<TextBlock Text="未选择图片" HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Foreground="Gray" FontSize="16" Name="NoImageText"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<StackPanel Grid.Row="5" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,25,0,0">
|
||||
<Button Content="取消" Width="120" Height="40" Click="CancelButton_Click" Margin="0,0,15,0"/>
|
||||
<Button Name="SaveButton" Content="保存" Width="120" Height="40" Click="SaveButton_Click" IsEnabled="False"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
/// <summary>
|
||||
/// AddPickNameBackgroundWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class AddPickNameBackgroundWindow : Window
|
||||
{
|
||||
private MainWindow mainWindow;
|
||||
private string selectedFilePath;
|
||||
public bool IsSuccess { get; private set; }
|
||||
|
||||
public AddPickNameBackgroundWindow(MainWindow owner)
|
||||
{
|
||||
InitializeComponent();
|
||||
mainWindow = owner;
|
||||
IsSuccess = false;
|
||||
|
||||
// 添加TextBox内容变化事件以检查是否可以保存
|
||||
BackgroundNameTextBox.TextChanged += (s, e) => ValidateSaveButton();
|
||||
}
|
||||
|
||||
private void BrowseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
OpenFileDialog openFileDialog = new OpenFileDialog
|
||||
{
|
||||
Filter = "图像文件|*.png;*.jpg;*.jpeg;*.bmp;*.gif",
|
||||
Title = "选择一个背景图片"
|
||||
};
|
||||
|
||||
if (openFileDialog.ShowDialog() == true)
|
||||
{
|
||||
selectedFilePath = openFileDialog.FileName;
|
||||
BackgroundPathTextBox.Text = selectedFilePath;
|
||||
|
||||
// 显示预览
|
||||
try
|
||||
{
|
||||
BitmapImage bitmap = new BitmapImage();
|
||||
bitmap.BeginInit();
|
||||
bitmap.UriSource = new Uri(selectedFilePath);
|
||||
bitmap.EndInit();
|
||||
BackgroundPreviewImage.Source = bitmap;
|
||||
NoImageText.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"无法加载图像: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
|
||||
// 自动填充名称建议(文件名,不包括扩展名)
|
||||
string suggestedName = Path.GetFileNameWithoutExtension(selectedFilePath);
|
||||
BackgroundNameTextBox.Text = suggestedName;
|
||||
|
||||
ValidateSaveButton();
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateSaveButton()
|
||||
{
|
||||
SaveButton.IsEnabled = !string.IsNullOrWhiteSpace(BackgroundNameTextBox.Text) && !string.IsNullOrEmpty(selectedFilePath);
|
||||
}
|
||||
|
||||
private void CancelButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private void SaveButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建pictures/picknamebackgrounds文件夹结构(如果不存在)
|
||||
string picturesFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "pictures");
|
||||
string backgroundsFolder = Path.Combine(picturesFolder, "picknamebackgrounds");
|
||||
|
||||
if (!Directory.Exists(picturesFolder))
|
||||
{
|
||||
Directory.CreateDirectory(picturesFolder);
|
||||
}
|
||||
|
||||
if (!Directory.Exists(backgroundsFolder))
|
||||
{
|
||||
Directory.CreateDirectory(backgroundsFolder);
|
||||
}
|
||||
|
||||
// 生成一个唯一的文件名(使用GUID)
|
||||
string extension = Path.GetExtension(selectedFilePath);
|
||||
string newFileName = $"{Guid.NewGuid()}{extension}";
|
||||
string destPath = Path.Combine(backgroundsFolder, newFileName);
|
||||
|
||||
// 复制文件到pictures/picknamebackgrounds文件夹
|
||||
File.Copy(selectedFilePath, destPath);
|
||||
|
||||
// 创建新的自定义背景对象
|
||||
var customBackground = new CustomPickNameBackground(BackgroundNameTextBox.Text, destPath);
|
||||
|
||||
// 添加到主窗口的设置中
|
||||
MainWindow.Settings.RandSettings.CustomPickNameBackgrounds.Add(customBackground);
|
||||
|
||||
// 更新ComboBox
|
||||
mainWindow.UpdatePickNameBackgroundsInComboBox();
|
||||
|
||||
// 保存设置
|
||||
MainWindow.SaveSettingsToFile();
|
||||
|
||||
IsSuccess = true;
|
||||
this.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"保存背景时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<Window x:Class="Ink_Canvas.CustomIconWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
|
||||
xmlns:local="clr-namespace:Ink_Canvas"
|
||||
mc:Ignorable="d"
|
||||
Title="自定义浮动栏图标" Height="450" Width="500" WindowStartupLocation="CenterScreen">
|
||||
<Grid Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="自定义浮动栏图标管理" FontSize="20" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<ListView Grid.Row="1" Name="CustomIconsListView" BorderBrush="#CCCCCC" BorderThickness="1">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridViewColumn Header="预览" Width="80">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Image Source="{Binding FilePath}" Width="32" Height="32"/>
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
<GridViewColumn Header="名称" Width="200" DisplayMemberBinding="{Binding Name}"/>
|
||||
<GridViewColumn Header="操作" Width="100">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Button Content="删除" Click="DeleteCustomIcon_Click" Tag="{Binding}"/>
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,15,0,0">
|
||||
<Button Content="关闭" Width="80" Click="CloseButton_Click"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
/// <summary>
|
||||
/// CustomIconWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class CustomIconWindow : Window
|
||||
{
|
||||
private MainWindow mainWindow;
|
||||
public ObservableCollection<CustomFloatingBarIcon> CustomIcons { get; set; }
|
||||
|
||||
public CustomIconWindow(MainWindow owner)
|
||||
{
|
||||
InitializeComponent();
|
||||
mainWindow = owner;
|
||||
|
||||
// 从主窗口的设置获取自定义图标列表
|
||||
CustomIcons = new ObservableCollection<CustomFloatingBarIcon>(MainWindow.Settings.Appearance.CustomFloatingBarImgs);
|
||||
CustomIconsListView.ItemsSource = CustomIcons;
|
||||
}
|
||||
|
||||
private void DeleteCustomIcon_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button button && button.Tag is CustomFloatingBarIcon icon)
|
||||
{
|
||||
// 从列表中移除图标
|
||||
CustomIcons.Remove(icon);
|
||||
|
||||
// 更新主窗口的设置
|
||||
MainWindow.Settings.Appearance.CustomFloatingBarImgs.Clear();
|
||||
foreach (var customIcon in CustomIcons)
|
||||
{
|
||||
MainWindow.Settings.Appearance.CustomFloatingBarImgs.Add(customIcon);
|
||||
}
|
||||
|
||||
// 如果当前选中的是被删除的图标,重置为默认图标
|
||||
if (MainWindow.Settings.Appearance.FloatingBarImg >= 8 &&
|
||||
MainWindow.Settings.Appearance.FloatingBarImg - 8 >= MainWindow.Settings.Appearance.CustomFloatingBarImgs.Count)
|
||||
{
|
||||
MainWindow.Settings.Appearance.FloatingBarImg = 0;
|
||||
mainWindow.ComboBoxFloatingBarImg.SelectedIndex = 0;
|
||||
mainWindow.UpdateFloatingBarIcon();
|
||||
}
|
||||
|
||||
// 更新ComboBox
|
||||
mainWindow.UpdateCustomIconsInComboBox();
|
||||
|
||||
// 保存设置
|
||||
MainWindow.SaveSettingsToFile();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<Window x:Class="Ink_Canvas.ManagePickNameBackgroundsWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
|
||||
xmlns:local="clr-namespace:Ink_Canvas"
|
||||
mc:Ignorable="d"
|
||||
Title="管理点名背景" Height="500" Width="750" WindowStartupLocation="CenterScreen">
|
||||
<Grid Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="管理自定义点名背景" FontSize="20" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<ListView Grid.Row="1" Name="BackgroundsListView" BorderBrush="#CCCCCC" BorderThickness="1">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridViewColumn Header="预览" Width="150">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Border BorderThickness="1" BorderBrush="#CCCCCC">
|
||||
<Image Source="{Binding FilePath}" Width="140" Height="80" Stretch="Uniform"/>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
<GridViewColumn Header="名称" Width="300" DisplayMemberBinding="{Binding Name}"/>
|
||||
<GridViewColumn Header="操作" Width="200">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Content="设为当前" Width="90" Margin="0,0,5,0" Click="SetAsCurrentButton_Click" Tag="{Binding}"/>
|
||||
<Button Content="删除" Width="70" Click="DeleteBackgroundButton_Click" Tag="{Binding}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,15,0,0">
|
||||
<Button Content="关闭" Width="120" Height="40" Click="CloseButton_Click"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
/// <summary>
|
||||
/// ManagePickNameBackgroundsWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class ManagePickNameBackgroundsWindow : Window
|
||||
{
|
||||
private MainWindow mainWindow;
|
||||
public ObservableCollection<CustomPickNameBackground> Backgrounds { get; set; }
|
||||
|
||||
public ManagePickNameBackgroundsWindow(MainWindow owner)
|
||||
{
|
||||
InitializeComponent();
|
||||
mainWindow = owner;
|
||||
|
||||
// 从主窗口的设置获取自定义背景列表
|
||||
Backgrounds = new ObservableCollection<CustomPickNameBackground>(MainWindow.Settings.RandSettings.CustomPickNameBackgrounds);
|
||||
BackgroundsListView.ItemsSource = Backgrounds;
|
||||
}
|
||||
|
||||
private void SetAsCurrentButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button button && button.Tag is CustomPickNameBackground background)
|
||||
{
|
||||
// 找到背景在列表中的索引(加8,因为前8个是默认值)
|
||||
int index = Backgrounds.IndexOf(background) + 1; // 增加1因为索引0将是"默认"
|
||||
|
||||
// 更新设置
|
||||
MainWindow.Settings.RandSettings.SelectedBackgroundIndex = index;
|
||||
|
||||
// 更新UI
|
||||
mainWindow.UpdatePickNameBackgroundDisplay();
|
||||
|
||||
// 保存设置
|
||||
MainWindow.SaveSettingsToFile();
|
||||
|
||||
MessageBox.Show($"已将\"{background.Name}\"设置为当前点名背景", "设置成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteBackgroundButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button button && button.Tag is CustomPickNameBackground background)
|
||||
{
|
||||
if (MessageBox.Show($"确定要删除背景\"{background.Name}\"吗?", "确认删除", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 尝试删除文件
|
||||
if (File.Exists(background.FilePath))
|
||||
{
|
||||
File.Delete(background.FilePath);
|
||||
}
|
||||
|
||||
// 从列表中移除背景
|
||||
Backgrounds.Remove(background);
|
||||
|
||||
// 更新主窗口的设置
|
||||
MainWindow.Settings.RandSettings.CustomPickNameBackgrounds.Clear();
|
||||
foreach (var bg in Backgrounds)
|
||||
{
|
||||
MainWindow.Settings.RandSettings.CustomPickNameBackgrounds.Add(bg);
|
||||
}
|
||||
|
||||
// 如果当前选中的是被删除的背景,重置为默认背景
|
||||
int selectedIndex = MainWindow.Settings.RandSettings.SelectedBackgroundIndex;
|
||||
if (selectedIndex > 0 && selectedIndex - 1 >= MainWindow.Settings.RandSettings.CustomPickNameBackgrounds.Count)
|
||||
{
|
||||
MainWindow.Settings.RandSettings.SelectedBackgroundIndex = 0;
|
||||
mainWindow.UpdatePickNameBackgroundDisplay();
|
||||
}
|
||||
|
||||
// 更新ComboBox
|
||||
mainWindow.UpdatePickNameBackgroundsInComboBox();
|
||||
|
||||
// 保存设置
|
||||
MainWindow.SaveSettingsToFile();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"删除背景时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
<Window x:Class="Ink_Canvas.Windows.PluginSettingsWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
|
||||
xmlns:local="clr-namespace:Ink_Canvas.Windows"
|
||||
mc:Ignorable="d"
|
||||
Title="插件管理" Height="550" Width="800"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
ResizeMode="CanResize"
|
||||
Background="#F9F9F9">
|
||||
|
||||
<Window.Resources>
|
||||
<!-- 定义必要的资源 -->
|
||||
<SolidColorBrush x:Key="BorderBrush" Color="#DDDDDD"/>
|
||||
<SolidColorBrush x:Key="SystemAccentColorLight1" Color="#3B82F6"/>
|
||||
<SolidColorBrush x:Key="SystemAccentColor" Color="#2563EB"/>
|
||||
<SolidColorBrush x:Key="SystemControlBackgroundChromeMediumBrush" Color="#F0F0F0"/>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 标题栏 -->
|
||||
<Border Grid.Row="0" Background="{DynamicResource SystemAccentColorLight1}" Height="60">
|
||||
<Grid>
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="20,0,0,0">
|
||||
<TextBlock Text="插件管理" FontSize="22" FontWeight="SemiBold" Foreground="White" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<Button x:Name="BtnClose" Content="" FontFamily="Segoe MDL2 Assets"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,20,0"
|
||||
Background="Transparent" BorderThickness="0" FontSize="16" Foreground="White"
|
||||
Click="BtnClose_Click"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<Grid Grid.Row="1" Margin="20">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="250"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 左侧插件列表 -->
|
||||
<Border Grid.Column="0" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}" Margin="0,0,10,0" CornerRadius="5">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Text="插件列表" Margin="10,10,0,5" FontSize="16" FontWeight="SemiBold"/>
|
||||
|
||||
<ListView Grid.Row="1" x:Name="PluginListView" BorderThickness="0" Margin="0,5,0,0"
|
||||
SelectionChanged="PluginListView_SelectionChanged">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Margin="0,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding Name}" Grid.Column="0" VerticalAlignment="Center"
|
||||
Foreground="Black" FontWeight="Normal" FontSize="14"/>
|
||||
<ui:ToggleSwitch Grid.Column="1" IsOn="{Binding IsEnabled}"
|
||||
Toggled="PluginToggleSwitch_Toggled"
|
||||
Tag="{Binding}" MinWidth="40"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 右侧插件详情和设置 -->
|
||||
<ScrollViewer Grid.Column="1" Margin="10,0,0,0" VerticalScrollBarVisibility="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 插件详情 -->
|
||||
<Border Grid.Row="0" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}" Padding="15" CornerRadius="5">
|
||||
<Grid x:Name="PluginDetailGrid">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="名称:" FontWeight="SemiBold" Margin="0,0,0,5"/>
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Name}" Margin="0,0,0,5"/>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="版本:" FontWeight="SemiBold" Margin="0,0,0,5"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Version}" Margin="0,0,0,5"/>
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="作者:" FontWeight="SemiBold" Margin="0,0,0,5"/>
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Author}" Margin="0,0,0,5"/>
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="描述:" FontWeight="SemiBold" Margin="0,0,0,5"/>
|
||||
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding Description}" TextWrapping="Wrap" Margin="0,0,0,5"/>
|
||||
|
||||
<Button Grid.Row="3" Grid.Column="2" x:Name="BtnDeletePlugin" Content="删除插件"
|
||||
Visibility="Collapsed" Click="BtnDeletePlugin_Click"
|
||||
Margin="10,0,0,0" Padding="8,3" HorizontalAlignment="Right"
|
||||
Background="#FFEE5555" Foreground="White"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 插件设置区域 -->
|
||||
<Border Grid.Row="1" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}" Margin="0,10,0,0" Padding="15" CornerRadius="5">
|
||||
<StackPanel>
|
||||
<TextBlock Text="插件设置" FontSize="16" FontWeight="SemiBold" Margin="0,0,0,10"/>
|
||||
<ContentControl x:Name="PluginSettingsContainer" MinHeight="50"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 插件配置内容区 -->
|
||||
<Border Grid.Row="2" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}" Margin="0,10,0,0" Padding="0" CornerRadius="5">
|
||||
<ContentControl x:Name="PluginContentContainer" Margin="0"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<Border Grid.Row="2" Background="{DynamicResource SystemControlBackgroundChromeMediumBrush}" Height="60">
|
||||
<Grid>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="20,0,0,0">
|
||||
<Button x:Name="BtnLoadPlugin" Content="加载本地插件" Click="BtnLoadPlugin_Click"
|
||||
Padding="15,5" Background="{DynamicResource SystemAccentColor}" Foreground="White"/>
|
||||
<Button x:Name="BtnSaveConfig" Content="保存状态" Click="BtnSaveConfig_Click"
|
||||
Padding="15,5" Margin="10,0,0,0" Background="{DynamicResource SystemAccentColor}" Foreground="White"
|
||||
ToolTip="手动保存当前所有插件的启用/禁用状态到配置文件"/>
|
||||
<Button x:Name="BtnExportPlugin" Content="导出插件" Click="BtnExportPlugin_Click"
|
||||
Padding="15,5" Margin="10,0,0,0" Background="{DynamicResource SystemAccentColor}" Foreground="White"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,711 @@
|
||||
using Ink_Canvas.Helpers.Plugins;
|
||||
using Ink_Canvas.Helpers.Plugins.BuiltIn;
|
||||
using Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher;
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using Ink_Canvas.Helpers;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ink_Canvas.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// PluginSettingsWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class PluginSettingsWindow : Window, INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 刷新插件列表
|
||||
/// </summary>
|
||||
public void RefreshPluginList()
|
||||
{
|
||||
LoadPlugins();
|
||||
|
||||
// 如果当前选中的插件仍然存在,保持其选中状态
|
||||
if (SelectedPlugin != null)
|
||||
{
|
||||
var matchingPlugin = Plugins.FirstOrDefault(p => p.Plugin.GetType().FullName == SelectedPlugin.GetType().FullName);
|
||||
if (matchingPlugin != null)
|
||||
{
|
||||
PluginListView.SelectedItem = matchingPlugin;
|
||||
}
|
||||
}
|
||||
|
||||
OnPropertyChanged(nameof(SelectedPlugin));
|
||||
LogHelper.WriteLogToFile("插件列表已刷新", LogHelper.LogType.Info);
|
||||
}
|
||||
|
||||
private IPlugin _selectedPlugin;
|
||||
|
||||
/// <summary>
|
||||
/// 当前选中的插件
|
||||
/// </summary>
|
||||
public IPlugin SelectedPlugin
|
||||
{
|
||||
get => _selectedPlugin;
|
||||
set
|
||||
{
|
||||
if (_selectedPlugin != value)
|
||||
{
|
||||
_selectedPlugin = value;
|
||||
OnPropertyChanged(nameof(SelectedPlugin));
|
||||
OnPropertyChanged(nameof(Name));
|
||||
OnPropertyChanged(nameof(Version));
|
||||
OnPropertyChanged(nameof(Author));
|
||||
OnPropertyChanged(nameof(Description));
|
||||
OnPropertyChanged(nameof(IsEnabled));
|
||||
OnPropertyChanged(nameof(IsBuiltIn));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Name => SelectedPlugin?.Name ?? string.Empty;
|
||||
public string Version => SelectedPlugin?.Version?.ToString() ?? string.Empty;
|
||||
public string Author => SelectedPlugin?.Author ?? string.Empty;
|
||||
public string Description => SelectedPlugin?.Description ?? string.Empty;
|
||||
public bool IsEnabled => SelectedPlugin is PluginBase plugin && plugin.IsEnabled;
|
||||
public bool IsBuiltIn => SelectedPlugin?.IsBuiltIn ?? false;
|
||||
|
||||
/// <summary>
|
||||
/// 插件列表
|
||||
/// </summary>
|
||||
public ObservableCollection<PluginViewModel> Plugins { get; } = new ObservableCollection<PluginViewModel>();
|
||||
|
||||
public PluginSettingsWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// 设置数据上下文
|
||||
PluginDetailGrid.DataContext = this;
|
||||
|
||||
// 设置导出按钮初始状态
|
||||
BtnExportPlugin.IsEnabled = false;
|
||||
BtnExportPlugin.ToolTip = "请先选择要导出的插件";
|
||||
|
||||
// 加载插件列表
|
||||
LoadPlugins();
|
||||
|
||||
// 注册窗口关闭事件
|
||||
this.Closing += PluginSettingsWindow_Closing;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 窗口关闭事件处理
|
||||
/// </summary>
|
||||
private void PluginSettingsWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 保存插件配置
|
||||
LogHelper.WriteLogToFile("插件管理窗口关闭,保存插件配置...", LogHelper.LogType.Info);
|
||||
PluginManager.Instance.SaveConfig();
|
||||
LogHelper.WriteLogToFile("插件配置已保存", LogHelper.LogType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"关闭窗口时保存插件配置出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载插件列表
|
||||
/// </summary>
|
||||
private void LoadPlugins()
|
||||
{
|
||||
Plugins.Clear();
|
||||
|
||||
LogHelper.WriteLogToFile($"开始加载插件列表到UI,插件总数: {PluginManager.Instance.Plugins.Count}", LogHelper.LogType.Info);
|
||||
|
||||
// 添加所有已加载的插件
|
||||
foreach (var plugin in PluginManager.Instance.Plugins)
|
||||
{
|
||||
bool isEnabled = false;
|
||||
|
||||
// 从插件实例获取启用状态
|
||||
if (plugin is PluginBase pluginBase)
|
||||
{
|
||||
isEnabled = pluginBase.IsEnabled;
|
||||
}
|
||||
|
||||
// 记录插件详细信息
|
||||
LogHelper.WriteLogToFile($"正在加载插件到UI: 类型={plugin.GetType().FullName}, 名称={plugin.Name ?? "未命名"}, 状态={isEnabled}",
|
||||
LogHelper.LogType.Info);
|
||||
|
||||
// 创建视图模型并添加到集合
|
||||
var viewModel = new PluginViewModel(plugin)
|
||||
{
|
||||
IsEnabled = isEnabled
|
||||
};
|
||||
Plugins.Add(viewModel);
|
||||
|
||||
LogHelper.WriteLogToFile($"已加载插件到UI列表: {plugin.Name},状态: {(isEnabled ? "启用" : "禁用")}", LogHelper.LogType.Info);
|
||||
}
|
||||
|
||||
// 绑定到ListView
|
||||
LogHelper.WriteLogToFile($"绑定 {Plugins.Count} 个插件到ListView", LogHelper.LogType.Info);
|
||||
PluginListView.ItemsSource = Plugins;
|
||||
|
||||
// 如果有插件,选择第一个
|
||||
if (Plugins.Count > 0)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"选择第一个插件: {Plugins[0].Name}", LogHelper.LogType.Info);
|
||||
PluginListView.SelectedIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile("没有找到任何插件", LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新属性变更通知
|
||||
/// </summary>
|
||||
/// <param name="propertyName">属性名称</param>
|
||||
protected void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件列表选择变更事件
|
||||
/// </summary>
|
||||
private void PluginListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (PluginListView.SelectedItem is PluginViewModel viewModel)
|
||||
{
|
||||
// 设置当前选中的插件
|
||||
SelectedPlugin = viewModel.Plugin;
|
||||
|
||||
// 加载插件设置界面
|
||||
PluginSettingsContainer.Content = SelectedPlugin.GetSettingsView();
|
||||
|
||||
// 设置删除按钮的可见性
|
||||
BtnDeletePlugin.Visibility = !SelectedPlugin.IsBuiltIn ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
// 设置导出按钮的可用状态
|
||||
BtnExportPlugin.IsEnabled = !SelectedPlugin.IsBuiltIn;
|
||||
if (SelectedPlugin.IsBuiltIn)
|
||||
{
|
||||
BtnExportPlugin.ToolTip = "内置插件无法导出";
|
||||
}
|
||||
else
|
||||
{
|
||||
BtnExportPlugin.ToolTip = "将插件导出为.iccpp文件";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedPlugin = null;
|
||||
PluginSettingsContainer.Content = null;
|
||||
BtnDeletePlugin.Visibility = Visibility.Collapsed;
|
||||
BtnExportPlugin.IsEnabled = false;
|
||||
BtnExportPlugin.ToolTip = "请先选择要导出的插件";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载本地插件按钮点击事件
|
||||
/// </summary>
|
||||
private void BtnLoadPlugin_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建文件对话框
|
||||
OpenFileDialog dialog = new OpenFileDialog
|
||||
{
|
||||
Filter = "ICC插件文件(*.iccpp)|*.iccpp",
|
||||
Title = "选择要加载的插件文件"
|
||||
};
|
||||
|
||||
// 显示对话框
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
// 获取插件文件路径
|
||||
string pluginPath = dialog.FileName;
|
||||
|
||||
// 检查是否在Plugins目录下
|
||||
string pluginsDirectory = Path.Combine(App.RootPath, "Plugins");
|
||||
string targetPath = Path.Combine(pluginsDirectory, Path.GetFileName(pluginPath));
|
||||
|
||||
// 确保Plugins目录存在
|
||||
if (!Directory.Exists(pluginsDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(pluginsDirectory);
|
||||
}
|
||||
|
||||
// 如果插件不在Plugins目录下,复制过去
|
||||
if (!string.Equals(pluginPath, targetPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
File.Copy(pluginPath, targetPath, true);
|
||||
pluginPath = targetPath;
|
||||
}
|
||||
|
||||
// 加载插件
|
||||
IPlugin plugin = PluginManager.Instance.LoadExternalPlugin(pluginPath);
|
||||
|
||||
if (plugin != null)
|
||||
{
|
||||
// 刷新插件列表
|
||||
LoadPlugins();
|
||||
|
||||
// 选择新加载的插件
|
||||
foreach (var item in Plugins)
|
||||
{
|
||||
if (item.Plugin == plugin)
|
||||
{
|
||||
PluginListView.SelectedItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MessageBox.Show($"插件 {plugin.Name} v{plugin.Version} 已成功加载!", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("插件加载失败,请检查文件是否有效。", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"加载插件时发生错误:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除插件按钮点击事件
|
||||
/// </summary>
|
||||
private void BtnDeletePlugin_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (SelectedPlugin == null) return;
|
||||
|
||||
// 不能删除内置插件
|
||||
if (SelectedPlugin.IsBuiltIn)
|
||||
{
|
||||
MessageBox.Show("内置插件无法删除。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
// 确认删除
|
||||
MessageBoxResult result = MessageBox.Show(
|
||||
$"确定要删除插件 {SelectedPlugin.Name} 吗?\n此操作将永久删除插件文件,无法恢复。",
|
||||
"删除确认",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Warning);
|
||||
|
||||
if (result == MessageBoxResult.Yes)
|
||||
{
|
||||
// 删除插件
|
||||
bool success = PluginManager.Instance.DeletePlugin(SelectedPlugin);
|
||||
|
||||
if (success)
|
||||
{
|
||||
// 刷新插件列表
|
||||
LoadPlugins();
|
||||
|
||||
// 如果还有插件,选择第一个
|
||||
if (Plugins.Count > 0)
|
||||
{
|
||||
PluginListView.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
MessageBox.Show($"插件 {SelectedPlugin.Name} 已成功删除。", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("删除插件失败,请稍后重试。", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出插件按钮点击事件
|
||||
/// </summary>
|
||||
private void BtnExportPlugin_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查是否有选中的插件
|
||||
if (SelectedPlugin == null)
|
||||
{
|
||||
MessageBox.Show("请先选择要导出的插件", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否为内置插件
|
||||
if (SelectedPlugin.IsBuiltIn)
|
||||
{
|
||||
MessageBox.Show("内置插件无法导出", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查插件文件是否存在
|
||||
string pluginPath = null;
|
||||
if (SelectedPlugin is PluginBase pluginBase)
|
||||
{
|
||||
pluginPath = pluginBase.PluginPath;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(pluginPath) || !File.Exists(pluginPath))
|
||||
{
|
||||
MessageBox.Show("插件文件不存在或无法访问", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建保存文件对话框
|
||||
SaveFileDialog dialog = new SaveFileDialog
|
||||
{
|
||||
Filter = "ICC插件文件(*.iccpp)|*.iccpp",
|
||||
Title = "导出插件",
|
||||
FileName = Path.GetFileName(pluginPath)
|
||||
};
|
||||
|
||||
// 显示对话框
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
// 获取目标路径
|
||||
string targetPath = dialog.FileName;
|
||||
|
||||
// 如果目标文件已存在,询问是否覆盖
|
||||
if (File.Exists(targetPath) && !string.Equals(pluginPath, targetPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
MessageBoxResult result = MessageBox.Show("目标文件已存在,是否覆盖?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||||
if (result != MessageBoxResult.Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 复制插件文件到目标路径
|
||||
if (!string.Equals(pluginPath, targetPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
File.Copy(pluginPath, targetPath, true);
|
||||
}
|
||||
|
||||
LogHelper.WriteLogToFile($"插件 {SelectedPlugin.Name} 已成功导出到: {targetPath}", LogHelper.LogType.Info);
|
||||
MessageBox.Show($"插件 {SelectedPlugin.Name} 已成功导出!", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"导出插件时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"导出插件时发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件开关切换事件
|
||||
/// </summary>
|
||||
private void PluginToggleSwitch_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (sender is iNKORE.UI.WPF.Modern.Controls.ToggleSwitch toggleSwitch &&
|
||||
toggleSwitch.Tag is IPlugin plugin)
|
||||
{
|
||||
// 记录当前开关状态
|
||||
bool targetState = toggleSwitch.IsOn;
|
||||
|
||||
// 记录插件类型名称和名称,用于稍后查找重载后的插件
|
||||
string pluginTypeName = plugin.GetType().FullName;
|
||||
string pluginName = plugin.Name;
|
||||
bool wasBuiltIn = plugin.IsBuiltIn;
|
||||
|
||||
LogHelper.WriteLogToFile($"UI开关切换: {pluginName}, 目标状态: {(targetState ? "启用" : "禁用")}", LogHelper.LogType.Info);
|
||||
|
||||
// 切换插件状态
|
||||
PluginManager.Instance.TogglePlugin(plugin, targetState);
|
||||
|
||||
// 立即同步保存配置到文件(确保状态被立即持久化)
|
||||
PluginManager.Instance.SaveConfig();
|
||||
LogHelper.WriteLogToFile($"插件状态已立即保存到配置文件", LogHelper.LogType.Info);
|
||||
|
||||
// 延迟一下再检查状态,确保变更已应用
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 查找最新的插件实例
|
||||
IPlugin currentPlugin = null;
|
||||
foreach (var p in PluginManager.Instance.Plugins)
|
||||
{
|
||||
if (p.GetType().FullName == pluginTypeName || p.Name == pluginName)
|
||||
{
|
||||
currentPlugin = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentPlugin == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"无法找到插件: {pluginName},UI状态可能不准确", LogHelper.LogType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查实际状态
|
||||
bool actualState = currentPlugin is PluginBase pb && pb.IsEnabled;
|
||||
LogHelper.WriteLogToFile($"插件 {pluginName} 实际状态: {(actualState ? "启用" : "禁用")}", LogHelper.LogType.Info);
|
||||
|
||||
// 更新视图模型
|
||||
PluginViewModel viewModel = null;
|
||||
if (toggleSwitch.DataContext is PluginViewModel vm)
|
||||
{
|
||||
viewModel = vm;
|
||||
}
|
||||
else
|
||||
{
|
||||
viewModel = Plugins.FirstOrDefault(p => p.Plugin == currentPlugin);
|
||||
}
|
||||
|
||||
if (viewModel != null)
|
||||
{
|
||||
// 确保视图模型状态与实际状态一致
|
||||
if (viewModel.IsEnabled != actualState)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"同步视图模型状态: {(actualState ? "启用" : "禁用")}", LogHelper.LogType.Info);
|
||||
viewModel.IsEnabled = actualState;
|
||||
}
|
||||
|
||||
// 确保UI开关状态与实际状态一致
|
||||
if (toggleSwitch.IsOn != actualState)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"同步UI开关状态: {(actualState ? "启用" : "禁用")}", LogHelper.LogType.Info);
|
||||
toggleSwitch.IsOn = actualState;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是当前选中的插件,更新属性
|
||||
if (currentPlugin == SelectedPlugin)
|
||||
{
|
||||
OnPropertyChanged(nameof(IsEnabled));
|
||||
}
|
||||
|
||||
// 对于内置插件,特别处理
|
||||
if (wasBuiltIn)
|
||||
{
|
||||
// 特殊插件刷新逻辑,如果是超级启动台插件,立即刷新UI
|
||||
if (currentPlugin is Helpers.Plugins.BuiltIn.SuperLauncherPlugin &&
|
||||
PluginSettingsContainer.Content is Helpers.Plugins.BuiltIn.SuperLauncher.LauncherSettingsControl)
|
||||
{
|
||||
// 重新获取设置界面
|
||||
PluginSettingsContainer.Content = currentPlugin.GetSettingsView();
|
||||
}
|
||||
}
|
||||
|
||||
LogHelper.WriteLogToFile($"插件 {pluginName} UI状态同步完成", LogHelper.LogType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"同步UI状态时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}), System.Windows.Threading.DispatcherPriority.Background);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"切换插件状态时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"切换插件状态时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存插件状态按钮点击事件
|
||||
/// </summary>
|
||||
private void BtnSaveConfig_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
int syncCount = 0;
|
||||
|
||||
// 遍历界面上所有插件视图模型,获取开关状态
|
||||
foreach (var viewModel in Plugins)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (viewModel.Plugin != null)
|
||||
{
|
||||
// 获取UI中开关的当前状态(从界面控件读取)
|
||||
bool uiState = viewModel.IsEnabled;
|
||||
|
||||
// 获取插件类型名,用于查找配置
|
||||
string pluginTypeName = viewModel.Plugin.GetType().FullName;
|
||||
|
||||
// 查找实际的插件实例(可能与viewModel.Plugin不同,因为可能已经重新加载)
|
||||
IPlugin actualPlugin = null;
|
||||
foreach (var p in PluginManager.Instance.Plugins)
|
||||
{
|
||||
if (p.GetType().FullName == pluginTypeName)
|
||||
{
|
||||
actualPlugin = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果找不到对应的实际插件实例,跳过
|
||||
if (actualPlugin == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"手动保存:无法找到与UI对应的插件实例:{viewModel.Name}", LogHelper.LogType.Warning);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取插件实际状态
|
||||
bool pluginState = false;
|
||||
if (actualPlugin is PluginBase pluginBase)
|
||||
{
|
||||
pluginState = pluginBase.IsEnabled;
|
||||
}
|
||||
|
||||
// 如果界面状态与插件实际状态不一致,应用界面状态
|
||||
if (uiState != pluginState)
|
||||
{
|
||||
// 应用界面的状态到插件
|
||||
PluginManager.Instance.TogglePlugin(actualPlugin, uiState);
|
||||
LogHelper.WriteLogToFile($"手动保存:同步插件 {actualPlugin.Name} 状态 {pluginState} -> {uiState}", LogHelper.LogType.Info);
|
||||
syncCount++;
|
||||
}
|
||||
|
||||
// 确保配置中的状态也与界面一致
|
||||
if (PluginManager.Instance.PluginStates.TryGetValue(pluginTypeName, out bool configState) && configState != uiState)
|
||||
{
|
||||
PluginManager.Instance.PluginStates[pluginTypeName] = uiState;
|
||||
LogHelper.WriteLogToFile($"手动保存:更新配置中插件 {actualPlugin.Name} 状态 {configState} -> {uiState}", LogHelper.LogType.Info);
|
||||
syncCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception pluginEx)
|
||||
{
|
||||
// 单个插件处理失败不应该影响其他插件
|
||||
LogHelper.WriteLogToFile($"手动保存:处理插件 {viewModel.Name} 时出错: {pluginEx.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存插件状态配置
|
||||
PluginManager.Instance.SaveConfig();
|
||||
|
||||
// 记录日志
|
||||
LogHelper.WriteLogToFile($"用户手动保存插件状态配置,同步了 {syncCount} 个状态变更", LogHelper.LogType.Info);
|
||||
|
||||
// 刷新插件列表,确保UI与最新状态同步
|
||||
RefreshPluginList();
|
||||
|
||||
// 显示保存成功提示
|
||||
string message = syncCount > 0
|
||||
? $"插件状态已成功保存,同步了 {syncCount} 个状态变更"
|
||||
: "插件状态已成功保存,所有插件状态已是最新";
|
||||
MessageBox.Show(message, "保存成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 记录错误日志
|
||||
LogHelper.WriteLogToFile($"手动保存插件状态时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
|
||||
// 显示错误信息
|
||||
MessageBox.Show($"保存插件状态时发生错误: {ex.Message}", "保存失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭按钮点击事件
|
||||
/// </summary>
|
||||
private void BtnClose_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// 直接关闭窗口,窗口关闭事件会处理配置保存
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件视图模型
|
||||
/// </summary>
|
||||
public class PluginViewModel : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 插件实例
|
||||
/// </summary>
|
||||
public IPlugin Plugin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件名称
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
string name = Plugin?.Name ?? "未命名插件";
|
||||
LogHelper.WriteLogToFile($"获取插件名称: {name},类型: {Plugin?.GetType().FullName ?? "未知"}", LogHelper.LogType.Info);
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件是否启用
|
||||
/// </summary>
|
||||
private bool _isEnabled;
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => _isEnabled;
|
||||
set
|
||||
{
|
||||
if (_isEnabled != value)
|
||||
{
|
||||
_isEnabled = value;
|
||||
OnPropertyChanged(nameof(IsEnabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PluginViewModel(IPlugin plugin)
|
||||
{
|
||||
Plugin = plugin;
|
||||
|
||||
// 获取实际状态
|
||||
_isEnabled = plugin is PluginBase pluginBase && pluginBase.IsEnabled;
|
||||
|
||||
// 记录日志
|
||||
LogHelper.WriteLogToFile($"创建插件视图模型: {plugin?.GetType().FullName ?? "未知"}, 名称: {plugin?.Name ?? "未命名"}", LogHelper.LogType.Info);
|
||||
|
||||
// 注册插件状态变更事件
|
||||
if (plugin is PluginBase pb)
|
||||
{
|
||||
pb.EnabledStateChanged += Plugin_EnabledStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理插件状态变更事件
|
||||
/// </summary>
|
||||
private void Plugin_EnabledStateChanged(object sender, bool isEnabled)
|
||||
{
|
||||
// 在UI线程上更新状态
|
||||
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
IsEnabled = isEnabled;
|
||||
|
||||
// 确保配置立即保存
|
||||
if (sender is IPlugin plugin)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"视图模型捕获到插件 {plugin.Name} 状态变更: {(isEnabled ? "启用" : "禁用")}", LogHelper.LogType.Info);
|
||||
Helpers.Plugins.PluginManager.Instance.SaveConfig();
|
||||
LogHelper.WriteLogToFile($"视图模型已触发配置保存", LogHelper.LogType.Info);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 属性变更通知
|
||||
/// </summary>
|
||||
protected void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,10 @@
|
||||
mc:Ignorable="d" WindowStyle="None" AllowsTransparency="True" Loaded="Window_Loaded"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
Title="Ink Canvas 抽奖" Height="500" Width="900">
|
||||
<Border Background="#F0F3F9" CornerRadius="10" BorderThickness="1" BorderBrush="#0066BF" Margin="0" ClipToBounds="True">
|
||||
<Border x:Name="MainBorder" CornerRadius="10" BorderThickness="1" BorderBrush="#0066BF" Margin="0" ClipToBounds="True">
|
||||
<Border.Background>
|
||||
<ImageBrush x:Name="BackgroundImage" Stretch="UniformToFill" Opacity="1.0"/>
|
||||
</Border.Background>
|
||||
<Canvas>
|
||||
<Grid Canvas.Left="0" Canvas.Right="0" Canvas.Top="0" Canvas.Bottom="0" Width="900" Height="309" HorizontalAlignment="Center" VerticalAlignment="Top">
|
||||
<Grid.ColumnDefinitions>
|
||||
|
||||
@@ -21,6 +21,40 @@ namespace Ink_Canvas {
|
||||
BorderBtnHelp.Visibility = settings.RandSettings.DisplayRandWindowNamesInputBtn == false ? Visibility.Collapsed : Visibility.Visible;
|
||||
RandMaxPeopleOneTime = settings.RandSettings.RandWindowOnceMaxStudents;
|
||||
RandDoneAutoCloseWaitTime = (int)settings.RandSettings.RandWindowOnceCloseLatency*1000;
|
||||
|
||||
// 加载背景
|
||||
LoadBackground(settings);
|
||||
}
|
||||
|
||||
private void LoadBackground(Settings settings)
|
||||
{
|
||||
try
|
||||
{
|
||||
int selectedIndex = settings.RandSettings.SelectedBackgroundIndex;
|
||||
if (selectedIndex <= 0)
|
||||
{
|
||||
// 默认背景(无背景)
|
||||
BackgroundImage.ImageSource = null;
|
||||
MainBorder.Background = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(240, 243, 249));
|
||||
}
|
||||
else if (selectedIndex <= settings.RandSettings.CustomPickNameBackgrounds.Count)
|
||||
{
|
||||
// 自定义背景
|
||||
var customBackground = settings.RandSettings.CustomPickNameBackgrounds[selectedIndex - 1];
|
||||
if (File.Exists(customBackground.FilePath))
|
||||
{
|
||||
var bitmap = new System.Windows.Media.Imaging.BitmapImage();
|
||||
bitmap.BeginInit();
|
||||
bitmap.UriSource = new Uri(customBackground.FilePath);
|
||||
bitmap.EndInit();
|
||||
BackgroundImage.ImageSource = bitmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"加载点名背景出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
public RandWindow(Settings settings, bool IsAutoClose) {
|
||||
@@ -31,6 +65,9 @@ namespace Ink_Canvas {
|
||||
BorderBtnHelp.Visibility = settings.RandSettings.DisplayRandWindowNamesInputBtn == false ? Visibility.Collapsed : Visibility.Visible;
|
||||
RandMaxPeopleOneTime = settings.RandSettings.RandWindowOnceMaxStudents;
|
||||
RandDoneAutoCloseWaitTime = (int)settings.RandSettings.RandWindowOnceCloseLatency * 1000;
|
||||
|
||||
// 加载背景
|
||||
LoadBackground(settings);
|
||||
|
||||
new Thread(new ThreadStart(() => {
|
||||
Thread.Sleep(100);
|
||||
|
||||
Reference in New Issue
Block a user