Add basic plugin sys

reference PKHeX.Core, main window loads assemblies & initializes
providing an ISaveProvider and the menustrip control (to insert controls
into)

pretty rough but should allow for inserting external control buttons &
allowing it to edit the UI a little

example: https://github.com/kwsch/PKHeXPluginExample
feedback is appreciated
This commit is contained in:
Kurt 2018-05-13 12:49:26 -07:00
parent 9d0ab043e7
commit 857919bd28
11 changed files with 141 additions and 10 deletions

View file

@ -0,0 +1,48 @@
namespace PKHeX.Core
{
/// <summary>
/// Simple interface representing a <see cref="PKM"/> viewer.
/// </summary>
public interface IPKMView
{
/// <summary>
/// Fetches the currently loaded <see cref="PKM"/> data from the viewer.
/// </summary>
PKM Data { get; }
/// <summary>
/// Indicates if the Viewer supports using Unicode characters or not.
/// </summary>
bool Unicode { get; }
/// <summary>
/// Indicates if the Viewer's controls have been fully initialized after a reload.
/// </summary>
bool FieldsInitialized { get; }
/// <summary>
/// Indicates if the Viewer is providing extra flexibility or not.
/// </summary>
bool HaX { get; }
/// <summary>
/// Indicates if the Viewer's controls are changing their values and should avoid triggering other updates.
/// </summary>
bool ChangingFields { get; set; }
/// <summary>
/// Fetches the currently loaded <see cref="PKM"/> data from the viewer by finishing any pending changes or auto-modifications.
/// </summary>
/// <param name="click">Cause the viewer to do extra actions to force validation of its children.</param>
/// <returns>Prepared <see cref="PKM"/> data from the viewer.</returns>
PKM PreparePKM(bool click = true);
/// <summary>
/// Loads a given <see cref="PKM"/> data to the viewer.
/// </summary>
/// <param name="pk">Pokémon data to load.</param>
/// <param name="focus">Cause the viewer to give focus to itself.</param>
/// <param name="skipConversionCheck">Cause the viewer to skip converting the data. Faster if it is known that the format is the same as the previous format.</param>
void PopulateFields(PKM pk, bool focus = true, bool skipConversionCheck = false);
}
}

View file

@ -0,0 +1,10 @@
namespace PKHeX.Core
{
public interface IPlugin
{
string Name { get; }
void Initialize(params object[] args);
void NotifySaveLoaded();
ISaveFileProvider SaveFileEditor { get; }
}
}

View file

@ -0,0 +1,23 @@
namespace PKHeX.Core
{
/// <summary>
/// Simple interface representing a Save File viewer.
/// </summary>
public interface ISaveFileProvider
{
/// <summary>
/// Retrieves the save file the <see cref="ISaveFileProvider"/> has control over.
/// </summary>
SaveFile SAV { get; }
/// <summary>
/// Retrieves the current box the <see cref="ISaveFileProvider"/> has control over.
/// </summary>
int CurrentBox { get; }
/// <summary>
/// Triggers a refresh of any individual <see cref="PKM"/> view slots.
/// </summary>
void ReloadSlots();
}
}

View file

@ -2,15 +2,9 @@ using PKHeX.Core;
namespace PKHeX.WinForms.Controls
{
public interface IMainEditor
public interface IMainEditor : IPKMView
{
bool Unicode { get; }
bool FieldsInitialized { get; }
bool ChangingFields { get; set; }
bool HaX { get; }
PKM pkm { get; }
void UpdateIVsGB(bool skipForm);
PKM pkm { get; }
}
}

View file

@ -62,6 +62,7 @@ namespace PKHeX.WinForms.Controls
public bool HaX { get; set; }
public byte[] LastData { private get; set; }
public PKM Data { get => pkm; set => pkm = value; }
public PKM pkm { get; private set; }
public bool FieldsInitialized { get; private set; }
public bool FieldsLoaded { get; private set; }

View file

@ -13,9 +13,10 @@ using static PKHeX.Core.MessageStrings;
namespace PKHeX.WinForms.Controls
{
public partial class SAVEditor : UserControl, ISlotViewer<PictureBox>
public partial class SAVEditor : UserControl, ISlotViewer<PictureBox>, ISaveFileProvider
{
public SaveFile SAV;
public SaveFile SAV { get; set; }
public int CurrentBox => Box.CurrentBox;
public SlotChangeManager M { get; }
public readonly Stack<SlotChange> UndoStack = new Stack<SlotChange>();
public readonly Stack<SlotChange> RedoStack = new Stack<SlotChange>();

View file

@ -83,6 +83,7 @@ namespace PKHeX.WinForms
"中文", // CHN
"Português", // Portuguese
};
private static readonly List<IPlugin> Plugins = new List<IPlugin>();
#endregion
#region Path Variables
@ -92,6 +93,7 @@ namespace PKHeX.WinForms
public static string MGDatabasePath => Path.Combine(WorkingDirectory, "mgdb");
public static string BackupPath => Path.Combine(WorkingDirectory, "bak");
private static string TemplatePath => Path.Combine(WorkingDirectory, "template");
private static string PluginPath => Path.Combine(WorkingDirectory, "plugins");
private const string ThreadPath = "https://projectpokemon.org/pkhex/";
private const string VersionPath = "https://raw.githubusercontent.com/kwsch/PKHeX/master/PKHeX.WinForms/Resources/text/version.txt";
@ -125,6 +127,8 @@ namespace PKHeX.WinForms
WinFormsUtil.Error(MsgSettingsLoadFail, e);
}
FormLoadPlugins();
PKME_Tabs.InitializeFields();
PKME_Tabs.TemplateFields(LoadTemplate(C_SAV.SAV));
}
@ -246,6 +250,14 @@ namespace PKHeX.WinForms
Settings.Version = Resources.ProgramVersion;
}
private void FormLoadPlugins()
{
if (!Directory.Exists(PluginPath))
return;
Plugins.AddRange(PluginLoader.LoadPlugins<IPlugin>(PluginPath));
foreach (var p in Plugins)
p.Initialize(C_SAV, PKME_Tabs, menuStrip1);
}
private static void DeleteConfig(string settingsFilename)
{
var dr = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, MsgSettingsResetCorrupt, MsgSettingsResetPrompt);
@ -746,6 +758,8 @@ namespace PKHeX.WinForms
PKME_Tabs.FlickerInterface();
PKME_Tabs.TemplateFields(LoadTemplate(sav));
foreach (var p in Plugins)
p.NotifySaveLoaded();
sav.Edited = false;
}
private static string GetProgramTitle()

View file

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace PKHeX.WinForms
{
public static class PluginLoader
{
public static IEnumerable<T> LoadPlugins<T>(string pluginPath)
{
var dllFileNames = Directory.EnumerateFiles(pluginPath, "*.dll", SearchOption.AllDirectories);
var assemblies = GetAssemblies(dllFileNames);
var pluginTypes = GetPluginsOfType<T>(assemblies);
return LoadPlugins<T>(pluginTypes);
}
private static IEnumerable<T> LoadPlugins<T>(IEnumerable<Type> pluginTypes)
{
return pluginTypes.Select(type => (T)Activator.CreateInstance(type));
}
private static IEnumerable<Assembly> GetAssemblies(IEnumerable<string> dllFileNames)
{
return dllFileNames.Select(AssemblyName.GetAssemblyName).Select(Assembly.Load);
}
private static IEnumerable<Type> GetPluginsOfType<T>(IEnumerable<Assembly> assemblies)
{
var pluginType = typeof(T);
foreach (var type in assemblies.Where(z => z != null).SelectMany(a => a.GetTypes()))
{
if (type.IsInterface || type.IsAbstract)
continue;
if (type.GetInterface(pluginType.FullName) == null)
continue;
yield return type;
}
}
}
}

View file

@ -245,6 +245,7 @@
<Compile Include="Controls\PKM Editor\PKMEditor.Designer.cs">
<DependentUpon>PKMEditor.cs</DependentUpon>
</Compile>
<Compile Include="MainWindow\PluginLoader.cs" />
<Compile Include="Misc\About.cs">
<SubType>Form</SubType>
</Compile>