add:插件系统
This commit is contained in:
@@ -1,9 +1,16 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.33530.505
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InkCanvasForClass", "Ink Canvas\InkCanvasForClass.csproj", "{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InkCanvas.PluginSdk", "Plugins\SDK\InkCanvas.PluginSdk.csproj", "{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InkCanvas.PluginHost", "Plugins\Host\InkCanvas.PluginHost.csproj", "{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleClockPlugin", "Plugins\Samples\SampleClockPlugin\SampleClockPlugin.csproj", "{60891C83-8F04-438E-8064-D827E7AC1817}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -38,6 +45,66 @@ Global
|
||||
{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}.Release|x64.Build.0 = Release|Any CPU
|
||||
{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}.Release|x86.Build.0 = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7081}.Release|x86.Build.0 = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E4B8F2A1-6C3D-4E9F-A1B2-3C4D5E6F7082}.Release|x86.Build.0 = Release|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Release|x64.Build.0 = Release|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{60891C83-8F04-438E-8064-D827E7AC1817}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// 增强的插件基类,提供对插件服务的访问和基本实现
|
||||
/// </summary>
|
||||
public abstract class EnhancedPluginBase : PluginBase, IEnhancedPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// 插件服务实例
|
||||
/// </summary>
|
||||
public IPluginService PluginService { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
protected EnhancedPluginBase()
|
||||
{
|
||||
PluginService = PluginServiceManager.Instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件启动时调用,在Initialize之后
|
||||
/// </summary>
|
||||
public virtual void OnStartup()
|
||||
{
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 已启动");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件关闭时调用,在Cleanup之前
|
||||
/// </summary>
|
||||
public virtual void OnShutdown()
|
||||
{
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 正在关闭");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件的菜单项
|
||||
/// </summary>
|
||||
/// <returns>菜单项集合</returns>
|
||||
public virtual MenuItem[] GetMenuItems()
|
||||
{
|
||||
return new MenuItem[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件的工具栏按钮
|
||||
/// </summary>
|
||||
/// <returns>工具栏按钮集合</returns>
|
||||
public virtual Button[] GetToolbarButtons()
|
||||
{
|
||||
return new Button[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件的状态栏信息
|
||||
/// </summary>
|
||||
/// <returns>状态栏信息</returns>
|
||||
public virtual string GetStatusBarInfo()
|
||||
{
|
||||
return $"{Name} v{Version} - {(IsEnabled ? "已启用" : "已禁用")}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件配置变更时调用
|
||||
/// </summary>
|
||||
public virtual void OnConfigurationChanged()
|
||||
{
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 配置已变更");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写初始化方法,调用OnStartup
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
OnStartup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写清理方法,调用OnShutdown
|
||||
/// </summary>
|
||||
public override void Cleanup()
|
||||
{
|
||||
OnShutdown();
|
||||
base.Cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// 增强的插件基类 V2,提供对三个专门服务接口的访问
|
||||
/// 插件开发者可以根据需要选择性地使用这些服务
|
||||
/// </summary>
|
||||
public abstract class EnhancedPluginBaseV2 : PluginBase, IEnhancedPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取服务实例
|
||||
/// </summary>
|
||||
public IGetService GetService { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 窗口服务实例
|
||||
/// </summary>
|
||||
public IWindowService WindowService { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作服务实例
|
||||
/// </summary>
|
||||
public IActionService ActionService { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件服务实例(兼容性)
|
||||
/// </summary>
|
||||
public IPluginService PluginService { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
protected EnhancedPluginBaseV2()
|
||||
{
|
||||
// 初始化所有服务实例
|
||||
PluginService = PluginServiceManager.Instance;
|
||||
GetService = PluginServiceManager.Instance;
|
||||
WindowService = PluginServiceManager.Instance;
|
||||
ActionService = PluginServiceManager.Instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件启动时调用,在Initialize之后
|
||||
/// </summary>
|
||||
public virtual void OnStartup()
|
||||
{
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 已启动");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件关闭时调用,在Cleanup之前
|
||||
/// </summary>
|
||||
public virtual void OnShutdown()
|
||||
{
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 正在关闭");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件的菜单项
|
||||
/// </summary>
|
||||
/// <returns>菜单项集合</returns>
|
||||
public virtual MenuItem[] GetMenuItems()
|
||||
{
|
||||
return new MenuItem[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件的工具栏按钮
|
||||
/// </summary>
|
||||
/// <returns>工具栏按钮集合</returns>
|
||||
public virtual Button[] GetToolbarButtons()
|
||||
{
|
||||
return new Button[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件的状态栏信息
|
||||
/// </summary>
|
||||
/// <returns>状态栏信息</returns>
|
||||
public virtual string GetStatusBarInfo()
|
||||
{
|
||||
return $"{Name} v{Version} - {(IsEnabled ? "已启用" : "已禁用")}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件配置变更时调用
|
||||
/// </summary>
|
||||
public virtual void OnConfigurationChanged()
|
||||
{
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 配置已变更");
|
||||
}
|
||||
|
||||
#region 便捷方法
|
||||
|
||||
/// <summary>
|
||||
/// 显示通知消息
|
||||
/// </summary>
|
||||
/// <param name="message">消息内容</param>
|
||||
/// <param name="type">消息类型</param>
|
||||
protected void ShowNotification(string message, NotificationType type = NotificationType.Info)
|
||||
{
|
||||
WindowService.ShowNotification(message, type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示确认对话框
|
||||
/// </summary>
|
||||
/// <param name="message">消息内容</param>
|
||||
/// <param name="title">标题</param>
|
||||
/// <returns>用户选择结果</returns>
|
||||
protected bool ShowConfirmDialog(string message, string title = "确认")
|
||||
{
|
||||
return WindowService.ShowConfirmDialog(message, title);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示输入对话框
|
||||
/// </summary>
|
||||
/// <param name="message">提示消息</param>
|
||||
/// <param name="title">标题</param>
|
||||
/// <param name="defaultValue">默认值</param>
|
||||
/// <returns>用户输入内容</returns>
|
||||
protected string ShowInputDialog(string message, string title = "输入", string defaultValue = "")
|
||||
{
|
||||
return WindowService.ShowInputDialog(message, title, defaultValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取系统设置
|
||||
/// </summary>
|
||||
/// <typeparam name="T">设置类型</typeparam>
|
||||
/// <param name="key">设置键</param>
|
||||
/// <param name="defaultValue">默认值</param>
|
||||
/// <returns>设置值</returns>
|
||||
protected T GetSetting<T>(string key, T defaultValue = default(T))
|
||||
{
|
||||
return GetService.GetSetting(key, defaultValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置系统设置
|
||||
/// </summary>
|
||||
/// <typeparam name="T">设置类型</typeparam>
|
||||
/// <param name="key">设置键</param>
|
||||
/// <param name="value">设置值</param>
|
||||
protected void SetSetting<T>(string key, T value)
|
||||
{
|
||||
ActionService.SetSetting(key, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存设置
|
||||
/// </summary>
|
||||
protected void SaveSettings()
|
||||
{
|
||||
ActionService.SaveSettings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除当前画布
|
||||
/// </summary>
|
||||
protected void ClearCanvas()
|
||||
{
|
||||
ActionService.ClearCanvas();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 撤销操作
|
||||
/// </summary>
|
||||
protected void Undo()
|
||||
{
|
||||
ActionService.Undo();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重做操作
|
||||
/// </summary>
|
||||
protected void Redo()
|
||||
{
|
||||
ActionService.Redo();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否可以撤销
|
||||
/// </summary>
|
||||
protected bool CanUndo => GetService.CanUndo;
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否可以重做
|
||||
/// </summary>
|
||||
protected bool CanRedo => GetService.CanRedo;
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前绘制模式
|
||||
/// </summary>
|
||||
protected int CurrentDrawingMode => GetService.CurrentDrawingMode;
|
||||
|
||||
/// <summary>
|
||||
/// 设置绘制模式
|
||||
/// </summary>
|
||||
/// <param name="mode">绘制模式</param>
|
||||
protected void SetDrawingMode(int mode)
|
||||
{
|
||||
ActionService.SetDrawingMode(mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册事件处理器
|
||||
/// </summary>
|
||||
/// <param name="eventName">事件名称</param>
|
||||
/// <param name="handler">事件处理器</param>
|
||||
protected void RegisterEventHandler(string eventName, System.EventHandler handler)
|
||||
{
|
||||
ActionService.RegisterEventHandler(eventName, handler);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注销事件处理器
|
||||
/// </summary>
|
||||
/// <param name="eventName">事件名称</param>
|
||||
/// <param name="handler">事件处理器</param>
|
||||
protected void UnregisterEventHandler(string eventName, System.EventHandler handler)
|
||||
{
|
||||
ActionService.UnregisterEventHandler(eventName, handler);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发事件
|
||||
/// </summary>
|
||||
/// <param name="eventName">事件名称</param>
|
||||
/// <param name="sender">事件发送者</param>
|
||||
/// <param name="args">事件参数</param>
|
||||
protected void TriggerEvent(string eventName, object sender, System.EventArgs args)
|
||||
{
|
||||
ActionService.TriggerEvent(eventName, sender, args);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@ namespace Ink_Canvas.Helpers.Plugins
|
||||
private readonly Version _pluginVersion;
|
||||
private bool _isInitialized;
|
||||
|
||||
public override string PluginStateKey => "ICCPP:" + (_pluginPath ?? string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// 创建 ICCPP 插件适配器
|
||||
/// </summary>
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// 增强的插件接口,提供对插件服务的访问
|
||||
/// </summary>
|
||||
public interface IEnhancedPlugin : IPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取插件服务实例
|
||||
/// </summary>
|
||||
IPluginService PluginService { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件启动时调用,在Initialize之后
|
||||
/// </summary>
|
||||
void OnStartup();
|
||||
|
||||
/// <summary>
|
||||
/// 插件关闭时调用,在Cleanup之前
|
||||
/// </summary>
|
||||
void OnShutdown();
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件的菜单项
|
||||
/// </summary>
|
||||
/// <returns>菜单项集合</returns>
|
||||
MenuItem[] GetMenuItems();
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件的工具栏按钮
|
||||
/// </summary>
|
||||
/// <returns>工具栏按钮集合</returns>
|
||||
Button[] GetToolbarButtons();
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件的状态栏信息
|
||||
/// </summary>
|
||||
/// <returns>状态栏信息</returns>
|
||||
string GetStatusBarInfo();
|
||||
|
||||
/// <summary>
|
||||
/// 插件配置变更时调用
|
||||
/// </summary>
|
||||
void OnConfigurationChanged();
|
||||
}
|
||||
}
|
||||
@@ -20,12 +20,12 @@ namespace Ink_Canvas.Helpers.Plugins
|
||||
/// <summary>
|
||||
/// 获取当前画布
|
||||
/// </summary>
|
||||
InkCanvas CurrentCanvas { get; }
|
||||
global::System.Windows.Controls.InkCanvas CurrentCanvas { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有画布页面
|
||||
/// </summary>
|
||||
List<Canvas> AllCanvasPages { get; }
|
||||
List<global::System.Windows.Controls.Canvas> AllCanvasPages { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前页面索引
|
||||
|
||||
@@ -34,6 +34,11 @@ namespace Ink_Canvas.Helpers.Plugins
|
||||
/// </summary>
|
||||
public string Id { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// 写入 <see cref="PluginManager"/> 配置时使用的稳定键(默认同类型全名;多实例类型如 SDK 目录插件应重写)。
|
||||
/// </summary>
|
||||
public virtual string PluginStateKey => GetType().FullName;
|
||||
|
||||
/// <summary>
|
||||
/// 插件路径
|
||||
/// </summary>
|
||||
|
||||
@@ -1,273 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// 插件配置管理器,允许插件管理自己的配置
|
||||
/// </summary>
|
||||
public class PluginConfigurationManager
|
||||
{
|
||||
private static readonly string PluginConfigDirectory = Path.Combine(App.RootPath, "PluginConfigs");
|
||||
private static readonly Dictionary<string, Dictionary<string, object>> _pluginConfigs = new Dictionary<string, Dictionary<string, object>>();
|
||||
private static readonly object _lockObject = new object();
|
||||
|
||||
static PluginConfigurationManager()
|
||||
{
|
||||
// 确保配置目录存在
|
||||
if (!Directory.Exists(PluginConfigDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(PluginConfigDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件配置值
|
||||
/// </summary>
|
||||
/// <typeparam name="T">配置值类型</typeparam>
|
||||
/// <param name="pluginName">插件名称</param>
|
||||
/// <param name="key">配置键</param>
|
||||
/// <param name="defaultValue">默认值</param>
|
||||
/// <returns>配置值</returns>
|
||||
public static T GetConfiguration<T>(string pluginName, string key, T defaultValue = default(T))
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_pluginConfigs.TryGetValue(pluginName, out var pluginConfig))
|
||||
{
|
||||
if (pluginConfig.TryGetValue(key, out var value))
|
||||
{
|
||||
if (value is T typedValue)
|
||||
{
|
||||
return typedValue;
|
||||
}
|
||||
|
||||
// 尝试类型转换
|
||||
try
|
||||
{
|
||||
return (T)Convert.ChangeType(value, typeof(T));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"获取插件 {pluginName} 配置 {key} 时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置插件配置值
|
||||
/// </summary>
|
||||
/// <typeparam name="T">配置值类型</typeparam>
|
||||
/// <param name="pluginName">插件名称</param>
|
||||
/// <param name="key">配置键</param>
|
||||
/// <param name="value">配置值</param>
|
||||
public static void SetConfiguration<T>(string pluginName, string key, T value)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_pluginConfigs.ContainsKey(pluginName))
|
||||
{
|
||||
_pluginConfigs[pluginName] = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
_pluginConfigs[pluginName][key] = value;
|
||||
|
||||
// 异步保存配置
|
||||
Task.Run(() => SavePluginConfiguration(pluginName));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"设置插件 {pluginName} 配置 {key} 时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除插件配置
|
||||
/// </summary>
|
||||
/// <param name="pluginName">插件名称</param>
|
||||
/// <param name="key">配置键</param>
|
||||
public static void RemoveConfiguration(string pluginName, string key)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_pluginConfigs.TryGetValue(pluginName, out var pluginConfig))
|
||||
{
|
||||
if (pluginConfig.Remove(key))
|
||||
{
|
||||
// 异步保存配置
|
||||
Task.Run(() => SavePluginConfiguration(pluginName));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"删除插件 {pluginName} 配置 {key} 时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件的所有配置
|
||||
/// </summary>
|
||||
/// <param name="pluginName">插件名称</param>
|
||||
/// <returns>配置字典</returns>
|
||||
public static Dictionary<string, object> GetAllConfigurations(string pluginName)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (_pluginConfigs.TryGetValue(pluginName, out var pluginConfig))
|
||||
{
|
||||
return new Dictionary<string, object>(pluginConfig);
|
||||
}
|
||||
return new Dictionary<string, object>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除插件的所有配置
|
||||
/// </summary>
|
||||
/// <param name="pluginName">插件名称</param>
|
||||
public static void ClearAllConfigurations(string pluginName)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_pluginConfigs.Remove(pluginName))
|
||||
{
|
||||
// 删除配置文件
|
||||
string configFile = Path.Combine(PluginConfigDirectory, $"{pluginName}.json");
|
||||
if (File.Exists(configFile))
|
||||
{
|
||||
File.Delete(configFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"清除插件 {pluginName} 所有配置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载插件配置
|
||||
/// </summary>
|
||||
/// <param name="pluginName">插件名称</param>
|
||||
public static void LoadPluginConfiguration(string pluginName)
|
||||
{
|
||||
try
|
||||
{
|
||||
string configFile = Path.Combine(PluginConfigDirectory, $"{pluginName}.json");
|
||||
if (File.Exists(configFile))
|
||||
{
|
||||
string json = File.ReadAllText(configFile);
|
||||
var config = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
_pluginConfigs[pluginName] = config ?? new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
LogHelper.WriteLogToFile($"已加载插件 {pluginName} 的配置");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"加载插件 {pluginName} 配置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存插件配置
|
||||
/// </summary>
|
||||
/// <param name="pluginName">插件名称</param>
|
||||
private static void SavePluginConfiguration(string pluginName)
|
||||
{
|
||||
try
|
||||
{
|
||||
Dictionary<string, object> pluginConfig;
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (!_pluginConfigs.TryGetValue(pluginName, out pluginConfig))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string configFile = Path.Combine(PluginConfigDirectory, $"{pluginName}.json");
|
||||
string json = JsonConvert.SerializeObject(pluginConfig, Formatting.Indented);
|
||||
File.WriteAllText(configFile, json);
|
||||
|
||||
LogHelper.WriteLogToFile($"已保存插件 {pluginName} 的配置");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"保存插件 {pluginName} 配置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载所有插件的配置
|
||||
/// </summary>
|
||||
public static void LoadAllPluginConfigurations()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(PluginConfigDirectory))
|
||||
{
|
||||
string[] configFiles = Directory.GetFiles(PluginConfigDirectory, "*.json");
|
||||
foreach (string configFile in configFiles)
|
||||
{
|
||||
string pluginName = Path.GetFileNameWithoutExtension(configFile);
|
||||
LoadPluginConfiguration(pluginName);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"加载所有插件配置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存所有插件的配置
|
||||
/// </summary>
|
||||
public static void SaveAllPluginConfigurations()
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
foreach (string pluginName in _pluginConfigs.Keys)
|
||||
{
|
||||
SavePluginConfiguration(pluginName);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"保存所有插件配置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using Ink_Canvas.Windows;
|
||||
using InkCanvasForClass.PluginHost;
|
||||
using InkCanvasForClass.PluginSdk;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -71,6 +73,23 @@ namespace Ink_Canvas.Helpers.Plugins
|
||||
/// </summary>
|
||||
private Dictionary<string, string> _pluginHashes = new Dictionary<string, string>();
|
||||
|
||||
/// <summary>
|
||||
/// SDK 插件程序集(按主 DLL 路径缓存)
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, Assembly> _sdkAssembliesByPath =
|
||||
new Dictionary<string, Assembly>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// 已加载的 SDK 插件核心实例(键为插件目录名)
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, IInkCanvasPlugin> _sdkCoreByFolderId =
|
||||
new Dictionary<string, IInkCanvasPlugin>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// SDK 插件登记的菜单 / 工具栏 / 设置页,供主窗口挂载。
|
||||
/// </summary>
|
||||
public CollectingPluginRegistry ExtensionRegistry { get; } = new CollectingPluginRegistry();
|
||||
|
||||
private PluginManager()
|
||||
{
|
||||
// 确保插件目录存在
|
||||
@@ -102,6 +121,60 @@ namespace Ink_Canvas.Helpers.Plugins
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetPluginStateKey(IPlugin plugin)
|
||||
{
|
||||
if (plugin is PluginBase pluginBase)
|
||||
{
|
||||
return pluginBase.PluginStateKey;
|
||||
}
|
||||
|
||||
return plugin.GetType().FullName;
|
||||
}
|
||||
|
||||
public IInkCanvasPlugin GetSdkPluginInstance(string folderId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(folderId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _sdkCoreByFolderId.TryGetValue(folderId, out var core) ? core : null;
|
||||
}
|
||||
|
||||
public IReadOnlyList<IInkCanvasPlugin> GetAllSdkPluginInstances()
|
||||
{
|
||||
return _sdkCoreByFolderId.Values.ToList();
|
||||
}
|
||||
|
||||
public IInkCanvasPlugin GetSdkPluginByName(string name)
|
||||
{
|
||||
return _sdkCoreByFolderId.Values.FirstOrDefault(p => p.Name == name);
|
||||
}
|
||||
|
||||
public void SetSdkPluginEnabledByName(string name, bool enable)
|
||||
{
|
||||
var adapter = Plugins.OfType<SdkPluginAdapter>().FirstOrDefault(a =>
|
||||
a.Name == name || string.Equals(a.FolderId, name, StringComparison.OrdinalIgnoreCase));
|
||||
if (adapter == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TogglePlugin(adapter, enable);
|
||||
}
|
||||
|
||||
public void UnloadSdkPluginByName(string name)
|
||||
{
|
||||
var adapter = Plugins.OfType<SdkPluginAdapter>().FirstOrDefault(a =>
|
||||
a.Name == name || string.Equals(a.FolderId, name, StringComparison.OrdinalIgnoreCase));
|
||||
if (adapter == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UnloadPlugin(adapter, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化插件系统
|
||||
/// </summary>
|
||||
@@ -123,6 +196,10 @@ namespace Ink_Canvas.Helpers.Plugins
|
||||
LogHelper.WriteLogToFile("正在加载外部插件...");
|
||||
LoadExternalPlugins();
|
||||
|
||||
// 加载 Plugins 子目录中的 SDK 插件(IInkCanvasPlugin)
|
||||
LogHelper.WriteLogToFile("正在加载 SDK 插件(子目录)...");
|
||||
LoadSdkPluginsFromSubfolders();
|
||||
|
||||
// 启用已配置为启用的插件
|
||||
LogHelper.WriteLogToFile("正在应用配置的插件状态...");
|
||||
EnableConfiguredPlugins();
|
||||
@@ -214,6 +291,87 @@ namespace Ink_Canvas.Helpers.Plugins
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 扫描 <see cref="PluginsDirectory"/> 下一层子目录,加载实现 <see cref="IInkCanvasPlugin"/> 的 SDK 插件。
|
||||
/// </summary>
|
||||
private void LoadSdkPluginsFromSubfolders()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(PluginsDirectory))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var pluginDir in Directory.GetDirectories(PluginsDirectory))
|
||||
{
|
||||
var folderId = Path.GetFileName(pluginDir.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar));
|
||||
if (string.IsNullOrEmpty(folderId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var dllFiles = Directory.GetFiles(pluginDir, "*.dll", SearchOption.AllDirectories);
|
||||
var mainDll = dllFiles.FirstOrDefault(f =>
|
||||
f.IndexOf($"{Path.DirectorySeparatorChar}lib{Path.DirectorySeparatorChar}", StringComparison.OrdinalIgnoreCase) < 0 &&
|
||||
f.IndexOf($"{Path.AltDirectorySeparatorChar}lib{Path.AltDirectorySeparatorChar}", StringComparison.OrdinalIgnoreCase) < 0 &&
|
||||
f.IndexOf($"{Path.DirectorySeparatorChar}runtimes{Path.DirectorySeparatorChar}", StringComparison.OrdinalIgnoreCase) < 0 &&
|
||||
f.IndexOf($"{Path.AltDirectorySeparatorChar}runtimes{Path.AltDirectorySeparatorChar}", StringComparison.OrdinalIgnoreCase) < 0);
|
||||
|
||||
if (mainDll == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!_sdkAssembliesByPath.TryGetValue(mainDll, out var assembly))
|
||||
{
|
||||
assembly = Assembly.LoadFrom(mainDll);
|
||||
_sdkAssembliesByPath[mainDll] = assembly;
|
||||
}
|
||||
|
||||
Type pluginType = null;
|
||||
try
|
||||
{
|
||||
pluginType = assembly.GetTypes()
|
||||
.FirstOrDefault(t =>
|
||||
typeof(IInkCanvasPlugin).IsAssignableFrom(t) &&
|
||||
!t.IsAbstract &&
|
||||
!t.IsInterface &&
|
||||
t.IsClass);
|
||||
}
|
||||
catch (ReflectionTypeLoadException ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"枚举 SDK 插件类型失败 {folderId}: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
if (pluginType == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var core = (IInkCanvasPlugin)Activator.CreateInstance(pluginType);
|
||||
_sdkCoreByFolderId[folderId] = core;
|
||||
|
||||
var adapter = new SdkPluginAdapter(folderId, core, mainDll);
|
||||
adapter.Initialize();
|
||||
Plugins.Add(adapter);
|
||||
|
||||
LogHelper.WriteLogToFile($"已加载 SDK 插件(子目录): {core.Name} — {folderId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"加载 SDK 插件目录 {folderId} 失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"扫描 SDK 插件子目录失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载单个外部插件
|
||||
/// </summary>
|
||||
@@ -349,7 +507,7 @@ namespace Ink_Canvas.Helpers.Plugins
|
||||
{
|
||||
try
|
||||
{
|
||||
string pluginTypeName = plugin.GetType().FullName;
|
||||
string pluginTypeName = GetPluginStateKey(plugin);
|
||||
|
||||
// 检查配置中的插件状态
|
||||
if (PluginStates.TryGetValue(pluginTypeName, out bool enabled))
|
||||
@@ -434,7 +592,7 @@ namespace Ink_Canvas.Helpers.Plugins
|
||||
{
|
||||
if (sender is IPlugin plugin)
|
||||
{
|
||||
string pluginTypeName = plugin.GetType().FullName;
|
||||
string pluginTypeName = GetPluginStateKey(plugin);
|
||||
|
||||
// 更新配置状态
|
||||
if (!PluginStates.ContainsKey(pluginTypeName) || PluginStates[pluginTypeName] != isEnabled)
|
||||
@@ -537,7 +695,7 @@ namespace Ink_Canvas.Helpers.Plugins
|
||||
|
||||
// 保存插件的当前状态
|
||||
bool wasEnabled = plugin.IsEnabled;
|
||||
string pluginTypeName = plugin.GetType().FullName;
|
||||
string pluginTypeName = GetPluginStateKey(plugin);
|
||||
|
||||
// 卸载插件
|
||||
UnloadPlugin(plugin);
|
||||
@@ -564,7 +722,7 @@ namespace Ink_Canvas.Helpers.Plugins
|
||||
}
|
||||
|
||||
// 更新配置(如果类型名称变化)
|
||||
string newPluginTypeName = newPlugin.GetType().FullName;
|
||||
string newPluginTypeName = GetPluginStateKey(newPlugin);
|
||||
if (pluginTypeName != newPluginTypeName && PluginStates.ContainsKey(pluginTypeName))
|
||||
{
|
||||
bool state = PluginStates[pluginTypeName];
|
||||
@@ -614,10 +772,15 @@ namespace Ink_Canvas.Helpers.Plugins
|
||||
// 从插件集合中移除
|
||||
Plugins.Remove(plugin);
|
||||
|
||||
if (plugin is SdkPluginAdapter sdkAdapter)
|
||||
{
|
||||
_sdkCoreByFolderId.Remove(sdkAdapter.FolderId);
|
||||
}
|
||||
|
||||
// 从配置中移除(如果需要)
|
||||
if (removeFromConfig && plugin.GetType() != null)
|
||||
{
|
||||
string pluginTypeName = plugin.GetType().FullName;
|
||||
string pluginTypeName = GetPluginStateKey(plugin);
|
||||
if (PluginStates.ContainsKey(pluginTypeName))
|
||||
{
|
||||
PluginStates.Remove(pluginTypeName);
|
||||
@@ -706,7 +869,7 @@ namespace Ink_Canvas.Helpers.Plugins
|
||||
|
||||
// 记录插件信息,用于日志
|
||||
string pluginName = plugin.Name;
|
||||
string pluginTypeName = plugin.GetType().FullName;
|
||||
string pluginTypeName = GetPluginStateKey(plugin);
|
||||
|
||||
LogHelper.WriteLogToFile($"开始切换插件 {pluginName} 状态为: {(enable ? "启用" : "禁用")}");
|
||||
|
||||
@@ -919,7 +1082,7 @@ namespace Ink_Canvas.Helpers.Plugins
|
||||
if (!string.IsNullOrEmpty(pluginPath) && File.Exists(pluginPath))
|
||||
{
|
||||
// 记录插件类型名称,用于后续状态检查
|
||||
string pluginTypeName = plugin.GetType().FullName;
|
||||
string pluginTypeName = GetPluginStateKey(plugin);
|
||||
bool targetState = enable;
|
||||
|
||||
// 使用调度器确保在UI线程执行热重载
|
||||
@@ -1322,7 +1485,7 @@ namespace Ink_Canvas.Helpers.Plugins
|
||||
// 对比配置,查找变更的插件
|
||||
foreach (var plugin in Plugins.ToList()) // 创建副本进行遍历,避免集合修改异常
|
||||
{
|
||||
string pluginTypeName = plugin.GetType().FullName;
|
||||
string pluginTypeName = GetPluginStateKey(plugin);
|
||||
|
||||
// 检查插件在配置中是否存在
|
||||
if (PluginStates.TryGetValue(pluginTypeName, out bool shouldBeEnabled))
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace Ink_Canvas.Helpers.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// 在加载任何 SDK 插件之前初始化宿主上下文(实现 <see cref="IPluginContext"/> 与 <see cref="IPluginService"/>)。
|
||||
/// </summary>
|
||||
public static class PluginRuntime
|
||||
{
|
||||
private static PluginSdkHostContext _context;
|
||||
|
||||
public static PluginSdkHostContext SdkContext => _context;
|
||||
|
||||
/// <summary>与 <see cref="SdkContext"/> 相同实例,便于旧代码通过 <see cref="PluginServiceManager"/> 访问。</summary>
|
||||
public static IPluginService Services => SdkContext != null ? (IPluginService)SdkContext : null;
|
||||
|
||||
public static void Initialize(MainWindow mainWindow)
|
||||
{
|
||||
if (_context == null)
|
||||
{
|
||||
_context = new PluginSdkHostContext();
|
||||
}
|
||||
|
||||
_context.SetMainWindow(mainWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,509 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// 插件服务管理器,实现IPluginService接口,提供对软件内部功能的访问
|
||||
/// 兼容旧代码的薄门面:与 <see cref="PluginSdkHostContext"/> 为同一实例,实现 <see cref="IPluginService"/>。
|
||||
/// </summary>
|
||||
public class PluginServiceManager : IPluginService
|
||||
public static class PluginServiceManager
|
||||
{
|
||||
private static PluginServiceManager _instance;
|
||||
private MainWindow _mainWindow;
|
||||
private Dictionary<string, EventHandler> _eventHandlers;
|
||||
|
||||
/// <summary>
|
||||
/// 单例实例
|
||||
/// </summary>
|
||||
public static PluginServiceManager Instance
|
||||
public static IPluginService Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
var ctx = PluginRuntime.SdkContext;
|
||||
if (ctx == null)
|
||||
{
|
||||
_instance = new PluginServiceManager();
|
||||
throw new InvalidOperationException("插件宿主尚未初始化:请先调用 PluginRuntime.Initialize(MainWindow)。");
|
||||
}
|
||||
return _instance;
|
||||
|
||||
return (IPluginService)ctx;
|
||||
}
|
||||
}
|
||||
|
||||
private PluginServiceManager()
|
||||
{
|
||||
_eventHandlers = new Dictionary<string, EventHandler>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置主窗口引用
|
||||
/// </summary>
|
||||
/// <param name="mainWindow">主窗口实例</param>
|
||||
public void SetMainWindow(MainWindow mainWindow)
|
||||
{
|
||||
_mainWindow = mainWindow;
|
||||
}
|
||||
|
||||
#region 窗口和UI访问
|
||||
|
||||
public Window MainWindow => _mainWindow;
|
||||
|
||||
public InkCanvas CurrentCanvas => null; // 暂时返回null,避免访问权限问题
|
||||
|
||||
public List<Canvas> AllCanvasPages => new List<Canvas>(); // 暂时返回空列表
|
||||
|
||||
public int CurrentPageIndex => 0; // 暂时返回0
|
||||
|
||||
public int TotalPageCount => 0; // 暂时返回0
|
||||
|
||||
public FrameworkElement FloatingToolBar => _mainWindow?.ViewboxFloatingBar;
|
||||
|
||||
public FrameworkElement LeftPanel => _mainWindow?.BlackboardLeftSide;
|
||||
|
||||
public FrameworkElement RightPanel => _mainWindow?.BlackboardRightSide;
|
||||
|
||||
public FrameworkElement TopPanel => _mainWindow?.BorderTools;
|
||||
|
||||
public FrameworkElement BottomPanel => _mainWindow?.BorderSettings;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 绘制工具状态
|
||||
|
||||
public int CurrentDrawingMode => 0; // 暂时返回0
|
||||
|
||||
public double CurrentInkWidth => 2.5; // 暂时返回默认值
|
||||
|
||||
public Color CurrentInkColor => Colors.Black; // 暂时返回默认值
|
||||
|
||||
public double CurrentHighlighterWidth => 20.0; // 暂时返回默认值
|
||||
|
||||
public int CurrentEraserSize => 2; // 暂时返回默认值
|
||||
|
||||
public int CurrentEraserType => 0; // 暂时返回默认值
|
||||
|
||||
public int CurrentEraserShape => 0; // 暂时返回默认值
|
||||
|
||||
public double CurrentInkAlpha => 255.0; // 暂时返回默认值
|
||||
|
||||
public int CurrentInkStyle => 0; // 暂时返回默认值
|
||||
|
||||
public string CurrentBackgroundColor => "#162924"; // 暂时返回默认值
|
||||
|
||||
#endregion
|
||||
|
||||
#region 应用状态
|
||||
|
||||
public bool IsDarkTheme => false; // 暂时返回默认值
|
||||
|
||||
public bool IsWhiteboardMode => false; // 暂时返回默认值
|
||||
|
||||
public bool IsPPTMode => false; // 暂时返回默认值
|
||||
|
||||
public bool IsFullScreenMode => false; // 暂时返回默认值
|
||||
|
||||
public bool IsCanvasMode => true; // 暂时返回默认值
|
||||
|
||||
public bool IsSelectionMode => false; // 暂时返回默认值
|
||||
|
||||
public bool IsEraserMode => false; // 暂时返回默认值
|
||||
|
||||
public bool IsShapeDrawingMode => false; // 暂时返回默认值
|
||||
|
||||
public bool IsHighlighterMode => false; // 暂时返回默认值
|
||||
|
||||
#endregion
|
||||
|
||||
#region IGetService 实现
|
||||
|
||||
public bool CanUndo => false; // 暂时返回默认值
|
||||
|
||||
public bool CanRedo => false; // 暂时返回默认值
|
||||
|
||||
public T GetSetting<T>(string key, T defaultValue = default(T))
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public List<IPlugin> GetAllPlugins()
|
||||
{
|
||||
return new List<IPlugin>(PluginManager.Instance.Plugins);
|
||||
}
|
||||
|
||||
public IPlugin GetPlugin(string pluginName)
|
||||
{
|
||||
return PluginManager.Instance.Plugins.FirstOrDefault(p => p.Name == pluginName);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IWindowService 实现
|
||||
|
||||
public void ShowSettingsWindow()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void HideSettingsWindow()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void ShowPluginSettingsWindow()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void HidePluginSettingsWindow()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void ShowHelpWindow()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void HideHelpWindow()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void ShowAboutWindow()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void HideAboutWindow()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void ShowNotification(string message, NotificationType type = NotificationType.Info)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public bool ShowConfirmDialog(string message, string title = "确认")
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
return false;
|
||||
}
|
||||
|
||||
public string ShowInputDialog(string message, string title = "输入", string defaultValue = "")
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public void SetFullScreen(bool isFullScreen)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SetTopMost(bool isTopMost)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SetWindowVisibility(bool isVisible)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void MinimizeWindow()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void MaximizeWindow()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void RestoreWindow()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void CloseWindow()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SetWindowPosition(double x, double y)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SetWindowSize(double width, double height)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public (double x, double y) GetWindowPosition()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
return (0, 0);
|
||||
}
|
||||
|
||||
public (double width, double height) GetWindowSize()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
return (800, 600);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IActionService 实现
|
||||
|
||||
public void ClearCanvas()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void ClearAllCanvases()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void AddNewPage()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void DeleteCurrentPage()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SwitchToPage(int pageIndex)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void NextPage()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void PreviousPage()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SetDrawingMode(int mode)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SetInkWidth(double width)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SetInkColor(Color color)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SetHighlighterWidth(double width)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SetEraserSize(int size)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SetEraserType(int type)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SetEraserShape(int shape)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SetInkAlpha(double alpha)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SetInkStyle(int style)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SetBackgroundColor(string color)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SaveCanvas(string filePath)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void LoadCanvas(string filePath)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void ExportAsImage(string filePath, string format)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void ExportAsPDF(string filePath)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void Undo()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void Redo()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SelectAll()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void DeselectAll()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void DeleteSelected()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void CopySelected()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void CutSelected()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void Paste()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SetSetting<T>(string key, T value)
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void SaveSettings()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void LoadSettings()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void ResetSettings()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void EnablePlugin(string pluginName)
|
||||
{
|
||||
var plugin = GetPlugin(pluginName);
|
||||
if (plugin != null)
|
||||
{
|
||||
PluginManager.Instance.TogglePlugin(plugin, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void DisablePlugin(string pluginName)
|
||||
{
|
||||
var plugin = GetPlugin(pluginName);
|
||||
if (plugin != null)
|
||||
{
|
||||
PluginManager.Instance.TogglePlugin(plugin, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnloadPlugin(string pluginName)
|
||||
{
|
||||
var plugin = GetPlugin(pluginName);
|
||||
if (plugin != null)
|
||||
{
|
||||
PluginManager.Instance.UnloadPlugin(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterEventHandler(string eventName, EventHandler handler)
|
||||
{
|
||||
if (!_eventHandlers.ContainsKey(eventName))
|
||||
{
|
||||
_eventHandlers[eventName] = handler;
|
||||
}
|
||||
else
|
||||
{
|
||||
_eventHandlers[eventName] += handler;
|
||||
}
|
||||
}
|
||||
|
||||
public void UnregisterEventHandler(string eventName, EventHandler handler)
|
||||
{
|
||||
if (_eventHandlers.ContainsKey(eventName))
|
||||
{
|
||||
_eventHandlers[eventName] -= handler;
|
||||
}
|
||||
}
|
||||
|
||||
public void TriggerEvent(string eventName, object sender, EventArgs args)
|
||||
{
|
||||
if (_eventHandlers.ContainsKey(eventName))
|
||||
{
|
||||
_eventHandlers[eventName]?.Invoke(sender, args);
|
||||
}
|
||||
}
|
||||
|
||||
public void RestartApplication()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void ExitApplication()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void CheckForUpdates()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void OpenHelpDocument()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
public void OpenAboutPage()
|
||||
{
|
||||
// 暂时不实现,避免访问权限问题
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,276 +0,0 @@
|
||||
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} 开始初始化");
|
||||
|
||||
// 示例:加载配置
|
||||
LoadConfig();
|
||||
|
||||
// 示例:注册自定义事件
|
||||
// MainWindow.Instance.SomeEvent += OnSomeEvent;
|
||||
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 初始化完成");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启用插件
|
||||
/// 在这里激活插件功能
|
||||
/// </summary>
|
||||
public override void Enable()
|
||||
{
|
||||
// 先调用基类方法,这样会设置插件状态和记录日志
|
||||
base.Enable();
|
||||
|
||||
// TODO: 在这里启用插件功能
|
||||
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 已启用");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 禁用插件
|
||||
/// 在这里停用插件功能
|
||||
/// </summary>
|
||||
public override void Disable()
|
||||
{
|
||||
// 先调用基类方法,这样会设置插件状态和记录日志
|
||||
base.Disable();
|
||||
|
||||
// TODO: 在这里禁用插件功能
|
||||
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 已禁用");
|
||||
}
|
||||
|
||||
/// <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);
|
||||
|
||||
// 设置控件内容
|
||||
Content = panel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.Windows.Controls;
|
||||
using InkCanvasForClass.PluginHost;
|
||||
using InkCanvasForClass.PluginSdk;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// 将基于 <see cref="IInkCanvasPlugin"/> 的外部插件适配为宿主统一的 <see cref="IPlugin"/>。
|
||||
/// </summary>
|
||||
public sealed class SdkPluginAdapter : PluginBase
|
||||
{
|
||||
private readonly IInkCanvasPlugin _core;
|
||||
private readonly string _folderId;
|
||||
|
||||
public SdkPluginAdapter(string folderId, IInkCanvasPlugin core, string mainAssemblyPath)
|
||||
{
|
||||
_folderId = folderId ?? throw new ArgumentNullException(nameof(folderId));
|
||||
_core = core ?? throw new ArgumentNullException(nameof(core));
|
||||
PluginPath = mainAssemblyPath ?? string.Empty;
|
||||
}
|
||||
|
||||
public string FolderId => _folderId;
|
||||
|
||||
public IInkCanvasPlugin Core => _core;
|
||||
|
||||
public override string PluginStateKey => "SdkFolder:" + _folderId;
|
||||
|
||||
public override string Name => _core.Name;
|
||||
|
||||
public override string Description => _core.Description;
|
||||
|
||||
public override Version Version => _core.Version;
|
||||
|
||||
public override string Author => _core.Author;
|
||||
|
||||
public override bool IsBuiltIn => false;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
var ctx = PluginRuntime.SdkContext;
|
||||
if (ctx == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"SDK 插件 {_core.Name} 初始化失败:宿主上下文未就绪", LogHelper.LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
_core.Initialize(ctx);
|
||||
|
||||
var registry = PluginManager.Instance.ExtensionRegistry;
|
||||
registry.SetCurrentPluginId(_folderId);
|
||||
try
|
||||
{
|
||||
if (_core is InkCanvasPluginBase pluginBase)
|
||||
{
|
||||
pluginBase.RegisterExtensions(registry);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
registry.SetCurrentPluginId(string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Enable()
|
||||
{
|
||||
if (IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_core is InkCanvasPluginBase b)
|
||||
{
|
||||
b.IsEnabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_core.Start();
|
||||
}
|
||||
|
||||
base.Enable();
|
||||
}
|
||||
|
||||
public override void Disable()
|
||||
{
|
||||
if (!IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_core is InkCanvasPluginBase b)
|
||||
{
|
||||
b.IsEnabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_core.Stop();
|
||||
}
|
||||
|
||||
base.Disable();
|
||||
}
|
||||
|
||||
public override UserControl GetSettingsView()
|
||||
{
|
||||
return _core.GetSettingsView();
|
||||
}
|
||||
|
||||
public override void Cleanup()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_core is InkCanvasPluginBase b)
|
||||
{
|
||||
b.IsEnabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_core.Stop();
|
||||
}
|
||||
|
||||
_core.Cleanup();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"SDK 插件 {_core.Name} Cleanup 出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
base.Cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Ink;
|
||||
@@ -140,6 +140,12 @@ namespace Ink_Canvas.Helpers
|
||||
OnUndoStateChanged?.Invoke(_currentIndex > -1);
|
||||
OnRedoStateChanged?.Invoke(_currentStrokeHistory.Count - _currentIndex - 1 > 0);
|
||||
}
|
||||
|
||||
/// <summary>当前历史是否允许撤销。</summary>
|
||||
public bool CanUndo => _currentIndex > -1;
|
||||
|
||||
/// <summary>当前历史是否允许重做。</summary>
|
||||
public bool CanRedo => _currentStrokeHistory.Count > 0 && _currentStrokeHistory.Count - _currentIndex - 1 > 0;
|
||||
}
|
||||
|
||||
public class TimeMachineHistory
|
||||
|
||||
@@ -157,6 +157,10 @@
|
||||
<PackageReference Include="AForge.Math" Version="2.2.5" />
|
||||
<PackageReference Include="WebDav.Client" Version="2.9.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Plugins\SDK\InkCanvas.PluginSdk.csproj" />
|
||||
<ProjectReference Include="..\Plugins\Host\InkCanvas.PluginHost.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(MSBuildRuntimeType)' == 'Full'">
|
||||
<COMReference Include="IWshRuntimeLibrary">
|
||||
<Guid>{F935DC20-1CF0-11D0-ADB9-00C04FD58A0B}</Guid>
|
||||
|
||||
@@ -80,6 +80,12 @@ namespace Ink_Canvas
|
||||
public int BoothResolutionWidth => _boothResolutionWidth;
|
||||
public int BoothResolutionHeight => _boothResolutionHeight;
|
||||
|
||||
/// <summary>供插件系统访问的白板页面列表(只读)。</summary>
|
||||
public IList<System.Windows.Controls.Canvas> WhiteboardPages => whiteboardPages;
|
||||
|
||||
/// <summary>供插件系统访问的当前页索引。</summary>
|
||||
public int CurrentPageIndex => currentPageIndex;
|
||||
|
||||
private static Cursor _cachedPenCursor = null;
|
||||
private static readonly object _cursorLock = new object();
|
||||
|
||||
@@ -2824,7 +2830,7 @@ namespace Ink_Canvas
|
||||
{
|
||||
try
|
||||
{
|
||||
// 初始化插件管理器
|
||||
PluginRuntime.Initialize(this);
|
||||
PluginManager.Instance.Initialize();
|
||||
LogHelper.WriteLogToFile("插件系统已初始化");
|
||||
}
|
||||
|
||||
@@ -370,7 +370,7 @@ namespace Ink_Canvas
|
||||
/// <param name="autoAlignCenter">
|
||||
/// 是否自動居中浮動工具欄
|
||||
/// </param>
|
||||
private async void HideSubPanels(string mode = null, bool autoAlignCenter = false)
|
||||
internal async void HideSubPanels(string mode = null, bool autoAlignCenter = false)
|
||||
{
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
@@ -3177,7 +3177,7 @@ namespace Ink_Canvas
|
||||
/// </summary>
|
||||
/// <param name="sender">发送者</param>
|
||||
/// <param name="e">路由事件参数</param>
|
||||
private async void BtnSettings_Click(object sender, RoutedEventArgs e)
|
||||
internal async void BtnSettings_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (BorderSettings.Visibility == Visibility.Visible)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Ink;
|
||||
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
/// <summary>
|
||||
/// 供 <see cref="Helpers.Plugins.PluginSdkHostContext"/> 调用的宿主 API,封装 UI 线程与内部墨迹逻辑。
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
internal void PluginHost_RunOnUiThread(Action action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Dispatcher.CheckAccess())
|
||||
{
|
||||
action();
|
||||
}
|
||||
else
|
||||
{
|
||||
Dispatcher.Invoke(action);
|
||||
}
|
||||
}
|
||||
|
||||
internal void PluginHost_Undo()
|
||||
{
|
||||
PluginHost_RunOnUiThread(() =>
|
||||
{
|
||||
if (inkCanvas.GetSelectedStrokes().Count != 0)
|
||||
{
|
||||
GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
|
||||
inkCanvas.Select(new StrokeCollection());
|
||||
}
|
||||
|
||||
var item = timeMachine.Undo();
|
||||
ApplyHistoryToCanvas(item);
|
||||
});
|
||||
}
|
||||
|
||||
internal void PluginHost_Redo()
|
||||
{
|
||||
PluginHost_RunOnUiThread(() =>
|
||||
{
|
||||
if (inkCanvas.GetSelectedStrokes().Count != 0)
|
||||
{
|
||||
GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
|
||||
inkCanvas.Select(new StrokeCollection());
|
||||
}
|
||||
|
||||
var item = timeMachine.Redo();
|
||||
ApplyHistoryToCanvas(item);
|
||||
});
|
||||
}
|
||||
|
||||
internal void PluginHost_ClearInk(bool erasedByCode)
|
||||
{
|
||||
PluginHost_RunOnUiThread(() => ClearStrokes(erasedByCode));
|
||||
}
|
||||
|
||||
internal bool PluginHost_CanUndo()
|
||||
{
|
||||
if (Dispatcher.CheckAccess())
|
||||
{
|
||||
return timeMachine != null && timeMachine.CanUndo;
|
||||
}
|
||||
|
||||
return Dispatcher.Invoke(() => timeMachine != null && timeMachine.CanUndo);
|
||||
}
|
||||
|
||||
internal bool PluginHost_CanRedo()
|
||||
{
|
||||
if (Dispatcher.CheckAccess())
|
||||
{
|
||||
return timeMachine != null && timeMachine.CanRedo;
|
||||
}
|
||||
|
||||
return Dispatcher.Invoke(() => timeMachine != null && timeMachine.CanRedo);
|
||||
}
|
||||
|
||||
internal void PluginHost_ShowInfo(string title, string message)
|
||||
{
|
||||
PluginHost_RunOnUiThread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
MessageBox.Show(message ?? string.Empty, title ?? string.Empty);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略对话框失败,避免插件拖垮宿主
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
internal bool PluginHost_ShowConfirm(string title, string message)
|
||||
{
|
||||
if (Dispatcher.CheckAccess())
|
||||
{
|
||||
try
|
||||
{
|
||||
return MessageBox.Show(message ?? string.Empty, title ?? string.Empty, MessageBoxButton.YesNo) ==
|
||||
MessageBoxResult.Yes;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return Dispatcher.Invoke(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
return MessageBox.Show(message ?? string.Empty, title ?? string.Empty, MessageBoxButton.YesNo) ==
|
||||
MessageBoxResult.Yes;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
internal string PluginHost_ShowInput(string title, string message, string defaultValue)
|
||||
{
|
||||
string Show()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Microsoft.VisualBasic.Interaction.InputBox(message ?? string.Empty, title ?? string.Empty,
|
||||
defaultValue ?? string.Empty);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return defaultValue ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
if (Dispatcher.CheckAccess())
|
||||
{
|
||||
return Show();
|
||||
}
|
||||
|
||||
return Dispatcher.Invoke(Show);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,6 +230,11 @@
|
||||
"System.Memory": "4.5.4"
|
||||
}
|
||||
},
|
||||
"System.ComponentModel.Annotations": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg=="
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.5",
|
||||
@@ -330,6 +335,19 @@
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ=="
|
||||
},
|
||||
"inkcanvas.pluginhost": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"InkCanvas.PluginSdk": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"inkcanvas.pluginsdk": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "[13.0.3, )",
|
||||
"System.ComponentModel.Annotations": "[5.0.0, )"
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.7.2/win": {
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows.Controls;
|
||||
using InkCanvasForClass.PluginSdk;
|
||||
|
||||
namespace InkCanvasForClass.PluginHost
|
||||
{
|
||||
/// <summary>
|
||||
/// 收集插件登记的菜单 / 工具栏 / 设置页,供宿主窗口在启动后统一挂载。
|
||||
/// </summary>
|
||||
public sealed class CollectingPluginRegistry : IPluginRegistry
|
||||
{
|
||||
private string _currentPluginId = "";
|
||||
|
||||
public string CurrentPluginId => _currentPluginId;
|
||||
|
||||
public ObservableCollection<MenuItemRegistration> MenuItems { get; } =
|
||||
new ObservableCollection<MenuItemRegistration>();
|
||||
|
||||
public ObservableCollection<ToolbarButtonRegistration> ToolbarButtons { get; } =
|
||||
new ObservableCollection<ToolbarButtonRegistration>();
|
||||
|
||||
public ObservableCollection<SettingsPageRegistration> SettingsPages { get; } =
|
||||
new ObservableCollection<SettingsPageRegistration>();
|
||||
|
||||
public void SetCurrentPluginId(string pluginId)
|
||||
{
|
||||
_currentPluginId = pluginId ?? "";
|
||||
}
|
||||
|
||||
public void RegisterMenuItem(string groupKey, MenuItem item)
|
||||
{
|
||||
if (item == null) return;
|
||||
MenuItems.Add(new MenuItemRegistration(_currentPluginId, groupKey ?? "", item));
|
||||
}
|
||||
|
||||
public void RegisterToolbarButton(Button button)
|
||||
{
|
||||
if (button == null) return;
|
||||
ToolbarButtons.Add(new ToolbarButtonRegistration(_currentPluginId, button));
|
||||
}
|
||||
|
||||
public void RegisterSettingsPage(string pageId, string displayName, Func<UserControl> createView)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(pageId) || createView == null) return;
|
||||
SettingsPages.Add(new SettingsPageRegistration(
|
||||
_currentPluginId,
|
||||
pageId,
|
||||
displayName ?? pageId,
|
||||
createView));
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
MenuItems.Clear();
|
||||
ToolbarButtons.Clear();
|
||||
SettingsPages.Clear();
|
||||
_currentPluginId = "";
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class MenuItemRegistration
|
||||
{
|
||||
public MenuItemRegistration(string pluginId, string groupKey, MenuItem item)
|
||||
{
|
||||
PluginId = pluginId ?? "";
|
||||
GroupKey = groupKey ?? "";
|
||||
Item = item ?? throw new ArgumentNullException(nameof(item));
|
||||
}
|
||||
|
||||
public string PluginId { get; }
|
||||
public string GroupKey { get; }
|
||||
public MenuItem Item { get; }
|
||||
}
|
||||
|
||||
public sealed class ToolbarButtonRegistration
|
||||
{
|
||||
public ToolbarButtonRegistration(string pluginId, Button button)
|
||||
{
|
||||
PluginId = pluginId ?? "";
|
||||
Button = button ?? throw new ArgumentNullException(nameof(button));
|
||||
}
|
||||
|
||||
public string PluginId { get; }
|
||||
public Button Button { get; }
|
||||
}
|
||||
|
||||
public sealed class SettingsPageRegistration
|
||||
{
|
||||
public SettingsPageRegistration(string pluginId, string pageId, string displayName, Func<UserControl> createView)
|
||||
{
|
||||
PluginId = pluginId ?? "";
|
||||
PageId = pageId ?? "";
|
||||
DisplayName = displayName ?? "";
|
||||
CreateView = createView ?? throw new ArgumentNullException(nameof(createView));
|
||||
}
|
||||
|
||||
public string PluginId { get; }
|
||||
public string PageId { get; }
|
||||
public string DisplayName { get; }
|
||||
public Func<UserControl> CreateView { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<RootNamespace>InkCanvasForClass.PluginHost</RootNamespace>
|
||||
<AssemblyName>InkCanvas.PluginHost</AssemblyName>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<Nullable>disable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SDK\InkCanvas.PluginSdk.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace InkCanvasForClass.PluginSdk
|
||||
{
|
||||
/// <summary>
|
||||
/// Ink Canvas 插件接口
|
||||
/// </summary>
|
||||
public interface IInkCanvasPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// 插件唯一标识符
|
||||
/// </summary>
|
||||
string Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件名称
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件描述
|
||||
/// </summary>
|
||||
string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件版本
|
||||
/// </summary>
|
||||
Version Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件作者
|
||||
/// </summary>
|
||||
string Author { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件主页URL
|
||||
/// </summary>
|
||||
string Homepage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件图标
|
||||
/// </summary>
|
||||
ImageSource Icon { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件初始化
|
||||
/// </summary>
|
||||
/// <param name="context">插件上下文</param>
|
||||
void Initialize(IPluginContext context);
|
||||
|
||||
/// <summary>
|
||||
/// 插件启动
|
||||
/// </summary>
|
||||
void Start();
|
||||
|
||||
/// <summary>
|
||||
/// 插件停止
|
||||
/// </summary>
|
||||
void Stop();
|
||||
|
||||
/// <summary>
|
||||
/// 插件清理
|
||||
/// </summary>
|
||||
void Cleanup();
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件设置界面
|
||||
/// </summary>
|
||||
/// <returns>设置界面控件</returns>
|
||||
UserControl GetSettingsView();
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件菜单项
|
||||
/// </summary>
|
||||
/// <returns>菜单项列表</returns>
|
||||
IEnumerable<MenuItem> GetMenuItems();
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件工具栏按钮
|
||||
/// </summary>
|
||||
/// <returns>工具栏按钮列表</returns>
|
||||
IEnumerable<Button> GetToolbarButtons();
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件状态栏信息
|
||||
/// </summary>
|
||||
/// <returns>状态栏信息</returns>
|
||||
string GetStatusBarInfo();
|
||||
|
||||
/// <summary>
|
||||
/// 插件是否已启用
|
||||
/// </summary>
|
||||
bool IsEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件启用状态变更事件
|
||||
/// </summary>
|
||||
event EventHandler<bool> EnabledChanged;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,569 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Ink;
|
||||
|
||||
namespace InkCanvasForClass.PluginSdk
|
||||
{
|
||||
/// <summary>
|
||||
/// 插件上下文接口,提供对主应用程序功能的访问
|
||||
/// </summary>
|
||||
public interface IPluginContext
|
||||
{
|
||||
/// <summary>
|
||||
/// 主窗口实例
|
||||
/// </summary>
|
||||
Window MainWindow { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前画布
|
||||
/// </summary>
|
||||
System.Windows.Controls.InkCanvas CurrentCanvas { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 所有画布页面
|
||||
/// </summary>
|
||||
IList<System.Windows.Controls.Canvas> AllCanvasPages { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前页面索引
|
||||
/// </summary>
|
||||
int CurrentPageIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 总页面数
|
||||
/// </summary>
|
||||
int TotalPageCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 浮动工具栏
|
||||
/// </summary>
|
||||
FrameworkElement FloatingToolBar { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 左侧面板
|
||||
/// </summary>
|
||||
FrameworkElement LeftPanel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 右侧面板
|
||||
/// </summary>
|
||||
FrameworkElement RightPanel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 顶部面板
|
||||
/// </summary>
|
||||
FrameworkElement TopPanel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 底部面板
|
||||
/// </summary>
|
||||
FrameworkElement BottomPanel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前绘制模式
|
||||
/// </summary>
|
||||
int CurrentDrawingMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前墨迹宽度
|
||||
/// </summary>
|
||||
double CurrentInkWidth { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前墨迹颜色
|
||||
/// </summary>
|
||||
Color CurrentInkColor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前高亮笔宽度
|
||||
/// </summary>
|
||||
double CurrentHighlighterWidth { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前橡皮擦大小
|
||||
/// </summary>
|
||||
int CurrentEraserSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前橡皮擦类型
|
||||
/// </summary>
|
||||
int CurrentEraserType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前橡皮擦形状
|
||||
/// </summary>
|
||||
int CurrentEraserShape { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前墨迹透明度
|
||||
/// </summary>
|
||||
double CurrentInkAlpha { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前墨迹样式
|
||||
/// </summary>
|
||||
int CurrentInkStyle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前背景颜色
|
||||
/// </summary>
|
||||
string CurrentBackgroundColor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为深色主题
|
||||
/// </summary>
|
||||
bool IsDarkTheme { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为白板模式
|
||||
/// </summary>
|
||||
bool IsWhiteboardMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为PPT模式
|
||||
/// </summary>
|
||||
bool IsPPTMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为全屏模式
|
||||
/// </summary>
|
||||
bool IsFullScreenMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为画布模式
|
||||
/// </summary>
|
||||
bool IsCanvasMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为选择模式
|
||||
/// </summary>
|
||||
bool IsSelectionMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为橡皮擦模式
|
||||
/// </summary>
|
||||
bool IsEraserMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为形状绘制模式
|
||||
/// </summary>
|
||||
bool IsShapeDrawingMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为高亮笔模式
|
||||
/// </summary>
|
||||
bool IsHighlighterMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否可以撤销
|
||||
/// </summary>
|
||||
bool CanUndo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否可以重做
|
||||
/// </summary>
|
||||
bool CanRedo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取设置值
|
||||
/// </summary>
|
||||
/// <typeparam name="T">设置类型</typeparam>
|
||||
/// <param name="key">设置键</param>
|
||||
/// <param name="defaultValue">默认值</param>
|
||||
/// <returns>设置值</returns>
|
||||
T GetSetting<T>(string key, T defaultValue = default(T));
|
||||
|
||||
/// <summary>
|
||||
/// 设置设置值
|
||||
/// </summary>
|
||||
/// <typeparam name="T">设置类型</typeparam>
|
||||
/// <param name="key">设置键</param>
|
||||
/// <param name="value">设置值</param>
|
||||
void SetSetting<T>(string key, T value);
|
||||
|
||||
/// <summary>
|
||||
/// 保存设置
|
||||
/// </summary>
|
||||
void SaveSettings();
|
||||
|
||||
/// <summary>
|
||||
/// 加载设置
|
||||
/// </summary>
|
||||
void LoadSettings();
|
||||
|
||||
/// <summary>
|
||||
/// 重置设置
|
||||
/// </summary>
|
||||
void ResetSettings();
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有插件
|
||||
/// </summary>
|
||||
/// <returns>插件列表</returns>
|
||||
IList<IInkCanvasPlugin> GetAllPlugins();
|
||||
|
||||
/// <summary>
|
||||
/// 根据名称获取插件
|
||||
/// </summary>
|
||||
/// <param name="pluginName">插件名称</param>
|
||||
/// <returns>插件实例</returns>
|
||||
IInkCanvasPlugin GetPlugin(string pluginName);
|
||||
|
||||
/// <summary>
|
||||
/// 启用插件
|
||||
/// </summary>
|
||||
/// <param name="pluginName">插件名称</param>
|
||||
void EnablePlugin(string pluginName);
|
||||
|
||||
/// <summary>
|
||||
/// 禁用插件
|
||||
/// </summary>
|
||||
/// <param name="pluginName">插件名称</param>
|
||||
void DisablePlugin(string pluginName);
|
||||
|
||||
/// <summary>
|
||||
/// 卸载插件
|
||||
/// </summary>
|
||||
/// <param name="pluginName">插件名称</param>
|
||||
void UnloadPlugin(string pluginName);
|
||||
|
||||
/// <summary>
|
||||
/// 显示设置窗口
|
||||
/// </summary>
|
||||
void ShowSettingsWindow();
|
||||
|
||||
/// <summary>
|
||||
/// 隐藏设置窗口
|
||||
/// </summary>
|
||||
void HideSettingsWindow();
|
||||
|
||||
/// <summary>
|
||||
/// 显示插件设置窗口
|
||||
/// </summary>
|
||||
void ShowPluginSettingsWindow();
|
||||
|
||||
/// <summary>
|
||||
/// 隐藏插件设置窗口
|
||||
/// </summary>
|
||||
void HidePluginSettingsWindow();
|
||||
|
||||
/// <summary>
|
||||
/// 显示帮助窗口
|
||||
/// </summary>
|
||||
void ShowHelpWindow();
|
||||
|
||||
/// <summary>
|
||||
/// 隐藏帮助窗口
|
||||
/// </summary>
|
||||
void HideHelpWindow();
|
||||
|
||||
/// <summary>
|
||||
/// 显示关于窗口
|
||||
/// </summary>
|
||||
void ShowAboutWindow();
|
||||
|
||||
/// <summary>
|
||||
/// 隐藏关于窗口
|
||||
/// </summary>
|
||||
void HideAboutWindow();
|
||||
|
||||
/// <summary>
|
||||
/// 显示通知
|
||||
/// </summary>
|
||||
/// <param name="message">消息内容</param>
|
||||
/// <param name="type">通知类型</param>
|
||||
void ShowNotification(string message, NotificationType type = NotificationType.Info);
|
||||
|
||||
/// <summary>
|
||||
/// 显示确认对话框
|
||||
/// </summary>
|
||||
/// <param name="message">消息内容</param>
|
||||
/// <param name="title">标题</param>
|
||||
/// <returns>用户选择结果</returns>
|
||||
bool ShowConfirmDialog(string message, string title = "确认");
|
||||
|
||||
/// <summary>
|
||||
/// 显示输入对话框
|
||||
/// </summary>
|
||||
/// <param name="message">提示消息</param>
|
||||
/// <param name="title">标题</param>
|
||||
/// <param name="defaultValue">默认值</param>
|
||||
/// <returns>用户输入内容</returns>
|
||||
string ShowInputDialog(string message, string title = "输入", string defaultValue = "");
|
||||
|
||||
/// <summary>
|
||||
/// 设置全屏模式
|
||||
/// </summary>
|
||||
/// <param name="isFullScreen">是否全屏</param>
|
||||
void SetFullScreen(bool isFullScreen);
|
||||
|
||||
/// <summary>
|
||||
/// 设置置顶模式
|
||||
/// </summary>
|
||||
/// <param name="isTopMost">是否置顶</param>
|
||||
void SetTopMost(bool isTopMost);
|
||||
|
||||
/// <summary>
|
||||
/// 设置窗口可见性
|
||||
/// </summary>
|
||||
/// <param name="isVisible">是否可见</param>
|
||||
void SetWindowVisibility(bool isVisible);
|
||||
|
||||
/// <summary>
|
||||
/// 最小化窗口
|
||||
/// </summary>
|
||||
void MinimizeWindow();
|
||||
|
||||
/// <summary>
|
||||
/// 最大化窗口
|
||||
/// </summary>
|
||||
void MaximizeWindow();
|
||||
|
||||
/// <summary>
|
||||
/// 还原窗口
|
||||
/// </summary>
|
||||
void RestoreWindow();
|
||||
|
||||
/// <summary>
|
||||
/// 关闭窗口
|
||||
/// </summary>
|
||||
void CloseWindow();
|
||||
|
||||
/// <summary>
|
||||
/// 设置窗口位置
|
||||
/// </summary>
|
||||
/// <param name="x">X坐标</param>
|
||||
/// <param name="y">Y坐标</param>
|
||||
void SetWindowPosition(double x, double y);
|
||||
|
||||
/// <summary>
|
||||
/// 设置窗口大小
|
||||
/// </summary>
|
||||
/// <param name="width">宽度</param>
|
||||
/// <param name="height">高度</param>
|
||||
void SetWindowSize(double width, double height);
|
||||
|
||||
/// <summary>
|
||||
/// 获取窗口位置
|
||||
/// </summary>
|
||||
/// <returns>窗口位置</returns>
|
||||
(double x, double y) GetWindowPosition();
|
||||
|
||||
/// <summary>
|
||||
/// 获取窗口大小
|
||||
/// </summary>
|
||||
/// <returns>窗口大小</returns>
|
||||
(double width, double height) GetWindowSize();
|
||||
|
||||
/// <summary>
|
||||
/// 清除当前画布
|
||||
/// </summary>
|
||||
void ClearCanvas();
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有画布
|
||||
/// </summary>
|
||||
void ClearAllCanvases();
|
||||
|
||||
/// <summary>
|
||||
/// 添加新页面
|
||||
/// </summary>
|
||||
void AddNewPage();
|
||||
|
||||
/// <summary>
|
||||
/// 删除当前页面
|
||||
/// </summary>
|
||||
void DeleteCurrentPage();
|
||||
|
||||
/// <summary>
|
||||
/// 切换到指定页面
|
||||
/// </summary>
|
||||
/// <param name="pageIndex">页面索引</param>
|
||||
void SwitchToPage(int pageIndex);
|
||||
|
||||
/// <summary>
|
||||
/// 下一页
|
||||
/// </summary>
|
||||
void NextPage();
|
||||
|
||||
/// <summary>
|
||||
/// 上一页
|
||||
/// </summary>
|
||||
void PreviousPage();
|
||||
|
||||
/// <summary>
|
||||
/// 设置绘制模式
|
||||
/// </summary>
|
||||
/// <param name="mode">绘制模式</param>
|
||||
void SetDrawingMode(int mode);
|
||||
|
||||
/// <summary>
|
||||
/// 设置墨迹宽度
|
||||
/// </summary>
|
||||
/// <param name="width">宽度</param>
|
||||
void SetInkWidth(double width);
|
||||
|
||||
/// <summary>
|
||||
/// 设置墨迹颜色
|
||||
/// </summary>
|
||||
/// <param name="color">颜色</param>
|
||||
void SetInkColor(Color color);
|
||||
|
||||
/// <summary>
|
||||
/// 设置高亮笔宽度
|
||||
/// </summary>
|
||||
/// <param name="width">宽度</param>
|
||||
void SetHighlighterWidth(double width);
|
||||
|
||||
/// <summary>
|
||||
/// 设置橡皮擦大小
|
||||
/// </summary>
|
||||
/// <param name="size">大小</param>
|
||||
void SetEraserSize(int size);
|
||||
|
||||
/// <summary>
|
||||
/// 设置橡皮擦类型
|
||||
/// </summary>
|
||||
/// <param name="type">类型</param>
|
||||
void SetEraserType(int type);
|
||||
|
||||
/// <summary>
|
||||
/// 设置橡皮擦形状
|
||||
/// </summary>
|
||||
/// <param name="shape">形状</param>
|
||||
void SetEraserShape(int shape);
|
||||
|
||||
/// <summary>
|
||||
/// 设置墨迹透明度
|
||||
/// </summary>
|
||||
/// <param name="alpha">透明度</param>
|
||||
void SetInkAlpha(double alpha);
|
||||
|
||||
/// <summary>
|
||||
/// 设置墨迹样式
|
||||
/// </summary>
|
||||
/// <param name="style">样式</param>
|
||||
void SetInkStyle(int style);
|
||||
|
||||
/// <summary>
|
||||
/// 设置背景颜色
|
||||
/// </summary>
|
||||
/// <param name="color">颜色</param>
|
||||
void SetBackgroundColor(string color);
|
||||
|
||||
/// <summary>
|
||||
/// 保存画布
|
||||
/// </summary>
|
||||
/// <param name="filePath">文件路径</param>
|
||||
void SaveCanvas(string filePath);
|
||||
|
||||
/// <summary>
|
||||
/// 加载画布
|
||||
/// </summary>
|
||||
/// <param name="filePath">文件路径</param>
|
||||
void LoadCanvas(string filePath);
|
||||
|
||||
/// <summary>
|
||||
/// 导出为图片
|
||||
/// </summary>
|
||||
/// <param name="filePath">文件路径</param>
|
||||
/// <param name="format">格式</param>
|
||||
void ExportAsImage(string filePath, string format);
|
||||
|
||||
/// <summary>
|
||||
/// 导出为PDF
|
||||
/// </summary>
|
||||
/// <param name="filePath">文件路径</param>
|
||||
void ExportAsPDF(string filePath);
|
||||
|
||||
/// <summary>
|
||||
/// 撤销操作
|
||||
/// </summary>
|
||||
void Undo();
|
||||
|
||||
/// <summary>
|
||||
/// 重做操作
|
||||
/// </summary>
|
||||
void Redo();
|
||||
|
||||
/// <summary>
|
||||
/// 全选
|
||||
/// </summary>
|
||||
void SelectAll();
|
||||
|
||||
/// <summary>
|
||||
/// 取消选择
|
||||
/// </summary>
|
||||
void DeselectAll();
|
||||
|
||||
/// <summary>
|
||||
/// 删除选中项
|
||||
/// </summary>
|
||||
void DeleteSelected();
|
||||
|
||||
/// <summary>
|
||||
/// 复制选中项
|
||||
/// </summary>
|
||||
void CopySelected();
|
||||
|
||||
/// <summary>
|
||||
/// 剪切选中项
|
||||
/// </summary>
|
||||
void CutSelected();
|
||||
|
||||
/// <summary>
|
||||
/// 粘贴
|
||||
/// </summary>
|
||||
void Paste();
|
||||
|
||||
/// <summary>
|
||||
/// 注册事件处理器
|
||||
/// </summary>
|
||||
/// <param name="eventName">事件名称</param>
|
||||
/// <param name="handler">事件处理器</param>
|
||||
void RegisterEventHandler(string eventName, EventHandler handler);
|
||||
|
||||
/// <summary>
|
||||
/// 注销事件处理器
|
||||
/// </summary>
|
||||
/// <param name="eventName">事件名称</param>
|
||||
/// <param name="handler">事件处理器</param>
|
||||
void UnregisterEventHandler(string eventName, EventHandler handler);
|
||||
|
||||
/// <summary>
|
||||
/// 触发事件
|
||||
/// </summary>
|
||||
/// <param name="eventName">事件名称</param>
|
||||
/// <param name="sender">事件发送者</param>
|
||||
/// <param name="args">事件参数</param>
|
||||
void TriggerEvent(string eventName, object sender, EventArgs args);
|
||||
|
||||
/// <summary>
|
||||
/// 重启应用程序
|
||||
/// </summary>
|
||||
void RestartApplication();
|
||||
|
||||
/// <summary>
|
||||
/// 退出应用程序
|
||||
/// </summary>
|
||||
void ExitApplication();
|
||||
|
||||
/// <summary>
|
||||
/// 检查更新
|
||||
/// </summary>
|
||||
void CheckForUpdates();
|
||||
|
||||
/// <summary>
|
||||
/// 打开帮助文档
|
||||
/// </summary>
|
||||
void OpenHelpDocument();
|
||||
|
||||
/// <summary>
|
||||
/// 打开关于页面
|
||||
/// </summary>
|
||||
void OpenAboutPage();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace InkCanvasForClass.PluginSdk
|
||||
{
|
||||
/// <summary>
|
||||
/// 方案 B:不依赖 Microsoft.Extensions.DependencyInjection 的轻量扩展注册表。
|
||||
/// 宿主在适当时机将登记项挂到菜单 / 工具栏 / 设置 UI。
|
||||
/// </summary>
|
||||
public interface IPluginRegistry
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前正在执行注册的插件目录 Id(由宿主在加载每个插件前设置)。
|
||||
/// </summary>
|
||||
string CurrentPluginId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 注册主菜单或上下文菜单中的项;<paramref name="groupKey"/> 由宿主解释(如 "Main.Plugins")。
|
||||
/// </summary>
|
||||
void RegisterMenuItem(string groupKey, MenuItem item);
|
||||
|
||||
/// <summary>
|
||||
/// 注册工具栏按钮。
|
||||
/// </summary>
|
||||
void RegisterToolbarButton(Button button);
|
||||
|
||||
/// <summary>
|
||||
/// 注册设置页;<paramref name="createView"/> 在打开设置时惰性创建。
|
||||
/// </summary>
|
||||
void RegisterSettingsPage(string pageId, string displayName, Func<UserControl> createView);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<RootNamespace>InkCanvasForClass.PluginSdk</RootNamespace>
|
||||
<UseWPF>true</UseWPF>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageId>InkCanvas.PluginSdk</PackageId>
|
||||
<PackageVersion>1.0.0</PackageVersion>
|
||||
<Authors>Ink Canvas Team</Authors>
|
||||
<Company>Ink Canvas</Company>
|
||||
<Product>Ink Canvas Plugin SDK</Product>
|
||||
<Description>SDK for developing Ink Canvas plugins - 墨迹画布插件开发SDK</Description>
|
||||
<PackageProjectUrl>https://github.com/your-org/ink-canvas</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/your-org/ink-canvas</RepositoryUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageTags>ink;canvas;plugin;sdk;wpf;drawing</PackageTags>
|
||||
<Copyright>Copyright © 2025</Copyright>
|
||||
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||
<FileVersion>1.0.0.0</FileVersion>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="README.md" Pack="true" PackagePath="README.md" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,268 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace InkCanvasForClass.PluginSdk
|
||||
{
|
||||
/// <summary>
|
||||
/// Ink Canvas 插件基类
|
||||
/// 提供插件的基本实现
|
||||
/// </summary>
|
||||
public abstract class InkCanvasPluginBase : IInkCanvasPlugin
|
||||
{
|
||||
private bool _isEnabled;
|
||||
private IPluginContext _context;
|
||||
|
||||
/// <summary>
|
||||
/// 插件唯一标识符
|
||||
/// </summary>
|
||||
public abstract string Id { get; }
|
||||
|
||||
/// <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>
|
||||
/// 插件主页URL
|
||||
/// </summary>
|
||||
public virtual string Homepage => string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 插件图标
|
||||
/// </summary>
|
||||
public virtual ImageSource Icon => null;
|
||||
|
||||
/// <summary>
|
||||
/// 插件是否已启用
|
||||
/// </summary>
|
||||
public virtual bool IsEnabled
|
||||
{
|
||||
get => _isEnabled;
|
||||
set
|
||||
{
|
||||
if (_isEnabled != value)
|
||||
{
|
||||
_isEnabled = value;
|
||||
OnEnabledChanged(value);
|
||||
EnabledChanged?.Invoke(this, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件启用状态变更事件
|
||||
/// </summary>
|
||||
public event EventHandler<bool> EnabledChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 插件上下文
|
||||
/// </summary>
|
||||
protected IPluginContext Context => _context;
|
||||
|
||||
/// <summary>
|
||||
/// 插件初始化
|
||||
/// </summary>
|
||||
/// <param name="context">插件上下文</param>
|
||||
public virtual void Initialize(IPluginContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 方案 B:在 <see cref="Initialize"/> 之后由宿主调用,用于向 <see cref="IPluginRegistry"/> 登记菜单、工具栏、设置页等。
|
||||
/// </summary>
|
||||
/// <param name="registry">宿主提供的注册表</param>
|
||||
public virtual void RegisterExtensions(IPluginRegistry registry)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件启动
|
||||
/// </summary>
|
||||
public virtual void Start()
|
||||
{
|
||||
// 默认实现为空
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件停止
|
||||
/// </summary>
|
||||
public virtual void Stop()
|
||||
{
|
||||
// 默认实现为空
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件清理
|
||||
/// </summary>
|
||||
public virtual void Cleanup()
|
||||
{
|
||||
// 默认实现为空
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件设置界面
|
||||
/// </summary>
|
||||
/// <returns>设置界面控件</returns>
|
||||
public virtual UserControl GetSettingsView()
|
||||
{
|
||||
return new UserControl();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件菜单项
|
||||
/// </summary>
|
||||
/// <returns>菜单项列表</returns>
|
||||
public virtual IEnumerable<MenuItem> GetMenuItems()
|
||||
{
|
||||
return new List<MenuItem>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件工具栏按钮
|
||||
/// </summary>
|
||||
/// <returns>工具栏按钮列表</returns>
|
||||
public virtual IEnumerable<Button> GetToolbarButtons()
|
||||
{
|
||||
return new List<Button>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件状态栏信息
|
||||
/// </summary>
|
||||
/// <returns>状态栏信息</returns>
|
||||
public virtual string GetStatusBarInfo()
|
||||
{
|
||||
return $"{Name} v{Version} - {(IsEnabled ? "已启用" : "已禁用")}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启用状态变更时的处理
|
||||
/// </summary>
|
||||
/// <param name="isEnabled">是否启用</param>
|
||||
protected virtual void OnEnabledChanged(bool isEnabled)
|
||||
{
|
||||
if (isEnabled)
|
||||
{
|
||||
Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示通知
|
||||
/// </summary>
|
||||
/// <param name="message">消息内容</param>
|
||||
/// <param name="type">通知类型</param>
|
||||
protected void ShowNotification(string message, NotificationType type = NotificationType.Info)
|
||||
{
|
||||
_context?.ShowNotification(message, type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示确认对话框
|
||||
/// </summary>
|
||||
/// <param name="message">消息内容</param>
|
||||
/// <param name="title">标题</param>
|
||||
/// <returns>用户选择结果</returns>
|
||||
protected bool ShowConfirmDialog(string message, string title = "确认")
|
||||
{
|
||||
return _context?.ShowConfirmDialog(message, title) ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示输入对话框
|
||||
/// </summary>
|
||||
/// <param name="message">提示消息</param>
|
||||
/// <param name="title">标题</param>
|
||||
/// <param name="defaultValue">默认值</param>
|
||||
/// <returns>用户输入内容</returns>
|
||||
protected string ShowInputDialog(string message, string title = "输入", string defaultValue = "")
|
||||
{
|
||||
return _context?.ShowInputDialog(message, title, defaultValue) ?? defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取设置值
|
||||
/// </summary>
|
||||
/// <typeparam name="T">设置类型</typeparam>
|
||||
/// <param name="key">设置键</param>
|
||||
/// <param name="defaultValue">默认值</param>
|
||||
/// <returns>设置值</returns>
|
||||
protected T GetSetting<T>(string key, T defaultValue = default)
|
||||
{
|
||||
if (_context == null) return defaultValue;
|
||||
return _context.GetSetting(key, defaultValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置设置值
|
||||
/// </summary>
|
||||
/// <typeparam name="T">设置类型</typeparam>
|
||||
/// <param name="key">设置键</param>
|
||||
/// <param name="value">设置值</param>
|
||||
protected void SetSetting<T>(string key, T value)
|
||||
{
|
||||
_context?.SetSetting(key, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存设置
|
||||
/// </summary>
|
||||
protected void SaveSettings()
|
||||
{
|
||||
_context?.SaveSettings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册事件处理器
|
||||
/// </summary>
|
||||
/// <param name="eventName">事件名称</param>
|
||||
/// <param name="handler">事件处理器</param>
|
||||
protected void RegisterEventHandler(string eventName, EventHandler handler)
|
||||
{
|
||||
_context?.RegisterEventHandler(eventName, handler);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注销事件处理器
|
||||
/// </summary>
|
||||
/// <param name="eventName">事件名称</param>
|
||||
/// <param name="handler">事件处理器</param>
|
||||
protected void UnregisterEventHandler(string eventName, EventHandler handler)
|
||||
{
|
||||
_context?.UnregisterEventHandler(eventName, handler);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发事件
|
||||
/// </summary>
|
||||
/// <param name="eventName">事件名称</param>
|
||||
/// <param name="sender">事件发送者</param>
|
||||
/// <param name="args">事件参数</param>
|
||||
protected void TriggerEvent(string eventName, object sender, EventArgs args)
|
||||
{
|
||||
_context?.TriggerEvent(eventName, sender, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace InkCanvasForClass.PluginSdk
|
||||
{
|
||||
/// <summary>
|
||||
/// 通知类型枚举
|
||||
/// </summary>
|
||||
public enum NotificationType
|
||||
{
|
||||
/// <summary>
|
||||
/// 信息
|
||||
/// </summary>
|
||||
Info,
|
||||
|
||||
/// <summary>
|
||||
/// 成功
|
||||
/// </summary>
|
||||
Success,
|
||||
|
||||
/// <summary>
|
||||
/// 警告
|
||||
/// </summary>
|
||||
Warning,
|
||||
|
||||
/// <summary>
|
||||
/// 错误
|
||||
/// </summary>
|
||||
Error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
# Ink Canvas Plugin SDK
|
||||
|
||||
Ink Canvas 插件开发SDK,用于开发墨迹画布应用的插件。
|
||||
|
||||
**命名空间**:`InkCanvasForClass.PluginSdk`(避免与 WPF 控件 `System.Windows.Controls.InkCanvas` 在引用 `InkCanvas.*` 时产生歧义)。
|
||||
|
||||
## 方案 B:轻量注册表(无 Microsoft.Extensions.DependencyInjection)
|
||||
|
||||
宿主在加载每个插件、调用 `Initialize` 之后,会调用 `InkCanvasPluginBase.RegisterExtensions(IPluginRegistry registry)`。插件可在此登记菜单项、工具栏按钮、设置页工厂;宿主窗口稍后统一挂载。注册表实现类型为 `InkCanvasForClass.PluginHost.CollectingPluginRegistry`,由主程序内 `PluginManager.Instance.ExtensionRegistry` 暴露。
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
dotnet add package InkCanvas.PluginSdk
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 创建插件项目
|
||||
|
||||
创建一个新的类库项目,并添加对 `InkCanvas.PluginSdk` 的引用。
|
||||
|
||||
### 2. 实现插件接口
|
||||
|
||||
```csharp
|
||||
using InkCanvas.PluginSdk;
|
||||
using System;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace MyPlugin
|
||||
{
|
||||
public class MyPlugin : InkCanvasPluginBase
|
||||
{
|
||||
public override string Id => "com.example.myplugin";
|
||||
public override string Name => "我的插件";
|
||||
public override string Description => "这是一个示例插件";
|
||||
public override Version Version => new Version(1, 0, 0);
|
||||
public override string Author => "插件作者";
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
// 插件启动时的逻辑
|
||||
ShowNotification("插件已启动!", NotificationType.Success);
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
// 插件停止时的逻辑
|
||||
ShowNotification("插件已停止!", NotificationType.Info);
|
||||
}
|
||||
|
||||
public override UserControl GetSettingsView()
|
||||
{
|
||||
// 返回插件设置界面
|
||||
return new MyPluginSettingsView();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 插件功能
|
||||
|
||||
#### 访问主应用程序功能
|
||||
|
||||
通过 `Context` 属性可以访问主应用程序的各种功能:
|
||||
|
||||
```csharp
|
||||
// 获取当前画布
|
||||
var canvas = Context.CurrentCanvas;
|
||||
|
||||
// 设置墨迹颜色
|
||||
Context.SetInkColor(Colors.Red);
|
||||
|
||||
// 清除画布
|
||||
Context.ClearCanvas();
|
||||
|
||||
// 显示通知
|
||||
Context.ShowNotification("操作完成!", NotificationType.Success);
|
||||
```
|
||||
|
||||
#### 创建菜单项
|
||||
|
||||
```csharp
|
||||
public override IEnumerable<MenuItem> GetMenuItems()
|
||||
{
|
||||
var menuItem = new MenuItem
|
||||
{
|
||||
Header = "我的功能",
|
||||
Icon = new Image { Source = MyIcon }
|
||||
};
|
||||
menuItem.Click += (s, e) => {
|
||||
// 处理菜单点击
|
||||
ShowNotification("菜单被点击了!");
|
||||
};
|
||||
|
||||
return new[] { menuItem };
|
||||
}
|
||||
```
|
||||
|
||||
#### 创建工具栏按钮
|
||||
|
||||
```csharp
|
||||
public override IEnumerable<Button> GetToolbarButtons()
|
||||
{
|
||||
var button = new Button
|
||||
{
|
||||
Content = "我的工具",
|
||||
ToolTip = "这是一个工具按钮"
|
||||
};
|
||||
button.Click += (s, e) => {
|
||||
// 处理按钮点击
|
||||
Context.SetInkColor(Colors.Blue);
|
||||
};
|
||||
|
||||
return new[] { button };
|
||||
}
|
||||
```
|
||||
|
||||
#### 事件处理
|
||||
|
||||
```csharp
|
||||
public override void Start()
|
||||
{
|
||||
// 注册事件处理器
|
||||
RegisterEventHandler("CanvasChanged", OnCanvasChanged);
|
||||
RegisterEventHandler("DrawingModeChanged", OnDrawingModeChanged);
|
||||
}
|
||||
|
||||
private void OnCanvasChanged(object sender, EventArgs e)
|
||||
{
|
||||
ShowNotification("画布已更改");
|
||||
}
|
||||
|
||||
private void OnDrawingModeChanged(object sender, EventArgs e)
|
||||
{
|
||||
ShowNotification($"绘制模式已更改为: {Context.CurrentDrawingMode}");
|
||||
}
|
||||
```
|
||||
|
||||
## API 参考
|
||||
|
||||
### IInkCanvasPlugin 接口
|
||||
|
||||
插件必须实现的主要接口。
|
||||
|
||||
### IPluginContext 接口
|
||||
|
||||
提供对主应用程序功能的访问。
|
||||
|
||||
### InkCanvasPluginBase 基类
|
||||
|
||||
提供插件的基本实现,建议继承此类。
|
||||
|
||||
## 示例插件
|
||||
|
||||
查看 `Examples` 文件夹中的示例插件,了解如何实现各种功能。
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT License
|
||||
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using InkCanvasForClass.PluginSdk;
|
||||
|
||||
namespace InkCanvasForClass.SamplePlugins
|
||||
{
|
||||
/// <summary>
|
||||
/// 示例插件:在当前 <see cref="InkCanvas"/> 右上角叠加显示实时时钟(不拦截笔触命中)。
|
||||
/// </summary>
|
||||
public sealed class ClockOverlayPlugin : InkCanvasPluginBase
|
||||
{
|
||||
public override string Id => "inkcanvas.sample.clock-overlay";
|
||||
|
||||
public override string Name => "画布时钟示例";
|
||||
|
||||
public override string Description => "在画布右上角显示当前时间(HH:mm:ss)";
|
||||
|
||||
public override Version Version => new Version(1, 0, 0);
|
||||
|
||||
public override string Author => "ICC CE Sample";
|
||||
|
||||
private Border _host;
|
||||
private TextBlock _timeText;
|
||||
private DispatcherTimer _timer;
|
||||
private System.Windows.Controls.InkCanvas _canvas;
|
||||
private SizeChangedEventHandler _sizeHandler;
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
var mw = Context?.MainWindow;
|
||||
var canvas = Context?.CurrentCanvas as System.Windows.Controls.InkCanvas;
|
||||
if (mw == null || canvas == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mw.Dispatcher.Invoke(() =>
|
||||
{
|
||||
_canvas = canvas;
|
||||
_host = new Border
|
||||
{
|
||||
Background = new SolidColorBrush(Color.FromArgb(170, 32, 32, 36)),
|
||||
CornerRadius = new CornerRadius(8),
|
||||
Padding = new Thickness(12, 8, 12, 8),
|
||||
IsHitTestVisible = false
|
||||
};
|
||||
_timeText = new TextBlock
|
||||
{
|
||||
FontSize = 24,
|
||||
FontWeight = FontWeights.SemiBold,
|
||||
Foreground = Brushes.White,
|
||||
FontFamily = new FontFamily("Segoe UI, Microsoft YaHei UI")
|
||||
};
|
||||
_host.Child = _timeText;
|
||||
Panel.SetZIndex(_host, 10050);
|
||||
|
||||
_canvas.Children.Add(_host);
|
||||
_sizeHandler = (s, e) => Reposition();
|
||||
_canvas.SizeChanged += _sizeHandler;
|
||||
Reposition();
|
||||
|
||||
_timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
|
||||
_timer.Tick += (_, __) => Tick();
|
||||
_timer.Start();
|
||||
Tick();
|
||||
});
|
||||
}
|
||||
|
||||
private void Tick()
|
||||
{
|
||||
if (_timeText != null)
|
||||
{
|
||||
_timeText.Text = DateTime.Now.ToString("HH:mm:ss");
|
||||
}
|
||||
}
|
||||
|
||||
private void Reposition()
|
||||
{
|
||||
if (_canvas == null || _host == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_host.UpdateLayout();
|
||||
var left = Math.Max(8, _canvas.ActualWidth - _host.ActualWidth - 20);
|
||||
InkCanvas.SetLeft(_host, left);
|
||||
InkCanvas.SetTop(_host, 20);
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
var mw = Context?.MainWindow;
|
||||
if (mw != null && (_canvas != null || _host != null))
|
||||
{
|
||||
mw.Dispatcher.Invoke(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_timer != null)
|
||||
{
|
||||
_timer.Stop();
|
||||
_timer = null;
|
||||
}
|
||||
|
||||
if (_canvas != null && _sizeHandler != null)
|
||||
{
|
||||
_canvas.SizeChanged -= _sizeHandler;
|
||||
_sizeHandler = null;
|
||||
}
|
||||
|
||||
if (_canvas != null && _host != null && _canvas.Children.Contains(_host))
|
||||
{
|
||||
_canvas.Children.Remove(_host);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 避免卸载时异常影响宿主
|
||||
}
|
||||
finally
|
||||
{
|
||||
_host = null;
|
||||
_timeText = null;
|
||||
_canvas = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
base.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<RootNamespace>InkCanvasForClass.SamplePlugins</RootNamespace>
|
||||
<AssemblyName>SampleClockPlugin</AssemblyName>
|
||||
<UseWPF>true</UseWPF>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<Nullable>disable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\SDK\InkCanvas.PluginSdk.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user