mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-13 12:14:16 +01:00
Merge pull request #1766 from goatcorp/new_im_hooks-rollup
[new_im_hooks] Rollup changes from master
This commit is contained in:
commit
b2b894b1cc
25 changed files with 376 additions and 114 deletions
2
.gitmodules
vendored
2
.gitmodules
vendored
|
|
@ -3,7 +3,7 @@
|
||||||
url = https://github.com/goatcorp/ImGuiScene
|
url = https://github.com/goatcorp/ImGuiScene
|
||||||
[submodule "lib/FFXIVClientStructs"]
|
[submodule "lib/FFXIVClientStructs"]
|
||||||
path = lib/FFXIVClientStructs
|
path = lib/FFXIVClientStructs
|
||||||
url = https://github.com/aers/FFXIVClientStructs.git
|
url = https://github.com/aers/FFXIVClientStructs
|
||||||
[submodule "lib/Nomade040-nmd"]
|
[submodule "lib/Nomade040-nmd"]
|
||||||
path = lib/Nomade040-nmd
|
path = lib/Nomade040-nmd
|
||||||
url = https://github.com/Nomade040/nmd.git
|
url = https://github.com/Nomade040/nmd.git
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Lumina" Version="3.16.0" />
|
<PackageReference Include="Lumina" Version="3.17.0" />
|
||||||
<PackageReference Include="Lumina.Excel" Version="6.5.2" />
|
<PackageReference Include="Lumina.Excel" Version="6.5.2" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.333">
|
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.333">
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Label="Feature">
|
<PropertyGroup Label="Feature">
|
||||||
<DalamudVersion>9.1.0.5</DalamudVersion>
|
<DalamudVersion>9.1.0.6</DalamudVersion>
|
||||||
<Description>XIV Launcher addon framework</Description>
|
<Description>XIV Launcher addon framework</Description>
|
||||||
<AssemblyVersion>$(DalamudVersion)</AssemblyVersion>
|
<AssemblyVersion>$(DalamudVersion)</AssemblyVersion>
|
||||||
<Version>$(DalamudVersion)</Version>
|
<Version>$(DalamudVersion)</Version>
|
||||||
|
|
@ -68,9 +68,9 @@
|
||||||
<PackageReference Include="goaaats.Reloaded.Hooks" Version="4.2.0-goat.4" />
|
<PackageReference Include="goaaats.Reloaded.Hooks" Version="4.2.0-goat.4" />
|
||||||
<PackageReference Include="goaaats.Reloaded.Assembler" Version="1.0.14-goat.2" />
|
<PackageReference Include="goaaats.Reloaded.Assembler" Version="1.0.14-goat.2" />
|
||||||
<PackageReference Include="JetBrains.Annotations" Version="2021.2.0" />
|
<PackageReference Include="JetBrains.Annotations" Version="2021.2.0" />
|
||||||
<PackageReference Include="Lumina" Version="3.16.0" />
|
<PackageReference Include="Lumina" Version="3.17.0" />
|
||||||
<PackageReference Include="Lumina.Excel" Version="6.5.2" />
|
<PackageReference Include="Lumina.Excel" Version="6.5.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="9.0.0-preview.1.24081.5" />
|
||||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.46-beta">
|
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.46-beta">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
|
|
@ -52,15 +51,12 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager
|
||||||
DefaultExcelLanguage = this.Language.ToLumina(),
|
DefaultExcelLanguage = this.Language.ToLumina(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var processModule = Process.GetCurrentProcess().MainModule;
|
this.GameData = new(
|
||||||
if (processModule != null)
|
Path.Combine(Path.GetDirectoryName(Environment.ProcessPath)!, "sqpack"),
|
||||||
|
luminaOptions)
|
||||||
{
|
{
|
||||||
this.GameData = new GameData(Path.Combine(Path.GetDirectoryName(processModule.FileName)!, "sqpack"), luminaOptions);
|
StreamPool = new(),
|
||||||
}
|
};
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new Exception("Could not main module.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Information("Lumina is ready: {0}", this.GameData.DataPath);
|
Log.Information("Lumina is ready: {0}", this.GameData.DataPath);
|
||||||
|
|
||||||
|
|
@ -107,7 +103,8 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Error(ex, "Could not download data.");
|
Log.Error(ex, "Could not initialize Lumina");
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,6 +158,7 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager
|
||||||
void IInternalDisposableService.DisposeService()
|
void IInternalDisposableService.DisposeService()
|
||||||
{
|
{
|
||||||
this.luminaCancellationTokenSource.Cancel();
|
this.luminaCancellationTokenSource.Cancel();
|
||||||
|
this.GameData.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class LauncherTroubleshootingInfo
|
private class LauncherTroubleshootingInfo
|
||||||
|
|
|
||||||
|
|
@ -47,14 +47,14 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM
|
||||||
this.addonContextMenuOnMenuSelectedHook.Enable();
|
this.addonContextMenuOnMenuSelectedHook.Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe delegate ushort RaptureAtkModuleOpenAddonByAgentDelegate(RaptureAtkModule* module, byte* addonName, AtkUnitBase* addon, int valueCount, AtkValue* values, AgentInterface* agent, nint a7, ushort parentAddonId);
|
private delegate ushort RaptureAtkModuleOpenAddonByAgentDelegate(RaptureAtkModule* module, byte* addonName, AtkUnitBase* addon, int valueCount, AtkValue* values, AgentInterface* agent, nint a7, ushort parentAddonId);
|
||||||
|
|
||||||
private unsafe delegate bool AddonContextMenuOnMenuSelectedDelegate(AddonContextMenu* addon, int selectedIdx, byte a3);
|
private delegate bool AddonContextMenuOnMenuSelectedDelegate(AddonContextMenu* addon, int selectedIdx, byte a3);
|
||||||
|
|
||||||
private unsafe delegate ushort RaptureAtkModuleOpenAddonDelegate(RaptureAtkModule* a1, uint addonNameId, uint valueCount, AtkValue* values, AgentInterface* parentAgent, ulong unk, ushort parentAddonId, int unk2);
|
private delegate ushort RaptureAtkModuleOpenAddonDelegate(RaptureAtkModule* a1, uint addonNameId, uint valueCount, AtkValue* values, AgentInterface* parentAgent, ulong unk, ushort parentAddonId, int unk2);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public event IContextMenu.OnMenuOpenedDelegate OnMenuOpened;
|
public event IContextMenu.OnMenuOpenedDelegate? OnMenuOpened;
|
||||||
|
|
||||||
private Dictionary<ContextMenuType, List<MenuItem>> MenuItems { get; } = new();
|
private Dictionary<ContextMenuType, List<MenuItem>> MenuItems { get; } = new();
|
||||||
|
|
||||||
|
|
@ -171,7 +171,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM
|
||||||
|
|
||||||
private void SetupGenericMenu(int headerCount, int sizeHeaderIdx, int returnHeaderIdx, int submenuHeaderIdx, IReadOnlyList<MenuItem> items, ref int valueCount, ref AtkValue* values)
|
private void SetupGenericMenu(int headerCount, int sizeHeaderIdx, int returnHeaderIdx, int submenuHeaderIdx, IReadOnlyList<MenuItem> items, ref int valueCount, ref AtkValue* values)
|
||||||
{
|
{
|
||||||
var itemsWithIdx = items.Select((item, idx) => (item, idx)).OrderBy(i => i.item.Priority);
|
var itemsWithIdx = items.Select((item, idx) => (item, idx)).OrderBy(i => i.item.Priority).ToArray();
|
||||||
var prefixItems = itemsWithIdx.Where(i => i.item.Priority < 0).ToArray();
|
var prefixItems = itemsWithIdx.Where(i => i.item.Priority < 0).ToArray();
|
||||||
var suffixItems = itemsWithIdx.Where(i => i.item.Priority >= 0).ToArray();
|
var suffixItems = itemsWithIdx.Where(i => i.item.Priority >= 0).ToArray();
|
||||||
|
|
||||||
|
|
@ -268,10 +268,10 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM
|
||||||
|
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
{
|
{
|
||||||
if (!item.Prefix.HasValue)
|
if (!item.Prefix.HasValue && !item.UseDefaultPrefix)
|
||||||
{
|
{
|
||||||
item.PrefixChar = 'D';
|
item.Prefix = MenuItem.DalamudDefaultPrefix;
|
||||||
item.PrefixColor = 539;
|
item.PrefixColor = MenuItem.DalamudDefaultPrefixColor;
|
||||||
Log.Warning($"Menu item \"{item.Name}\" has no prefix, defaulting to Dalamud's. Menu items outside of a submenu must have a prefix.");
|
Log.Warning($"Menu item \"{item.Name}\" has no prefix, defaulting to Dalamud's. Menu items outside of a submenu must have a prefix.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -378,13 +378,13 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM
|
||||||
{
|
{
|
||||||
// The in game menu actually supports 32 items, but the last item can't have a visible submenu arrow.
|
// The in game menu actually supports 32 items, but the last item can't have a visible submenu arrow.
|
||||||
// As such, we'll only work with 31 items.
|
// As such, we'll only work with 31 items.
|
||||||
const int MaxMenuItems = 31;
|
const int maxMenuItems = 31;
|
||||||
if (items.Count + nativeMenuSize > MaxMenuItems)
|
if (items.Count + nativeMenuSize > maxMenuItems)
|
||||||
{
|
{
|
||||||
Log.Warning($"Menu size exceeds {MaxMenuItems} items, truncating.");
|
Log.Warning($"Menu size exceeds {maxMenuItems} items, truncating.");
|
||||||
var orderedItems = items.OrderBy(i => i.Priority).ToArray();
|
var orderedItems = items.OrderBy(i => i.Priority).ToArray();
|
||||||
var newItems = orderedItems[..(MaxMenuItems - nativeMenuSize - 1)];
|
var newItems = orderedItems[..(maxMenuItems - nativeMenuSize - 1)];
|
||||||
var submenuItems = orderedItems[(MaxMenuItems - nativeMenuSize - 1)..];
|
var submenuItems = orderedItems[(maxMenuItems - nativeMenuSize - 1)..];
|
||||||
return newItems.Append(new MenuItem
|
return newItems.Append(new MenuItem
|
||||||
{
|
{
|
||||||
Prefix = SeIconChar.BoxedLetterD,
|
Prefix = SeIconChar.BoxedLetterD,
|
||||||
|
|
@ -450,7 +450,6 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM
|
||||||
if (callbackId < 0)
|
if (callbackId < 0)
|
||||||
{
|
{
|
||||||
selectedIdx = -callbackId - 1;
|
selectedIdx = -callbackId - 1;
|
||||||
goto original;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -461,17 +460,17 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM
|
||||||
{
|
{
|
||||||
if (item.OnClicked == null)
|
if (item.OnClicked == null)
|
||||||
throw new InvalidOperationException("Item has no OnClicked handler");
|
throw new InvalidOperationException("Item has no OnClicked handler");
|
||||||
item.OnClicked.InvokeSafely(new(
|
item.OnClicked.InvokeSafely(new MenuItemClickedArgs(
|
||||||
(name, items) =>
|
(name, submenuItems) =>
|
||||||
{
|
{
|
||||||
short x, y;
|
short x, y;
|
||||||
addon->AtkUnitBase.GetPosition(&x, &y);
|
addon->AtkUnitBase.GetPosition(&x, &y);
|
||||||
this.OpenSubmenu(name ?? item.Name, items, x, y);
|
this.OpenSubmenu(name ?? item.Name, submenuItems, x, y);
|
||||||
openedSubmenu = true;
|
openedSubmenu = true;
|
||||||
},
|
},
|
||||||
this.SelectedParentAddon,
|
this.SelectedParentAddon,
|
||||||
this.SelectedAgent,
|
this.SelectedAgent,
|
||||||
this.SelectedMenuType.Value,
|
this.SelectedMenuType ?? default,
|
||||||
this.SelectedEventInterfaces));
|
this.SelectedEventInterfaces));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
@ -479,14 +478,14 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM
|
||||||
Log.Error(e, "Error while handling context menu click");
|
Log.Error(e, "Error while handling context menu click");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close with clicky sound
|
// Close with click sound
|
||||||
if (!openedSubmenu)
|
if (!openedSubmenu)
|
||||||
addon->AtkUnitBase.FireCallbackInt(-2);
|
addon->AtkUnitBase.FireCallbackInt(-2);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
original:
|
original:
|
||||||
// Eventually handled by inventorycontext here: 14022BBD0 (6.51)
|
// Eventually handled by inventory context here: 14022BBD0 (6.51)
|
||||||
return this.addonContextMenuOnMenuSelectedHook.Original(addon, selectedIdx, a3);
|
return this.addonContextMenuOnMenuSelectedHook.Original(addon, selectedIdx, a3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -511,7 +510,7 @@ internal class ContextMenuPluginScoped : IInternalDisposableService, IContextMen
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public event IContextMenu.OnMenuOpenedDelegate OnMenuOpened;
|
public event IContextMenu.OnMenuOpenedDelegate? OnMenuOpened;
|
||||||
|
|
||||||
private Dictionary<ContextMenuType, List<MenuItem>> MenuItems { get; } = new();
|
private Dictionary<ContextMenuType, List<MenuItem>> MenuItems { get; } = new();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,8 +70,18 @@ public abstract unsafe class MenuArgs
|
||||||
/// Almost always an agent pointer. You can use this to find out what type of context menu it is.
|
/// Almost always an agent pointer. You can use this to find out what type of context menu it is.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <exception cref="InvalidOperationException">Thrown when the context menu is not a <see cref="ContextMenuType.Default"/>.</exception>
|
/// <exception cref="InvalidOperationException">Thrown when the context menu is not a <see cref="ContextMenuType.Default"/>.</exception>
|
||||||
public IReadOnlySet<nint> EventInterfaces =>
|
public IReadOnlySet<nint> EventInterfaces
|
||||||
this.MenuType != ContextMenuType.Default ?
|
{
|
||||||
this.eventInterfaces :
|
get
|
||||||
throw new InvalidOperationException("Not a default context menu");
|
{
|
||||||
|
if (this.MenuType is ContextMenuType.Default)
|
||||||
|
{
|
||||||
|
return this.eventInterfaces ?? new HashSet<nint>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Not a default context menu");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,16 @@ namespace Dalamud.Game.Gui.ContextMenu;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed record MenuItem
|
public sealed record MenuItem
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The default prefix used if no specific preset is specified.
|
||||||
|
/// </summary>
|
||||||
|
public const SeIconChar DalamudDefaultPrefix = SeIconChar.BoxedLetterD;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The default prefix color used if no specific preset is specified.
|
||||||
|
/// </summary>
|
||||||
|
public const ushort DalamudDefaultPrefixColor = 539;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the display name of the menu item.
|
/// Gets or sets the display name of the menu item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -46,6 +56,11 @@ public sealed record MenuItem
|
||||||
/// Gets or sets the color of the <see cref="Prefix"/>. Specifies a <see cref="UIColor"/> row id.
|
/// Gets or sets the color of the <see cref="Prefix"/>. Specifies a <see cref="UIColor"/> row id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort PrefixColor { get; set; }
|
public ushort PrefixColor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the dev wishes to intentionally use the default prefix symbol and color.
|
||||||
|
/// </summary>
|
||||||
|
public bool UseDefaultPrefix { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the callback to be invoked when the menu item is clicked.
|
/// Gets or sets the callback to be invoked when the menu item is clicked.
|
||||||
|
|
|
||||||
|
|
@ -73,13 +73,16 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar
|
||||||
this.configuration.QueueSave();
|
this.configuration.QueueSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public IReadOnlyList<IReadOnlyDtrBarEntry> Entries => this.entries;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public DtrBarEntry Get(string title, SeString? text = null)
|
public DtrBarEntry Get(string title, SeString? text = null)
|
||||||
{
|
{
|
||||||
if (this.entries.Any(x => x.Title == title) || this.newEntries.Any(x => x.Title == title))
|
if (this.entries.Any(x => x.Title == title) || this.newEntries.Any(x => x.Title == title))
|
||||||
throw new ArgumentException("An entry with the same title already exists.");
|
throw new ArgumentException("An entry with the same title already exists.");
|
||||||
|
|
||||||
var entry = new DtrBarEntry(title, null);
|
var entry = new DtrBarEntry(this.configuration, title, null);
|
||||||
entry.Text = text;
|
entry.Text = text;
|
||||||
|
|
||||||
// Add the entry to the end of the order list, if it's not there already.
|
// Add the entry to the end of the order list, if it's not there already.
|
||||||
|
|
@ -196,7 +199,7 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar
|
||||||
|
|
||||||
foreach (var data in this.entries)
|
foreach (var data in this.entries)
|
||||||
{
|
{
|
||||||
var isHide = this.configuration.DtrIgnore!.Any(x => x == data.Title) || !data.Shown;
|
var isHide = data.UserHidden || !data.Shown;
|
||||||
|
|
||||||
if (data is { Dirty: true, Added: true, Text: not null, TextNode: not null })
|
if (data is { Dirty: true, Added: true, Text: not null, TextNode: not null })
|
||||||
{
|
{
|
||||||
|
|
@ -499,6 +502,9 @@ internal class DtrBarPluginScoped : IInternalDisposableService, IDtrBar
|
||||||
private readonly DtrBar dtrBarService = Service<DtrBar>.Get();
|
private readonly DtrBar dtrBarService = Service<DtrBar>.Get();
|
||||||
|
|
||||||
private readonly Dictionary<string, DtrBarEntry> pluginEntries = new();
|
private readonly Dictionary<string, DtrBarEntry> pluginEntries = new();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public IReadOnlyList<IReadOnlyDtrBarEntry> Entries => this.dtrBarService.Entries;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
void IInternalDisposableService.DisposeService()
|
void IInternalDisposableService.DisposeService()
|
||||||
|
|
@ -510,7 +516,7 @@ internal class DtrBarPluginScoped : IInternalDisposableService, IDtrBar
|
||||||
|
|
||||||
this.pluginEntries.Clear();
|
this.pluginEntries.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public DtrBarEntry Get(string title, SeString? text = null)
|
public DtrBarEntry Get(string title, SeString? text = null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,115 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
using Dalamud.Configuration.Internal;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
|
||||||
namespace Dalamud.Game.Gui.Dtr;
|
namespace Dalamud.Game.Gui.Dtr;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interface representing a read-only entry in the server info bar.
|
||||||
|
/// </summary>
|
||||||
|
public interface IReadOnlyDtrBarEntry
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the title of this entry.
|
||||||
|
/// </summary>
|
||||||
|
public string Title { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether this entry has a click action.
|
||||||
|
/// </summary>
|
||||||
|
public bool HasClickAction { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the text of this entry.
|
||||||
|
/// </summary>
|
||||||
|
public SeString Text { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a tooltip to be shown when the user mouses over the dtr entry.
|
||||||
|
/// </summary>
|
||||||
|
public SeString Tooltip { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether this entry should be shown.
|
||||||
|
/// </summary>
|
||||||
|
public bool Shown { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether or not the user has hidden this entry from view through the Dalamud settings.
|
||||||
|
/// </summary>
|
||||||
|
public bool UserHidden { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Triggers the click action of this entry.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True, if a click action was registered and executed.</returns>
|
||||||
|
public bool TriggerClickAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interface representing an entry in the server info bar.
|
||||||
|
/// </summary>
|
||||||
|
public interface IDtrBarEntry : IReadOnlyDtrBarEntry
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the text of this entry.
|
||||||
|
/// </summary>
|
||||||
|
public new SeString? Text { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a tooltip to be shown when the user mouses over the dtr entry.
|
||||||
|
/// </summary>
|
||||||
|
public new SeString? Tooltip { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether this entry is visible.
|
||||||
|
/// </summary>
|
||||||
|
public new bool Shown { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a action to be invoked when the user clicks on the dtr entry.
|
||||||
|
/// </summary>
|
||||||
|
public Action? OnClick { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove this entry from the bar.
|
||||||
|
/// You will need to re-acquire it from DtrBar to reuse it.
|
||||||
|
/// </summary>
|
||||||
|
public void Remove();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class representing an entry in the server info bar.
|
/// Class representing an entry in the server info bar.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed unsafe class DtrBarEntry : IDisposable
|
public sealed unsafe class DtrBarEntry : IDisposable, IDtrBarEntry
|
||||||
{
|
{
|
||||||
|
private readonly DalamudConfiguration configuration;
|
||||||
|
|
||||||
private bool shownBacking = true;
|
private bool shownBacking = true;
|
||||||
private SeString? textBacking = null;
|
private SeString? textBacking = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="DtrBarEntry"/> class.
|
/// Initializes a new instance of the <see cref="DtrBarEntry"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="configuration">Dalamud configuration, used to check if the entry is hidden by the user.</param>
|
||||||
/// <param name="title">The title of the bar entry.</param>
|
/// <param name="title">The title of the bar entry.</param>
|
||||||
/// <param name="textNode">The corresponding text node.</param>
|
/// <param name="textNode">The corresponding text node.</param>
|
||||||
internal DtrBarEntry(string title, AtkTextNode* textNode)
|
internal DtrBarEntry(DalamudConfiguration configuration, string title, AtkTextNode* textNode)
|
||||||
{
|
{
|
||||||
|
this.configuration = configuration;
|
||||||
this.Title = title;
|
this.Title = title;
|
||||||
this.TextNode = textNode;
|
this.TextNode = textNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Gets the title of this entry.
|
|
||||||
/// </summary>
|
|
||||||
public string Title { get; init; }
|
public string Title { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IDtrBarEntry.Text" />
|
||||||
/// Gets or sets the text of this entry.
|
|
||||||
/// </summary>
|
|
||||||
public SeString? Text
|
public SeString? Text
|
||||||
{
|
{
|
||||||
get => this.textBacking;
|
get => this.textBacking;
|
||||||
|
|
@ -41,10 +119,8 @@ public sealed unsafe class DtrBarEntry : IDisposable
|
||||||
this.Dirty = true;
|
this.Dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IDtrBarEntry.Tooltip" />
|
||||||
/// Gets or sets a tooltip to be shown when the user mouses over the dtr entry.
|
|
||||||
/// </summary>
|
|
||||||
public SeString? Tooltip { get; set; }
|
public SeString? Tooltip { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -52,9 +128,10 @@ public sealed unsafe class DtrBarEntry : IDisposable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action? OnClick { get; set; }
|
public Action? OnClick { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Gets or sets a value indicating whether this entry is visible.
|
public bool HasClickAction => this.OnClick != null;
|
||||||
/// </summary>
|
|
||||||
|
/// <inheritdoc cref="IDtrBarEntry.Shown" />
|
||||||
public bool Shown
|
public bool Shown
|
||||||
{
|
{
|
||||||
get => this.shownBacking;
|
get => this.shownBacking;
|
||||||
|
|
@ -65,6 +142,10 @@ public sealed unsafe class DtrBarEntry : IDisposable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
[Api10ToDo("Maybe make this config scoped to internalname?")]
|
||||||
|
public bool UserHidden => this.configuration.DtrIgnore?.Any(x => x == this.Title) ?? false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the internal text node of this entry.
|
/// Gets or sets the internal text node of this entry.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -84,6 +165,16 @@ public sealed unsafe class DtrBarEntry : IDisposable
|
||||||
/// Gets or sets a value indicating whether this entry has just been added.
|
/// Gets or sets a value indicating whether this entry has just been added.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal bool Added { get; set; } = false;
|
internal bool Added { get; set; } = false;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool TriggerClickAction()
|
||||||
|
{
|
||||||
|
if (this.OnClick == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this.OnClick.Invoke();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remove this entry from the bar.
|
/// Remove this entry from the bar.
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,16 @@ using Dalamud.Interface.ManagedFontAtlas.Internals;
|
||||||
using Dalamud.Interface.Style;
|
using Dalamud.Interface.Style;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
|
using Dalamud.Logging.Internal;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using Dalamud.Utility.Timing;
|
using Dalamud.Utility.Timing;
|
||||||
|
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
|
||||||
using ImGuiScene;
|
using ImGuiScene;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using PInvoke;
|
using PInvoke;
|
||||||
using Serilog;
|
|
||||||
using SharpDX;
|
using SharpDX;
|
||||||
using SharpDX.Direct3D;
|
using SharpDX.Direct3D;
|
||||||
using SharpDX.Direct3D11;
|
using SharpDX.Direct3D11;
|
||||||
|
|
@ -67,6 +70,8 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const float DefaultFontSizePx = (DefaultFontSizePt * 4.0f) / 3.0f;
|
public const float DefaultFontSizePx = (DefaultFontSizePt * 4.0f) / 3.0f;
|
||||||
|
|
||||||
|
private static readonly ModuleLog Log = new("INTERFACE");
|
||||||
|
|
||||||
private readonly ConcurrentBag<IDeferredDisposable> deferredDisposeTextures = new();
|
private readonly ConcurrentBag<IDeferredDisposable> deferredDisposeTextures = new();
|
||||||
private readonly ConcurrentBag<ILockedImFont> deferredDisposeImFontLockeds = new();
|
private readonly ConcurrentBag<ILockedImFont> deferredDisposeImFontLockeds = new();
|
||||||
|
|
||||||
|
|
@ -146,6 +151,13 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
public static ImFontPtr IconFont =>
|
public static ImFontPtr IconFont =>
|
||||||
WhenFontsReady().IconFontHandle!.LockUntilPostFrame().OrElse(ImGui.GetIO().FontDefault);
|
WhenFontsReady().IconFontHandle!.LockUntilPostFrame().OrElse(ImGui.GetIO().FontDefault);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an included FontAwesome icon font with fixed width.
|
||||||
|
/// <strong>Accessing this static property outside of the main thread is dangerous and not supported.</strong>
|
||||||
|
/// </summary>
|
||||||
|
public static ImFontPtr IconFontFixedWidth =>
|
||||||
|
WhenFontsReady().IconFontFixedWidthHandle!.LockUntilPostFrame().OrElse(ImGui.GetIO().FontDefault);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets an included monospaced font.<br />
|
/// Gets an included monospaced font.<br />
|
||||||
/// <strong>Accessing this static property outside of the main thread is dangerous and not supported.</strong>
|
/// <strong>Accessing this static property outside of the main thread is dangerous and not supported.</strong>
|
||||||
|
|
@ -163,6 +175,11 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public FontHandle? IconFontHandle { get; private set; }
|
public FontHandle? IconFontHandle { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the icon font handle with fixed width.
|
||||||
|
/// </summary>
|
||||||
|
public FontHandle? IconFontFixedWidthHandle { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the mono font handle.
|
/// Gets the mono font handle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -396,7 +413,7 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// no sampler for now because the ImGui implementation we copied doesn't allow for changing it
|
// no sampler for now because the ImGui implementation we copied doesn't allow for changing it
|
||||||
return new DalamudTextureWrap(new D3DTextureWrap(resView, width, height));
|
return new DalamudTextureWrap(new D3DTextureWrap(resView, width, height));
|
||||||
}
|
}
|
||||||
|
|
@ -492,7 +509,7 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
atlas.BuildTask.GetAwaiter().GetResult();
|
atlas.BuildTask.GetAwaiter().GetResult();
|
||||||
return im;
|
return im;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static void RenderImGui(RawDX11Scene scene)
|
private static void RenderImGui(RawDX11Scene scene)
|
||||||
{
|
{
|
||||||
|
|
@ -806,6 +823,14 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
GlyphMinAdvanceX = DefaultFontSizePx,
|
GlyphMinAdvanceX = DefaultFontSizePx,
|
||||||
GlyphMaxAdvanceX = DefaultFontSizePx,
|
GlyphMaxAdvanceX = DefaultFontSizePx,
|
||||||
})));
|
})));
|
||||||
|
this.IconFontFixedWidthHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle(
|
||||||
|
e => e.OnPreBuild(tk => tk.AddDalamudAssetFont(
|
||||||
|
DalamudAsset.FontAwesomeFreeSolid,
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
SizePx = Service<FontAtlasFactory>.Get().DefaultFontSpec.SizePx,
|
||||||
|
GlyphRanges = new ushort[] { 0x20, 0x20, 0x00 },
|
||||||
|
})));
|
||||||
this.MonoFontHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle(
|
this.MonoFontHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle(
|
||||||
e => e.OnPreBuild(
|
e => e.OnPreBuild(
|
||||||
tk => tk.AddDalamudAssetFont(
|
tk => tk.AddDalamudAssetFont(
|
||||||
|
|
@ -822,6 +847,13 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
tk.GetFont(this.DefaultFontHandle),
|
tk.GetFont(this.DefaultFontHandle),
|
||||||
tk.GetFont(this.MonoFontHandle),
|
tk.GetFont(this.MonoFontHandle),
|
||||||
missingOnly: true);
|
missingOnly: true);
|
||||||
|
|
||||||
|
// Fill missing glyphs in IconFontFixedWidth with IconFont and fit ratio
|
||||||
|
tk.CopyGlyphsAcrossFonts(
|
||||||
|
tk.GetFont(this.IconFontHandle),
|
||||||
|
tk.GetFont(this.IconFontFixedWidthHandle),
|
||||||
|
missingOnly: true);
|
||||||
|
tk.FitRatio(tk.GetFont(this.IconFontFixedWidthHandle));
|
||||||
});
|
});
|
||||||
this.DefaultFontHandle.ImFontChanged += (_, font) =>
|
this.DefaultFontHandle.ImFontChanged += (_, font) =>
|
||||||
{
|
{
|
||||||
|
|
@ -844,7 +876,7 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will wait for scene on its own. We just wait for this.dalamudAtlas.BuildTask in this.InitScene.
|
// This will wait for scene on its own. We just wait for this.dalamudAtlas.BuildTask in this.InitScene.
|
||||||
_ = this.dalamudAtlas.BuildFontsAsync();
|
_ = this.dalamudAtlas.BuildFontsAsync();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ internal class FontAwesomeTestWidget : IDataWindowWidget
|
||||||
private int selectedIconCategory;
|
private int selectedIconCategory;
|
||||||
private string iconSearchInput = string.Empty;
|
private string iconSearchInput = string.Empty;
|
||||||
private bool iconSearchChanged = true;
|
private bool iconSearchChanged = true;
|
||||||
|
private bool useFixedWidth = false;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public string[]? CommandShortcuts { get; init; } = { "fa", "fatest", "fontawesome" };
|
public string[]? CommandShortcuts { get; init; } = { "fa", "fatest", "fontawesome" };
|
||||||
|
|
@ -80,6 +81,8 @@ internal class FontAwesomeTestWidget : IDataWindowWidget
|
||||||
{
|
{
|
||||||
this.iconSearchChanged = true;
|
this.iconSearchChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui.Checkbox("Use fixed width font", ref this.useFixedWidth);
|
||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(10f);
|
ImGuiHelpers.ScaledDummy(10f);
|
||||||
for (var i = 0; i < this.icons?.Count; i++)
|
for (var i = 0; i < this.icons?.Count; i++)
|
||||||
|
|
@ -88,7 +91,7 @@ internal class FontAwesomeTestWidget : IDataWindowWidget
|
||||||
ImGuiHelpers.ScaledRelativeSameLine(50f);
|
ImGuiHelpers.ScaledRelativeSameLine(50f);
|
||||||
ImGui.Text($"{this.iconNames?[i]}");
|
ImGui.Text($"{this.iconNames?[i]}");
|
||||||
ImGuiHelpers.ScaledRelativeSameLine(280f);
|
ImGuiHelpers.ScaledRelativeSameLine(280f);
|
||||||
ImGui.PushFont(UiBuilder.IconFont);
|
ImGui.PushFont(this.useFixedWidth ? InterfaceManager.IconFontFixedWidth : InterfaceManager.IconFont);
|
||||||
ImGui.Text(this.icons[i].ToIconString());
|
ImGui.Text(this.icons[i].ToIconString());
|
||||||
ImGui.PopFont();
|
ImGui.PopFont();
|
||||||
ImGuiHelpers.ScaledDummy(2f);
|
ImGuiHelpers.ScaledDummy(2f);
|
||||||
|
|
|
||||||
|
|
@ -1128,34 +1128,33 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
this.DrawChangelog(logEntry);
|
this.DrawChangelog(logEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private record PluginInstallerAvailablePluginProxy(RemotePluginManifest? RemoteManifest, LocalPlugin? LocalPlugin);
|
|
||||||
|
|
||||||
#pragma warning disable SA1201
|
#pragma warning disable SA1201
|
||||||
private void DrawAvailablePluginList()
|
private record PluginInstallerAvailablePluginProxy(RemotePluginManifest? RemoteManifest, LocalPlugin? LocalPlugin);
|
||||||
#pragma warning restore SA1201
|
|
||||||
|
private IEnumerable<PluginInstallerAvailablePluginProxy> GatherProxies()
|
||||||
{
|
{
|
||||||
|
var proxies = new List<PluginInstallerAvailablePluginProxy>();
|
||||||
|
|
||||||
var availableManifests = this.pluginListAvailable;
|
var availableManifests = this.pluginListAvailable;
|
||||||
var installedPlugins = this.pluginListInstalled.ToList(); // Copy intended
|
var installedPlugins = this.pluginListInstalled.ToList(); // Copy intended
|
||||||
|
|
||||||
if (availableManifests.Count == 0)
|
if (availableManifests.Count == 0)
|
||||||
{
|
{
|
||||||
ImGui.TextColored(ImGuiColors.DalamudGrey, Locs.TabBody_SearchNoCompatible);
|
ImGui.TextColored(ImGuiColors.DalamudGrey, Locs.TabBody_SearchNoCompatible);
|
||||||
return;
|
return proxies;
|
||||||
}
|
}
|
||||||
|
|
||||||
var filteredAvailableManifests = availableManifests
|
var filteredAvailableManifests = availableManifests
|
||||||
.Where(rm => !this.IsManifestFiltered(rm))
|
.Where(rm => !this.IsManifestFiltered(rm))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
if (filteredAvailableManifests.Count == 0)
|
if (filteredAvailableManifests.Count == 0)
|
||||||
{
|
{
|
||||||
ImGui.TextColored(ImGuiColors.DalamudGrey2, Locs.TabBody_SearchNoMatching);
|
ImGui.TextColored(ImGuiColors.DalamudGrey2, Locs.TabBody_SearchNoMatching);
|
||||||
return;
|
return proxies;
|
||||||
}
|
}
|
||||||
|
|
||||||
var proxies = new List<PluginInstallerAvailablePluginProxy>();
|
|
||||||
|
|
||||||
// Go through all AVAILABLE manifests, associate them with a NON-DEV local plugin, if one is available, and remove it from the pile
|
// Go through all AVAILABLE manifests, associate them with a NON-DEV local plugin, if one is available, and remove it from the pile
|
||||||
foreach (var availableManifest in this.categoryManager.GetCurrentCategoryContent(filteredAvailableManifests).Cast<RemotePluginManifest>())
|
foreach (var availableManifest in this.categoryManager.GetCurrentCategoryContent(filteredAvailableManifests).Cast<RemotePluginManifest>())
|
||||||
{
|
{
|
||||||
|
|
@ -1168,7 +1167,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
if (plugin != null)
|
if (plugin != null)
|
||||||
{
|
{
|
||||||
installedPlugins.Remove(plugin);
|
installedPlugins.Remove(plugin);
|
||||||
proxies.Add(new PluginInstallerAvailablePluginProxy(null, plugin));
|
proxies.Add(new PluginInstallerAvailablePluginProxy(availableManifest, plugin));
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -1187,8 +1186,14 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
proxies.Add(new PluginInstallerAvailablePluginProxy(null, installedPlugin));
|
proxies.Add(new PluginInstallerAvailablePluginProxy(null, installedPlugin));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return proxies;
|
||||||
|
}
|
||||||
|
#pragma warning restore SA1201
|
||||||
|
|
||||||
|
private void DrawAvailablePluginList()
|
||||||
|
{
|
||||||
var i = 0;
|
var i = 0;
|
||||||
foreach (var proxy in proxies)
|
foreach (var proxy in this.GatherProxies())
|
||||||
{
|
{
|
||||||
IPluginManifest applicableManifest = proxy.LocalPlugin != null ? proxy.LocalPlugin.Manifest : proxy.RemoteManifest;
|
IPluginManifest applicableManifest = proxy.LocalPlugin != null ? proxy.LocalPlugin.Manifest : proxy.RemoteManifest;
|
||||||
|
|
||||||
|
|
@ -1199,7 +1204,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
|
|
||||||
if (proxy.LocalPlugin != null)
|
if (proxy.LocalPlugin != null)
|
||||||
{
|
{
|
||||||
this.DrawInstalledPlugin(proxy.LocalPlugin, i++, true);
|
this.DrawInstalledPlugin(proxy.LocalPlugin, i++, proxy.RemoteManifest, true);
|
||||||
}
|
}
|
||||||
else if (proxy.RemoteManifest != null)
|
else if (proxy.RemoteManifest != null)
|
||||||
{
|
{
|
||||||
|
|
@ -1237,7 +1242,12 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
if (filterTesting && !manager.HasTestingOptIn(plugin.Manifest))
|
if (filterTesting && !manager.HasTestingOptIn(plugin.Manifest))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
this.DrawInstalledPlugin(plugin, i++);
|
// Find the applicable remote manifest
|
||||||
|
var remoteManifest = this.pluginListAvailable
|
||||||
|
.FirstOrDefault(rm => rm.InternalName == plugin.Manifest.InternalName &&
|
||||||
|
rm.RepoUrl == plugin.Manifest.RepoUrl);
|
||||||
|
|
||||||
|
this.DrawInstalledPlugin(plugin, i++, remoteManifest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1266,7 +1276,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
var i = 0;
|
var i = 0;
|
||||||
foreach (var plugin in filteredList)
|
foreach (var plugin in filteredList)
|
||||||
{
|
{
|
||||||
this.DrawInstalledPlugin(plugin, i++);
|
this.DrawInstalledPlugin(plugin, i++, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2173,17 +2183,18 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
ImGuiHelpers.ScaledDummy(10);
|
ImGuiHelpers.ScaledDummy(10);
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
||||||
this.DrawVisitRepoUrlButton(manifest.RepoUrl, true);
|
if (this.DrawVisitRepoUrlButton(manifest.RepoUrl, true))
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGuiHelpers.ScaledDummy(3);
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGuiHelpers.ScaledDummy(3);
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
if (!manifest.SourceRepo.IsThirdParty && manifest.AcceptsFeedback)
|
if (!manifest.SourceRepo.IsThirdParty && manifest.AcceptsFeedback)
|
||||||
{
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
this.DrawSendFeedbackButton(manifest, false, true);
|
this.DrawSendFeedbackButton(manifest, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(5);
|
ImGuiHelpers.ScaledDummy(5);
|
||||||
|
|
||||||
if (this.DrawPluginImages(null, manifest, isThirdParty, index))
|
if (this.DrawPluginImages(null, manifest, isThirdParty, index))
|
||||||
|
|
@ -2251,7 +2262,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawInstalledPlugin(LocalPlugin plugin, int index, bool showInstalled = false)
|
private void DrawInstalledPlugin(LocalPlugin plugin, int index, RemotePluginManifest? remoteManifest, bool showInstalled = false)
|
||||||
{
|
{
|
||||||
var configuration = Service<DalamudConfiguration>.Get();
|
var configuration = Service<DalamudConfiguration>.Get();
|
||||||
var commandManager = Service<CommandManager>.Get();
|
var commandManager = Service<CommandManager>.Get();
|
||||||
|
|
@ -2376,7 +2387,9 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.PushID($"installed{index}{plugin.Manifest.InternalName}");
|
ImGui.PushID($"installed{index}{plugin.Manifest.InternalName}");
|
||||||
var hasChangelog = !plugin.Manifest.Changelog.IsNullOrEmpty();
|
|
||||||
|
var applicableChangelog = plugin.IsTesting ? remoteManifest?.Changelog : remoteManifest?.TestingChangelog;
|
||||||
|
var hasChangelog = !applicableChangelog.IsNullOrWhitespace();
|
||||||
var didDrawChangelogInsideCollapsible = false;
|
var didDrawChangelogInsideCollapsible = false;
|
||||||
|
|
||||||
if (this.DrawPluginCollapsingHeader(label, plugin, plugin.Manifest, plugin.IsThirdParty, trouble, availablePluginUpdate != default, false, false, plugin.IsOrphaned, () => this.DrawInstalledPluginContextMenu(plugin, testingOptIn), index))
|
if (this.DrawPluginCollapsingHeader(label, plugin, plugin.Manifest, plugin.IsThirdParty, trouble, availablePluginUpdate != default, false, false, plugin.IsOrphaned, () => this.DrawInstalledPluginContextMenu(plugin, testingOptIn), index))
|
||||||
|
|
@ -2473,11 +2486,15 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
|
|
||||||
if (canFeedback)
|
if (canFeedback)
|
||||||
{
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
this.DrawSendFeedbackButton(plugin.Manifest, plugin.IsTesting, false);
|
this.DrawSendFeedbackButton(plugin.Manifest, plugin.IsTesting, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (availablePluginUpdate != default && !plugin.IsDev)
|
if (availablePluginUpdate != default && !plugin.IsDev)
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
this.DrawUpdateSinglePluginButton(availablePluginUpdate);
|
this.DrawUpdateSinglePluginButton(availablePluginUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.TextColored(ImGuiColors.DalamudGrey3, $" v{plugin.EffectiveVersion}");
|
ImGui.TextColored(ImGuiColors.DalamudGrey3, $" v{plugin.EffectiveVersion}");
|
||||||
|
|
@ -2494,7 +2511,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
if (ImGui.TreeNode(Locs.PluginBody_CurrentChangeLog(plugin.EffectiveVersion)))
|
if (ImGui.TreeNode(Locs.PluginBody_CurrentChangeLog(plugin.EffectiveVersion)))
|
||||||
{
|
{
|
||||||
didDrawChangelogInsideCollapsible = true;
|
didDrawChangelogInsideCollapsible = true;
|
||||||
this.DrawInstalledPluginChangelog(plugin.Manifest);
|
this.DrawInstalledPluginChangelog(applicableChangelog);
|
||||||
ImGui.TreePop();
|
ImGui.TreePop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2502,9 +2519,10 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
if (availablePluginUpdate != default && !availablePluginUpdate.UpdateManifest.Changelog.IsNullOrWhitespace())
|
if (availablePluginUpdate != default && !availablePluginUpdate.UpdateManifest.Changelog.IsNullOrWhitespace())
|
||||||
{
|
{
|
||||||
var availablePluginUpdateVersion = availablePluginUpdate.UseTesting ? availablePluginUpdate.UpdateManifest.TestingAssemblyVersion : availablePluginUpdate.UpdateManifest.AssemblyVersion;
|
var availablePluginUpdateVersion = availablePluginUpdate.UseTesting ? availablePluginUpdate.UpdateManifest.TestingAssemblyVersion : availablePluginUpdate.UpdateManifest.AssemblyVersion;
|
||||||
if (ImGui.TreeNode(Locs.PluginBody_UpdateChangeLog(availablePluginUpdateVersion)))
|
var availableChangelog = availablePluginUpdate.UseTesting ? availablePluginUpdate.UpdateManifest.TestingChangelog : availablePluginUpdate.UpdateManifest.Changelog;
|
||||||
|
if (!availableChangelog.IsNullOrWhitespace() && ImGui.TreeNode(Locs.PluginBody_UpdateChangeLog(availablePluginUpdateVersion)))
|
||||||
{
|
{
|
||||||
this.DrawInstalledPluginChangelog(availablePluginUpdate.UpdateManifest);
|
this.DrawInstalledPluginChangelog(availableChangelog);
|
||||||
ImGui.TreePop();
|
ImGui.TreePop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2512,13 +2530,13 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
|
|
||||||
if (thisWasUpdated && hasChangelog && !didDrawChangelogInsideCollapsible)
|
if (thisWasUpdated && hasChangelog && !didDrawChangelogInsideCollapsible)
|
||||||
{
|
{
|
||||||
this.DrawInstalledPluginChangelog(plugin.Manifest);
|
this.DrawInstalledPluginChangelog(applicableChangelog);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.PopID();
|
ImGui.PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawInstalledPluginChangelog(IPluginManifest manifest)
|
private void DrawInstalledPluginChangelog(string changelog)
|
||||||
{
|
{
|
||||||
ImGuiHelpers.ScaledDummy(5);
|
ImGuiHelpers.ScaledDummy(5);
|
||||||
|
|
||||||
|
|
@ -2531,7 +2549,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
{
|
{
|
||||||
ImGui.Text("Changelog:");
|
ImGui.Text("Changelog:");
|
||||||
ImGuiHelpers.ScaledDummy(2);
|
ImGuiHelpers.ScaledDummy(2);
|
||||||
ImGuiHelpers.SafeTextWrapped(manifest.Changelog!);
|
ImGuiHelpers.SafeTextWrapped(changelog!);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndChild();
|
ImGui.EndChild();
|
||||||
|
|
@ -2878,8 +2896,6 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
|
|
||||||
private void DrawUpdateSinglePluginButton(AvailablePluginUpdate update)
|
private void DrawUpdateSinglePluginButton(AvailablePluginUpdate update)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Download))
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Download))
|
||||||
{
|
{
|
||||||
Task.Run(() => this.UpdateSinglePlugin(update));
|
Task.Run(() => this.UpdateSinglePlugin(update));
|
||||||
|
|
@ -2949,8 +2965,6 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
|
|
||||||
private void DrawSendFeedbackButton(IPluginManifest manifest, bool isTesting, bool big)
|
private void DrawSendFeedbackButton(IPluginManifest manifest, bool isTesting, bool big)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
var clicked = big ?
|
var clicked = big ?
|
||||||
ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Comment, Locs.FeedbackModal_Title) :
|
ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Comment, Locs.FeedbackModal_Title) :
|
||||||
ImGuiComponents.IconButton(FontAwesomeIcon.Comment);
|
ImGuiComponents.IconButton(FontAwesomeIcon.Comment);
|
||||||
|
|
@ -3195,7 +3209,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawVisitRepoUrlButton(string? repoUrl, bool big)
|
private bool DrawVisitRepoUrlButton(string? repoUrl, bool big)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(repoUrl) && repoUrl.StartsWith("https://"))
|
if (!string.IsNullOrEmpty(repoUrl) && repoUrl.StartsWith("https://"))
|
||||||
{
|
{
|
||||||
|
|
@ -3222,7 +3236,11 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
|
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGui.SetTooltip(Locs.PluginButtonToolTip_VisitPluginUrl);
|
ImGui.SetTooltip(Locs.PluginButtonToolTip_VisitPluginUrl);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool DrawPluginImages(LocalPlugin? plugin, IPluginManifest manifest, bool isThirdParty, int index)
|
private bool DrawPluginImages(LocalPlugin? plugin, IPluginManifest manifest, bool isThirdParty, int index)
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,12 @@ internal class TitleScreenMenuWindow : Window, IDisposable
|
||||||
var scale = ImGui.GetIO().FontGlobalScale;
|
var scale = ImGui.GetIO().FontGlobalScale;
|
||||||
var entries = this.titleScreenMenu.Entries;
|
var entries = this.titleScreenMenu.Entries;
|
||||||
|
|
||||||
|
var hovered = ImGui.IsWindowHovered(
|
||||||
|
ImGuiHoveredFlags.RootAndChildWindows |
|
||||||
|
ImGuiHoveredFlags.AllowWhenBlockedByActiveItem);
|
||||||
|
|
||||||
|
Service<InterfaceManager>.Get().OverrideGameCursor = !hovered;
|
||||||
|
|
||||||
switch (this.state)
|
switch (this.state)
|
||||||
{
|
{
|
||||||
case State.Show:
|
case State.Show:
|
||||||
|
|
@ -187,8 +193,11 @@ internal class TitleScreenMenuWindow : Window, IDisposable
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ImGui.IsWindowHovered(ImGuiHoveredFlags.RootAndChildWindows |
|
// Don't check for hover if we're in the middle of an animation, as it will cause flickering.
|
||||||
ImGuiHoveredFlags.AllowWhenBlockedByActiveItem))
|
if (this.moveEasings.Any(x => !x.Value.IsDone))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!hovered)
|
||||||
{
|
{
|
||||||
this.state = State.FadeOut;
|
this.state = State.FadeOut;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Dalamud.Interface.Internal;
|
using Dalamud.Interface.Internal;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
|
@ -27,6 +27,13 @@ public interface IFontAtlasBuildToolkitPostBuild : IFontAtlasBuildToolkit
|
||||||
/// <returns>The texture index.</returns>
|
/// <returns>The texture index.</returns>
|
||||||
int StoreTexture(IDalamudTextureWrap textureWrap, bool disposeOnError);
|
int StoreTexture(IDalamudTextureWrap textureWrap, bool disposeOnError);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fits a font to a fixed 1:1 ratio adjusting glyph positions horizontally and vertically to fit within font size boundaries.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="font">The font to fit.</param>
|
||||||
|
/// <param name="rebuildLookupTable">Whether to call target.BuildLookupTable().</param>
|
||||||
|
void FitRatio(ImFontPtr font, bool rebuildLookupTable = true);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copies glyphs across fonts, in a safer way.<br />
|
/// Copies glyphs across fonts, in a safer way.<br />
|
||||||
/// If the font does not belong to the current atlas, this function is a no-op.
|
/// If the font does not belong to the current atlas, this function is a no-op.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
@ -166,7 +166,7 @@ internal sealed partial class FontAtlasFactory
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public int StoreTexture(IDalamudTextureWrap textureWrap, bool disposeOnError) =>
|
public int StoreTexture(IDalamudTextureWrap textureWrap, bool disposeOnError) =>
|
||||||
this.data.AddNewTexture(textureWrap, disposeOnError);
|
this.data.AddNewTexture(textureWrap, disposeOnError);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void RegisterPostBuild(Action action) => this.registeredPostBuildActions.Add(action);
|
public void RegisterPostBuild(Action action) => this.registeredPostBuildActions.Add(action);
|
||||||
|
|
||||||
|
|
@ -391,12 +391,10 @@ internal sealed partial class FontAtlasFactory
|
||||||
});
|
});
|
||||||
|
|
||||||
case DalamudAsset.LodestoneGameSymbol when !this.factory.HasGameSymbolsFontFile:
|
case DalamudAsset.LodestoneGameSymbol when !this.factory.HasGameSymbolsFontFile:
|
||||||
{
|
|
||||||
return this.AddGameGlyphs(
|
return this.AddGameGlyphs(
|
||||||
new(GameFontFamily.Axis, fontConfig.SizePx),
|
new(GameFontFamily.Axis, fontConfig.SizePx),
|
||||||
fontConfig.GlyphRanges,
|
fontConfig.GlyphRanges,
|
||||||
fontConfig.MergeFont);
|
fontConfig.MergeFont);
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return this.factory.AddFont(
|
return this.factory.AddFont(
|
||||||
|
|
@ -858,5 +856,30 @@ internal sealed partial class FontAtlasFactory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void FitRatio(ImFontPtr font, bool rebuildLookupTable = true)
|
||||||
|
{
|
||||||
|
var nsize = font.FontSize;
|
||||||
|
var glyphs = font.GlyphsWrapped();
|
||||||
|
foreach (ref var glyph in glyphs.DataSpan)
|
||||||
|
{
|
||||||
|
var ratio = 1f;
|
||||||
|
if (glyph.X1 - glyph.X0 > nsize)
|
||||||
|
ratio = Math.Max(ratio, (glyph.X1 - glyph.X0) / nsize);
|
||||||
|
if (glyph.Y1 - glyph.Y0 > nsize)
|
||||||
|
ratio = Math.Max(ratio, (glyph.Y1 - glyph.Y0) / nsize);
|
||||||
|
var w = MathF.Round((glyph.X1 - glyph.X0) / ratio, MidpointRounding.ToZero);
|
||||||
|
var h = MathF.Round((glyph.Y1 - glyph.Y0) / ratio, MidpointRounding.AwayFromZero);
|
||||||
|
glyph.X0 = MathF.Round((nsize - w) / 2f, MidpointRounding.ToZero);
|
||||||
|
glyph.Y0 = MathF.Round((nsize - h) / 2f, MidpointRounding.AwayFromZero);
|
||||||
|
glyph.X1 = glyph.X0 + w;
|
||||||
|
glyph.Y1 = glyph.Y0 + h;
|
||||||
|
glyph.AdvanceX = nsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rebuildLookupTable)
|
||||||
|
this.BuildLookupTable(font);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,13 @@ using Dalamud.Plugin;
|
||||||
using Dalamud.Plugin.Internal.Types;
|
using Dalamud.Plugin.Internal.Types;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
|
||||||
using ImGuiScene;
|
using ImGuiScene;
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
using SharpDX.Direct3D11;
|
using SharpDX.Direct3D11;
|
||||||
|
|
||||||
namespace Dalamud.Interface;
|
namespace Dalamud.Interface;
|
||||||
|
|
@ -53,6 +57,7 @@ public sealed class UiBuilder : IDisposable
|
||||||
private IFontHandle? defaultFontHandle;
|
private IFontHandle? defaultFontHandle;
|
||||||
private IFontHandle? iconFontHandle;
|
private IFontHandle? iconFontHandle;
|
||||||
private IFontHandle? monoFontHandle;
|
private IFontHandle? monoFontHandle;
|
||||||
|
private IFontHandle? iconFontFixedWidthHandle;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="UiBuilder"/> class and registers it.
|
/// Initializes a new instance of the <see cref="UiBuilder"/> class and registers it.
|
||||||
|
|
@ -106,7 +111,7 @@ public sealed class UiBuilder : IDisposable
|
||||||
/// Event that is fired when the plugin should open its configuration interface.
|
/// Event that is fired when the plugin should open its configuration interface.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action OpenConfigUi;
|
public event Action OpenConfigUi;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event that is fired when the plugin should open its main interface.
|
/// Event that is fired when the plugin should open its main interface.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -251,6 +256,16 @@ public sealed class UiBuilder : IDisposable
|
||||||
this.InterfaceManagerWithScene?.IconFontHandle
|
this.InterfaceManagerWithScene?.IconFontHandle
|
||||||
?? throw new InvalidOperationException("Scene is not yet ready.")));
|
?? throw new InvalidOperationException("Scene is not yet ready.")));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the default Dalamud icon font based on FontAwesome 5 free solid with a fixed width and vertically centered glyphs.
|
||||||
|
/// </summary>
|
||||||
|
public IFontHandle IconFontFixedWidthHandle =>
|
||||||
|
this.iconFontFixedWidthHandle ??=
|
||||||
|
this.scopedFinalizer.Add(
|
||||||
|
new FontHandleWrapper(
|
||||||
|
this.InterfaceManagerWithScene?.IconFontFixedWidthHandle
|
||||||
|
?? throw new InvalidOperationException("Scene is not yet ready.")));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the default Dalamud monospaced font based on Inconsolata Regular.
|
/// Gets the default Dalamud monospaced font based on Inconsolata Regular.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -266,7 +281,7 @@ public sealed class UiBuilder : IDisposable
|
||||||
/// new() { SizePx = UiBuilder.DefaultFontSizePx })));
|
/// new() { SizePx = UiBuilder.DefaultFontSizePx })));
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public IFontHandle MonoFontHandle =>
|
public IFontHandle MonoFontHandle =>
|
||||||
this.monoFontHandle ??=
|
this.monoFontHandle ??=
|
||||||
this.scopedFinalizer.Add(
|
this.scopedFinalizer.Add(
|
||||||
new FontHandleWrapper(
|
new FontHandleWrapper(
|
||||||
|
|
@ -630,7 +645,7 @@ public sealed class UiBuilder : IDisposable
|
||||||
{
|
{
|
||||||
this.OpenConfigUi?.InvokeSafely();
|
this.OpenConfigUi?.InvokeSafely();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Open the registered configuration UI, if it exists.
|
/// Open the registered configuration UI, if it exists.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -838,5 +853,5 @@ public sealed class UiBuilder : IDisposable
|
||||||
|
|
||||||
private void WrappedOnImFontChanged(IFontHandle obj, ILockedImFont lockedFont) =>
|
private void WrappedOnImFontChanged(IFontHandle obj, ILockedImFont lockedFont) =>
|
||||||
this.ImFontChanged?.Invoke(obj, lockedFont);
|
this.ImFontChanged?.Invoke(obj, lockedFont);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
namespace Dalamud.Plugin;
|
namespace Dalamud.Plugin;
|
||||||
|
|
||||||
|
[Api10ToDo("Refactor into an interface, add wrappers for OpenMainUI and OpenConfigUI")]
|
||||||
public record InstalledPluginState(string Name, string InternalName, bool IsLoaded, Version Version);
|
public record InstalledPluginState(string Name, string InternalName, bool IsLoaded, Version Version);
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,11 @@ internal record RemotePluginManifest : PluginManifest
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public PluginRepository SourceRepo { get; set; } = null!;
|
public PluginRepository SourceRepo { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the changelog to be shown when obtaining the testing version of the plugin.
|
||||||
|
/// </summary>
|
||||||
|
public string? TestingChangelog { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this plugin is eligible for testing.
|
/// Gets a value indicating whether this plugin is eligible for testing.
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,12 @@ internal class CallGateChannel
|
||||||
if (arg == null)
|
if (arg == null)
|
||||||
{
|
{
|
||||||
if (paramType.IsValueType)
|
if (paramType.IsValueType)
|
||||||
|
{
|
||||||
|
if (paramType.IsGenericType && paramType.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||||
|
continue;
|
||||||
|
|
||||||
throw new IpcValueNullError(this.Name, paramType, i);
|
throw new IpcValueNullError(this.Name, paramType, i);
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
using Dalamud.Game.Gui.ContextMenu;
|
using Dalamud.Game.Gui.ContextMenu;
|
||||||
using Dalamud.Game.Text;
|
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
|
||||||
|
|
||||||
namespace Dalamud.Plugin.Services;
|
namespace Dalamud.Plugin.Services;
|
||||||
|
|
||||||
|
|
@ -16,8 +14,9 @@ public interface IContextMenu
|
||||||
public delegate void OnMenuOpenedDelegate(MenuOpenedArgs args);
|
public delegate void OnMenuOpenedDelegate(MenuOpenedArgs args);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event that gets fired every time the game framework updates.
|
/// Event that gets fired whenever any context menu is opened.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>Use this event and then check if the triggering addon is the desired addon, then add custom context menu items to the provided args.</remarks>
|
||||||
event OnMenuOpenedDelegate OnMenuOpened;
|
event OnMenuOpenedDelegate OnMenuOpened;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -25,6 +24,7 @@ public interface IContextMenu
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="menuType">The type of context menu to add the item to.</param>
|
/// <param name="menuType">The type of context menu to add the item to.</param>
|
||||||
/// <param name="item">The item to add.</param>
|
/// <param name="item">The item to add.</param>
|
||||||
|
/// <remarks>Used to add a context menu entry to <em>all</em> context menus.</remarks>
|
||||||
void AddMenuItem(ContextMenuType menuType, MenuItem item);
|
void AddMenuItem(ContextMenuType menuType, MenuItem item);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -32,6 +32,7 @@ public interface IContextMenu
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="menuType">The type of context menu to remove the item from.</param>
|
/// <param name="menuType">The type of context menu to remove the item from.</param>
|
||||||
/// <param name="item">The item to add.</param>
|
/// <param name="item">The item to add.</param>
|
||||||
|
/// <remarks>Used to remove a context menu entry from <em>all</em> context menus.</remarks>
|
||||||
/// <returns><see langword="true"/> if the item was removed, <see langword="false"/> if it was not found.</returns>
|
/// <returns><see langword="true"/> if the item was removed, <see langword="false"/> if it was not found.</returns>
|
||||||
bool RemoveMenuItem(ContextMenuType menuType, MenuItem item);
|
bool RemoveMenuItem(ContextMenuType menuType, MenuItem item);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using Dalamud.Game.Gui.Dtr;
|
using Dalamud.Game.Gui.Dtr;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
namespace Dalamud.Plugin.Services;
|
namespace Dalamud.Plugin.Services;
|
||||||
|
|
||||||
|
|
@ -10,6 +12,11 @@ namespace Dalamud.Plugin.Services;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IDtrBar
|
public interface IDtrBar
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a read-only list of all DTR bar entries.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<IReadOnlyDtrBarEntry> Entries { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a DTR bar entry.
|
/// Get a DTR bar entry.
|
||||||
/// This allows you to add your own text, and users to sort it.
|
/// This allows you to add your own text, and users to sort it.
|
||||||
|
|
@ -18,6 +25,7 @@ public interface IDtrBar
|
||||||
/// <param name="text">The text the entry shows.</param>
|
/// <param name="text">The text the entry shows.</param>
|
||||||
/// <returns>The entry object used to update, hide and remove the entry.</returns>
|
/// <returns>The entry object used to update, hide and remove the entry.</returns>
|
||||||
/// <exception cref="ArgumentException">Thrown when an entry with the specified title exists.</exception>
|
/// <exception cref="ArgumentException">Thrown when an entry with the specified title exists.</exception>
|
||||||
|
[Api10ToDo("Return IDtrBarEntry instead of DtrBarEntry")]
|
||||||
public DtrBarEntry Get(string title, SeString? text = null);
|
public DtrBarEntry Get(string title, SeString? text = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ internal static class ServiceManager
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private static readonly TaskCompletionSource BlockingServicesLoadedTaskCompletionSource = new();
|
private static readonly TaskCompletionSource BlockingServicesLoadedTaskCompletionSource = new();
|
||||||
|
private static readonly CancellationTokenSource UnloadCancellationTokenSource = new();
|
||||||
|
|
||||||
private static ManualResetEvent unloadResetEvent = new(false);
|
private static ManualResetEvent unloadResetEvent = new(false);
|
||||||
|
|
||||||
|
|
@ -107,6 +108,12 @@ internal static class ServiceManager
|
||||||
/// Gets task that gets completed when all blocking early loading services are done loading.
|
/// Gets task that gets completed when all blocking early loading services are done loading.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Task BlockingResolved { get; } = BlockingServicesLoadedTaskCompletionSource.Task;
|
public static Task BlockingResolved { get; } = BlockingServicesLoadedTaskCompletionSource.Task;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a cancellation token that will be cancelled once Dalamud needs to unload, be it due to a failure state
|
||||||
|
/// during initialization or during regular operation.
|
||||||
|
/// </summary>
|
||||||
|
public static CancellationToken UnloadCancellationToken => UnloadCancellationTokenSource.Token;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes Provided Services and FFXIVClientStructs.
|
/// Initializes Provided Services and FFXIVClientStructs.
|
||||||
|
|
@ -374,6 +381,8 @@ internal static class ServiceManager
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
UnloadCancellationTokenSource.Cancel();
|
||||||
|
|
||||||
Log.Error(e, "Failed resolving services");
|
Log.Error(e, "Failed resolving services");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -401,6 +410,8 @@ internal static class ServiceManager
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void UnloadAllServices()
|
public static void UnloadAllServices()
|
||||||
{
|
{
|
||||||
|
UnloadCancellationTokenSource.Cancel();
|
||||||
|
|
||||||
var framework = Service<Framework>.GetNullable(Service<Framework>.ExceptionPropagationMode.None);
|
var framework = Service<Framework>.GetNullable(Service<Framework>.ExceptionPropagationMode.None);
|
||||||
if (framework is { IsInFrameworkUpdateThread: false, IsFrameworkUnloading: false })
|
if (framework is { IsInFrameworkUpdateThread: false, IsFrameworkUnloading: false })
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ internal static class Service<T> where T : IServiceType
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!instanceTcs.Task.IsCompleted)
|
if (!instanceTcs.Task.IsCompleted)
|
||||||
instanceTcs.Task.Wait();
|
instanceTcs.Task.Wait(ServiceManager.UnloadCancellationToken);
|
||||||
return instanceTcs.Task.Result;
|
return instanceTcs.Task.Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -344,7 +344,10 @@ public static class Util
|
||||||
_ = NativeFunctions.MessageBoxW(Process.GetCurrentProcess().MainWindowHandle, message, caption, flags);
|
_ = NativeFunctions.MessageBoxW(Process.GetCurrentProcess().MainWindowHandle, message, caption, flags);
|
||||||
|
|
||||||
if (exit)
|
if (exit)
|
||||||
|
{
|
||||||
|
Log.CloseAndFlush();
|
||||||
Environment.Exit(-1);
|
Environment.Exit(-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 98c1de8b94bcdfce4dc79a61cc0e8b17773777f0
|
Subproject commit 3d572e5301fbcb3194d59d7c995db067eba5cc0b
|
||||||
Loading…
Add table
Add a link
Reference in a new issue