224 lines
7.9 KiB
C#
224 lines
7.9 KiB
C#
namespace AcDream.Plugin.Abstractions;
|
|
|
|
/// <summary>
|
|
/// Stable, presentation-neutral description of one top-level plugin window.
|
|
/// The graphical host uses this metadata for its plugin sidepanel and retained
|
|
/// window registry; no App/UI type crosses the plugin boundary.
|
|
/// </summary>
|
|
/// <param name="WindowId">
|
|
/// Stable id within the owning plugin. It is part of the persisted window-layout
|
|
/// key, so it must not be localized or changed between releases.
|
|
/// </param>
|
|
/// <param name="Title">User-facing window title.</param>
|
|
public sealed record PluginPanelDescriptor(string WindowId, string Title)
|
|
{
|
|
/// <summary>
|
|
/// Optional one-to-three-character fallback drawn in the sidepanel button
|
|
/// when no DAT icon is supplied. The host derives initials from
|
|
/// <see cref="Title"/> when this is empty.
|
|
/// </summary>
|
|
public string? IconText { get; init; }
|
|
|
|
/// <summary>
|
|
/// Optional installed-client RenderSurface DID. Zero asks the host to draw
|
|
/// <see cref="IconText"/> instead. Plugins never receive the resulting GPU
|
|
/// resource and remain BCL-only.
|
|
/// </summary>
|
|
public uint IconSurfaceId { get; init; }
|
|
|
|
/// <summary>
|
|
/// Initial visibility used only when no per-character persisted layout is
|
|
/// available. Hiding the window never disables the plugin.
|
|
/// </summary>
|
|
public bool StartVisible { get; init; } = true;
|
|
|
|
/// <summary>Whether this window receives a button in the shared sidepanel.</summary>
|
|
public bool ShowInSidePanel { get; init; } = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Host-authenticated plugin identity attached to registrations by the scoped
|
|
/// plugin lifetime. Plugins cannot choose or spoof this value.
|
|
/// </summary>
|
|
public readonly record struct PluginUiOwner(string Id, string DisplayName);
|
|
|
|
/// <summary>
|
|
/// Plugin-facing UI registration. A plugin ships a markup file (KSML-style) +
|
|
/// a binding object exposing the data properties the markup binds to, and
|
|
/// registers it from <c>Enable()</c>. Graphical hosts buffer registrations until
|
|
/// their retained UI exists. A host whose <see cref="IPluginHost.HasUi"/> is
|
|
/// <see langword="false"/> exposes <see cref="NoOpUiRegistry.Instance"/> and
|
|
/// intentionally discards registrations.
|
|
/// </summary>
|
|
public interface IUiRegistry
|
|
{
|
|
/// <param name="markupPath">Absolute path to the plugin's panel markup file.</param>
|
|
/// <param name="binding">Object whose properties the markup's {Bindings} resolve against.</param>
|
|
void AddMarkupPanel(string markupPath, object binding);
|
|
|
|
/// <summary>
|
|
/// Registers a first-class plugin window. The host keeps the plugin lifetime
|
|
/// independent from the window's visible/minimized state.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Defaulting to the API-v1 method keeps older/custom hosts source-compatible;
|
|
/// acdream's graphical scoped host overrides this route and preserves all
|
|
/// descriptor metadata.
|
|
/// </remarks>
|
|
void AddPanel(
|
|
PluginPanelDescriptor descriptor,
|
|
string markupPath,
|
|
object binding)
|
|
=> AddMarkupPanel(markupPath, binding);
|
|
|
|
/// <summary>
|
|
/// Registers a window whose lifetime may be ended independently while the
|
|
/// plugin keeps running. Disposing the token removes the retained window
|
|
/// and its sidepanel entry.
|
|
/// </summary>
|
|
IDisposable RegisterPanel(
|
|
PluginPanelDescriptor descriptor,
|
|
string markupPath,
|
|
object binding)
|
|
{
|
|
AddPanel(descriptor, markupPath, binding);
|
|
return NoOpUiRegistration.Instance;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Registers an independently removable window from in-memory KSML. This
|
|
/// is the BCL-only seam used by VTank-compatible Meta Create View actions;
|
|
/// plugins do not need to create temporary files or import App types.
|
|
/// </summary>
|
|
IDisposable RegisterPanelContent(
|
|
PluginPanelDescriptor descriptor,
|
|
string markupContent,
|
|
object binding) => NoOpUiRegistration.Instance;
|
|
|
|
/// <summary>Queries this plugin's own registered view by title or stable id.</summary>
|
|
bool ViewExists(string viewName) => false;
|
|
bool IsViewVisible(string viewName) => false;
|
|
bool ControlExists(string viewName, string controlName) => false;
|
|
bool SetControlLabel(string viewName, string controlName, string label) => false;
|
|
bool SetControlVisible(string viewName, string controlName, bool visible) => false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Host-infrastructure extension used to give each plugin a removable UI
|
|
/// registration lifetime. Plugins continue to call
|
|
/// <see cref="IUiRegistry.AddMarkupPanel"/>; the shared plugin host wraps that
|
|
/// call and owns the returned token so failed initialization, failed enable,
|
|
/// and shutdown can roll the registration back without trusting plugin code.
|
|
/// </summary>
|
|
public interface IScopedUiRegistry : IUiRegistry
|
|
{
|
|
IDisposable RegisterMarkupPanel(string markupPath, object binding);
|
|
|
|
/// <summary>
|
|
/// Host-only scoped registration carrying the manifest-derived owner.
|
|
/// Disposal removes both the retained window and its sidepanel entry.
|
|
/// </summary>
|
|
IDisposable RegisterPanel(
|
|
PluginUiOwner owner,
|
|
PluginPanelDescriptor descriptor,
|
|
string markupPath,
|
|
object binding)
|
|
=> RegisterMarkupPanel(markupPath, binding);
|
|
|
|
/// <summary>Host-owned registration for in-memory plugin markup.</summary>
|
|
IDisposable RegisterPanelContent(
|
|
PluginUiOwner owner,
|
|
PluginPanelDescriptor descriptor,
|
|
string markupContent,
|
|
object binding) => NoOpUiRegistration.Instance;
|
|
|
|
bool ViewExists(PluginUiOwner owner, string viewName) => false;
|
|
bool IsViewVisible(PluginUiOwner owner, string viewName) => false;
|
|
bool ControlExists(
|
|
PluginUiOwner owner,
|
|
string viewName,
|
|
string controlName) => false;
|
|
bool SetControlLabel(
|
|
PluginUiOwner owner,
|
|
string viewName,
|
|
string controlName,
|
|
string label) => false;
|
|
bool SetControlVisible(
|
|
PluginUiOwner owner,
|
|
string viewName,
|
|
string controlName,
|
|
bool visible) => false;
|
|
}
|
|
|
|
/// <summary>Shared empty registration returned by UI-less/legacy hosts.</summary>
|
|
public sealed class NoOpUiRegistration : IDisposable
|
|
{
|
|
public static NoOpUiRegistration Instance { get; } = new();
|
|
|
|
private NoOpUiRegistration()
|
|
{
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// BCL-only UI sink for no-window plugin hosts. It intentionally retains
|
|
/// neither markup paths nor binding objects, so a UI registration cannot keep
|
|
/// a plugin assembly alive after its collectible load context is unloaded.
|
|
/// </summary>
|
|
public sealed class NoOpUiRegistry : IScopedUiRegistry
|
|
{
|
|
public static NoOpUiRegistry Instance { get; } = new();
|
|
|
|
private NoOpUiRegistry()
|
|
{
|
|
}
|
|
|
|
public void AddMarkupPanel(string markupPath, object binding)
|
|
{
|
|
}
|
|
|
|
public void AddPanel(
|
|
PluginPanelDescriptor descriptor,
|
|
string markupPath,
|
|
object binding)
|
|
{
|
|
}
|
|
|
|
public IDisposable RegisterPanel(
|
|
PluginPanelDescriptor descriptor,
|
|
string markupPath,
|
|
object binding) => NoOpUiRegistration.Instance;
|
|
|
|
public IDisposable RegisterPanelContent(
|
|
PluginPanelDescriptor descriptor,
|
|
string markupContent,
|
|
object binding) => NoOpUiRegistration.Instance;
|
|
|
|
public IDisposable RegisterMarkupPanel(string markupPath, object binding) =>
|
|
NoOpRegistration.Instance;
|
|
|
|
public IDisposable RegisterPanel(
|
|
PluginUiOwner owner,
|
|
PluginPanelDescriptor descriptor,
|
|
string markupPath,
|
|
object binding) => NoOpRegistration.Instance;
|
|
|
|
public IDisposable RegisterPanelContent(
|
|
PluginUiOwner owner,
|
|
PluginPanelDescriptor descriptor,
|
|
string markupContent,
|
|
object binding) => NoOpUiRegistration.Instance;
|
|
|
|
private sealed class NoOpRegistration : IDisposable
|
|
{
|
|
internal static NoOpRegistration Instance { get; } = new();
|
|
|
|
public void Dispose()
|
|
{
|
|
}
|
|
}
|
|
}
|