From 84b626d3448606796d47120e15b27c46b291fea3 Mon Sep 17 00:00:00 2001 From: PrefacedCorg <1876568293@qq.com> Date: Fri, 10 Apr 2026 01:24:57 +0800 Subject: [PATCH] =?UTF-8?q?add:=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 屎山 --- Ink Canvas/App.xaml.cs | 37 +++ Ink Canvas/InkCanvasForClass.csproj | 5 + Ink Canvas/Plugins/InkCanvasService.cs | 62 ++++ Ink Canvas/Plugins/PluginManager.cs | 287 ++++++++++++++++++ .../SettingsViews/Pages/PluginPage.xaml | 24 ++ .../SettingsViews/Pages/PluginPage.xaml.cs | 120 ++++++++ .../Pages/PluginSettingsPage.xaml | 14 + .../Pages/PluginSettingsPage.xaml.cs | 71 +++++ .../Windows/SettingsViews/SettingsWindow.xaml | 9 + .../SettingsViews/SettingsWindow.xaml.cs | 51 +++- Ink Canvas/packages.lock.json | 33 ++ InkCanvas.PluginSdk/IInkCanvasService.cs | 12 + InkCanvas.PluginSdk/IPlugin.cs | 19 ++ InkCanvas.PluginSdk/IPluginHost.cs | 12 + .../InkCanvas.PluginSdk.csproj | 11 + InkCanvas.PluginSdk/PluginBase.cs | 60 ++++ InkCanvas.PluginSdk/PluginInfo.cs | 16 + 17 files changed, 842 insertions(+), 1 deletion(-) create mode 100644 Ink Canvas/Plugins/InkCanvasService.cs create mode 100644 Ink Canvas/Plugins/PluginManager.cs create mode 100644 Ink Canvas/Windows/SettingsViews/Pages/PluginPage.xaml create mode 100644 Ink Canvas/Windows/SettingsViews/Pages/PluginPage.xaml.cs create mode 100644 Ink Canvas/Windows/SettingsViews/Pages/PluginSettingsPage.xaml create mode 100644 Ink Canvas/Windows/SettingsViews/Pages/PluginSettingsPage.xaml.cs create mode 100644 InkCanvas.PluginSdk/IInkCanvasService.cs create mode 100644 InkCanvas.PluginSdk/IPlugin.cs create mode 100644 InkCanvas.PluginSdk/IPluginHost.cs create mode 100644 InkCanvas.PluginSdk/InkCanvas.PluginSdk.csproj create mode 100644 InkCanvas.PluginSdk/PluginBase.cs create mode 100644 InkCanvas.PluginSdk/PluginInfo.cs diff --git a/Ink Canvas/App.xaml.cs b/Ink Canvas/App.xaml.cs index 2238c408..065caf0f 100644 --- a/Ink Canvas/App.xaml.cs +++ b/Ink Canvas/App.xaml.cs @@ -1,5 +1,6 @@ using H.NotifyIcon; using Ink_Canvas.Helpers; +using Ink_Canvas.Plugins; using Ink_Canvas.Properties; using iNKORE.UI.WPF.Modern.Controls; using Microsoft.Win32; @@ -1075,6 +1076,18 @@ namespace Ink_Canvas var mainWindow = new MainWindow(); MainWindow = mainWindow; + // 注册 InkCanvas 服务供插件使用 + try + { + var inkCanvasService = new Plugins.InkCanvasService(mainWindow); + Plugins.PluginManager.Instance.RegisterService(inkCanvasService); + LogHelper.WriteLogToFile("InkCanvasService registered for plugins"); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"Failed to register InkCanvasService: {ex.Message}", LogHelper.LogType.Error); + } + // 主窗口加载完成后关闭启动画面 mainWindow.Loaded += (s, args) => { @@ -1161,6 +1174,18 @@ namespace Ink_Canvas LogHelper.WriteLogToFile($"初始化上传帮助类时出错: {ex.Message}", LogHelper.LogType.Error); } + // 加载插件 + try + { + LogHelper.WriteLogToFile("开始加载插件"); + await PluginManager.Instance.LoadAllAsync(); + LogHelper.WriteLogToFile(string.Format("插件加载完成,共加载 {0} 个插件", PluginManager.Instance.Plugins.Count)); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile(string.Format("加载插件时出错: {0}", ex.Message), LogHelper.LogType.Error); + } + } private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e) @@ -1437,6 +1462,18 @@ namespace Ink_Canvas private void App_Exit(object sender, ExitEventArgs e) { + // 卸载所有插件 + try + { + LogHelper.WriteLogToFile("正在卸载插件..."); + PluginManager.Instance.UnloadAll(); + LogHelper.WriteLogToFile("插件卸载完成"); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"卸载插件时出错: {ex.Message}", LogHelper.LogType.Error); + } + // 仅在软件内主动退出时关闭看门狗,并写入退出信号 try { diff --git a/Ink Canvas/InkCanvasForClass.csproj b/Ink Canvas/InkCanvasForClass.csproj index db46b9b2..aa8f11d7 100644 --- a/Ink Canvas/InkCanvasForClass.csproj +++ b/Ink Canvas/InkCanvasForClass.csproj @@ -141,7 +141,12 @@ + + + + + diff --git a/Ink Canvas/Plugins/InkCanvasService.cs b/Ink Canvas/Plugins/InkCanvasService.cs new file mode 100644 index 00000000..3624b868 --- /dev/null +++ b/Ink Canvas/Plugins/InkCanvasService.cs @@ -0,0 +1,62 @@ +using Ink_Canvas.Plugins; +using System; +using System.Threading.Tasks; +using System.Windows.Threading; + +namespace Ink_Canvas.Plugins +{ + public class InkCanvasService : IInkCanvasService + { + private readonly MainWindow _mainWindow; + + public InkCanvasService(MainWindow mainWindow) + { + _mainWindow = mainWindow; + } + + public void OpenWhiteboard() + { + if (_mainWindow != null) + { + _mainWindow.Dispatcher.Invoke(() => + { + try + { + _mainWindow.UnFoldFloatingBar_MouseUp(null, null); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"Error opening whiteboard: {ex.Message}"); + } + }); + } + } + + public void CloseWhiteboard() + { + if (_mainWindow != null) + { + _mainWindow.Dispatcher.Invoke(() => + { + try + { + _mainWindow.FoldFloatingBar_MouseUp(null, null); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"Error closing whiteboard: {ex.Message}"); + } + }); + } + } + + public async Task OpenWhiteboardAsync(int delayMilliseconds = 0) + { + if (delayMilliseconds > 0) + { + await Task.Delay(delayMilliseconds); + } + OpenWhiteboard(); + } + } +} diff --git a/Ink Canvas/Plugins/PluginManager.cs b/Ink Canvas/Plugins/PluginManager.cs new file mode 100644 index 00000000..74e133f7 --- /dev/null +++ b/Ink Canvas/Plugins/PluginManager.cs @@ -0,0 +1,287 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; +using System.Threading.Tasks; + +namespace Ink_Canvas.Plugins +{ + public class PluginManager : IPluginHost + { + private static PluginManager _instance; + public static PluginManager Instance + { + get + { + if (_instance == null) + { + _instance = new PluginManager(); + } + return _instance; + } + } + + private readonly Dictionary _services = new Dictionary(); + private readonly string _pluginsDirectory; + private readonly List _plugins = new List(); + private readonly Dictionary _assemblyContexts = new Dictionary(); + + public IReadOnlyList Plugins + { + get { return _plugins.AsReadOnly(); } + } + + public event EventHandler PluginLoaded; + public event EventHandler PluginUnloaded; + public event EventHandler LogMessage; + + private PluginManager() + { + _pluginsDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins"); + + if (!Directory.Exists(_pluginsDirectory)) + { + Directory.CreateDirectory(_pluginsDirectory); + } + } + + public async Task LoadAllAsync() + { + try + { + if (!Directory.Exists(_pluginsDirectory)) + { + Log("Plugins directory does not exist, skipping plugin loading"); + return; + } + + Log(string.Format("Loading plugins from: {0}", _pluginsDirectory)); + + var pluginFiles = new List(); + + try + { + var topLevelFiles = Directory.GetFiles(_pluginsDirectory, "*.dll", SearchOption.TopDirectoryOnly); + pluginFiles.AddRange(topLevelFiles); + Log(string.Format("Found {0} top-level plugin files", topLevelFiles.Length)); + } + catch (Exception ex) + { + LogError(string.Format("Error getting top-level plugin files: {0}", ex.Message)); + } + + try + { + var subDirectories = Directory.GetDirectories(_pluginsDirectory); + foreach (var subDir in subDirectories) + { + try + { + var subDirFiles = Directory.GetFiles(subDir, "*.dll", SearchOption.TopDirectoryOnly); + pluginFiles.AddRange(subDirFiles); + Log(string.Format("Found {0} plugin files in directory: {1}", subDirFiles.Length, Path.GetFileName(subDir))); + } + catch (Exception ex) + { + LogError(string.Format("Error scanning subdirectory {0}: {1}", Path.GetFileName(subDir), ex.Message)); + } + } + } + catch (Exception ex) + { + LogError(string.Format("Error getting subdirectories: {0}", ex.Message)); + } + + Log(string.Format("Found total {0} potential plugin files", pluginFiles.Count)); + + foreach (var pluginFile in pluginFiles) + { + try + { + await LoadPluginAsync(pluginFile); + } + catch (Exception ex) + { + LogError(string.Format("Failed to load plugin from {0}", Path.GetFileName(pluginFile)), ex); + } + } + + _plugins.Sort((a, b) => a.Order.CompareTo(b.Order)); + Log(string.Format("Plugin loading complete. Loaded {0} plugins", _plugins.Count)); + } + catch (Exception ex) + { + LogError("Failed to load plugins", ex); + } + } + + private async Task LoadPluginAsync(string pluginFile) + { + var fileName = Path.GetFileName(pluginFile); + Log(string.Format("Loading plugin: {0}", fileName)); + + var alc = new PluginAssemblyLoadContext(pluginFile, isCollectible: true); + + try + { + var assembly = alc.LoadFromAssemblyPath(pluginFile); + var pluginTypes = assembly.GetTypes() + .Where(t => typeof(IPlugin).IsAssignableFrom(t) && !t.IsAbstract && t.IsClass); + + foreach (var pluginType in pluginTypes) + { + var pluginInstance = Activator.CreateInstance(pluginType) as IPlugin; + if (pluginInstance == null) continue; + + var pluginInfo = new PluginInfo + { + Id = pluginInstance.Id, + Name = pluginInstance.Name, + Version = pluginInstance.Version, + Description = pluginInstance.Description, + Author = pluginInstance.Author, + Order = pluginInstance.Order, + Instance = pluginInstance, + IsLoaded = true + }; + + _plugins.Add(pluginInfo); + _assemblyContexts[pluginInfo.Id] = alc; + + try + { + pluginInstance.Initialize(this); + Log(string.Format("Plugin loaded: {0} v{1} by {2}", pluginInfo.Name, pluginInfo.Version, pluginInfo.Author)); + OnPluginLoaded(pluginInfo); + } + catch (Exception ex) + { + LogError(string.Format("Failed to initialize plugin {0}", pluginInfo.Name), ex); + _plugins.Remove(pluginInfo); + _assemblyContexts.Remove(pluginInfo.Id); + alc.Unload(); + } + } + } + catch + { + alc.Unload(); + throw; + } + + await Task.CompletedTask; + } + + public void UnloadPlugin(PluginInfo plugin) + { + try + { + plugin.Instance.Shutdown(); + _plugins.Remove(plugin); + plugin.IsLoaded = false; + + if (_assemblyContexts.TryGetValue(plugin.Id, out var alc)) + { + _assemblyContexts.Remove(plugin.Id); + alc.Unload(); + } + + Log(string.Format("Plugin unloaded: {0}", plugin.Name)); + OnPluginUnloaded(plugin); + } + catch (Exception ex) + { + LogError(string.Format("Failed to unload plugin {0}", plugin.Name), ex); + } + } + + public void UnloadAll() + { + foreach (var plugin in _plugins.ToList()) + { + UnloadPlugin(plugin); + } + } + + public void Log(string message) + { + OnLogMessage(message); + System.Diagnostics.Debug.WriteLine(string.Format("[Plugin] {0}", message)); + } + + public void LogError(string message, Exception ex = null) + { + var fullMessage = ex != null ? string.Format("{0}: {1}", message, ex.Message) : message; + OnLogMessage(string.Format("ERROR: {0}", fullMessage)); + System.Diagnostics.Debug.WriteLine(string.Format("[Plugin ERROR] {0}", fullMessage)); + if (ex != null) + { + System.Diagnostics.Debug.WriteLine(ex.StackTrace); + } + } + + public T GetService() where T : class + { + if (_services.TryGetValue(typeof(T), out var service)) + { + return service as T; + } + return null; + } + + public void RegisterService(T service) where T : class + { + _services[typeof(T)] = service; + } + + protected virtual void OnPluginLoaded(PluginInfo pluginInfo) + { + var handler = PluginLoaded; + if (handler != null) + { + handler(this, pluginInfo); + } + } + + protected virtual void OnPluginUnloaded(PluginInfo pluginInfo) + { + var handler = PluginUnloaded; + if (handler != null) + { + handler(this, pluginInfo); + } + } + + protected virtual void OnLogMessage(string message) + { + var handler = LogMessage; + if (handler != null) + { + handler(this, message); + } + } + + private class PluginAssemblyLoadContext : AssemblyLoadContext + { + private readonly AssemblyDependencyResolver _resolver; + + public PluginAssemblyLoadContext(string pluginPath, bool isCollectible) + : base(string.Format("PluginContext_{0}", Path.GetFileNameWithoutExtension(pluginPath)), isCollectible) + { + _resolver = new AssemblyDependencyResolver(pluginPath); + } + + protected override Assembly Load(AssemblyName assemblyName) + { + var assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); + if (assemblyPath != null) + { + return LoadFromAssemblyPath(assemblyPath); + } + return null; + } + } + } +} diff --git a/Ink Canvas/Windows/SettingsViews/Pages/PluginPage.xaml b/Ink Canvas/Windows/SettingsViews/Pages/PluginPage.xaml new file mode 100644 index 00000000..c473e006 --- /dev/null +++ b/Ink Canvas/Windows/SettingsViews/Pages/PluginPage.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + diff --git a/Ink Canvas/Windows/SettingsViews/Pages/PluginPage.xaml.cs b/Ink Canvas/Windows/SettingsViews/Pages/PluginPage.xaml.cs new file mode 100644 index 00000000..0019845f --- /dev/null +++ b/Ink Canvas/Windows/SettingsViews/Pages/PluginPage.xaml.cs @@ -0,0 +1,120 @@ +using Ink_Canvas.Plugins; +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; + +namespace Ink_Canvas.Windows.SettingsViews.Pages +{ + public partial class PluginPage : iNKORE.UI.WPF.Modern.Controls.Page + { + public PluginPage() + { + InitializeComponent(); + Loaded += PluginPage_Loaded; + } + + private void PluginPage_Loaded(object sender, RoutedEventArgs e) + { + LoadPlugins(); + } + + public void LoadPlugins() + { + try + { + var pluginManager = PluginManager.Instance; + var plugins = pluginManager.Plugins; + + PluginCountText.Text = string.Format("已加载 {0} 个插件", plugins.Count); + + if (plugins.Count == 0) + { + PluginContainer.Children.Clear(); + var noPluginText = new TextBlock + { + Text = "没有找到插件,请将插件文件放置在 Plugins 目录中", + TextWrapping = TextWrapping.Wrap, + Foreground = Brushes.Gray, + Margin = new Thickness(0, 10, 0, 0) + }; + PluginContainer.Children.Add(noPluginText); + return; + } + + PluginContainer.Children.Clear(); + + foreach (var pluginInfo in plugins) + { + var pluginCard = CreatePluginCard(pluginInfo); + PluginContainer.Children.Add(pluginCard); + } + } + catch (Exception ex) + { + PluginCountText.Text = string.Format("加载插件时出错:{0}", ex.Message); + } + } + + private Border CreatePluginCard(PluginInfo pluginInfo) + { + var card = new Border + { + Background = Brushes.White, + BorderBrush = Brushes.LightGray, + BorderThickness = new Thickness(1), + CornerRadius = new CornerRadius(8), + Margin = new Thickness(0, 0, 0, 10), + Padding = new Thickness(15) + }; + + var stackPanel = new StackPanel(); + + var titlePanel = new DockPanel { LastChildFill = true }; + + var nameText = new TextBlock + { + Text = pluginInfo.Name, + FontSize = 16, + FontWeight = FontWeights.Bold, + Foreground = Brushes.Black, + Margin = new Thickness(0, 0, 10, 0) + }; + DockPanel.SetDock(nameText, Dock.Left); + + var versionText = new TextBlock + { + Text = string.Format("v{0}", pluginInfo.Version), + FontSize = 12, + Foreground = Brushes.Gray, + VerticalAlignment = VerticalAlignment.Center + }; + + titlePanel.Children.Add(nameText); + titlePanel.Children.Add(versionText); + + var descriptionText = new TextBlock + { + Text = pluginInfo.Description, + FontSize = 12, + Foreground = Brushes.DarkGray, + TextWrapping = TextWrapping.Wrap, + Margin = new Thickness(0, 5, 0, 5) + }; + + var authorText = new TextBlock + { + Text = string.Format("作者:{0}", pluginInfo.Author), + FontSize = 11, + Foreground = Brushes.Gray + }; + + stackPanel.Children.Add(titlePanel); + stackPanel.Children.Add(descriptionText); + stackPanel.Children.Add(authorText); + + card.Child = stackPanel; + return card; + } + } +} diff --git a/Ink Canvas/Windows/SettingsViews/Pages/PluginSettingsPage.xaml b/Ink Canvas/Windows/SettingsViews/Pages/PluginSettingsPage.xaml new file mode 100644 index 00000000..2e45820e --- /dev/null +++ b/Ink Canvas/Windows/SettingsViews/Pages/PluginSettingsPage.xaml @@ -0,0 +1,14 @@ + + + + + diff --git a/Ink Canvas/Windows/SettingsViews/Pages/PluginSettingsPage.xaml.cs b/Ink Canvas/Windows/SettingsViews/Pages/PluginSettingsPage.xaml.cs new file mode 100644 index 00000000..6c84559e --- /dev/null +++ b/Ink Canvas/Windows/SettingsViews/Pages/PluginSettingsPage.xaml.cs @@ -0,0 +1,71 @@ +using Ink_Canvas.Plugins; +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; + +namespace Ink_Canvas.Windows.SettingsViews.Pages +{ + public partial class PluginSettingsPage : iNKORE.UI.WPF.Modern.Controls.Page + { + private static PluginInfo _currentPlugin; + + public static PluginInfo CurrentPlugin + { + get { return _currentPlugin; } + set + { + _currentPlugin = value; + } + } + + public PluginSettingsPage() + { + InitializeComponent(); + Loaded += PluginSettingsPage_Loaded; + } + + private void PluginSettingsPage_Loaded(object sender, RoutedEventArgs e) + { + LoadPluginSettings(); + } + + public void LoadPluginSettings() + { + if (_currentPlugin == null) + { + return; + } + + try + { + var settingsView = _currentPlugin.Instance.GetSettingsView(); + if (settingsView != null && settingsView is UIElement uiElement) + { + var parent = VisualTreeHelper.GetParent(uiElement); + if (parent != null) + { + if (parent is Decorator decorator) + { + decorator.Child = null; + } + else if (parent is ContentControl contentControl) + { + contentControl.Content = null; + } + else if (parent is Panel panel) + { + panel.Children.Remove(uiElement); + } + } + + PluginSettingsContent.Content = uiElement; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(string.Format("加载插件设置时出错: {0}", ex.Message)); + } + } + } +} diff --git a/Ink Canvas/Windows/SettingsViews/SettingsWindow.xaml b/Ink Canvas/Windows/SettingsViews/SettingsWindow.xaml index e651a6d0..1382fd42 100644 --- a/Ink Canvas/Windows/SettingsViews/SettingsWindow.xaml +++ b/Ink Canvas/Windows/SettingsViews/SettingsWindow.xaml @@ -228,6 +228,15 @@ + + + + + diff --git a/Ink Canvas/Windows/SettingsViews/SettingsWindow.xaml.cs b/Ink Canvas/Windows/SettingsViews/SettingsWindow.xaml.cs index c13267e0..356c8016 100644 --- a/Ink Canvas/Windows/SettingsViews/SettingsWindow.xaml.cs +++ b/Ink Canvas/Windows/SettingsViews/SettingsWindow.xaml.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Windows; using System.Windows.Interop; using System.Windows.Navigation; +using System.Windows.Media; using MessageBox = System.Windows.MessageBox; using Screen = System.Windows.Forms.Screen; @@ -15,6 +16,7 @@ namespace Ink_Canvas.Windows.SettingsViews { private readonly Dictionary _pageTypes; private readonly Dictionary _pages = new Dictionary(); + private readonly Dictionary _pluginPages = new Dictionary(); // 保存窗口原始位置和大小 private double _originalLeft; @@ -44,7 +46,9 @@ namespace Ink_Canvas.Windows.SettingsViews { "FontsPage", typeof(FontsPage) }, { "StartupPage", typeof(StartupPage) }, { "AboutPage", typeof(AboutPage) }, - { "Settings", typeof(SettingsPage) } + { "Settings", typeof(SettingsPage) }, + { "PluginPage", typeof(PluginPage) }, + { "PluginSettingsPage", typeof(PluginSettingsPage) } }; // 默认选中首页 @@ -65,6 +69,7 @@ namespace Ink_Canvas.Windows.SettingsViews { SetMaxSizeAndCenter(); RegisterDpiChangedListener(); + LoadPluginSettingsPages(); }; // 窗口关闭时释放资源 @@ -258,6 +263,12 @@ namespace Ink_Canvas.Windows.SettingsViews string tag = selectedItem.Tag as string; if (!string.IsNullOrEmpty(tag) && _pageTypes.ContainsKey(tag)) { + // 如果是插件设置页面,设置当前插件 + if (_pluginPages.TryGetValue(tag, out var plugin)) + { + PluginSettingsPage.CurrentPlugin = plugin; + } + // 避免重复导航到当前页面 if (rootFrame.SourcePageType != _pageTypes[tag]) { @@ -458,6 +469,44 @@ namespace Ink_Canvas.Windows.SettingsViews return items; } + + private void LoadPluginSettingsPages() + { + try + { + var pluginManager = Ink_Canvas.Plugins.PluginManager.Instance; + var plugins = pluginManager.Plugins; + + foreach (var plugin in plugins) + { + var settingsView = plugin.Instance.GetSettingsView(); + if (settingsView != null) + { + var pageTag = string.Format("PluginSettings_{0}", plugin.Id); + + _pageTypes[pageTag] = typeof(PluginSettingsPage); + _pluginPages[pageTag] = plugin; + + var navItem = new NavigationViewItem + { + Content = string.Format("{0} 设置", plugin.Name), + Tag = pageTag + }; + + navItem.Icon = new FontIcon + { + Glyph = "\uE713" + }; + + NavigationViewControl.MenuItems.Add(navItem); + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(string.Format("加载插件设置页面时出错: {0}", ex.Message)); + } + } #endregion } } diff --git a/Ink Canvas/packages.lock.json b/Ink Canvas/packages.lock.json index 09b35601..e5121031 100644 --- a/Ink Canvas/packages.lock.json +++ b/Ink Canvas/packages.lock.json @@ -82,6 +82,16 @@ "MdXaml.Plugins": "1.27.0" } }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Direct", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "Microsoft.International.Converters.PinYinConverter": { "type": "Direct", "requested": "[1.0.0, )", @@ -179,6 +189,16 @@ "System.CodeDom": "10.0.5" } }, + "System.Private.Uri": { + "type": "Direct", + "requested": "[4.3.2, )", + "resolved": "4.3.2", + "contentHash": "o1+7RJnu3Ik3PazR7Z7tJhjPdE000Eq2KGLLWhqJJKXj04wrS8lwb1OFtDF9jzXXADhUuZNJZlPc98uwwqmpFA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.1", + "Microsoft.NETCore.Targets": "1.1.3" + } + }, "WebDav.Client": { "type": "Direct", "requested": "[2.9.0, )", @@ -221,11 +241,21 @@ "resolved": "1.27.0", "contentHash": "We7LtBdoukRg9mqTfa1f5n8z/GQPMKBRj3URk9DiMuqzIHkW1lTgK5njVPSScxsRt4YzW22423tSnLWNm2MJKg==" }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "5.0.0", "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.3", + "contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==" + }, "Microsoft.Win32.Registry": { "type": "Transitive", "resolved": "5.0.0", @@ -308,6 +338,9 @@ "type": "Transitive", "resolved": "4.5.0", "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "inkcanvas.pluginsdk": { + "type": "Project" } }, "net6.0-windows10.0.19041/win-arm64": { diff --git a/InkCanvas.PluginSdk/IInkCanvasService.cs b/InkCanvas.PluginSdk/IInkCanvasService.cs new file mode 100644 index 00000000..7df1cdb3 --- /dev/null +++ b/InkCanvas.PluginSdk/IInkCanvasService.cs @@ -0,0 +1,12 @@ +using System; +using System.Threading.Tasks; + +namespace Ink_Canvas.Plugins +{ + public interface IInkCanvasService + { + void OpenWhiteboard(); + void CloseWhiteboard(); + Task OpenWhiteboardAsync(int delayMilliseconds = 0); + } +} diff --git a/InkCanvas.PluginSdk/IPlugin.cs b/InkCanvas.PluginSdk/IPlugin.cs new file mode 100644 index 00000000..1f2505fe --- /dev/null +++ b/InkCanvas.PluginSdk/IPlugin.cs @@ -0,0 +1,19 @@ +using System; + +namespace Ink_Canvas.Plugins +{ + public interface IPlugin + { + string Id { get; } + string Name { get; } + string Version { get; } + string Description { get; } + string Author { get; } + int Order { get; } + + void Initialize(IPluginHost host); + void Shutdown(); + object GetMainView(); + object GetSettingsView(); + } +} diff --git a/InkCanvas.PluginSdk/IPluginHost.cs b/InkCanvas.PluginSdk/IPluginHost.cs new file mode 100644 index 00000000..7638650c --- /dev/null +++ b/InkCanvas.PluginSdk/IPluginHost.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ink_Canvas.Plugins +{ + public interface IPluginHost + { + void Log(string message); + void LogError(string message, Exception ex = null); + T GetService() where T : class; + void RegisterService(T service) where T : class; + } +} diff --git a/InkCanvas.PluginSdk/InkCanvas.PluginSdk.csproj b/InkCanvas.PluginSdk/InkCanvas.PluginSdk.csproj new file mode 100644 index 00000000..f4c72ceb --- /dev/null +++ b/InkCanvas.PluginSdk/InkCanvas.PluginSdk.csproj @@ -0,0 +1,11 @@ + + + + net6.0-windows10.0.19041.0 + true + disable + disable + Ink_Canvas.Plugins + + + diff --git a/InkCanvas.PluginSdk/PluginBase.cs b/InkCanvas.PluginSdk/PluginBase.cs new file mode 100644 index 00000000..bde3686f --- /dev/null +++ b/InkCanvas.PluginSdk/PluginBase.cs @@ -0,0 +1,60 @@ +using System; + +namespace Ink_Canvas.Plugins +{ + public abstract class PluginBase : IPlugin + { + protected IPluginHost Host { get; private set; } + + public abstract string Id { get; } + public abstract string Name { get; } + public abstract string Version { get; } + public abstract string Description { get; } + public abstract string Author { get; } + public abstract int Order { get; } + + public virtual void Initialize(IPluginHost host) + { + Host = host; + } + + public virtual void Shutdown() + { + } + + public virtual object GetMainView() + { + return null; + } + + public virtual object GetSettingsView() + { + return null; + } + + protected void Log(string message) + { + if (Host != null) + { + Host.Log(message); + } + } + + protected void LogError(string message, Exception ex = null) + { + if (Host != null) + { + Host.LogError(message, ex); + } + } + + protected T GetService() where T : class + { + if (Host != null) + { + return Host.GetService(); + } + return null; + } + } +} diff --git a/InkCanvas.PluginSdk/PluginInfo.cs b/InkCanvas.PluginSdk/PluginInfo.cs new file mode 100644 index 00000000..ff90089b --- /dev/null +++ b/InkCanvas.PluginSdk/PluginInfo.cs @@ -0,0 +1,16 @@ +using System; + +namespace Ink_Canvas.Plugins +{ + public class PluginInfo + { + public string Id { get; set; } + public string Name { get; set; } + public string Version { get; set; } + public string Description { get; set; } + public string Author { get; set; } + public int Order { get; set; } + public IPlugin Instance { get; set; } + public bool IsLoaded { get; set; } + } +}