mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-13 12:14:16 +01:00
* Add IInternal/PublicDisposableService Plugins are exposed interfaces that are not inherited from `IDisposable`, but services implementing plugin interfaces often implement `IDisposable`. Some plugins may try to call `IDisposable.Dispose` on everything provided, and it also is possible to use `using` clause too eagerly while working on Dalamud itself, such as writing `using var smth = await Service<SomeService>.GetAsync();`. Such behaviors often lead to a difficult-to-debug errors, and making those services either not an `IDisposable` or making `IDisposable.Dispose` do nothing if the object has been loaded would prevent such errors. As `ServiceManager` must be the only class dealing with construction and disposal of services, `IInternalDisposableService` has been added to limit who can dispose the object. `IPublicDisposableService` also has been added to classes that can be constructed and accessed directly by plugins; for those, `Dispose` will be ignored if the instance is a service instance, and only `DisposeService` will respond. In addition, `DalamudPluginInterface` and `UiBuilder` also have been changed so that their `IDisposable.Dispose` no longer respond, and instead, internal functions have been added to only allow disposal from Dalamud. * Cleanup * Postmerge fixes * More explanation on RunOnFrameworkThread(ClearHooks) * Mark ReliableFileStorage public ctor obsolete --------- Co-authored-by: goat <16760685+goaaats@users.noreply.github.com>
239 lines
7.8 KiB
C#
239 lines
7.8 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
|
|
using Dalamud.Game.ClientState.Keys;
|
|
using Dalamud.Interface.Internal;
|
|
using Dalamud.IoC;
|
|
using Dalamud.IoC.Internal;
|
|
using Dalamud.Plugin.Services;
|
|
using Dalamud.Utility;
|
|
|
|
namespace Dalamud.Interface;
|
|
|
|
/// <summary>
|
|
/// Class responsible for managing elements in the title screen menu.
|
|
/// </summary>
|
|
[InterfaceVersion("1.0")]
|
|
[ServiceManager.BlockingEarlyLoadedService]
|
|
internal class TitleScreenMenu : IServiceType, ITitleScreenMenu
|
|
{
|
|
/// <summary>
|
|
/// Gets the texture size needed for title screen menu logos.
|
|
/// </summary>
|
|
internal const uint TextureSize = 64;
|
|
|
|
private readonly List<TitleScreenMenuEntry> entries = new();
|
|
private TitleScreenMenuEntry[]? entriesView;
|
|
|
|
[ServiceManager.ServiceConstructor]
|
|
private TitleScreenMenu()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Event to be called when the entry list has been changed.
|
|
/// </summary>
|
|
internal event Action? EntryListChange;
|
|
|
|
/// <inheritdoc/>
|
|
public IReadOnlyList<TitleScreenMenuEntry> Entries
|
|
{
|
|
get
|
|
{
|
|
lock (this.entries)
|
|
{
|
|
if (!this.entries.Any())
|
|
return Array.Empty<TitleScreenMenuEntry>();
|
|
|
|
return this.entriesView ??= this.entries.OrderByDescending(x => x.IsInternal).ToArray();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public TitleScreenMenuEntry AddEntry(string text, IDalamudTextureWrap texture, Action onTriggered)
|
|
{
|
|
if (texture.Height != TextureSize || texture.Width != TextureSize)
|
|
{
|
|
throw new ArgumentException("Texture must be 64x64");
|
|
}
|
|
|
|
TitleScreenMenuEntry entry;
|
|
lock (this.entries)
|
|
{
|
|
var entriesOfAssembly = this.entries.Where(x => x.CallingAssembly == Assembly.GetCallingAssembly()).ToList();
|
|
var priority = entriesOfAssembly.Any()
|
|
? unchecked(entriesOfAssembly.Select(x => x.Priority).Max() + 1)
|
|
: 0;
|
|
entry = new(Assembly.GetCallingAssembly(), priority, text, texture, onTriggered);
|
|
var i = this.entries.BinarySearch(entry);
|
|
if (i < 0)
|
|
i = ~i;
|
|
this.entries.Insert(i, entry);
|
|
this.entriesView = null;
|
|
}
|
|
|
|
this.EntryListChange?.InvokeSafely();
|
|
return entry;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public TitleScreenMenuEntry AddEntry(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered)
|
|
{
|
|
if (texture.Height != TextureSize || texture.Width != TextureSize)
|
|
{
|
|
throw new ArgumentException("Texture must be 64x64");
|
|
}
|
|
|
|
TitleScreenMenuEntry entry;
|
|
lock (this.entries)
|
|
{
|
|
entry = new(Assembly.GetCallingAssembly(), priority, text, texture, onTriggered);
|
|
var i = this.entries.BinarySearch(entry);
|
|
if (i < 0)
|
|
i = ~i;
|
|
this.entries.Insert(i, entry);
|
|
this.entriesView = null;
|
|
}
|
|
|
|
this.EntryListChange?.InvokeSafely();
|
|
return entry;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void RemoveEntry(TitleScreenMenuEntry entry)
|
|
{
|
|
lock (this.entries)
|
|
{
|
|
this.entries.Remove(entry);
|
|
this.entriesView = null;
|
|
}
|
|
|
|
this.EntryListChange?.InvokeSafely();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a new entry to the title screen menu.
|
|
/// </summary>
|
|
/// <param name="priority">Priority of the entry.</param>
|
|
/// <param name="text">The text to show.</param>
|
|
/// <param name="texture">The texture to show.</param>
|
|
/// <param name="onTriggered">The action to execute when the option is selected.</param>
|
|
/// <returns>A <see cref="TitleScreenMenu"/> object that can be used to manage the entry.</returns>
|
|
/// <exception cref="ArgumentException">Thrown when the texture provided does not match the required resolution(64x64).</exception>
|
|
internal TitleScreenMenuEntry AddEntryCore(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered)
|
|
{
|
|
if (texture.Height != TextureSize || texture.Width != TextureSize)
|
|
{
|
|
throw new ArgumentException("Texture must be 64x64");
|
|
}
|
|
|
|
TitleScreenMenuEntry entry;
|
|
lock (this.entries)
|
|
{
|
|
entry = new(null, priority, text, texture, onTriggered)
|
|
{
|
|
IsInternal = true,
|
|
};
|
|
this.entries.Add(entry);
|
|
this.entriesView = null;
|
|
}
|
|
|
|
this.EntryListChange?.InvokeSafely();
|
|
return entry;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a new entry to the title screen menu.
|
|
/// </summary>
|
|
/// <param name="text">The text to show.</param>
|
|
/// <param name="texture">The texture to show.</param>
|
|
/// <param name="onTriggered">The action to execute when the option is selected.</param>
|
|
/// <param name="showConditionKeys">The keys that have to be held to display the menu.</param>
|
|
/// <returns>A <see cref="TitleScreenMenu"/> object that can be used to manage the entry.</returns>
|
|
/// <exception cref="ArgumentException">Thrown when the texture provided does not match the required resolution(64x64).</exception>
|
|
internal TitleScreenMenuEntry AddEntryCore(
|
|
string text,
|
|
IDalamudTextureWrap texture,
|
|
Action onTriggered,
|
|
params VirtualKey[] showConditionKeys)
|
|
{
|
|
if (texture.Height != TextureSize || texture.Width != TextureSize)
|
|
{
|
|
throw new ArgumentException("Texture must be 64x64");
|
|
}
|
|
|
|
TitleScreenMenuEntry entry;
|
|
lock (this.entries)
|
|
{
|
|
var entriesOfAssembly = this.entries.Where(x => x.CallingAssembly == null).ToList();
|
|
var priority = entriesOfAssembly.Any()
|
|
? unchecked(entriesOfAssembly.Select(x => x.Priority).Max() + 1)
|
|
: 0;
|
|
entry = new(null, priority, text, texture, onTriggered, showConditionKeys)
|
|
{
|
|
IsInternal = true,
|
|
};
|
|
this.entries.Add(entry);
|
|
this.entriesView = null;
|
|
}
|
|
|
|
this.EntryListChange?.InvokeSafely();
|
|
return entry;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Plugin-scoped version of a TitleScreenMenu service.
|
|
/// </summary>
|
|
[PluginInterface]
|
|
[InterfaceVersion("1.0")]
|
|
[ServiceManager.ScopedService]
|
|
#pragma warning disable SA1015
|
|
[ResolveVia<ITitleScreenMenu>]
|
|
#pragma warning restore SA1015
|
|
internal class TitleScreenMenuPluginScoped : IInternalDisposableService, ITitleScreenMenu
|
|
{
|
|
[ServiceManager.ServiceDependency]
|
|
private readonly TitleScreenMenu titleScreenMenuService = Service<TitleScreenMenu>.Get();
|
|
|
|
private readonly List<TitleScreenMenuEntry> pluginEntries = new();
|
|
|
|
/// <inheritdoc/>
|
|
public IReadOnlyList<TitleScreenMenuEntry>? Entries => this.titleScreenMenuService.Entries;
|
|
|
|
/// <inheritdoc/>
|
|
void IInternalDisposableService.DisposeService()
|
|
{
|
|
foreach (var entry in this.pluginEntries)
|
|
{
|
|
this.titleScreenMenuService.RemoveEntry(entry);
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public TitleScreenMenuEntry AddEntry(string text, IDalamudTextureWrap texture, Action onTriggered)
|
|
{
|
|
var entry = this.titleScreenMenuService.AddEntry(text, texture, onTriggered);
|
|
this.pluginEntries.Add(entry);
|
|
|
|
return entry;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public TitleScreenMenuEntry AddEntry(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered)
|
|
{
|
|
var entry = this.titleScreenMenuService.AddEntry(priority, text, texture, onTriggered);
|
|
this.pluginEntries.Add(entry);
|
|
|
|
return entry;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void RemoveEntry(TitleScreenMenuEntry entry)
|
|
{
|
|
this.pluginEntries.Remove(entry);
|
|
this.titleScreenMenuService.RemoveEntry(entry);
|
|
}
|
|
}
|