Allow external tools to have any namespace / class name if annotated

This change is more convenient at the cost of a little duplicate code in
ToolManager. The annotation is [ExternalToolEntryPointForm], and having no more
than one such class per assembly is enforced.
This commit is contained in:
YoshiRulz 2020-01-30 23:32:20 +10:00
parent eab44d2d94
commit 1fdb7a6fe2
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
4 changed files with 88 additions and 62 deletions

View File

@ -102,13 +102,15 @@ namespace BizHawk.Client.ApiHawk
}
}
var customFormType = externalToolFile.GetTypes().FirstOrDefault(t => t != null && t.FullName == "BizHawk.Client.EmuHawk.CustomMainForm");
var availTypes = externalToolFile.GetTypes();
var customFormType = availTypes.FirstOrDefault(t => t.FullName == "BizHawk.Client.EmuHawk.CustomMainForm")
?? availTypes.SingleOrDefault(t => typeof(IExternalToolForm).IsAssignableFrom(t) && t.CustomAttributes.Any(cad => cad.AttributeType == typeof(ExternalToolEntryPointFormAttribute)));
if (customFormType == null)
{
item.ToolTipText = "Does not have a CustomMainForm";
item.ToolTipText = "Does not have a correctly-formatted CustomMainForm or a form decorated with [ExternalToolEntryPointForm]";
item.Enabled = false;
}
item.Tag = fileName;
item.Tag = (fileName, customFormType?.FullName);
attributes = externalToolFile.GetCustomAttributes(typeof(BizHawkExternalToolUsageAttribute), false);
if (attributes != null && attributes.Length == 1)

View File

@ -0,0 +1,7 @@
using System;
namespace BizHawk.Client.Common
{
[AttributeUsage(AttributeTargets.Class)]
public class ExternalToolEntryPointFormAttribute : Attribute {}
}

View File

@ -1415,7 +1415,8 @@ namespace BizHawk.Client.EmuHawk
{
item.Click += delegate
{
Tools.Load<IExternalToolForm>((string)item.Tag);
var (fileName, customFormTypeName) = ((string, string)) item.Tag;
Tools.LoadExternalToolForm(fileName, customFormTypeName);
};
}
else

View File

@ -94,84 +94,49 @@ namespace BizHawk.Client.EmuHawk
public T Load<T>(string toolPath, bool focus = true)
where T : class, IToolForm
{
bool isExternal = typeof(T) == typeof(IExternalToolForm);
if (!IsAvailable<T>() && !isExternal)
{
return null;
}
T existingTool;
if (isExternal)
{
existingTool = (T)_tools.FirstOrDefault(t => t is T && t.GetType().Assembly.Location == toolPath);
}
else
{
existingTool = (T)_tools.FirstOrDefault(t => t is T);
}
if (!IsAvailable<T>()) return null;
var existingTool = _tools.OfType<T>().FirstOrDefault();
if (existingTool != null)
{
if (existingTool.IsDisposed)
{
_tools.Remove(existingTool);
}
else
if (!existingTool.IsDisposed)
{
if (focus)
{
existingTool.Show();
existingTool.Focus();
}
return existingTool;
}
_tools.Remove(existingTool);
}
IToolForm newTool = CreateInstance<T>(toolPath);
if (newTool == null)
{
return null;
}
if (newTool is Form form)
{
form.Owner = _owner;
}
if (isExternal)
{
ApiInjector.UpdateApis(_apiProvider, newTool);
}
var newTool = CreateInstance<T>(toolPath);
if (newTool == null) return null;
if (newTool is Form form) form.Owner = _owner;
ServiceInjector.UpdateServices(_emulator.ServiceProvider, newTool);
SetBaseProperties(newTool);
string toolType = typeof(T).ToString();
var toolTypeName = typeof(T).ToString();
// auto settings
if (newTool is IToolFormAutoConfig tool)
if (newTool is IToolFormAutoConfig autoConfigTool)
{
if (!_config.CommonToolSettings.TryGetValue(toolType, out var settings))
{
settings = new ToolDialogSettings();
_config.CommonToolSettings[toolType] = settings;
}
AttachSettingHooks(tool, settings);
AttachSettingHooks(
autoConfigTool,
_config.CommonToolSettings.TryGetValue(toolTypeName, out var settings)
? settings
: (_config.CommonToolSettings[toolTypeName] = new ToolDialogSettings())
);
}
// custom settings
if (HasCustomConfig(newTool))
{
if (!_config.CustomToolSettings.TryGetValue(toolType, out var settings))
{
settings = new Dictionary<string, object>();
_config.CustomToolSettings[toolType] = settings;
}
InstallCustomConfig(newTool, settings);
InstallCustomConfig(
newTool,
_config.CustomToolSettings.TryGetValue(toolTypeName, out var settings)
? settings
: (_config.CustomToolSettings[toolTypeName] = new Dictionary<string, object>())
);
}
newTool.Restart();
@ -186,6 +151,56 @@ namespace BizHawk.Client.EmuHawk
return (T)newTool;
}
/// <summary>Loads the external tool's entry form.</summary>
public IExternalToolForm LoadExternalToolForm(string toolPath, string customFormTypeName, bool focus = true)
{
var existingTool = _tools.OfType<IExternalToolForm>().FirstOrDefault(t => t.GetType().Assembly.Location == toolPath);
if (existingTool != null)
{
if (!existingTool.IsDisposed)
{
if (focus)
{
existingTool.Show();
existingTool.Focus();
}
return existingTool;
}
_tools.Remove(existingTool);
}
var newTool = (IExternalToolForm) CreateInstance(typeof(IExternalToolForm), toolPath, customFormTypeName);
if (newTool == null) return null;
if (newTool is Form form) form.Owner = _owner;
ApiInjector.UpdateApis(_apiProvider, newTool);
ServiceInjector.UpdateServices(_emulator.ServiceProvider, newTool);
SetBaseProperties(newTool);
// auto settings
if (newTool is IToolFormAutoConfig autoConfigTool)
{
AttachSettingHooks(
autoConfigTool,
_config.CommonToolSettings.TryGetValue(customFormTypeName, out var settings)
? settings
: (_config.CommonToolSettings[customFormTypeName] = new ToolDialogSettings())
);
}
// custom settings
if (HasCustomConfig(newTool))
{
InstallCustomConfig(
newTool,
_config.CustomToolSettings.TryGetValue(customFormTypeName, out var settings)
? settings
: (_config.CustomToolSettings[customFormTypeName] = new Dictionary<string, object>())
);
}
newTool.Restart();
newTool.Show();
return newTool;
}
public void AutoLoad()
{
var genericSettings = _config.CommonToolSettings
@ -629,8 +644,9 @@ namespace BizHawk.Client.EmuHawk
/// </summary>
/// <param name="toolType">Type of tool you want to create</param>
/// <param name="dllPath">Path dll for an external tool</param>
/// <param name="toolTypeName">For external tools, <see cref="Type.FullName"/> of the entry form's type (<paramref name="toolType"/> will be <see cref="IExternalToolForm"/>)</param>
/// <returns>New instance of an IToolForm</returns>
private IToolForm CreateInstance(Type toolType, string dllPath)
private IToolForm CreateInstance(Type toolType, string dllPath, string toolTypeName = null)
{
IToolForm tool;
@ -645,7 +661,7 @@ namespace BizHawk.Client.EmuHawk
{
try
{
tool = Activator.CreateInstanceFrom(dllPath, "BizHawk.Client.EmuHawk.CustomMainForm").Unwrap() as IExternalToolForm;
tool = Activator.CreateInstanceFrom(dllPath, toolTypeName ?? "BizHawk.Client.EmuHawk.CustomMainForm").Unwrap() as IExternalToolForm;
if (tool == null)
{
MessageBox.Show($"It seems that the object CustomMainForm does not implement {nameof(IExternalToolForm)}. Please review the code.", "No, no, no. Wrong Way !", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);