mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Merge branch 'refs/heads/master' into apiX
# Conflicts: # lib/FFXIVClientStructs
This commit is contained in:
commit
61f47449fd
20 changed files with 316 additions and 207 deletions
|
|
@ -111,9 +111,17 @@ namespace logging {
|
||||||
* @param arg1 First format parameter.
|
* @param arg1 First format parameter.
|
||||||
* @param args Second and further format parameters, if any.
|
* @param args Second and further format parameters, if any.
|
||||||
*/
|
*/
|
||||||
template<typename Arg, typename...Args>
|
template<typename Arg, typename... Args>
|
||||||
void print(Level level, const char* fmt, Arg&& arg1, Args&&...args) {
|
void print(Level level, const char* fmt, Arg&& arg1, Args&&... args) {
|
||||||
print(level, std::vformat(fmt, std::make_format_args(to_format_arg(std::forward<Arg>(arg1)), to_format_arg(std::forward<Args>(args))...)));
|
// make_format_args only accepts references now due to https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2905r2.html
|
||||||
|
// we can switch std::runtime_format in C++26 :) https://isocpp.org/files/papers/P2918R0.html
|
||||||
|
auto transformed_args = std::make_tuple(to_format_arg(arg1), to_format_arg(args)...);
|
||||||
|
auto format_args = std::apply(
|
||||||
|
[&](auto&... elems) { return std::make_format_args(elems...); },
|
||||||
|
transformed_args
|
||||||
|
);
|
||||||
|
|
||||||
|
print(level, std::vformat(fmt, format_args));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename...Args> void V(Args&&...args) { print(Level::Verbose, std::forward<Args>(args)...); }
|
template<typename...Args> void V(Args&&...args) { print(Level::Verbose, std::forward<Args>(args)...); }
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,12 @@ internal sealed class DalamudConfiguration : IInternalDisposableService
|
||||||
[Obsolete("It happens that nobody touched this setting", true)]
|
[Obsolete("It happens that nobody touched this setting", true)]
|
||||||
public float FontGammaLevel { get; set; } = 1.4f;
|
public float FontGammaLevel { get; set; } = 1.4f;
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the opacity of the IME state indicator.</summary>
|
||||||
|
/// <value>0 will hide the state indicator. 1 will make the state indicator fully visible. Values outside the
|
||||||
|
/// range will be clamped to [0, 1].</value>
|
||||||
|
/// <remarks>See <see cref="SeIconChar.ImeHiragana"/> to <see cref="SeIconChar.ImeChineseLatin"/>.</remarks>
|
||||||
|
public float ImeStateIndicatorOpacity { get; set; } = 1f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether or not plugin UI should be hidden.
|
/// Gets or sets a value indicating whether or not plugin UI should be hidden.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Label="Feature">
|
<PropertyGroup Label="Feature">
|
||||||
<DalamudVersion>9.1.0.7</DalamudVersion>
|
<DalamudVersion>9.1.0.9</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>
|
||||||
|
|
|
||||||
|
|
@ -221,7 +221,7 @@ internal class AddonEventManagerPluginScoped : IInternalDisposableService, IAddo
|
||||||
{
|
{
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
|
||||||
this.eventManagerService.AddPluginEventController(plugin.Manifest.WorkingPluginId);
|
this.eventManagerService.AddPluginEventController(plugin.EffectiveWorkingPluginId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
@ -233,16 +233,16 @@ internal class AddonEventManagerPluginScoped : IInternalDisposableService, IAddo
|
||||||
this.eventManagerService.ResetCursor();
|
this.eventManagerService.ResetCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.eventManagerService.RemovePluginEventController(this.plugin.Manifest.WorkingPluginId);
|
this.eventManagerService.RemovePluginEventController(this.plugin.EffectiveWorkingPluginId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IAddonEventHandle? AddEvent(IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventHandler eventHandler)
|
public IAddonEventHandle? AddEvent(IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventHandler eventHandler)
|
||||||
=> this.eventManagerService.AddEvent(this.plugin.Manifest.WorkingPluginId, atkUnitBase, atkResNode, eventType, eventHandler);
|
=> this.eventManagerService.AddEvent(this.plugin.EffectiveWorkingPluginId, atkUnitBase, atkResNode, eventType, eventHandler);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void RemoveEvent(IAddonEventHandle eventHandle)
|
public void RemoveEvent(IAddonEventHandle eventHandle)
|
||||||
=> this.eventManagerService.RemoveEvent(this.plugin.Manifest.WorkingPluginId, eventHandle);
|
=> this.eventManagerService.RemoveEvent(this.plugin.EffectiveWorkingPluginId, eventHandle);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetCursor(AddonCursorType cursor)
|
public void SetCursor(AddonCursorType cursor)
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,11 @@ public class AutoTranslatePayload : Payload, ITextProvider
|
||||||
{
|
{
|
||||||
private string text;
|
private string text;
|
||||||
|
|
||||||
[JsonProperty]
|
[JsonProperty("group")]
|
||||||
private uint group;
|
public uint Group { get; private set; }
|
||||||
|
|
||||||
[JsonProperty]
|
[JsonProperty("key")]
|
||||||
private uint key;
|
public uint Key { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="AutoTranslatePayload"/> class.
|
/// Initializes a new instance of the <see cref="AutoTranslatePayload"/> class.
|
||||||
|
|
@ -34,8 +34,8 @@ public class AutoTranslatePayload : Payload, ITextProvider
|
||||||
public AutoTranslatePayload(uint group, uint key)
|
public AutoTranslatePayload(uint group, uint key)
|
||||||
{
|
{
|
||||||
// TODO: friendlier ctor? not sure how to handle that given how weird the tables are
|
// TODO: friendlier ctor? not sure how to handle that given how weird the tables are
|
||||||
this.group = group;
|
this.Group = group;
|
||||||
this.key = key;
|
this.Key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -69,20 +69,20 @@ public class AutoTranslatePayload : Payload, ITextProvider
|
||||||
/// <returns>A string that represents the current object.</returns>
|
/// <returns>A string that represents the current object.</returns>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"{this.Type} - Group: {this.group}, Key: {this.key}, Text: {this.Text}";
|
return $"{this.Type} - Group: {this.Group}, Key: {this.Key}, Text: {this.Text}";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override byte[] EncodeImpl()
|
protected override byte[] EncodeImpl()
|
||||||
{
|
{
|
||||||
var keyBytes = MakeInteger(this.key);
|
var keyBytes = MakeInteger(this.Key);
|
||||||
|
|
||||||
var chunkLen = keyBytes.Length + 2;
|
var chunkLen = keyBytes.Length + 2;
|
||||||
var bytes = new List<byte>()
|
var bytes = new List<byte>()
|
||||||
{
|
{
|
||||||
START_BYTE,
|
START_BYTE,
|
||||||
(byte)SeStringChunkType.AutoTranslateKey, (byte)chunkLen,
|
(byte)SeStringChunkType.AutoTranslateKey, (byte)chunkLen,
|
||||||
(byte)this.group,
|
(byte)this.Group,
|
||||||
};
|
};
|
||||||
bytes.AddRange(keyBytes);
|
bytes.AddRange(keyBytes);
|
||||||
bytes.Add(END_BYTE);
|
bytes.Add(END_BYTE);
|
||||||
|
|
@ -95,9 +95,9 @@ public class AutoTranslatePayload : Payload, ITextProvider
|
||||||
{
|
{
|
||||||
// this seems to always be a bare byte, and not following normal integer encoding
|
// this seems to always be a bare byte, and not following normal integer encoding
|
||||||
// the values in the table are all <70 so this is presumably ok
|
// the values in the table are all <70 so this is presumably ok
|
||||||
this.group = reader.ReadByte();
|
this.Group = reader.ReadByte();
|
||||||
|
|
||||||
this.key = GetInteger(reader);
|
this.Key = GetInteger(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string Resolve()
|
private string Resolve()
|
||||||
|
|
@ -112,13 +112,13 @@ public class AutoTranslatePayload : Payload, ITextProvider
|
||||||
// try to get the row in the Completion table itself, because this is 'easiest'
|
// try to get the row in the Completion table itself, because this is 'easiest'
|
||||||
// The row may not exist at all (if the Key is for another table), or it could be the wrong row
|
// The row may not exist at all (if the Key is for another table), or it could be the wrong row
|
||||||
// (again, if it's meant for another table)
|
// (again, if it's meant for another table)
|
||||||
row = sheet.GetRow(this.key);
|
row = sheet.GetRow(this.Key);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
} // don't care, row will be null
|
} // don't care, row will be null
|
||||||
|
|
||||||
if (row?.Group == this.group)
|
if (row?.Group == this.Group)
|
||||||
{
|
{
|
||||||
// if the row exists in this table and the group matches, this is actually the correct data
|
// if the row exists in this table and the group matches, this is actually the correct data
|
||||||
value = row.Text;
|
value = row.Text;
|
||||||
|
|
@ -129,30 +129,30 @@ public class AutoTranslatePayload : Payload, ITextProvider
|
||||||
{
|
{
|
||||||
// we need to get the linked table and do the lookup there instead
|
// we need to get the linked table and do the lookup there instead
|
||||||
// in this case, there will only be one entry for this group id
|
// in this case, there will only be one entry for this group id
|
||||||
row = sheet.First(r => r.Group == this.group);
|
row = sheet.First(r => r.Group == this.Group);
|
||||||
// many of the names contain valid id ranges after the table name, but we don't need those
|
// many of the names contain valid id ranges after the table name, but we don't need those
|
||||||
var actualTableName = row.LookupTable.RawString.Split('[')[0];
|
var actualTableName = row.LookupTable.RawString.Split('[')[0];
|
||||||
|
|
||||||
var name = actualTableName switch
|
var name = actualTableName switch
|
||||||
{
|
{
|
||||||
"Action" => this.DataResolver.GetExcelSheet<Lumina.Excel.GeneratedSheets.Action>().GetRow(this.key).Name,
|
"Action" => this.DataResolver.GetExcelSheet<Lumina.Excel.GeneratedSheets.Action>().GetRow(this.Key).Name,
|
||||||
"ActionComboRoute" => this.DataResolver.GetExcelSheet<ActionComboRoute>().GetRow(this.key).Name,
|
"ActionComboRoute" => this.DataResolver.GetExcelSheet<ActionComboRoute>().GetRow(this.Key).Name,
|
||||||
"BuddyAction" => this.DataResolver.GetExcelSheet<BuddyAction>().GetRow(this.key).Name,
|
"BuddyAction" => this.DataResolver.GetExcelSheet<BuddyAction>().GetRow(this.Key).Name,
|
||||||
"ClassJob" => this.DataResolver.GetExcelSheet<ClassJob>().GetRow(this.key).Name,
|
"ClassJob" => this.DataResolver.GetExcelSheet<ClassJob>().GetRow(this.Key).Name,
|
||||||
"Companion" => this.DataResolver.GetExcelSheet<Companion>().GetRow(this.key).Singular,
|
"Companion" => this.DataResolver.GetExcelSheet<Companion>().GetRow(this.Key).Singular,
|
||||||
"CraftAction" => this.DataResolver.GetExcelSheet<CraftAction>().GetRow(this.key).Name,
|
"CraftAction" => this.DataResolver.GetExcelSheet<CraftAction>().GetRow(this.Key).Name,
|
||||||
"GeneralAction" => this.DataResolver.GetExcelSheet<GeneralAction>().GetRow(this.key).Name,
|
"GeneralAction" => this.DataResolver.GetExcelSheet<GeneralAction>().GetRow(this.Key).Name,
|
||||||
"GuardianDeity" => this.DataResolver.GetExcelSheet<GuardianDeity>().GetRow(this.key).Name,
|
"GuardianDeity" => this.DataResolver.GetExcelSheet<GuardianDeity>().GetRow(this.Key).Name,
|
||||||
"MainCommand" => this.DataResolver.GetExcelSheet<MainCommand>().GetRow(this.key).Name,
|
"MainCommand" => this.DataResolver.GetExcelSheet<MainCommand>().GetRow(this.Key).Name,
|
||||||
"Mount" => this.DataResolver.GetExcelSheet<Mount>().GetRow(this.key).Singular,
|
"Mount" => this.DataResolver.GetExcelSheet<Mount>().GetRow(this.Key).Singular,
|
||||||
"Pet" => this.DataResolver.GetExcelSheet<Pet>().GetRow(this.key).Name,
|
"Pet" => this.DataResolver.GetExcelSheet<Pet>().GetRow(this.Key).Name,
|
||||||
"PetAction" => this.DataResolver.GetExcelSheet<PetAction>().GetRow(this.key).Name,
|
"PetAction" => this.DataResolver.GetExcelSheet<PetAction>().GetRow(this.Key).Name,
|
||||||
"PetMirage" => this.DataResolver.GetExcelSheet<PetMirage>().GetRow(this.key).Name,
|
"PetMirage" => this.DataResolver.GetExcelSheet<PetMirage>().GetRow(this.Key).Name,
|
||||||
"PlaceName" => this.DataResolver.GetExcelSheet<PlaceName>().GetRow(this.key).Name,
|
"PlaceName" => this.DataResolver.GetExcelSheet<PlaceName>().GetRow(this.Key).Name,
|
||||||
"Race" => this.DataResolver.GetExcelSheet<Race>().GetRow(this.key).Masculine,
|
"Race" => this.DataResolver.GetExcelSheet<Race>().GetRow(this.Key).Masculine,
|
||||||
"TextCommand" => this.DataResolver.GetExcelSheet<TextCommand>().GetRow(this.key).Command,
|
"TextCommand" => this.ResolveTextCommand(),
|
||||||
"Tribe" => this.DataResolver.GetExcelSheet<Tribe>().GetRow(this.key).Masculine,
|
"Tribe" => this.DataResolver.GetExcelSheet<Tribe>().GetRow(this.Key).Masculine,
|
||||||
"Weather" => this.DataResolver.GetExcelSheet<Weather>().GetRow(this.key).Name,
|
"Weather" => this.DataResolver.GetExcelSheet<Weather>().GetRow(this.Key).Name,
|
||||||
_ => throw new Exception(actualTableName),
|
_ => throw new Exception(actualTableName),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -160,10 +160,18 @@ public class AutoTranslatePayload : Payload, ITextProvider
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.Error(e, $"AutoTranslatePayload - failed to resolve: {this.Type} - Group: {this.group}, Key: {this.key}");
|
Log.Error(e, $"AutoTranslatePayload - failed to resolve: {this.Type} - Group: {this.Group}, Key: {this.Key}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Lumina.Text.SeString ResolveTextCommand()
|
||||||
|
{
|
||||||
|
// TextCommands prioritize the `Alias` field, if it not empty
|
||||||
|
// Example for this is /rangerpose2l which becomes /blackrangerposeb in chat
|
||||||
|
var result = this.DataResolver.GetExcelSheet<TextCommand>().GetRow(this.Key);
|
||||||
|
return result.Alias.Payloads.Count > 0 ? result.Alias : result.Command;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ internal class GameInteropProviderPluginScoped : IGameInteropProvider, IInternal
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Hook<T> HookFromFunctionPointerVariable<T>(IntPtr address, T detour) where T : Delegate
|
public Hook<T> HookFromFunctionPointerVariable<T>(nint address, T detour) where T : Delegate
|
||||||
{
|
{
|
||||||
var hook = Hook<T>.FromFunctionPointerVariable(address, detour);
|
var hook = Hook<T>.FromFunctionPointerVariable(address, detour);
|
||||||
this.trackedHooks.Add(hook);
|
this.trackedHooks.Add(hook);
|
||||||
|
|
@ -71,13 +71,21 @@ internal class GameInteropProviderPluginScoped : IGameInteropProvider, IInternal
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Hook<T> HookFromAddress<T>(IntPtr procAddress, T detour, IGameInteropProvider.HookBackend backend = IGameInteropProvider.HookBackend.Automatic) where T : Delegate
|
public Hook<T> HookFromAddress<T>(nint procAddress, T detour, IGameInteropProvider.HookBackend backend = IGameInteropProvider.HookBackend.Automatic) where T : Delegate
|
||||||
{
|
{
|
||||||
var hook = Hook<T>.FromAddress(procAddress, detour, backend == IGameInteropProvider.HookBackend.MinHook);
|
var hook = Hook<T>.FromAddress(procAddress, detour, backend == IGameInteropProvider.HookBackend.MinHook);
|
||||||
this.trackedHooks.Add(hook);
|
this.trackedHooks.Add(hook);
|
||||||
return hook;
|
return hook;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public Hook<T> HookFromAddress<T>(UIntPtr procAddress, T detour, IGameInteropProvider.HookBackend backend = IGameInteropProvider.HookBackend.Automatic) where T : Delegate
|
||||||
|
=> this.HookFromAddress((nint)procAddress, detour, backend);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public unsafe Hook<T> HookFromAddress<T>(void* procAddress, T detour, IGameInteropProvider.HookBackend backend = IGameInteropProvider.HookBackend.Automatic) where T : Delegate
|
||||||
|
=> this.HookFromAddress((nint)procAddress, detour, backend);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Hook<T> HookFromSignature<T>(string signature, T detour, IGameInteropProvider.HookBackend backend = IGameInteropProvider.HookBackend.Automatic) where T : Delegate
|
public Hook<T> HookFromSignature<T>(string signature, T detour, IGameInteropProvider.HookBackend backend = IGameInteropProvider.HookBackend.Automatic) where T : Delegate
|
||||||
=> this.HookFromAddress(this.scanner.ScanText(signature), detour, backend);
|
=> this.HookFromAddress(this.scanner.ScanText(signature), detour, backend);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Unicode;
|
using System.Text.Unicode;
|
||||||
|
|
||||||
|
using Dalamud.Configuration.Internal;
|
||||||
using Dalamud.Game.Text;
|
using Dalamud.Game.Text;
|
||||||
using Dalamud.Hooking.WndProcHook;
|
using Dalamud.Hooking.WndProcHook;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
|
|
@ -74,6 +75,9 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService
|
||||||
|
|
||||||
private static readonly delegate* unmanaged<ImGuiInputTextState*, StbTextEditState*, void> StbTextUndo;
|
private static readonly delegate* unmanaged<ImGuiInputTextState*, StbTextEditState*, void> StbTextUndo;
|
||||||
|
|
||||||
|
[ServiceManager.ServiceDependency]
|
||||||
|
private readonly DalamudConfiguration dalamudConfiguration = Service<DalamudConfiguration>.Get();
|
||||||
|
|
||||||
[ServiceManager.ServiceDependency]
|
[ServiceManager.ServiceDependency]
|
||||||
private readonly WndProcHookManager wndProcHookManager = Service<WndProcHookManager>.Get();
|
private readonly WndProcHookManager wndProcHookManager = Service<WndProcHookManager>.Get();
|
||||||
|
|
||||||
|
|
@ -774,30 +778,42 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService
|
||||||
ImGui.GetStyle().WindowRounding);
|
ImGui.GetStyle().WindowRounding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var stateOpacity = Math.Clamp(this.dalamudConfiguration.ImeStateIndicatorOpacity, 0, 1);
|
||||||
|
var stateBg = ImGui.GetColorU32(
|
||||||
|
new Vector4(1, 1, 1, MathF.Pow(stateOpacity, 2)) * *ImGui.GetStyleColorVec4(ImGuiCol.WindowBg));
|
||||||
|
var stateFg =
|
||||||
|
ImGui.GetColorU32(new Vector4(1, 1, 1, stateOpacity) * *ImGui.GetStyleColorVec4(ImGuiCol.Text));
|
||||||
if (!expandUpward && drawIme)
|
if (!expandUpward && drawIme)
|
||||||
{
|
{
|
||||||
for (var dx = -2; dx <= 2; dx++)
|
if (stateBg >= 0x1000000)
|
||||||
{
|
{
|
||||||
for (var dy = -2; dy <= 2; dy++)
|
for (var dx = -2; dx <= 2; dx++)
|
||||||
{
|
{
|
||||||
if (dx != 0 || dy != 0)
|
for (var dy = -2; dy <= 2; dy++)
|
||||||
{
|
{
|
||||||
imeIconFont.RenderChar(
|
if (dx != 0 || dy != 0)
|
||||||
drawList,
|
{
|
||||||
imeIconFont.FontSize,
|
imeIconFont.RenderChar(
|
||||||
cursor + new Vector2(dx, dy),
|
drawList,
|
||||||
ImGui.GetColorU32(ImGuiCol.WindowBg),
|
imeIconFont.FontSize,
|
||||||
ime.inputModeIcon);
|
cursor + new Vector2(dx, dy),
|
||||||
|
stateBg,
|
||||||
|
ime.inputModeIcon);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
imeIconFont.RenderChar(
|
if (stateFg >= 0x1000000)
|
||||||
drawList,
|
{
|
||||||
imeIconFont.FontSize,
|
imeIconFont.RenderChar(
|
||||||
cursor,
|
drawList,
|
||||||
ImGui.GetColorU32(ImGuiCol.Text),
|
imeIconFont.FontSize,
|
||||||
ime.inputModeIcon);
|
cursor,
|
||||||
|
stateFg,
|
||||||
|
ime.inputModeIcon);
|
||||||
|
}
|
||||||
|
|
||||||
cursor.Y += candTextSize.Y + spaceY;
|
cursor.Y += candTextSize.Y + spaceY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -851,28 +867,34 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService
|
||||||
|
|
||||||
if (expandUpward && drawIme)
|
if (expandUpward && drawIme)
|
||||||
{
|
{
|
||||||
for (var dx = -2; dx <= 2; dx++)
|
if (stateBg >= 0x1000000)
|
||||||
{
|
{
|
||||||
for (var dy = -2; dy <= 2; dy++)
|
for (var dx = -2; dx <= 2; dx++)
|
||||||
{
|
{
|
||||||
if (dx != 0 || dy != 0)
|
for (var dy = -2; dy <= 2; dy++)
|
||||||
{
|
{
|
||||||
imeIconFont.RenderChar(
|
if (dx != 0 || dy != 0)
|
||||||
drawList,
|
{
|
||||||
imeIconFont.FontSize,
|
imeIconFont.RenderChar(
|
||||||
cursor + new Vector2(dx, dy),
|
drawList,
|
||||||
ImGui.GetColorU32(ImGuiCol.WindowBg),
|
imeIconFont.FontSize,
|
||||||
ime.inputModeIcon);
|
cursor + new Vector2(dx, dy),
|
||||||
|
ImGui.GetColorU32(ImGuiCol.WindowBg),
|
||||||
|
ime.inputModeIcon);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
imeIconFont.RenderChar(
|
if (stateFg >= 0x1000000)
|
||||||
drawList,
|
{
|
||||||
imeIconFont.FontSize,
|
imeIconFont.RenderChar(
|
||||||
cursor,
|
drawList,
|
||||||
ImGui.GetColorU32(ImGuiCol.Text),
|
imeIconFont.FontSize,
|
||||||
ime.inputModeIcon);
|
cursor,
|
||||||
|
ImGui.GetColorU32(ImGuiCol.Text),
|
||||||
|
ime.inputModeIcon);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -2438,7 +2438,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
if (this.hasDevPlugins)
|
if (this.hasDevPlugins)
|
||||||
{
|
{
|
||||||
ImGuiHelpers.ScaledDummy(3);
|
ImGuiHelpers.ScaledDummy(3);
|
||||||
ImGui.TextColored(ImGuiColors.DalamudGrey, $"WorkingPluginId: {manifest.WorkingPluginId}");
|
ImGui.TextColored(ImGuiColors.DalamudGrey, $"WorkingPluginId: {plugin.EffectiveWorkingPluginId}");
|
||||||
ImGuiHelpers.ScaledDummy(3);
|
ImGuiHelpers.ScaledDummy(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2621,10 +2621,10 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
|
|
||||||
var applicableForProfiles = plugin.Manifest.SupportsProfiles /*&& !plugin.IsDev*/;
|
var applicableForProfiles = plugin.Manifest.SupportsProfiles /*&& !plugin.IsDev*/;
|
||||||
var profilesThatWantThisPlugin = profileManager.Profiles
|
var profilesThatWantThisPlugin = profileManager.Profiles
|
||||||
.Where(x => x.WantsPlugin(plugin.Manifest.WorkingPluginId) != null)
|
.Where(x => x.WantsPlugin(plugin.EffectiveWorkingPluginId) != null)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
var isInSingleProfile = profilesThatWantThisPlugin.Length == 1;
|
var isInSingleProfile = profilesThatWantThisPlugin.Length == 1;
|
||||||
var isDefaultPlugin = profileManager.IsInDefaultProfile(plugin.Manifest.WorkingPluginId);
|
var isDefaultPlugin = profileManager.IsInDefaultProfile(plugin.EffectiveWorkingPluginId);
|
||||||
|
|
||||||
// Disable everything if the updater is running or another plugin is operating
|
// Disable everything if the updater is running or another plugin is operating
|
||||||
var disabled = this.updateStatus == OperationStatus.InProgress || this.installStatus == OperationStatus.InProgress;
|
var disabled = this.updateStatus == OperationStatus.InProgress || this.installStatus == OperationStatus.InProgress;
|
||||||
|
|
@ -2659,17 +2659,17 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
|
|
||||||
foreach (var profile in profileManager.Profiles.Where(x => !x.IsDefaultProfile))
|
foreach (var profile in profileManager.Profiles.Where(x => !x.IsDefaultProfile))
|
||||||
{
|
{
|
||||||
var inProfile = profile.WantsPlugin(plugin.Manifest.WorkingPluginId) != null;
|
var inProfile = profile.WantsPlugin(plugin.EffectiveWorkingPluginId) != null;
|
||||||
if (ImGui.Checkbox($"###profilePick{profile.Guid}{plugin.Manifest.InternalName}", ref inProfile))
|
if (ImGui.Checkbox($"###profilePick{profile.Guid}{plugin.Manifest.InternalName}", ref inProfile))
|
||||||
{
|
{
|
||||||
if (inProfile)
|
if (inProfile)
|
||||||
{
|
{
|
||||||
Task.Run(() => profile.AddOrUpdateAsync(plugin.Manifest.WorkingPluginId, plugin.Manifest.InternalName, true))
|
Task.Run(() => profile.AddOrUpdateAsync(plugin.EffectiveWorkingPluginId, plugin.Manifest.InternalName, true))
|
||||||
.ContinueWith(this.DisplayErrorContinuation, Locs.Profiles_CouldNotAdd);
|
.ContinueWith(this.DisplayErrorContinuation, Locs.Profiles_CouldNotAdd);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task.Run(() => profile.RemoveAsync(plugin.Manifest.WorkingPluginId))
|
Task.Run(() => profile.RemoveAsync(plugin.EffectiveWorkingPluginId))
|
||||||
.ContinueWith(this.DisplayErrorContinuation, Locs.Profiles_CouldNotRemove);
|
.ContinueWith(this.DisplayErrorContinuation, Locs.Profiles_CouldNotRemove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2689,11 +2689,11 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Times))
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Times))
|
||||||
{
|
{
|
||||||
// TODO: Work this out
|
// TODO: Work this out
|
||||||
Task.Run(() => profileManager.DefaultProfile.AddOrUpdateAsync(plugin.Manifest.WorkingPluginId, plugin.Manifest.InternalName, plugin.IsLoaded, false))
|
Task.Run(() => profileManager.DefaultProfile.AddOrUpdateAsync(plugin.EffectiveWorkingPluginId, plugin.Manifest.InternalName, plugin.IsLoaded, false))
|
||||||
.GetAwaiter().GetResult();
|
.GetAwaiter().GetResult();
|
||||||
foreach (var profile in profileManager.Profiles.Where(x => !x.IsDefaultProfile && x.Plugins.Any(y => y.InternalName == plugin.Manifest.InternalName)))
|
foreach (var profile in profileManager.Profiles.Where(x => !x.IsDefaultProfile && x.Plugins.Any(y => y.InternalName == plugin.Manifest.InternalName)))
|
||||||
{
|
{
|
||||||
Task.Run(() => profile.RemoveAsync(plugin.Manifest.WorkingPluginId, false))
|
Task.Run(() => profile.RemoveAsync(plugin.EffectiveWorkingPluginId, false))
|
||||||
.GetAwaiter().GetResult();
|
.GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2718,7 +2718,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGui.SetTooltip(Locs.PluginButtonToolTip_UnloadFailed);
|
ImGui.SetTooltip(Locs.PluginButtonToolTip_UnloadFailed);
|
||||||
}
|
}
|
||||||
else if (this.enableDisableStatus == OperationStatus.InProgress && this.enableDisableWorkingPluginId == plugin.Manifest.WorkingPluginId)
|
else if (this.enableDisableStatus == OperationStatus.InProgress && this.enableDisableWorkingPluginId == plugin.EffectiveWorkingPluginId)
|
||||||
{
|
{
|
||||||
ImGuiComponents.DisabledToggleButton(toggleId, this.loadingIndicatorKind == LoadingIndicatorKind.EnablingSingle);
|
ImGuiComponents.DisabledToggleButton(toggleId, this.loadingIndicatorKind == LoadingIndicatorKind.EnablingSingle);
|
||||||
}
|
}
|
||||||
|
|
@ -2743,9 +2743,9 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
{
|
{
|
||||||
// Reload the devPlugin manifest if it's a dev plugin
|
// Reload the devPlugin manifest if it's a dev plugin
|
||||||
// The plugin might rely on changed values in the manifest
|
// The plugin might rely on changed values in the manifest
|
||||||
if (plugin.IsDev)
|
if (plugin is LocalDevPlugin devPlugin)
|
||||||
{
|
{
|
||||||
plugin.ReloadManifest();
|
devPlugin.ReloadManifest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -2761,13 +2761,13 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
{
|
{
|
||||||
this.enableDisableStatus = OperationStatus.InProgress;
|
this.enableDisableStatus = OperationStatus.InProgress;
|
||||||
this.loadingIndicatorKind = LoadingIndicatorKind.DisablingSingle;
|
this.loadingIndicatorKind = LoadingIndicatorKind.DisablingSingle;
|
||||||
this.enableDisableWorkingPluginId = plugin.Manifest.WorkingPluginId;
|
this.enableDisableWorkingPluginId = plugin.EffectiveWorkingPluginId;
|
||||||
|
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await plugin.UnloadAsync();
|
await plugin.UnloadAsync();
|
||||||
await applicableProfile.AddOrUpdateAsync(
|
await applicableProfile.AddOrUpdateAsync(
|
||||||
plugin.Manifest.WorkingPluginId, plugin.Manifest.InternalName, false, false);
|
plugin.EffectiveWorkingPluginId, plugin.Manifest.InternalName, false, false);
|
||||||
|
|
||||||
notifications.AddNotification(Locs.Notifications_PluginDisabled(plugin.Manifest.Name), Locs.Notifications_PluginDisabledTitle, NotificationType.Success);
|
notifications.AddNotification(Locs.Notifications_PluginDisabled(plugin.Manifest.Name), Locs.Notifications_PluginDisabledTitle, NotificationType.Success);
|
||||||
}).ContinueWith(t =>
|
}).ContinueWith(t =>
|
||||||
|
|
@ -2782,9 +2782,9 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
{
|
{
|
||||||
this.enableDisableStatus = OperationStatus.InProgress;
|
this.enableDisableStatus = OperationStatus.InProgress;
|
||||||
this.loadingIndicatorKind = LoadingIndicatorKind.EnablingSingle;
|
this.loadingIndicatorKind = LoadingIndicatorKind.EnablingSingle;
|
||||||
this.enableDisableWorkingPluginId = plugin.Manifest.WorkingPluginId;
|
this.enableDisableWorkingPluginId = plugin.EffectiveWorkingPluginId;
|
||||||
|
|
||||||
await applicableProfile.AddOrUpdateAsync(plugin.Manifest.WorkingPluginId, plugin.Manifest.InternalName, true, false);
|
await applicableProfile.AddOrUpdateAsync(plugin.EffectiveWorkingPluginId, plugin.Manifest.InternalName, true, false);
|
||||||
await plugin.LoadAsync(PluginLoadReason.Installer);
|
await plugin.LoadAsync(PluginLoadReason.Installer);
|
||||||
|
|
||||||
notifications.AddNotification(Locs.Notifications_PluginEnabled(plugin.Manifest.Name), Locs.Notifications_PluginEnabledTitle, NotificationType.Success);
|
notifications.AddNotification(Locs.Notifications_PluginEnabled(plugin.Manifest.Name), Locs.Notifications_PluginEnabledTitle, NotificationType.Success);
|
||||||
|
|
@ -2805,7 +2805,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
if (shouldUpdate)
|
if (shouldUpdate)
|
||||||
{
|
{
|
||||||
// We need to update the profile right here, because PM will not enable the plugin otherwise
|
// We need to update the profile right here, because PM will not enable the plugin otherwise
|
||||||
await applicableProfile.AddOrUpdateAsync(plugin.Manifest.WorkingPluginId, plugin.Manifest.InternalName, true, false);
|
await applicableProfile.AddOrUpdateAsync(plugin.EffectiveWorkingPluginId, plugin.Manifest.InternalName, true, false);
|
||||||
await this.UpdateSinglePlugin(availableUpdate);
|
await this.UpdateSinglePlugin(availableUpdate);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -3076,7 +3076,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
if (localPlugin is LocalDevPlugin plugin)
|
if (localPlugin is LocalDevPlugin plugin)
|
||||||
{
|
{
|
||||||
var isInDefaultProfile =
|
var isInDefaultProfile =
|
||||||
Service<ProfileManager>.Get().IsInDefaultProfile(localPlugin.Manifest.WorkingPluginId);
|
Service<ProfileManager>.Get().IsInDefaultProfile(localPlugin.EffectiveWorkingPluginId);
|
||||||
|
|
||||||
// https://colorswall.com/palette/2868/
|
// https://colorswall.com/palette/2868/
|
||||||
var greenColor = new Vector4(0x5C, 0xB8, 0x5C, 0xFF) / 0xFF;
|
var greenColor = new Vector4(0x5C, 0xB8, 0x5C, 0xFF) / 0xFF;
|
||||||
|
|
@ -3426,7 +3426,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
this.pluginListAvailable.Sort((p1, p2) => p1.Name.CompareTo(p2.Name));
|
this.pluginListAvailable.Sort((p1, p2) => p1.Name.CompareTo(p2.Name));
|
||||||
|
|
||||||
var profman = Service<ProfileManager>.Get();
|
var profman = Service<ProfileManager>.Get();
|
||||||
this.pluginListInstalled.Sort((p1, p2) => profman.IsInDefaultProfile(p1.Manifest.WorkingPluginId).CompareTo(profman.IsInDefaultProfile(p2.Manifest.WorkingPluginId)));
|
this.pluginListInstalled.Sort((p1, p2) => profman.IsInDefaultProfile(p1.EffectiveWorkingPluginId).CompareTo(profman.IsInDefaultProfile(p2.EffectiveWorkingPluginId)));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new InvalidEnumArgumentException("Unknown plugin sort type.");
|
throw new InvalidEnumArgumentException("Unknown plugin sort type.");
|
||||||
|
|
|
||||||
|
|
@ -324,7 +324,7 @@ internal class ProfileManagerWidget
|
||||||
|
|
||||||
if (ImGui.Selectable($"{plugin.Manifest.Name}{(plugin is LocalDevPlugin ? "(dev plugin)" : string.Empty)}###selector{plugin.Manifest.InternalName}"))
|
if (ImGui.Selectable($"{plugin.Manifest.Name}{(plugin is LocalDevPlugin ? "(dev plugin)" : string.Empty)}###selector{plugin.Manifest.InternalName}"))
|
||||||
{
|
{
|
||||||
Task.Run(() => profile.AddOrUpdateAsync(plugin.Manifest.WorkingPluginId, plugin.Manifest.InternalName, true, false))
|
Task.Run(() => profile.AddOrUpdateAsync(plugin.EffectiveWorkingPluginId, plugin.Manifest.InternalName, true, false))
|
||||||
.ContinueWith(this.installer.DisplayErrorContinuation, Locs.ErrorCouldNotChangeState);
|
.ContinueWith(this.installer.DisplayErrorContinuation, Locs.ErrorCouldNotChangeState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -430,7 +430,7 @@ internal class ProfileManagerWidget
|
||||||
foreach (var profileEntry in profile.Plugins.ToArray())
|
foreach (var profileEntry in profile.Plugins.ToArray())
|
||||||
{
|
{
|
||||||
didAny = true;
|
didAny = true;
|
||||||
var pmPlugin = pm.InstalledPlugins.FirstOrDefault(x => x.Manifest.WorkingPluginId == profileEntry.WorkingPluginId);
|
var pmPlugin = pm.InstalledPlugins.FirstOrDefault(x => x.EffectiveWorkingPluginId == profileEntry.WorkingPluginId);
|
||||||
var btnOffset = 2;
|
var btnOffset = 2;
|
||||||
|
|
||||||
if (pmPlugin != null)
|
if (pmPlugin != null)
|
||||||
|
|
@ -485,7 +485,7 @@ internal class ProfileManagerWidget
|
||||||
FontAwesomeIcon.Check,
|
FontAwesomeIcon.Check,
|
||||||
"Yes, use this one"))
|
"Yes, use this one"))
|
||||||
{
|
{
|
||||||
profileEntry.WorkingPluginId = firstAvailableInstalled.Manifest.WorkingPluginId;
|
profileEntry.WorkingPluginId = firstAvailableInstalled.EffectiveWorkingPluginId;
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await profman.ApplyAllWantStatesAsync();
|
await profman.ApplyAllWantStatesAsync();
|
||||||
|
|
|
||||||
|
|
@ -152,13 +152,12 @@ internal class SettingsWindow : Window
|
||||||
settingsTab.OnOpen();
|
settingsTab.OnOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.BeginChild($"###settings_scrolling_{settingsTab.Title}", new Vector2(-1, -1), false))
|
using var tabChild = ImRaii.Child(
|
||||||
{
|
$"###settings_scrolling_{settingsTab.Title}",
|
||||||
|
new Vector2(-1, -1),
|
||||||
|
false);
|
||||||
|
if (tabChild)
|
||||||
settingsTab.Draw();
|
settingsTab.Draw();
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndChild();
|
|
||||||
ImGui.EndTabItem();
|
|
||||||
}
|
}
|
||||||
else if (settingsTab.IsOpen)
|
else if (settingsTab.IsOpen)
|
||||||
{
|
{
|
||||||
|
|
@ -208,33 +207,34 @@ internal class SettingsWindow : Window
|
||||||
|
|
||||||
ImGui.SetCursorPos(windowSize - ImGuiHelpers.ScaledVector2(70));
|
ImGui.SetCursorPos(windowSize - ImGuiHelpers.ScaledVector2(70));
|
||||||
|
|
||||||
if (ImGui.BeginChild("###settingsFinishButton"))
|
using (var buttonChild = ImRaii.Child("###settingsFinishButton"))
|
||||||
{
|
{
|
||||||
using var disabled = ImRaii.Disabled(this.tabs.Any(x => x.Entries.Any(y => !y.IsValid)));
|
if (buttonChild)
|
||||||
|
|
||||||
using (ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 100f))
|
|
||||||
{
|
{
|
||||||
using var font = ImRaii.PushFont(InterfaceManager.IconFont);
|
using var disabled = ImRaii.Disabled(this.tabs.Any(x => x.Entries.Any(y => !y.IsValid)));
|
||||||
|
|
||||||
if (ImGui.Button(FontAwesomeIcon.Save.ToIconString(), new Vector2(40)))
|
using (ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 100f))
|
||||||
{
|
{
|
||||||
this.Save();
|
using var font = ImRaii.PushFont(InterfaceManager.IconFont);
|
||||||
|
|
||||||
if (!ImGui.IsKeyDown(ImGuiKey.ModShift))
|
if (ImGui.Button(FontAwesomeIcon.Save.ToIconString(), new Vector2(40)))
|
||||||
this.IsOpen = false;
|
{
|
||||||
|
this.Save();
|
||||||
|
|
||||||
|
if (!ImGui.IsKeyDown(ImGuiKey.ModShift))
|
||||||
|
this.IsOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.IsItemHovered())
|
||||||
|
{
|
||||||
|
ImGui.SetTooltip(!ImGui.IsKeyDown(ImGuiKey.ModShift)
|
||||||
|
? Loc.Localize("DalamudSettingsSaveAndExit", "Save changes and close")
|
||||||
|
: Loc.Localize("DalamudSettingsSave", "Save changes"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
{
|
|
||||||
ImGui.SetTooltip(!ImGui.IsKeyDown(ImGuiKey.ModShift)
|
|
||||||
? Loc.Localize("DalamudSettingsSaveAndExit", "Save changes and close")
|
|
||||||
: Loc.Localize("DalamudSettingsSave", "Save changes"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndChild();
|
|
||||||
|
|
||||||
ImGui.SetCursorPos(new Vector2(windowSize.X - 250, ImGui.GetTextLineHeightWithSpacing() + (ImGui.GetStyle().FramePadding.Y * 2)));
|
ImGui.SetCursorPos(new Vector2(windowSize.X - 250, ImGui.GetTextLineHeightWithSpacing() + (ImGui.GetStyle().FramePadding.Y * 2)));
|
||||||
ImGui.SetNextItemWidth(240);
|
ImGui.SetNextItemWidth(240);
|
||||||
ImGui.InputTextWithHint("###searchInput", "Search for settings...", ref this.searchInput, 100);
|
ImGui.InputTextWithHint("###searchInput", "Search for settings...", ref this.searchInput, 100);
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,9 @@ Contribute at: https://github.com/goatcorp/Dalamud
|
||||||
{
|
{
|
||||||
var windowSize = ImGui.GetWindowSize();
|
var windowSize = ImGui.GetWindowSize();
|
||||||
|
|
||||||
ImGui.BeginChild("scrolling", Vector2.Zero, false, ImGuiWindowFlags.NoScrollbar);
|
using var child = ImRaii.Child("scrolling", new Vector2(-1, -10 * ImGuiHelpers.GlobalScale), false, ImGuiWindowFlags.NoScrollbar);
|
||||||
|
if (!child)
|
||||||
|
return;
|
||||||
|
|
||||||
if (this.resetNow)
|
if (this.resetNow)
|
||||||
{
|
{
|
||||||
|
|
@ -295,8 +297,6 @@ Contribute at: https://github.com/goatcorp/Dalamud
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndChild();
|
|
||||||
|
|
||||||
base.Draw();
|
base.Draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Text;
|
||||||
using CheapLoc;
|
using CheapLoc;
|
||||||
using Dalamud.Configuration.Internal;
|
using Dalamud.Configuration.Internal;
|
||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
|
using Dalamud.Game.Text;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.FontIdentifier;
|
using Dalamud.Interface.FontIdentifier;
|
||||||
using Dalamud.Interface.GameFonts;
|
using Dalamud.Interface.GameFonts;
|
||||||
|
|
@ -136,6 +137,27 @@ public class SettingsTabLook : SettingsTab
|
||||||
Loc.Localize("DalamudSettingReducedMotionHint", "This will suppress certain animations from Dalamud, such as the notification popup."),
|
Loc.Localize("DalamudSettingReducedMotionHint", "This will suppress certain animations from Dalamud, such as the notification popup."),
|
||||||
c => c.ReduceMotions ?? false,
|
c => c.ReduceMotions ?? false,
|
||||||
(v, c) => c.ReduceMotions = v),
|
(v, c) => c.ReduceMotions = v),
|
||||||
|
|
||||||
|
new SettingsEntry<float>(
|
||||||
|
Loc.Localize("DalamudSettingImeStateIndicatorOpacity", "IME State Indicator Opacity (CJK only)"),
|
||||||
|
Loc.Localize("DalamudSettingImeStateIndicatorOpacityHint", "When any of CJK IMEs is in use, the state of IME will be shown with the opacity specified here."),
|
||||||
|
c => c.ImeStateIndicatorOpacity,
|
||||||
|
(v, c) => c.ImeStateIndicatorOpacity = v)
|
||||||
|
{
|
||||||
|
CustomDraw = static e =>
|
||||||
|
{
|
||||||
|
ImGuiHelpers.SafeTextWrapped(e.Name!);
|
||||||
|
|
||||||
|
var v = e.Value * 100f;
|
||||||
|
if (ImGui.SliderFloat($"###{e}", ref v, 0f, 100f, "%.1f%%"))
|
||||||
|
e.Value = v / 100f;
|
||||||
|
ImGui.SameLine();
|
||||||
|
|
||||||
|
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, v / 100);
|
||||||
|
ImGui.TextUnformatted("\uE020\uE021\uE022\uE023\uE024\uE025\uE026\uE027");
|
||||||
|
ImGui.PopStyleVar(1);
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
public override string Title => Loc.Localize("DalamudSettingsVisual", "Look & Feel");
|
public override string Title => Loc.Localize("DalamudSettingsVisual", "Look & Feel");
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
using Dalamud.Configuration.Internal;
|
using Dalamud.Configuration.Internal;
|
||||||
|
|
||||||
|
|
@ -50,10 +51,22 @@ internal sealed class SettingsEntry<T> : SettingsEntry
|
||||||
|
|
||||||
public delegate void SaveSettingDelegate(T? value, DalamudConfiguration config);
|
public delegate void SaveSettingDelegate(T? value, DalamudConfiguration config);
|
||||||
|
|
||||||
public T? Value => this.valueBacking == default ? default : (T)this.valueBacking;
|
public T? Value
|
||||||
|
{
|
||||||
|
get => this.valueBacking == default ? default : (T)this.valueBacking;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (Equals(value, this.valueBacking))
|
||||||
|
return;
|
||||||
|
this.valueBacking = value;
|
||||||
|
this.change?.Invoke(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string Description { get; }
|
public string Description { get; }
|
||||||
|
|
||||||
|
public Action<SettingsEntry<T>>? CustomDraw { get; init; }
|
||||||
|
|
||||||
public Func<T?, string?>? CheckValidity { get; init; }
|
public Func<T?, string?>? CheckValidity { get; init; }
|
||||||
|
|
||||||
public Func<T?, string?>? CheckWarning { get; init; }
|
public Func<T?, string?>? CheckWarning { get; init; }
|
||||||
|
|
@ -68,7 +81,11 @@ internal sealed class SettingsEntry<T> : SettingsEntry
|
||||||
|
|
||||||
var type = typeof(T);
|
var type = typeof(T);
|
||||||
|
|
||||||
if (type == typeof(DirectoryInfo))
|
if (this.CustomDraw is not null)
|
||||||
|
{
|
||||||
|
this.CustomDraw.Invoke(this);
|
||||||
|
}
|
||||||
|
else if (type == typeof(DirectoryInfo))
|
||||||
{
|
{
|
||||||
ImGuiHelpers.SafeTextWrapped(this.Name);
|
ImGuiHelpers.SafeTextWrapped(this.Name);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,40 +109,31 @@ public static partial class ImRaii
|
||||||
|
|
||||||
public static unsafe IEndObject TabItem(string label, ImGuiTabItemFlags flags)
|
public static unsafe IEndObject TabItem(string label, ImGuiTabItemFlags flags)
|
||||||
{
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(label);
|
||||||
|
|
||||||
// One-off for now, we should make this into a generic solution if we need it more often
|
// One-off for now, we should make this into a generic solution if we need it more often
|
||||||
const int ImGuiNET_Util_StackAllocationSizeLimit = 2048;
|
const int labelMaxAlloc = 2048;
|
||||||
|
|
||||||
byte* native_label;
|
var labelByteCount = Encoding.UTF8.GetByteCount(label);
|
||||||
int label_byteCount = 0;
|
|
||||||
if (label != null)
|
if (labelByteCount > labelMaxAlloc)
|
||||||
{
|
{
|
||||||
label_byteCount = Encoding.UTF8.GetByteCount(label);
|
throw new ArgumentOutOfRangeException(nameof(label), $"Label is too long. (Longer than {labelMaxAlloc} bytes)");
|
||||||
|
|
||||||
if (label_byteCount > ImGuiNET_Util_StackAllocationSizeLimit)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException("label", "Label is too long. (Longer than 2048 bytes)");
|
|
||||||
}
|
|
||||||
|
|
||||||
byte* native_label_stackBytes = stackalloc byte[label_byteCount + 1];
|
|
||||||
native_label = native_label_stackBytes;
|
|
||||||
|
|
||||||
int native_label_offset;
|
|
||||||
fixed (char* utf16Ptr = label)
|
|
||||||
{
|
|
||||||
native_label_offset = Encoding.UTF8.GetBytes(utf16Ptr, label.Length, native_label, label_byteCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
native_label[native_label_offset] = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
native_label = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
byte* p_open = null;
|
var nativeLabelStackBytes = stackalloc byte[labelByteCount + 1];
|
||||||
byte ret = ImGuiNative.igBeginTabItem(native_label, p_open, flags);
|
|
||||||
|
|
||||||
return new EndUnconditionally(ImGuiNative.igEndTabItem, ret != 0);
|
int nativeLabelOffset;
|
||||||
|
fixed (char* utf16Ptr = label)
|
||||||
|
{
|
||||||
|
nativeLabelOffset = Encoding.UTF8.GetBytes(utf16Ptr, label.Length, nativeLabelStackBytes, labelByteCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeLabelStackBytes[nativeLabelOffset] = 0;
|
||||||
|
|
||||||
|
var ret = ImGuiNative.igBeginTabItem(nativeLabelStackBytes, null, flags);
|
||||||
|
|
||||||
|
return new EndConditionally(ImGuiNative.igEndTabItem, ret != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEndObject TabItem(string label, ref bool open)
|
public static IEndObject TabItem(string label, ref bool open)
|
||||||
|
|
|
||||||
|
|
@ -398,7 +398,7 @@ public sealed class DalamudPluginInterface : IDisposable
|
||||||
if (currentConfig == null)
|
if (currentConfig == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.configs.Save(currentConfig, this.plugin.InternalName, this.plugin.Manifest.WorkingPluginId);
|
this.configs.Save(currentConfig, this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -425,7 +425,7 @@ public sealed class DalamudPluginInterface : IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
// this shouldn't be a thing, I think, but just in case
|
// this shouldn't be a thing, I think, but just in case
|
||||||
return this.configs.Load(this.plugin.InternalName, this.plugin.Manifest.WorkingPluginId);
|
return this.configs.Load(this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -1029,7 +1029,7 @@ internal class PluginManager : IInternalDisposableService
|
||||||
{
|
{
|
||||||
var plugin = metadata.InstalledPlugin;
|
var plugin = metadata.InstalledPlugin;
|
||||||
|
|
||||||
var workingPluginId = metadata.InstalledPlugin.Manifest.WorkingPluginId;
|
var workingPluginId = metadata.InstalledPlugin.EffectiveWorkingPluginId;
|
||||||
if (workingPluginId == Guid.Empty)
|
if (workingPluginId == Guid.Empty)
|
||||||
throw new Exception("Existing plugin had no WorkingPluginId");
|
throw new Exception("Existing plugin had no WorkingPluginId");
|
||||||
|
|
||||||
|
|
@ -1331,16 +1331,16 @@ internal class PluginManager : IInternalDisposableService
|
||||||
|
|
||||||
foreach (var installedPlugin in this.InstalledPlugins)
|
foreach (var installedPlugin in this.InstalledPlugins)
|
||||||
{
|
{
|
||||||
if (installedPlugin.Manifest.WorkingPluginId == Guid.Empty)
|
if (installedPlugin.EffectiveWorkingPluginId == Guid.Empty)
|
||||||
throw new Exception($"{(installedPlugin is LocalDevPlugin ? "DevPlugin" : "Plugin")} '{installedPlugin.Manifest.InternalName}' has an empty WorkingPluginId.");
|
throw new Exception($"{(installedPlugin is LocalDevPlugin ? "DevPlugin" : "Plugin")} '{installedPlugin.Manifest.InternalName}' has an empty WorkingPluginId.");
|
||||||
|
|
||||||
if (seenIds.Contains(installedPlugin.Manifest.WorkingPluginId))
|
if (seenIds.Contains(installedPlugin.EffectiveWorkingPluginId))
|
||||||
{
|
{
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
$"{(installedPlugin is LocalDevPlugin ? "DevPlugin" : "Plugin")} '{installedPlugin.Manifest.InternalName}' has a duplicate WorkingPluginId '{installedPlugin.Manifest.WorkingPluginId}'");
|
$"{(installedPlugin is LocalDevPlugin ? "DevPlugin" : "Plugin")} '{installedPlugin.Manifest.InternalName}' has a duplicate WorkingPluginId '{installedPlugin.EffectiveWorkingPluginId}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
seenIds.Add(installedPlugin.Manifest.WorkingPluginId);
|
seenIds.Add(installedPlugin.EffectiveWorkingPluginId);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.profileManager.ParanoiaValidateProfiles();
|
this.profileManager.ParanoiaValidateProfiles();
|
||||||
|
|
@ -1388,7 +1388,7 @@ internal class PluginManager : IInternalDisposableService
|
||||||
{
|
{
|
||||||
// Only remove entries from the default profile that are NOT currently tied to an active LocalPlugin
|
// Only remove entries from the default profile that are NOT currently tied to an active LocalPlugin
|
||||||
var guidsToRemove = this.profileManager.DefaultProfile.Plugins
|
var guidsToRemove = this.profileManager.DefaultProfile.Plugins
|
||||||
.Where(x => this.InstalledPlugins.All(y => y.Manifest.WorkingPluginId != x.WorkingPluginId))
|
.Where(x => this.InstalledPlugins.All(y => y.EffectiveWorkingPluginId != x.WorkingPluginId))
|
||||||
.Select(x => x.WorkingPluginId)
|
.Select(x => x.WorkingPluginId)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
|
|
@ -1560,9 +1560,9 @@ internal class PluginManager : IInternalDisposableService
|
||||||
// This will also happen if you are installing a plugin with the installer, and that's intended!
|
// This will also happen if you are installing a plugin with the installer, and that's intended!
|
||||||
// It means that, if you have a profile which has unsatisfied plugins, installing a matching plugin will
|
// It means that, if you have a profile which has unsatisfied plugins, installing a matching plugin will
|
||||||
// enter it into the profiles it can match.
|
// enter it into the profiles it can match.
|
||||||
if (plugin.Manifest.WorkingPluginId == Guid.Empty)
|
if (plugin.EffectiveWorkingPluginId == Guid.Empty)
|
||||||
throw new Exception("Plugin should have a WorkingPluginId at this point");
|
throw new Exception("Plugin should have a WorkingPluginId at this point");
|
||||||
this.profileManager.MigrateProfilesToGuidsForPlugin(plugin.Manifest.InternalName, plugin.Manifest.WorkingPluginId);
|
this.profileManager.MigrateProfilesToGuidsForPlugin(plugin.Manifest.InternalName, plugin.EffectiveWorkingPluginId);
|
||||||
|
|
||||||
var wantedByAnyProfile = false;
|
var wantedByAnyProfile = false;
|
||||||
|
|
||||||
|
|
@ -1573,7 +1573,7 @@ internal class PluginManager : IInternalDisposableService
|
||||||
loadPlugin &= !isBoot;
|
loadPlugin &= !isBoot;
|
||||||
|
|
||||||
var wantsInDefaultProfile =
|
var wantsInDefaultProfile =
|
||||||
this.profileManager.DefaultProfile.WantsPlugin(plugin.Manifest.WorkingPluginId);
|
this.profileManager.DefaultProfile.WantsPlugin(plugin.EffectiveWorkingPluginId);
|
||||||
if (wantsInDefaultProfile == null)
|
if (wantsInDefaultProfile == null)
|
||||||
{
|
{
|
||||||
// We don't know about this plugin, so we don't want to do anything here.
|
// We don't know about this plugin, so we don't want to do anything here.
|
||||||
|
|
@ -1582,7 +1582,7 @@ internal class PluginManager : IInternalDisposableService
|
||||||
|
|
||||||
// Check if any profile wants this plugin. We need to do this here, since we want to allow loading a dev plugin if a non-default profile wants it active.
|
// Check if any profile wants this plugin. We need to do this here, since we want to allow loading a dev plugin if a non-default profile wants it active.
|
||||||
// Note that this will not add the plugin to the default profile. That's done below in any other case.
|
// Note that this will not add the plugin to the default profile. That's done below in any other case.
|
||||||
wantedByAnyProfile = await this.profileManager.GetWantStateAsync(plugin.Manifest.WorkingPluginId, plugin.Manifest.InternalName, false, false);
|
wantedByAnyProfile = await this.profileManager.GetWantStateAsync(plugin.EffectiveWorkingPluginId, plugin.Manifest.InternalName, false, false);
|
||||||
|
|
||||||
// If it is wanted by any other profile, we do want to load it.
|
// If it is wanted by any other profile, we do want to load it.
|
||||||
if (wantedByAnyProfile)
|
if (wantedByAnyProfile)
|
||||||
|
|
@ -1592,28 +1592,28 @@ internal class PluginManager : IInternalDisposableService
|
||||||
{
|
{
|
||||||
// We didn't want this plugin, and StartOnBoot is on. That means we don't want it and it should stay off until manually enabled.
|
// We didn't want this plugin, and StartOnBoot is on. That means we don't want it and it should stay off until manually enabled.
|
||||||
Log.Verbose("DevPlugin {Name} disabled and StartOnBoot => disable", plugin.Manifest.InternalName);
|
Log.Verbose("DevPlugin {Name} disabled and StartOnBoot => disable", plugin.Manifest.InternalName);
|
||||||
await this.profileManager.DefaultProfile.AddOrUpdateAsync(plugin.Manifest.WorkingPluginId, plugin.Manifest.InternalName, false, false);
|
await this.profileManager.DefaultProfile.AddOrUpdateAsync(plugin.EffectiveWorkingPluginId, plugin.Manifest.InternalName, false, false);
|
||||||
loadPlugin = false;
|
loadPlugin = false;
|
||||||
}
|
}
|
||||||
else if (wantsInDefaultProfile == true && devPlugin.StartOnBoot)
|
else if (wantsInDefaultProfile == true && devPlugin.StartOnBoot)
|
||||||
{
|
{
|
||||||
// We wanted this plugin, and StartOnBoot is on. That means we actually do want it.
|
// We wanted this plugin, and StartOnBoot is on. That means we actually do want it.
|
||||||
Log.Verbose("DevPlugin {Name} enabled and StartOnBoot => enable", plugin.Manifest.InternalName);
|
Log.Verbose("DevPlugin {Name} enabled and StartOnBoot => enable", plugin.Manifest.InternalName);
|
||||||
await this.profileManager.DefaultProfile.AddOrUpdateAsync(plugin.Manifest.WorkingPluginId, plugin.Manifest.InternalName, true, false);
|
await this.profileManager.DefaultProfile.AddOrUpdateAsync(plugin.EffectiveWorkingPluginId, plugin.Manifest.InternalName, true, false);
|
||||||
loadPlugin = !doNotLoad;
|
loadPlugin = !doNotLoad;
|
||||||
}
|
}
|
||||||
else if (wantsInDefaultProfile == true && !devPlugin.StartOnBoot)
|
else if (wantsInDefaultProfile == true && !devPlugin.StartOnBoot)
|
||||||
{
|
{
|
||||||
// We wanted this plugin, but StartOnBoot is off. This means we don't want it anymore.
|
// We wanted this plugin, but StartOnBoot is off. This means we don't want it anymore.
|
||||||
Log.Verbose("DevPlugin {Name} enabled and !StartOnBoot => disable", plugin.Manifest.InternalName);
|
Log.Verbose("DevPlugin {Name} enabled and !StartOnBoot => disable", plugin.Manifest.InternalName);
|
||||||
await this.profileManager.DefaultProfile.AddOrUpdateAsync(plugin.Manifest.WorkingPluginId, plugin.Manifest.InternalName, false, false);
|
await this.profileManager.DefaultProfile.AddOrUpdateAsync(plugin.EffectiveWorkingPluginId, plugin.Manifest.InternalName, false, false);
|
||||||
loadPlugin = false;
|
loadPlugin = false;
|
||||||
}
|
}
|
||||||
else if (wantsInDefaultProfile == false && !devPlugin.StartOnBoot)
|
else if (wantsInDefaultProfile == false && !devPlugin.StartOnBoot)
|
||||||
{
|
{
|
||||||
// We didn't want this plugin, and StartOnBoot is off. We don't want it.
|
// We didn't want this plugin, and StartOnBoot is off. We don't want it.
|
||||||
Log.Verbose("DevPlugin {Name} disabled and !StartOnBoot => disable", plugin.Manifest.InternalName);
|
Log.Verbose("DevPlugin {Name} disabled and !StartOnBoot => disable", plugin.Manifest.InternalName);
|
||||||
await this.profileManager.DefaultProfile.AddOrUpdateAsync(plugin.Manifest.WorkingPluginId, plugin.Manifest.InternalName, false, false);
|
await this.profileManager.DefaultProfile.AddOrUpdateAsync(plugin.EffectiveWorkingPluginId, plugin.Manifest.InternalName, false, false);
|
||||||
loadPlugin = false;
|
loadPlugin = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1626,7 +1626,7 @@ internal class PluginManager : IInternalDisposableService
|
||||||
|
|
||||||
// Plugins that aren't in any profile will be added to the default profile with this call.
|
// Plugins that aren't in any profile will be added to the default profile with this call.
|
||||||
// We are skipping a double-lookup for dev plugins that are wanted by non-default profiles, as noted above.
|
// We are skipping a double-lookup for dev plugins that are wanted by non-default profiles, as noted above.
|
||||||
wantedByAnyProfile = wantedByAnyProfile || await this.profileManager.GetWantStateAsync(plugin.Manifest.WorkingPluginId, plugin.Manifest.InternalName, defaultState);
|
wantedByAnyProfile = wantedByAnyProfile || await this.profileManager.GetWantStateAsync(plugin.EffectiveWorkingPluginId, plugin.Manifest.InternalName, defaultState);
|
||||||
Log.Information("{Name} defaultState: {State} wantedByAnyProfile: {WantedByAny} loadPlugin: {LoadPlugin}", plugin.Manifest.InternalName, defaultState, wantedByAnyProfile, loadPlugin);
|
Log.Information("{Name} defaultState: {State} wantedByAnyProfile: {WantedByAny} loadPlugin: {LoadPlugin}", plugin.Manifest.InternalName, defaultState, wantedByAnyProfile, loadPlugin);
|
||||||
|
|
||||||
if (loadPlugin)
|
if (loadPlugin)
|
||||||
|
|
|
||||||
|
|
@ -183,8 +183,8 @@ internal class ProfileManager : IServiceType
|
||||||
var installedPlugin = pm.InstalledPlugins.FirstOrDefault(x => x.Manifest.InternalName == plugin.InternalName);
|
var installedPlugin = pm.InstalledPlugins.FirstOrDefault(x => x.Manifest.InternalName == plugin.InternalName);
|
||||||
if (installedPlugin != null)
|
if (installedPlugin != null)
|
||||||
{
|
{
|
||||||
Log.Information("Satisfying plugin {InternalName} for profile {Name} with {Guid}", plugin.InternalName, newModel.Name, installedPlugin.Manifest.WorkingPluginId);
|
Log.Information("Satisfying plugin {InternalName} for profile {Name} with {Guid}", plugin.InternalName, newModel.Name, installedPlugin.EffectiveWorkingPluginId);
|
||||||
plugin.WorkingPluginId = installedPlugin.Manifest.WorkingPluginId;
|
plugin.WorkingPluginId = installedPlugin.EffectiveWorkingPluginId;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -237,7 +237,7 @@ internal class ProfileManager : IServiceType
|
||||||
var pm = Service<PluginManager>.Get();
|
var pm = Service<PluginManager>.Get();
|
||||||
foreach (var installedPlugin in pm.InstalledPlugins)
|
foreach (var installedPlugin in pm.InstalledPlugins)
|
||||||
{
|
{
|
||||||
var wantThis = wantActive.Any(x => x.WorkingPluginId == installedPlugin.Manifest.WorkingPluginId);
|
var wantThis = wantActive.Any(x => x.WorkingPluginId == installedPlugin.EffectiveWorkingPluginId);
|
||||||
switch (wantThis)
|
switch (wantThis)
|
||||||
{
|
{
|
||||||
case true when !installedPlugin.IsLoaded:
|
case true when !installedPlugin.IsLoaded:
|
||||||
|
|
|
||||||
|
|
@ -50,14 +50,6 @@ internal class LocalDevPlugin : LocalPlugin, IDisposable
|
||||||
Log.Verbose("{InternalName} was assigned new devPlugin GUID {Guid}", this.InternalName, this.devSettings.WorkingPluginId);
|
Log.Verbose("{InternalName} was assigned new devPlugin GUID {Guid}", this.InternalName, this.devSettings.WorkingPluginId);
|
||||||
configuration.QueueSave();
|
configuration.QueueSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the ID in the manifest is wrong, force the good one
|
|
||||||
if (this.DevImposedWorkingPluginId != this.manifest.WorkingPluginId)
|
|
||||||
{
|
|
||||||
Debug.Assert(this.DevImposedWorkingPluginId != Guid.Empty, "Empty guid for devPlugin");
|
|
||||||
this.manifest.WorkingPluginId = this.DevImposedWorkingPluginId;
|
|
||||||
this.SaveManifest("dev imposed working plugin id");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.AutomaticReload)
|
if (this.AutomaticReload)
|
||||||
{
|
{
|
||||||
|
|
@ -99,7 +91,10 @@ internal class LocalDevPlugin : LocalPlugin, IDisposable
|
||||||
/// Gets an ID uniquely identifying this specific instance of a devPlugin.
|
/// Gets an ID uniquely identifying this specific instance of a devPlugin.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Guid DevImposedWorkingPluginId => this.devSettings.WorkingPluginId;
|
public Guid DevImposedWorkingPluginId => this.devSettings.WorkingPluginId;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Guid EffectiveWorkingPluginId => this.DevImposedWorkingPluginId;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a list of validation problems that have been dismissed by the user.
|
/// Gets a list of validation problems that have been dismissed by the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -148,6 +143,23 @@ internal class LocalDevPlugin : LocalPlugin, IDisposable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reload the manifest if it exists, to update possible changes.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="Exception">Thrown if the manifest could not be loaded.</exception>
|
||||||
|
public void ReloadManifest()
|
||||||
|
{
|
||||||
|
var manifestPath = LocalPluginManifest.GetManifestFile(this.DllFile);
|
||||||
|
if (manifestPath.Exists)
|
||||||
|
this.manifest = LocalPluginManifest.Load(manifestPath) ?? throw new Exception("Could not reload manifest.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void OnPreReload()
|
||||||
|
{
|
||||||
|
this.ReloadManifest();
|
||||||
|
}
|
||||||
|
|
||||||
private void OnFileChanged(object sender, FileSystemEventArgs args)
|
private void OnFileChanged(object sender, FileSystemEventArgs args)
|
||||||
{
|
{
|
||||||
var current = Interlocked.Increment(ref this.reloadCounter);
|
var current = Interlocked.Increment(ref this.reloadCounter);
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ internal class LocalPlugin : IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an installation instance ID for this plugin, if it doesn't have one yet
|
// Create an installation instance ID for this plugin, if it doesn't have one yet
|
||||||
if (this.manifest.WorkingPluginId == Guid.Empty)
|
if (this.manifest.WorkingPluginId == Guid.Empty && !this.IsDev)
|
||||||
{
|
{
|
||||||
this.manifest.WorkingPluginId = Guid.NewGuid();
|
this.manifest.WorkingPluginId = Guid.NewGuid();
|
||||||
|
|
||||||
|
|
@ -162,7 +162,7 @@ internal class LocalPlugin : IDisposable
|
||||||
/// INCLUDES the default profile.
|
/// INCLUDES the default profile.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsWantedByAnyProfile =>
|
public bool IsWantedByAnyProfile =>
|
||||||
Service<ProfileManager>.Get().GetWantStateAsync(this.manifest.WorkingPluginId, this.Manifest.InternalName, false, false).GetAwaiter().GetResult();
|
Service<ProfileManager>.Get().GetWantStateAsync(this.EffectiveWorkingPluginId, this.Manifest.InternalName, false, false).GetAwaiter().GetResult();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this plugin's API level is out of date.
|
/// Gets a value indicating whether this plugin's API level is out of date.
|
||||||
|
|
@ -215,6 +215,11 @@ internal class LocalPlugin : IDisposable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Version EffectiveVersion => this.manifest.EffectiveVersion;
|
public Version EffectiveVersion => this.manifest.EffectiveVersion;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the effective working plugin ID for this plugin.
|
||||||
|
/// </summary>
|
||||||
|
public virtual Guid EffectiveWorkingPluginId => this.manifest.WorkingPluginId;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the service scope for this plugin.
|
/// Gets the service scope for this plugin.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -271,11 +276,8 @@ internal class LocalPlugin : IDisposable
|
||||||
await this.pluginLoadStateLock.WaitAsync();
|
await this.pluginLoadStateLock.WaitAsync();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (reloading && this.IsDev)
|
if (reloading)
|
||||||
{
|
this.OnPreReload();
|
||||||
// Reload the manifest in-case there were changes here too.
|
|
||||||
this.ReloadManifest();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we reload a plugin we don't want to delete it. Makes sense, right?
|
// If we reload a plugin we don't want to delete it. Makes sense, right?
|
||||||
if (this.manifest.ScheduledForDeletion)
|
if (this.manifest.ScheduledForDeletion)
|
||||||
|
|
@ -578,24 +580,6 @@ internal class LocalPlugin : IDisposable
|
||||||
this.SaveManifest("scheduling for deletion");
|
this.SaveManifest("scheduling for deletion");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reload the manifest if it exists, preserve the internal Disabled state.
|
|
||||||
/// </summary>
|
|
||||||
public void ReloadManifest()
|
|
||||||
{
|
|
||||||
var manifestPath = LocalPluginManifest.GetManifestFile(this.DllFile);
|
|
||||||
if (manifestPath.Exists)
|
|
||||||
{
|
|
||||||
// Save some state that we do actually want to carry over
|
|
||||||
var guid = this.manifest.WorkingPluginId;
|
|
||||||
|
|
||||||
this.manifest = LocalPluginManifest.Load(manifestPath) ?? throw new Exception("Could not reload manifest.");
|
|
||||||
this.manifest.WorkingPluginId = guid;
|
|
||||||
|
|
||||||
this.SaveManifest("dev reload");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the repository this plugin was installed from.
|
/// Get the repository this plugin was installed from.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -620,6 +604,13 @@ internal class LocalPlugin : IDisposable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="reason">Why it should be saved.</param>
|
/// <param name="reason">Why it should be saved.</param>
|
||||||
protected void SaveManifest(string reason) => this.manifest.Save(this.manifestFile, reason);
|
protected void SaveManifest(string reason) => this.manifest.Save(this.manifestFile, reason);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called before a plugin is reloaded.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnPreReload()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private static void SetupLoaderConfig(LoaderConfig config)
|
private static void SetupLoaderConfig(LoaderConfig config)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ public interface IGameInteropProvider
|
||||||
/// <param name="detour">Callback function. Delegate must have a same original function prototype.</param>
|
/// <param name="detour">Callback function. Delegate must have a same original function prototype.</param>
|
||||||
/// <returns>The hook with the supplied parameters.</returns>
|
/// <returns>The hook with the supplied parameters.</returns>
|
||||||
/// <typeparam name="T">Delegate of detour.</typeparam>
|
/// <typeparam name="T">Delegate of detour.</typeparam>
|
||||||
public Hook<T> HookFromFunctionPointerVariable<T>(IntPtr address, T detour) where T : Delegate;
|
public Hook<T> HookFromFunctionPointerVariable<T>(nint address, T detour) where T : Delegate;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a hook by rewriting import table address.
|
/// Creates a hook by rewriting import table address.
|
||||||
|
|
@ -85,7 +85,31 @@ public interface IGameInteropProvider
|
||||||
/// <param name="backend">Hooking library to use.</param>
|
/// <param name="backend">Hooking library to use.</param>
|
||||||
/// <returns>The hook with the supplied parameters.</returns>
|
/// <returns>The hook with the supplied parameters.</returns>
|
||||||
/// <typeparam name="T">Delegate of detour.</typeparam>
|
/// <typeparam name="T">Delegate of detour.</typeparam>
|
||||||
Hook<T> HookFromAddress<T>(IntPtr procAddress, T detour, HookBackend backend = HookBackend.Automatic) where T : Delegate;
|
Hook<T> HookFromAddress<T>(nint procAddress, T detour, HookBackend backend = HookBackend.Automatic) where T : Delegate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a hook. Hooking address is inferred by calling to GetProcAddress() function.
|
||||||
|
/// The hook is not activated until Enable() method is called.
|
||||||
|
/// Please do not use MinHook unless you have thoroughly troubleshot why Reloaded does not work.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="procAddress">A memory address to install a hook.</param>
|
||||||
|
/// <param name="detour">Callback function. Delegate must have a same original function prototype.</param>
|
||||||
|
/// <param name="backend">Hooking library to use.</param>
|
||||||
|
/// <returns>The hook with the supplied parameters.</returns>
|
||||||
|
/// <typeparam name="T">Delegate of detour.</typeparam>
|
||||||
|
Hook<T> HookFromAddress<T>(nuint procAddress, T detour, HookBackend backend = HookBackend.Automatic) where T : Delegate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a hook. Hooking address is inferred by calling to GetProcAddress() function.
|
||||||
|
/// The hook is not activated until Enable() method is called.
|
||||||
|
/// Please do not use MinHook unless you have thoroughly troubleshot why Reloaded does not work.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="procAddress">A memory address to install a hook.</param>
|
||||||
|
/// <param name="detour">Callback function. Delegate must have a same original function prototype.</param>
|
||||||
|
/// <param name="backend">Hooking library to use.</param>
|
||||||
|
/// <returns>The hook with the supplied parameters.</returns>
|
||||||
|
/// <typeparam name="T">Delegate of detour.</typeparam>
|
||||||
|
unsafe Hook<T> HookFromAddress<T>(void* procAddress, T detour, HookBackend backend = HookBackend.Automatic) where T : Delegate;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a hook from a signature into the Dalamud target module.
|
/// Creates a hook from a signature into the Dalamud target module.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue