Compare commits

..

1 commit

Author SHA1 Message Date
github-actions[bot]
dc5556d09c Update ClientStructs
Some checks are pending
Build Dalamud / Build on Windows (push) Waiting to run
Build Dalamud / Check API Compatibility (push) Blocked by required conditions
Build Dalamud / Deploy dalamud-distrib staging (push) Blocked by required conditions
2025-09-29 12:51:07 +00:00
31 changed files with 417 additions and 686 deletions

View file

@ -19,14 +19,8 @@ public interface IBuddyMember
/// <summary>
/// Gets the object ID of this buddy.
/// </summary>
[Obsolete("Renamed to EntityId")]
uint ObjectId { get; }
/// <summary>
/// Gets the entity ID of this buddy.
/// </summary>
uint EntityId { get; }
/// <summary>
/// Gets the actor associated with this buddy.
/// </summary>
@ -89,9 +83,6 @@ internal unsafe class BuddyMember : IBuddyMember
/// <inheritdoc />
public uint ObjectId => this.Struct->EntityId;
/// <inheritdoc />
public uint EntityId => this.Struct->EntityId;
/// <inheritdoc />
public IGameObject? GameObject => this.objectTable.SearchById(this.ObjectId);

View file

@ -15,8 +15,8 @@ using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Application.Network;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.Event;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using FFXIVClientStructs.FFXIV.Client.Network;
using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
@ -36,9 +36,9 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
private readonly GameLifecycle lifecycle;
private readonly ClientStateAddressResolver address;
private readonly Hook<HandleZoneInitPacketDelegate> handleZoneInitPacketHook;
private readonly Hook<EventFramework.Delegates.SetTerritoryTypeId> setupTerritoryTypeHook;
private readonly Hook<UIModule.Delegates.HandlePacket> uiModuleHandlePacketHook;
private readonly Hook<SetCurrentInstanceDelegate> setCurrentInstanceHook;
private Hook<LogoutCallbackInterface.Delegates.OnLogout> onLogoutHook;
[ServiceManager.ServiceDependency]
private readonly Framework framework = Service<Framework>.Get();
@ -46,12 +46,6 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
[ServiceManager.ServiceDependency]
private readonly NetworkHandlers networkHandlers = Service<NetworkHandlers>.Get();
private Hook<LogoutCallbackInterface.Delegates.OnLogout> onLogoutHook;
private bool initialized;
private ushort territoryTypeId;
private bool isPvP;
private uint mapId;
private uint instance;
private bool lastConditionNone = true;
[ServiceManager.ServiceConstructor]
@ -65,37 +59,26 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
this.ClientLanguage = (ClientLanguage)dalamud.StartInfo.Language;
this.handleZoneInitPacketHook = Hook<HandleZoneInitPacketDelegate>.FromAddress(this.AddressResolver.HandleZoneInitPacket, this.HandleZoneInitPacketDetour);
this.uiModuleHandlePacketHook = Hook<UIModule.Delegates.HandlePacket>.FromAddress((nint)UIModule.StaticVirtualTablePointer->HandlePacket, this.UIModuleHandlePacketDetour);
this.setCurrentInstanceHook = Hook<SetCurrentInstanceDelegate>.FromAddress(this.AddressResolver.SetCurrentInstance, this.SetCurrentInstanceDetour);
var setTerritoryTypeAddr = EventFramework.Addresses.SetTerritoryTypeId.Value;
Log.Verbose($"SetupTerritoryType address {Util.DescribeAddress(setTerritoryTypeAddr)}");
this.setupTerritoryTypeHook = Hook<EventFramework.Delegates.SetTerritoryTypeId>.FromAddress(setTerritoryTypeAddr, this.SetupTerritoryTypeDetour);
this.uiModuleHandlePacketHook = Hook<UIModule.Delegates.HandlePacket>.FromAddress((nint)UIModule.StaticVirtualTablePointer->HandlePacket, this.UIModuleHandlePacketDetour);
this.framework.Update += this.FrameworkOnOnUpdateEvent;
this.networkHandlers.CfPop += this.NetworkHandlersOnCfPop;
this.handleZoneInitPacketHook.Enable();
this.setupTerritoryTypeHook.Enable();
this.uiModuleHandlePacketHook.Enable();
this.setCurrentInstanceHook.Enable();
this.framework.RunOnTick(this.Setup);
}
private unsafe delegate void ProcessPacketPlayerSetupDelegate(nint a1, nint packet);
private unsafe delegate void HandleZoneInitPacketDelegate(nint a1, uint localPlayerEntityId, nint packet, byte type);
private unsafe delegate void SetCurrentInstanceDelegate(NetworkModuleProxy* thisPtr, short instanceId);
/// <inheritdoc/>
public event Action<ZoneInitEventArgs> ZoneInit;
/// <inheritdoc/>
public event Action<ushort>? TerritoryChanged;
/// <inheritdoc/>
public event Action<uint>? MapIdChanged;
/// <inheritdoc/>
public event Action<uint>? InstanceChanged;
/// <inheritdoc/>
public event IClientState.ClassJobChangeDelegate? ClassJobChanged;
@ -121,65 +104,15 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
public ClientLanguage ClientLanguage { get; }
/// <inheritdoc/>
public ushort TerritoryType
{
get => this.territoryTypeId;
private set
{
if (this.territoryTypeId != value)
{
this.territoryTypeId = value;
if (this.initialized)
{
Log.Debug("TerritoryType changed: {0}", value);
this.TerritoryChanged?.InvokeSafely(value);
}
var rowRef = LuminaUtils.CreateRef<TerritoryType>(value);
if (rowRef.IsValid)
{
this.IsPvP = rowRef.Value.IsPvpZone;
}
}
}
}
public ushort TerritoryType { get; private set; }
/// <inheritdoc/>
public uint MapId
public unsafe uint MapId
{
get => this.mapId;
private set
get
{
if (this.mapId != value)
{
this.mapId = value;
if (this.initialized)
{
Log.Debug("MapId changed: {0}", value);
this.MapIdChanged?.InvokeSafely(value);
}
}
}
}
/// <inheritdoc/>
public uint Instance
{
get => this.instance;
private set
{
if (this.instance != value)
{
this.instance = value;
if (this.initialized)
{
Log.Debug("Instance changed: {0}", value);
this.InstanceChanged?.InvokeSafely(value);
}
}
var agentMap = AgentMap.Instance();
return agentMap != null ? agentMap->CurrentMapId : 0;
}
}
@ -200,31 +133,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
}
/// <inheritdoc/>
public bool IsPvP
{
get => this.isPvP;
private set
{
if (this.isPvP != value)
{
this.isPvP = value;
if (this.initialized)
{
if (value)
{
Log.Debug("EnterPvP");
this.EnterPvP?.InvokeSafely();
}
else
{
Log.Debug("LeavePvP");
this.LeavePvP?.InvokeSafely();
}
}
}
}
}
public bool IsPvP { get; private set; }
/// <inheritdoc/>
public bool IsPvPExcludingDen => this.IsPvP && this.TerritoryType != 250;
@ -263,12 +172,11 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
/// </summary>
void IInternalDisposableService.DisposeService()
{
this.handleZoneInitPacketHook.Dispose();
this.setupTerritoryTypeHook.Dispose();
this.uiModuleHandlePacketHook.Dispose();
this.onLogoutHook.Dispose();
this.setCurrentInstanceHook.Dispose();
this.framework.Update -= this.OnFrameworkUpdate;
this.framework.Update -= this.FrameworkOnOnUpdateEvent;
this.networkHandlers.CfPop -= this.NetworkHandlersOnCfPop;
}
@ -278,28 +186,43 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
this.onLogoutHook.Enable();
this.TerritoryType = (ushort)GameMain.Instance()->CurrentTerritoryTypeId;
this.MapId = AgentMap.Instance()->CurrentMapId;
this.Instance = UIState.Instance()->PublicInstance.InstanceId;
this.initialized = true;
this.framework.Update += this.OnFrameworkUpdate;
}
private void HandleZoneInitPacketDetour(nint a1, uint localPlayerEntityId, nint packet, byte type)
private unsafe void SetupTerritoryTypeDetour(EventFramework* eventFramework, ushort territoryType)
{
this.handleZoneInitPacketHook.Original(a1, localPlayerEntityId, packet, type);
this.SetTerritoryType(territoryType);
this.setupTerritoryTypeHook.Original(eventFramework, territoryType);
}
try
private unsafe void SetTerritoryType(ushort territoryType)
{
if (this.TerritoryType == territoryType)
return;
Log.Debug("TerritoryType changed: {0}", territoryType);
this.TerritoryType = territoryType;
this.TerritoryChanged?.InvokeSafely(territoryType);
var rowRef = LuminaUtils.CreateRef<TerritoryType>(territoryType);
if (rowRef.IsValid)
{
var eventArgs = ZoneInitEventArgs.Read(packet);
Log.Debug($"ZoneInit: {eventArgs}");
this.ZoneInit?.InvokeSafely(eventArgs);
this.TerritoryType = (ushort)eventArgs.TerritoryType.RowId;
}
catch (Exception ex)
{
Log.Error(ex, "Exception during ZoneInit");
var isPvP = rowRef.Value.IsPvpZone;
if (isPvP != this.IsPvP)
{
this.IsPvP = isPvP;
if (this.IsPvP)
{
Log.Debug("EnterPvP");
this.EnterPvP?.InvokeSafely();
}
else
{
Log.Debug("LeavePvP");
this.LeavePvP?.InvokeSafely();
}
}
}
}
@ -351,16 +274,8 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
}
}
private unsafe void SetCurrentInstanceDetour(NetworkModuleProxy* thisPtr, short instanceId)
private void FrameworkOnOnUpdateEvent(IFramework framework1)
{
this.setCurrentInstanceHook.Original(thisPtr, instanceId);
this.Instance = (uint)instanceId;
}
private unsafe void OnFrameworkUpdate(IFramework framework)
{
this.MapId = AgentMap.Instance()->CurrentMapId;
var condition = Service<Conditions.Condition>.GetNullable();
var gameGui = Service<GameGui>.GetNullable();
var data = Service<DataManager>.GetNullable();
@ -442,10 +357,7 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat
/// </summary>
internal ClientStatePluginScoped()
{
this.clientStateService.ZoneInit += this.ZoneInitForward;
this.clientStateService.TerritoryChanged += this.TerritoryChangedForward;
this.clientStateService.MapIdChanged += this.MapIdChangedForward;
this.clientStateService.InstanceChanged += this.InstanceChangedForward;
this.clientStateService.ClassJobChanged += this.ClassJobChangedForward;
this.clientStateService.LevelChanged += this.LevelChangedForward;
this.clientStateService.Login += this.LoginForward;
@ -455,18 +367,9 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat
this.clientStateService.CfPop += this.ContentFinderPopForward;
}
/// <inheritdoc/>
public event Action<ZoneInitEventArgs> ZoneInit;
/// <inheritdoc/>
public event Action<ushort>? TerritoryChanged;
/// <inheritdoc/>
public event Action<uint>? MapIdChanged;
/// <inheritdoc/>
public event Action<uint>? InstanceChanged;
/// <inheritdoc/>
public event IClientState.ClassJobChangeDelegate? ClassJobChanged;
@ -497,9 +400,6 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat
/// <inheritdoc/>
public uint MapId => this.clientStateService.MapId;
/// <inheritdoc/>
public uint Instance => this.clientStateService.Instance;
/// <inheritdoc/>
public IPlayerCharacter? LocalPlayer => this.clientStateService.LocalPlayer;
@ -527,10 +427,7 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat
/// <inheritdoc/>
void IInternalDisposableService.DisposeService()
{
this.clientStateService.ZoneInit -= this.ZoneInitForward;
this.clientStateService.TerritoryChanged -= this.TerritoryChangedForward;
this.clientStateService.MapIdChanged -= this.MapIdChangedForward;
this.clientStateService.InstanceChanged -= this.InstanceChangedForward;
this.clientStateService.ClassJobChanged -= this.ClassJobChangedForward;
this.clientStateService.LevelChanged -= this.LevelChangedForward;
this.clientStateService.Login -= this.LoginForward;
@ -539,12 +436,7 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat
this.clientStateService.LeavePvP -= this.ExitPvPForward;
this.clientStateService.CfPop -= this.ContentFinderPopForward;
this.ZoneInit = null;
this.TerritoryChanged = null;
this.MapIdChanged = null;
this.InstanceChanged = null;
this.ClassJobChanged = null;
this.LevelChanged = null;
this.Login = null;
this.Logout = null;
this.EnterPvP = null;
@ -552,14 +444,8 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat
this.CfPop = null;
}
private void ZoneInitForward(ZoneInitEventArgs eventArgs) => this.ZoneInit?.Invoke(eventArgs);
private void TerritoryChangedForward(ushort territoryId) => this.TerritoryChanged?.Invoke(territoryId);
private void MapIdChangedForward(uint mapId) => this.MapIdChanged?.Invoke(mapId);
private void InstanceChangedForward(uint instanceId) => this.InstanceChanged?.Invoke(instanceId);
private void ClassJobChangedForward(uint classJobId) => this.ClassJobChanged?.Invoke(classJobId);
private void LevelChangedForward(uint classJobId, uint level) => this.LevelChanged?.Invoke(classJobId, level);

View file

@ -10,24 +10,19 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver
/// <summary>
/// Gets the address of the keyboard state.
/// </summary>
public nint KeyboardState { get; private set; }
public IntPtr KeyboardState { get; private set; }
/// <summary>
/// Gets the address of the keyboard state index array which translates the VK enumeration to the key state.
/// </summary>
public nint KeyboardStateIndexArray { get; private set; }
public IntPtr KeyboardStateIndexArray { get; private set; }
// Functions
/// <summary>
/// Gets the address of the method that handles the ZoneInit packet.
/// Gets the address of the method which sets up the player.
/// </summary>
public nint HandleZoneInitPacket { get; private set; }
/// <summary>
/// Gets the address of the method that sets the current public instance.
/// </summary>
public nint SetCurrentInstance { get; private set; }
public IntPtr ProcessPacketPlayerSetup { get; private set; }
/// <summary>
/// Scan for and setup any configured address pointers.
@ -35,8 +30,7 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver
/// <param name="sig">The signature scanner to facilitate setup.</param>
protected override void Setup64Bit(ISigScanner sig)
{
this.HandleZoneInitPacket = sig.ScanText("E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 44 0F B6 45");
this.SetCurrentInstance = sig.ScanText("E8 ?? ?? ?? ?? 0F B6 55 ?? 48 8D 0D ?? ?? ?? ?? C0 EA"); // NetworkModuleProxy.SetCurrentInstance
this.ProcessPacketPlayerSetup = sig.ScanText("40 53 48 83 EC 20 48 8D 0D ?? ?? ?? ?? 48 8B DA E8 ?? ?? ?? ?? 48 8B D3"); // not in cs struct
// These resolve to fixed offsets only, without the base address added in, so GetStaticAddressFromSig() can't be used.
// lea rcx, ds:1DB9F74h[rax*4] KeyboardState

View file

@ -35,14 +35,8 @@ public interface IGameObject : IEquatable<IGameObject>
/// <summary>
/// Gets the data ID for linking to other respective game data.
/// </summary>
[Obsolete("Renamed to BaseId")]
public uint DataId { get; }
/// <summary>
/// Gets the base ID for linking to other respective game data.
/// </summary>
public uint BaseId { get; }
/// <summary>
/// Gets the ID of this GameObject's owner.
/// </summary>
@ -214,9 +208,6 @@ internal unsafe partial class GameObject : IGameObject
/// <inheritdoc/>
public uint DataId => this.Struct->BaseId;
/// <inheritdoc/>
public uint BaseId => this.Struct->BaseId;
/// <inheritdoc/>
public uint OwnerId => this.Struct->OwnerId;

View file

@ -40,14 +40,8 @@ public interface IPartyMember
/// <summary>
/// Gets the actor ID of this party member.
/// </summary>
[Obsolete("Renamed to EntityId")]
uint ObjectId { get; }
/// <summary>
/// Gets the entity ID of this party member.
/// </summary>
uint EntityId { get; }
/// <summary>
/// Gets the actor associated with this buddy.
/// </summary>
@ -121,55 +115,87 @@ internal unsafe class PartyMember : IPartyMember
this.Address = address;
}
/// <inheritdoc/>
/// <summary>
/// Gets the address of this party member in memory.
/// </summary>
public IntPtr Address { get; }
/// <inheritdoc/>
/// <summary>
/// Gets a list of buffs or debuffs applied to this party member.
/// </summary>
public StatusList Statuses => new(&this.Struct->StatusManager);
/// <inheritdoc/>
/// <summary>
/// Gets the position of the party member.
/// </summary>
public Vector3 Position => this.Struct->Position;
/// <inheritdoc/>
/// <summary>
/// Gets the content ID of the party member.
/// </summary>
public long ContentId => (long)this.Struct->ContentId;
/// <inheritdoc/>
/// <summary>
/// Gets the actor ID of this party member.
/// </summary>
public uint ObjectId => this.Struct->EntityId;
/// <inheritdoc/>
public uint EntityId => this.Struct->EntityId;
/// <summary>
/// Gets the actor associated with this buddy.
/// </summary>
/// <remarks>
/// This iterates the actor table, it should be used with care.
/// </remarks>
public IGameObject? GameObject => Service<ObjectTable>.Get().SearchById(this.ObjectId);
/// <inheritdoc/>
public IGameObject? GameObject => Service<ObjectTable>.Get().SearchById(this.EntityId);
/// <inheritdoc/>
/// <summary>
/// Gets the current HP of this party member.
/// </summary>
public uint CurrentHP => this.Struct->CurrentHP;
/// <inheritdoc/>
/// <summary>
/// Gets the maximum HP of this party member.
/// </summary>
public uint MaxHP => this.Struct->MaxHP;
/// <inheritdoc/>
/// <summary>
/// Gets the current MP of this party member.
/// </summary>
public ushort CurrentMP => this.Struct->CurrentMP;
/// <inheritdoc/>
/// <summary>
/// Gets the maximum MP of this party member.
/// </summary>
public ushort MaxMP => this.Struct->MaxMP;
/// <inheritdoc/>
/// <summary>
/// Gets the territory this party member is located in.
/// </summary>
public RowRef<Lumina.Excel.Sheets.TerritoryType> Territory => LuminaUtils.CreateRef<Lumina.Excel.Sheets.TerritoryType>(this.Struct->TerritoryType);
/// <inheritdoc/>
/// <summary>
/// Gets the World this party member resides in.
/// </summary>
public RowRef<Lumina.Excel.Sheets.World> World => LuminaUtils.CreateRef<Lumina.Excel.Sheets.World>(this.Struct->HomeWorld);
/// <inheritdoc/>
/// <summary>
/// Gets the displayname of this party member.
/// </summary>
public SeString Name => SeString.Parse(this.Struct->Name);
/// <inheritdoc/>
/// <summary>
/// Gets the sex of this party member.
/// </summary>
public byte Sex => this.Struct->Sex;
/// <inheritdoc/>
/// <summary>
/// Gets the classjob of this party member.
/// </summary>
public RowRef<Lumina.Excel.Sheets.ClassJob> ClassJob => LuminaUtils.CreateRef<Lumina.Excel.Sheets.ClassJob>(this.Struct->ClassJob);
/// <inheritdoc/>
/// <summary>
/// Gets the level of this party member.
/// </summary>
public byte Level => this.Struct->Level;
private FFXIVClientStructs.FFXIV.Client.Game.Group.PartyMember* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Group.PartyMember*)this.Address;

View file

@ -1,90 +0,0 @@
using System.Linq;
using System.Text;
using Dalamud.Data;
using Lumina.Excel.Sheets;
namespace Dalamud.Game.ClientState;
/// <summary>
/// Provides event data for when the game should initialize a zone.
/// </summary>
public class ZoneInitEventArgs : EventArgs
{
/// <summary>
/// Gets the territory type of the zone being entered.
/// </summary>
public TerritoryType TerritoryType { get; private set; }
/// <summary>
/// Gets the instance number of the zone, used when multiple copies of an area are active.
/// </summary>
public ushort Instance { get; private set; }
/// <summary>
/// Gets the associated content finder condition for the zone, if any.
/// </summary>
public ContentFinderCondition ContentFinderCondition { get; private set; }
/// <summary>
/// Gets the current weather in the zone upon entry.
/// </summary>
public Weather Weather { get; private set; }
/// <summary>
/// Gets the set of active festivals in the zone.
/// </summary>
public Festival[] ActiveFestivals { get; private set; } = [];
/// <summary>
/// Gets the phases corresponding to the active festivals.
/// </summary>
public ushort[] ActiveFestivalPhases { get; private set; } = [];
/// <summary>
/// Reads raw zone initialization data from a network packet and constructs the event arguments.
/// </summary>
/// <param name="packet">A pointer to the raw packet data.</param>
/// <returns>A <see cref="ZoneInitEventArgs"/> populated from the packet.</returns>
public static unsafe ZoneInitEventArgs Read(nint packet)
{
var dataManager = Service<DataManager>.Get();
var eventArgs = new ZoneInitEventArgs();
var flags = *(byte*)(packet + 0x12);
eventArgs.TerritoryType = dataManager.GetExcelSheet<TerritoryType>().GetRow(*(ushort*)(packet + 0x02));
eventArgs.Instance = flags >= 0 ? (ushort)0 : *(ushort*)(packet + 0x04);
eventArgs.ContentFinderCondition = dataManager.GetExcelSheet<ContentFinderCondition>().GetRow(*(ushort*)(packet + 0x06));
eventArgs.Weather = dataManager.GetExcelSheet<Weather>().GetRow(*(byte*)(packet + 0x10));
const int NumFestivals = 4;
eventArgs.ActiveFestivals = new Festival[NumFestivals];
eventArgs.ActiveFestivalPhases = new ushort[NumFestivals];
// There are also 4 festival ids and phases for PlayerState at +0x3E and +0x46 respectively,
// but it's unclear why they exist as separate entries and why they would be different.
for (var i = 0; i < NumFestivals; i++)
{
eventArgs.ActiveFestivals[i] = dataManager.GetExcelSheet<Festival>().GetRow(*(ushort*)(packet + 0x2E + (i * 2)));
eventArgs.ActiveFestivalPhases[i] = *(ushort*)(packet + 0x36 + (i * 2));
}
return eventArgs;
}
/// <inheritdoc/>
public override string ToString()
{
var sb = new StringBuilder("ZoneInitEventArgs { ");
sb.Append($"TerritoryTypeId = {this.TerritoryType.RowId}, ");
sb.Append($"Instance = {this.Instance}, ");
sb.Append($"ContentFinderCondition = {this.ContentFinderCondition.RowId}, ");
sb.Append($"Weather = {this.Weather.RowId}, ");
sb.Append($"ActiveFestivals = [{string.Join(", ", this.ActiveFestivals.Select(f => f.RowId))}], ");
sb.Append($"ActiveFestivalPhases = [{string.Join(", ", this.ActiveFestivalPhases)}]");
sb.Append(" }");
return sb.ToString();
}
}

View file

@ -40,7 +40,7 @@ internal class BuddyListWidget : IDataWindowWidget
}
else
{
ImGui.Text($"[Companion] {member.Address.ToInt64():X} - {member.EntityId} - {member.DataID}");
ImGui.Text($"[Companion] {member.Address.ToInt64():X} - {member.ObjectId} - {member.DataID}");
if (this.resolveGameData)
{
var gameObject = member.GameObject;
@ -64,7 +64,7 @@ internal class BuddyListWidget : IDataWindowWidget
}
else
{
ImGui.Text($"[Pet] {member.Address.ToInt64():X} - {member.EntityId} - {member.DataID}");
ImGui.Text($"[Pet] {member.Address.ToInt64():X} - {member.ObjectId} - {member.DataID}");
if (this.resolveGameData)
{
var gameObject = member.GameObject;
@ -91,7 +91,7 @@ internal class BuddyListWidget : IDataWindowWidget
for (var i = 0; i < count; i++)
{
var member = buddyList[i];
ImGui.Text($"[BattleBuddy] [{i}] {member?.Address.ToInt64():X} - {member?.EntityId} - {member?.DataID}");
ImGui.Text($"[BattleBuddy] [{i}] {member?.Address.ToInt64():X} - {member?.ObjectId} - {member?.DataID}");
if (this.resolveGameData)
{
var gameObject = member?.GameObject;

View file

@ -1,16 +1,14 @@
using Dalamud.Utility.Internal;
namespace Dalamud.Interface.Internal.Windows.Settings;
namespace Dalamud.Interface.Internal.Windows.Settings;
/// <summary>
/// Basic, drawable settings entry.
/// </summary>
internal abstract class SettingsEntry
public abstract class SettingsEntry
{
/// <summary>
/// Gets or sets the public, searchable name of this settings entry.
/// </summary>
public LazyLoc Name { get; protected set; }
public string? Name { get; protected set; }
/// <summary>
/// Gets or sets a value indicating whether this entry is valid.

View file

@ -1,20 +1,20 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using Dalamud.Interface.Utility;
namespace Dalamud.Interface.Internal.Windows.Settings;
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")]
internal abstract class SettingsTab : IDisposable
public abstract class SettingsTab : IDisposable
{
public abstract SettingsEntry[] Entries { get; }
public abstract string Title { get; }
public abstract SettingsOpenKind Kind { get; }
public bool IsOpen { get; set; } = false;
public virtual bool IsVisible { get; } = true;
public virtual void OnOpen()
{
foreach (var settingsEntry in this.Entries)

View file

@ -17,19 +17,20 @@ namespace Dalamud.Interface.Internal.Windows.Settings;
/// <summary>
/// The window that allows for general configuration of Dalamud itself.
/// </summary>
internal sealed class SettingsWindow : Window
internal class SettingsWindow : Window
{
private readonly SettingsTab[] tabs;
private string searchInput = string.Empty;
private bool isSearchInputPrefilled = false;
private SettingsTab? setActiveTab;
private SettingsTab setActiveTab = null!;
/// <summary>
/// Initializes a new instance of the <see cref="SettingsWindow"/> class.
/// </summary>
public SettingsWindow()
: base(Title, ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoScrollbar)
: base(Loc.Localize("DalamudSettingsHeader", "Dalamud Settings") + "###XlSettings2", ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoScrollbar)
{
this.Size = new Vector2(740, 550);
this.SizeConstraints = new WindowSizeConstraints()
@ -51,10 +52,6 @@ internal sealed class SettingsWindow : Window
];
}
private static string Title => Loc.Localize("DalamudSettingsHeader", "Dalamud Settings") + "###XlSettings2";
private SettingsTab? CurrentlyOpenTab => this.tabs.FirstOrDefault(tab => tab.IsOpen);
/// <summary>
/// Open the settings window to the tab specified by <paramref name="kind"/>.
/// </summary>
@ -62,7 +59,7 @@ internal sealed class SettingsWindow : Window
public void OpenTo(SettingsOpenKind kind)
{
this.IsOpen = true;
this.setActiveTab = this.tabs.Single(tab => tab.Kind == kind);
this.SetOpenTab(kind);
}
/// <summary>
@ -86,19 +83,12 @@ internal sealed class SettingsWindow : Window
/// <inheritdoc/>
public override void OnOpen()
{
var localization = Service<Localization>.Get();
foreach (var settingsTab in this.tabs)
{
settingsTab.Load();
}
if (!this.isSearchInputPrefilled)
{
this.searchInput = string.Empty;
}
localization.LocalizationChanged += this.OnLocalizationChanged;
if (!this.isSearchInputPrefilled) this.searchInput = string.Empty;
base.OnOpen();
}
@ -109,7 +99,6 @@ internal sealed class SettingsWindow : Window
var configuration = Service<DalamudConfiguration>.Get();
var interfaceManager = Service<InterfaceManager>.Get();
var fontAtlasFactory = Service<FontAtlasFactory>.Get();
var localization = Service<Localization>.Get();
var scaleChanged = !Equals(ImGui.GetIO().FontGlobalScale, configuration.GlobalUiScale);
var rebuildFont = !Equals(fontAtlasFactory.DefaultFontSpec, configuration.DefaultFontSpec);
@ -118,7 +107,7 @@ internal sealed class SettingsWindow : Window
ImGui.GetIO().FontGlobalScale = configuration.GlobalUiScale;
if (scaleChanged)
{
interfaceManager.InvokeGlobalScaleChanged();
Service<InterfaceManager>.Get().InvokeGlobalScaleChanged();
}
fontAtlasFactory.DefaultFontSpecOverride = null;
@ -126,7 +115,7 @@ internal sealed class SettingsWindow : Window
if (rebuildFont)
{
interfaceManager.RebuildFonts();
interfaceManager.InvokeFontChanged();
Service<InterfaceManager>.Get().InvokeFontChanged();
}
foreach (var settingsTab in this.tabs)
@ -144,29 +133,98 @@ internal sealed class SettingsWindow : Window
this.isSearchInputPrefilled = false;
this.searchInput = string.Empty;
}
localization.LocalizationChanged -= this.OnLocalizationChanged;
}
/// <inheritdoc/>
public override void Draw()
{
ImGui.SetNextItemWidth(-1);
using (ImRaii.Disabled(this.CurrentlyOpenTab is SettingsTabAbout))
ImGui.InputTextWithHint("###searchInput"u8, Loc.Localize("DalamudSettingsSearchPlaceholder", "Search for settings..."), ref this.searchInput, 100, ImGuiInputTextFlags.AutoSelectAll);
ImGui.Spacing();
var windowSize = ImGui.GetWindowSize();
using (var tabBar = ImRaii.TabBar("###settingsTabs"u8))
if (ImGui.BeginTabBar("###settingsTabs"u8))
{
if (tabBar)
if (string.IsNullOrEmpty(this.searchInput))
{
if (string.IsNullOrEmpty(this.searchInput))
this.DrawTabs();
else
this.DrawSearchResults();
foreach (var settingsTab in this.tabs.Where(x => x.IsVisible))
{
var flags = ImGuiTabItemFlags.NoCloseWithMiddleMouseButton;
if (this.setActiveTab == settingsTab)
{
flags |= ImGuiTabItemFlags.SetSelected;
this.setActiveTab = null;
}
using var tab = ImRaii.TabItem(settingsTab.Title, flags);
if (tab)
{
if (!settingsTab.IsOpen)
{
settingsTab.IsOpen = true;
settingsTab.OnOpen();
}
// Don't add padding for the about tab(credits)
{
using var padding = ImRaii.PushStyle(
ImGuiStyleVar.WindowPadding,
new Vector2(2, 2),
settingsTab is not SettingsTabAbout);
using var borderColor = ImRaii.PushColor(
ImGuiCol.Border,
ImGui.GetColorU32(ImGuiCol.ChildBg));
using var tabChild = ImRaii.Child(
$"###settings_scrolling_{settingsTab.Title}",
new Vector2(-1, -1),
true);
if (tabChild)
settingsTab.Draw();
}
settingsTab.PostDraw();
}
else if (settingsTab.IsOpen)
{
settingsTab.IsOpen = false;
settingsTab.OnClose();
}
}
}
else
{
if (ImGui.BeginTabItem("Search Results"u8))
{
var any = false;
foreach (var settingsTab in this.tabs.Where(x => x.IsVisible))
{
var eligible = settingsTab.Entries.Where(x => !x.Name.IsNullOrEmpty() && x.Name.ToLowerInvariant().Contains(this.searchInput.ToLowerInvariant())).ToArray();
if (!eligible.Any())
continue;
any = true;
ImGui.TextColored(ImGuiColors.DalamudGrey, settingsTab.Title);
ImGui.Dummy(new Vector2(5));
foreach (var settingsTabEntry in eligible)
{
settingsTabEntry.Draw();
ImGuiHelpers.ScaledDummy(3);
}
ImGui.Separator();
ImGui.Dummy(new Vector2(10));
}
if (!any)
ImGui.TextColored(ImGuiColors.DalamudGrey, "No results found..."u8);
ImGui.EndTabItem();
}
}
ImGui.EndTabBar();
}
ImGui.SetCursorPos(windowSize - ImGuiHelpers.ScaledVector2(70));
@ -198,88 +256,10 @@ internal sealed class SettingsWindow : Window
}
}
}
}
private void DrawTabs()
{
foreach (var settingsTab in this.tabs)
{
var flags = ImGuiTabItemFlags.NoCloseWithMiddleMouseButton;
if (this.setActiveTab == settingsTab)
{
flags |= ImGuiTabItemFlags.SetSelected;
this.setActiveTab = null;
}
using var tab = ImRaii.TabItem(settingsTab.Title, flags);
if (tab)
{
if (!settingsTab.IsOpen)
{
settingsTab.IsOpen = true;
settingsTab.OnOpen();
}
// Don't add padding for the About tab (credits)
{
using var padding = ImRaii.PushStyle(
ImGuiStyleVar.WindowPadding,
new Vector2(2, 2),
settingsTab is not SettingsTabAbout);
using var borderColor = ImRaii.PushColor(
ImGuiCol.Border,
ImGui.GetColorU32(ImGuiCol.ChildBg));
using var tabChild = ImRaii.Child(
$"###settings_scrolling_{settingsTab.Title}",
new Vector2(-1, -1),
true);
if (tabChild)
settingsTab.Draw();
}
settingsTab.PostDraw();
}
else if (settingsTab.IsOpen)
{
settingsTab.IsOpen = false;
settingsTab.OnClose();
}
}
}
private void DrawSearchResults()
{
using var tab = ImRaii.TabItem(Loc.Localize("DalamudSettingsSearchResults", "Search Results"));
if (!tab) return;
var any = false;
foreach (var settingsTab in this.tabs)
{
var eligible = settingsTab.Entries.Where(x => !x.Name.Key.IsNullOrEmpty() && x.Name.ToString().Contains(this.searchInput, StringComparison.InvariantCultureIgnoreCase));
if (!eligible.Any())
continue;
any |= true;
ImGui.TextColored(ImGuiColors.DalamudGrey, settingsTab.Title);
ImGui.Dummy(new Vector2(5));
foreach (var settingsTabEntry in eligible)
{
settingsTabEntry.Draw();
ImGuiHelpers.ScaledDummy(3);
}
ImGui.Separator();
ImGui.Dummy(new Vector2(10));
}
if (!any)
ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsNoSearchResultsFound", "No results found..."));
ImGui.SetCursorPos(new Vector2(windowSize.X - 250, ImGui.GetTextLineHeightWithSpacing() + (ImGui.GetStyle().FramePadding.Y * 2)));
ImGui.SetNextItemWidth(240);
ImGui.InputTextWithHint("###searchInput"u8, "Search for settings..."u8, ref this.searchInput, 100);
}
private void Save()
@ -321,9 +301,17 @@ internal sealed class SettingsWindow : Window
Service<InterfaceManager>.Get().RebuildFonts();
}
private void OnLocalizationChanged(string langCode)
private void SetOpenTab(SettingsOpenKind kind)
{
this.WindowName = Title;
this.setActiveTab = this.CurrentlyOpenTab;
this.setActiveTab = kind switch
{
SettingsOpenKind.General => this.tabs[0],
SettingsOpenKind.LookAndFeel => this.tabs[1],
SettingsOpenKind.AutoUpdates => this.tabs[2],
SettingsOpenKind.ServerInfoBar => this.tabs[3],
SettingsOpenKind.Experimental => this.tabs[4],
SettingsOpenKind.About => this.tabs[5],
_ => throw new ArgumentOutOfRangeException(nameof(kind), kind, null),
};
}
}

View file

@ -20,7 +20,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.UI;
namespace Dalamud.Interface.Internal.Windows.Settings.Tabs;
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")]
internal sealed class SettingsTabAbout : SettingsTab
public class SettingsTabAbout : SettingsTab
{
private const float CreditFps = 60.0f;
private const string ThankYouText = "Thank you!";
@ -209,12 +209,10 @@ Contribute at: https://github.com/goatcorp/Dalamud
.CreateFontAtlas(nameof(SettingsTabAbout), FontAtlasAutoRebuildMode.Async);
}
public override SettingsEntry[] Entries { get; } = { };
public override string Title => Loc.Localize("DalamudAbout", "About");
public override SettingsOpenKind Kind => SettingsOpenKind.About;
public override SettingsEntry[] Entries { get; } = [];
/// <inheritdoc/>
public override unsafe void OnOpen()
{
@ -289,7 +287,7 @@ Contribute at: https://github.com/goatcorp/Dalamud
var windowX = ImGui.GetWindowSize().X;
foreach (var creditsLine in this.creditsText.Split(["\r\n", "\r", "\n"], StringSplitOptions.None))
foreach (var creditsLine in this.creditsText.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None))
{
var lineLenX = ImGui.CalcTextSize(creditsLine).X;

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
@ -18,7 +18,7 @@ using Dalamud.Plugin.Internal.Types;
namespace Dalamud.Interface.Internal.Windows.Settings.Tabs;
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")]
internal sealed class SettingsTabAutoUpdates : SettingsTab
public class SettingsTabAutoUpdates : SettingsTab
{
private AutoUpdateBehavior behavior;
private bool updateDisabledPlugins;
@ -31,8 +31,6 @@ internal sealed class SettingsTabAutoUpdates : SettingsTab
public override string Title => Loc.Localize("DalamudSettingsAutoUpdates", "Auto-Updates");
public override SettingsOpenKind Kind => SettingsOpenKind.AutoUpdates;
public override void Draw()
{
ImGui.TextColoredWrapped(ImGuiColors.DalamudWhite, Loc.Localize("DalamudSettingsAutoUpdateHint",

View file

@ -14,19 +14,17 @@ using Dalamud.Interface.Utility;
namespace Dalamud.Interface.Internal.Windows.Settings.Tabs;
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")]
internal sealed class SettingsTabDtr : SettingsTab
public class SettingsTabDtr : SettingsTab
{
private List<string>? dtrOrder;
private List<string>? dtrIgnore;
private int dtrSpacing;
private bool dtrSwapDirection;
public override SettingsEntry[] Entries { get; } = Array.Empty<SettingsEntry>();
public override string Title => Loc.Localize("DalamudSettingsServerInfoBar", "Server Info Bar");
public override SettingsOpenKind Kind => SettingsOpenKind.ServerInfoBar;
public override SettingsEntry[] Entries { get; } = [];
public override void Draw()
{
ImGui.TextColoredWrapped(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingServerInfoBarHint", "Plugins can put additional information into your server information bar(where world & time can be seen).\nYou can reorder and disable these here."));
@ -127,8 +125,8 @@ internal sealed class SettingsTabDtr : SettingsTab
ImGui.GetIO().MousePos = moveMouseTo[moveMouseToIndex];
}
configuration.DtrOrder = [.. order, .. orderLeft];
configuration.DtrIgnore = [.. ignore, .. ignoreLeft];
configuration.DtrOrder = order.Concat(orderLeft).ToList();
configuration.DtrIgnore = ignore.Concat(ignoreLeft).ToList();
if (isOrderChange)
dtrBar.ApplySort();

View file

@ -6,11 +6,11 @@ using Dalamud.Bindings.ImGui;
using Dalamud.Configuration.Internal;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Internal.ReShadeHandling;
using Dalamud.Interface.Internal.Windows.PluginInstaller;
using Dalamud.Interface.Internal.Windows.Settings.Widgets;
using Dalamud.Interface.Utility;
using Dalamud.Plugin.Internal;
using Dalamud.Utility;
using Dalamud.Utility.Internal;
namespace Dalamud.Interface.Internal.Windows.Settings.Tabs;
@ -18,28 +18,33 @@ namespace Dalamud.Interface.Internal.Windows.Settings.Tabs;
"StyleCop.CSharp.DocumentationRules",
"SA1600:Elements should be documented",
Justification = "Internals")]
internal sealed class SettingsTabExperimental : SettingsTab
public class SettingsTabExperimental : SettingsTab
{
public override string Title => Loc.Localize("DalamudSettingsExperimental", "Experimental");
public override SettingsOpenKind Kind => SettingsOpenKind.Experimental;
public override SettingsEntry[] Entries { get; } =
[
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingsPluginTest", "Get plugin testing builds"),
LazyLoc.Localize("DalamudSettingsPluginTestHint", "Receive testing prereleases for selected plugins.\nTo opt-in to testing builds for a plugin, you have to right click it in the \"Installed Plugins\" tab of the plugin installer and select \"Receive plugin testing versions\"."),
Loc.Localize("DalamudSettingsPluginTest", "Get plugin testing builds"),
string.Format(
Loc.Localize(
"DalamudSettingsPluginTestHint",
"Receive testing prereleases for selected plugins.\nTo opt-in to testing builds for a plugin, you have to right click it in the \"{0}\" tab of the plugin installer and select \"{1}\"."),
PluginCategoryManager.Locs.Group_Installed,
PluginInstallerWindow.Locs.PluginContext_TestingOptIn),
c => c.DoPluginTest,
(v, c) => c.DoPluginTest = v),
new HintSettingsEntry(
LazyLoc.Localize("DalamudSettingsPluginTestWarning", "Testing plugins may contain bugs or crash your game. Please only enable this if you are aware of the risks."),
Loc.Localize(
"DalamudSettingsPluginTestWarning",
"Testing plugins may contain bugs or crash your game. Please only enable this if you are aware of the risks."),
ImGuiColors.DalamudRed),
new GapSettingsEntry(5),
new ButtonSettingsEntry(
LazyLoc.Localize("DalamudSettingsClearHidden", "Clear hidden plugins"),
LazyLoc.Localize("DalamudSettingsClearHiddenHint", "Restore plugins you have previously hidden from the plugin installer."),
Loc.Localize("DalamudSettingsClearHidden", "Clear hidden plugins"),
Loc.Localize(
"DalamudSettingsClearHiddenHint",
"Restore plugins you have previously hidden from the plugin installer."),
() =>
{
Service<DalamudConfiguration>.Get().HiddenPluginInternalName.Clear();
@ -51,16 +56,23 @@ internal sealed class SettingsTabExperimental : SettingsTab
new DevPluginsSettingsEntry(),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingEnableImGuiAsserts", "Enable ImGui asserts"),
LazyLoc.Localize("DalamudSettingEnableImGuiAssertsHint",
Loc.Localize(
"DalamudSettingEnableImGuiAsserts",
"Enable ImGui asserts"),
Loc.Localize(
"DalamudSettingEnableImGuiAssertsHint",
"If this setting is enabled, a window containing further details will be shown when an internal assertion in ImGui fails.\nWe recommend enabling this when developing plugins. " +
"This setting does not persist and will reset when the game restarts.\nUse the setting below to enable it at startup."),
c => Service<InterfaceManager>.Get().ShowAsserts,
(v, _) => Service<InterfaceManager>.Get().ShowAsserts = v),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingEnableImGuiAssertsAtStartup", "Always enable ImGui asserts at startup"),
LazyLoc.Localize("DalamudSettingEnableImGuiAssertsAtStartupHint", "This will enable ImGui asserts every time the game starts."),
Loc.Localize(
"DalamudSettingEnableImGuiAssertsAtStartup",
"Always enable ImGui asserts at startup"),
Loc.Localize(
"DalamudSettingEnableImGuiAssertsAtStartupHint",
"This will enable ImGui asserts every time the game starts."),
c => c.ImGuiAssertsEnabledAtStartup ?? false,
(v, c) => c.ImGuiAssertsEnabledAtStartup = v),
@ -71,8 +83,10 @@ internal sealed class SettingsTabExperimental : SettingsTab
new GapSettingsEntry(5, true),
new EnumSettingsEntry<ReShadeHandlingMode>(
LazyLoc.Localize("DalamudSettingsReShadeHandlingMode", "ReShade handling mode"),
LazyLoc.Localize("DalamudSettingsReShadeHandlingModeHint", "You may try different options to work around problems you may encounter.\nRestart is required for changes to take effect."),
Loc.Localize("DalamudSettingsReShadeHandlingMode", "ReShade handling mode"),
Loc.Localize(
"DalamudSettingsReShadeHandlingModeHint",
"You may try different options to work around problems you may encounter.\nRestart is required for changes to take effect."),
c => c.ReShadeHandlingMode,
(v, c) => c.ReShadeHandlingMode = v,
fallbackValue: ReShadeHandlingMode.Default,
@ -132,6 +146,8 @@ internal sealed class SettingsTabExperimental : SettingsTab
*/
];
public override string Title => Loc.Localize("DalamudSettingsExperimental", "Experimental");
public override void Draw()
{
base.Draw();

View file

@ -1,29 +1,23 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using CheapLoc;
using Dalamud.Game.Text;
using Dalamud.Interface.Internal.Windows.Settings.Widgets;
using Dalamud.Utility.Internal;
namespace Dalamud.Interface.Internal.Windows.Settings.Tabs;
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")]
internal sealed class SettingsTabGeneral : SettingsTab
public class SettingsTabGeneral : SettingsTab
{
public override string Title => Loc.Localize("DalamudSettingsGeneral", "General");
public override SettingsOpenKind Kind => SettingsOpenKind.General;
public override SettingsEntry[] Entries { get; } =
[
{
new LanguageChooserSettingsEntry(),
new GapSettingsEntry(5),
new EnumSettingsEntry<XivChatType>(
LazyLoc.Localize("DalamudSettingsChannel", "Dalamud Chat Channel"),
LazyLoc.Localize("DalamudSettingsChannelHint", "Select the chat channel that is to be used for general Dalamud messages."),
Loc.Localize("DalamudSettingsChannel", "Dalamud Chat Channel"),
Loc.Localize("DalamudSettingsChannelHint", "Select the chat channel that is to be used for general Dalamud messages."),
c => c.GeneralChatType,
(v, c) => c.GeneralChatType = v,
warning: v =>
@ -39,47 +33,49 @@ internal sealed class SettingsTabGeneral : SettingsTab
new GapSettingsEntry(5),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingsWaitForPluginsOnStartup", "Wait for plugins before game loads"),
LazyLoc.Localize("DalamudSettingsWaitForPluginsOnStartupHint", "Do not let the game load, until plugins are loaded."),
Loc.Localize("DalamudSettingsWaitForPluginsOnStartup", "Wait for plugins before game loads"),
Loc.Localize("DalamudSettingsWaitForPluginsOnStartupHint", "Do not let the game load, until plugins are loaded."),
c => c.IsResumeGameAfterPluginLoad,
(v, c) => c.IsResumeGameAfterPluginLoad = v),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingsFlash", "Flash FFXIV window on duty pop"),
LazyLoc.Localize("DalamudSettingsFlashHint", "Flash the FFXIV window in your task bar when a duty is ready."),
Loc.Localize("DalamudSettingsFlash", "Flash FFXIV window on duty pop"),
Loc.Localize("DalamudSettingsFlashHint", "Flash the FFXIV window in your task bar when a duty is ready."),
c => c.DutyFinderTaskbarFlash,
(v, c) => c.DutyFinderTaskbarFlash = v),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingsDutyFinderMessage", "Chatlog message on duty pop"),
LazyLoc.Localize("DalamudSettingsDutyFinderMessageHint", "Send a message in FFXIV chat when a duty is ready."),
Loc.Localize("DalamudSettingsDutyFinderMessage", "Chatlog message on duty pop"),
Loc.Localize("DalamudSettingsDutyFinderMessageHint", "Send a message in FFXIV chat when a duty is ready."),
c => c.DutyFinderChatMessage,
(v, c) => c.DutyFinderChatMessage = v),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingsPrintDalamudWelcomeMsg", "Display Dalamud's welcome message"),
LazyLoc.Localize("DalamudSettingsPrintDalamudWelcomeMsgHint", "Display Dalamud's welcome message in FFXIV chat when logging in with a character."),
Loc.Localize("DalamudSettingsPrintDalamudWelcomeMsg", "Display Dalamud's welcome message"),
Loc.Localize("DalamudSettingsPrintDalamudWelcomeMsgHint", "Display Dalamud's welcome message in FFXIV chat when logging in with a character."),
c => c.PrintDalamudWelcomeMsg,
(v, c) => c.PrintDalamudWelcomeMsg = v),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingsPrintPluginsWelcomeMsg", "Display loaded plugins in the welcome message"),
LazyLoc.Localize("DalamudSettingsPrintPluginsWelcomeMsgHint", "Display loaded plugins in FFXIV chat when logging in with a character."),
Loc.Localize("DalamudSettingsPrintPluginsWelcomeMsg", "Display loaded plugins in the welcome message"),
Loc.Localize("DalamudSettingsPrintPluginsWelcomeMsgHint", "Display loaded plugins in FFXIV chat when logging in with a character."),
c => c.PrintPluginsWelcomeMsg,
(v, c) => c.PrintPluginsWelcomeMsg = v),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingsSystemMenu", "Dalamud buttons in system menu"),
LazyLoc.Localize("DalamudSettingsSystemMenuMsgHint", "Add buttons for Dalamud plugins and settings to the system menu."),
Loc.Localize("DalamudSettingsSystemMenu", "Dalamud buttons in system menu"),
Loc.Localize("DalamudSettingsSystemMenuMsgHint", "Add buttons for Dalamud plugins and settings to the system menu."),
c => c.DoButtonsSystemMenu,
(v, c) => c.DoButtonsSystemMenu = v),
new GapSettingsEntry(5),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingDoMbCollect", "Anonymously upload market board data"),
LazyLoc.Localize("DalamudSettingDoMbCollectHint", "Anonymously provide data about in-game economics to Universalis when browsing the market board. This data can't be tied to you in any way and everyone benefits!"),
Loc.Localize("DalamudSettingDoMbCollect", "Anonymously upload market board data"),
Loc.Localize("DalamudSettingDoMbCollectHint", "Anonymously provide data about in-game economics to Universalis when browsing the market board. This data can't be tied to you in any way and everyone benefits!"),
c => c.IsMbCollect,
(v, c) => c.IsMbCollect = v),
];
};
public override string Title => Loc.Localize("DalamudSettingsGeneral", "General");
}

View file

@ -7,58 +7,54 @@ using CheapLoc;
using Dalamud.Bindings.ImGui;
using Dalamud.Configuration.Internal;
using Dalamud.Game;
using Dalamud.Game.Text;
using Dalamud.Interface.Colors;
using Dalamud.Interface.FontIdentifier;
using Dalamud.Interface.GameFonts;
using Dalamud.Interface.ImGuiFontChooserDialog;
using Dalamud.Interface.ImGuiNotification.Internal;
using Dalamud.Interface.Internal.Windows.PluginInstaller;
using Dalamud.Interface.Internal.Windows.Settings.Widgets;
using Dalamud.Interface.ManagedFontAtlas.Internals;
using Dalamud.Interface.Utility;
using Dalamud.Utility;
using Dalamud.Utility.Internal;
using Serilog;
namespace Dalamud.Interface.Internal.Windows.Settings.Tabs;
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")]
internal sealed class SettingsTabLook : SettingsTab
public class SettingsTabLook : SettingsTab
{
private static readonly (string, float)[] GlobalUiScalePresets =
[
{
("80%##DalamudSettingsGlobalUiScaleReset96", 0.8f),
("100%##DalamudSettingsGlobalUiScaleReset12", 1f),
("117%##DalamudSettingsGlobalUiScaleReset14", 14 / 12f),
("150%##DalamudSettingsGlobalUiScaleReset18", 1.5f),
("200%##DalamudSettingsGlobalUiScaleReset24", 2f),
("300%##DalamudSettingsGlobalUiScaleReset36", 3f),
];
};
private float globalUiScale;
private IFontSpec defaultFontSpec = null!;
public override string Title => Loc.Localize("DalamudSettingsVisual", "Look & Feel");
public override SettingsOpenKind Kind => SettingsOpenKind.LookAndFeel;
public override SettingsEntry[] Entries { get; } =
[
new GapSettingsEntry(5, true),
new ButtonSettingsEntry(
LazyLoc.Localize("DalamudSettingsOpenStyleEditor", "Open Style Editor"),
LazyLoc.Localize("DalamudSettingsStyleEditorHint", "Modify the look & feel of Dalamud windows."),
Loc.Localize("DalamudSettingsOpenStyleEditor", "Open Style Editor"),
Loc.Localize("DalamudSettingsStyleEditorHint", "Modify the look & feel of Dalamud windows."),
() => Service<DalamudInterface>.Get().OpenStyleEditor()),
new ButtonSettingsEntry(
LazyLoc.Localize("DalamudSettingsOpenNotificationEditor", "Modify Notification Position"),
LazyLoc.Localize("DalamudSettingsNotificationEditorHint", "Choose where Dalamud notifications appear on the screen."),
Loc.Localize("DalamudSettingsOpenNotificationEditor", "Modify Notification Position"),
Loc.Localize("DalamudSettingsNotificationEditorHint", "Choose where Dalamud notifications appear on the screen."),
() => Service<NotificationManager>.Get().StartPositionChooser()),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingsUseDarkMode", "Use Windows immersive/dark mode"),
LazyLoc.Localize("DalamudSettingsUseDarkModeHint", "This will cause the FFXIV window title bar to follow your preferred Windows color settings, and switch to dark mode if enabled."),
Loc.Localize("DalamudSettingsUseDarkMode", "Use Windows immersive/dark mode"),
Loc.Localize("DalamudSettingsUseDarkModeHint", "This will cause the FFXIV window title bar to follow your preferred Windows color settings, and switch to dark mode if enabled."),
c => c.WindowIsImmersive,
(v, c) => c.WindowIsImmersive = v,
b =>
@ -76,87 +72,91 @@ internal sealed class SettingsTabLook : SettingsTab
new GapSettingsEntry(5, true),
new HintSettingsEntry(LazyLoc.Localize("DalamudSettingToggleUiHideOptOutNote", "Plugins may independently opt out of the settings below.")),
new HintSettingsEntry(Loc.Localize("DalamudSettingToggleUiHideOptOutNote", "Plugins may independently opt out of the settings below.")),
new GapSettingsEntry(3),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingToggleUiHide", "Hide plugin UI when the game UI is toggled off"),
LazyLoc.Localize("DalamudSettingToggleUiHideHint", "Hide any open windows by plugins when toggling the game overlay."),
Loc.Localize("DalamudSettingToggleUiHide", "Hide plugin UI when the game UI is toggled off"),
Loc.Localize("DalamudSettingToggleUiHideHint", "Hide any open windows by plugins when toggling the game overlay."),
c => c.ToggleUiHide,
(v, c) => c.ToggleUiHide = v),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingToggleUiHideDuringCutscenes", "Hide plugin UI during cutscenes"),
LazyLoc.Localize("DalamudSettingToggleUiHideDuringCutscenesHint", "Hide any open windows by plugins during cutscenes."),
Loc.Localize("DalamudSettingToggleUiHideDuringCutscenes", "Hide plugin UI during cutscenes"),
Loc.Localize("DalamudSettingToggleUiHideDuringCutscenesHint", "Hide any open windows by plugins during cutscenes."),
c => c.ToggleUiHideDuringCutscenes,
(v, c) => c.ToggleUiHideDuringCutscenes = v),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingToggleUiHideDuringGpose", "Hide plugin UI while gpose is active"),
LazyLoc.Localize("DalamudSettingToggleUiHideDuringGposeHint", "Hide any open windows by plugins while gpose is active."),
Loc.Localize("DalamudSettingToggleUiHideDuringGpose", "Hide plugin UI while gpose is active"),
Loc.Localize("DalamudSettingToggleUiHideDuringGposeHint", "Hide any open windows by plugins while gpose is active."),
c => c.ToggleUiHideDuringGpose,
(v, c) => c.ToggleUiHideDuringGpose = v),
new GapSettingsEntry(5, true),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingToggleFocusManagement", "Use escape to close Dalamud windows"),
LazyLoc.Localize("DalamudSettingToggleFocusManagementHint", "This will cause Dalamud windows to behave like in-game windows when pressing escape.\nThey will close one after another until all are closed. May not work for all plugins."),
Loc.Localize("DalamudSettingToggleFocusManagement", "Use escape to close Dalamud windows"),
Loc.Localize("DalamudSettingToggleFocusManagementHint", "This will cause Dalamud windows to behave like in-game windows when pressing escape.\nThey will close one after another until all are closed. May not work for all plugins."),
c => c.IsFocusManagementEnabled,
(v, c) => c.IsFocusManagementEnabled = v),
// This is applied every frame in InterfaceManager::CheckViewportState()
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingToggleViewports", "Enable multi-monitor windows"),
LazyLoc.Localize("DalamudSettingToggleViewportsHint", "This will allow you move plugin windows onto other monitors.\nWill only work in Borderless Window or Windowed mode."),
Loc.Localize("DalamudSettingToggleViewports", "Enable multi-monitor windows"),
Loc.Localize("DalamudSettingToggleViewportsHint", "This will allow you move plugin windows onto other monitors.\nWill only work in Borderless Window or Windowed mode."),
c => !c.IsDisableViewport,
(v, c) => c.IsDisableViewport = !v),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingToggleDocking", "Enable window docking"),
LazyLoc.Localize("DalamudSettingToggleDockingHint", "This will allow you to fuse and tab plugin windows."),
Loc.Localize("DalamudSettingToggleDocking", "Enable window docking"),
Loc.Localize("DalamudSettingToggleDockingHint", "This will allow you to fuse and tab plugin windows."),
c => c.IsDocking,
(v, c) => c.IsDocking = v),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingEnablePluginUIAdditionalOptions", "Add a button to the title bar of plugin windows to open additional options"),
LazyLoc.Localize("DalamudSettingEnablePluginUIAdditionalOptionsHint", "This will allow you to pin certain plugin windows, make them clickthrough or adjust their opacity.\nThis may not be supported by all of your plugins. Contact the plugin author if you want them to support this feature."),
Loc.Localize(
"DalamudSettingEnablePluginUIAdditionalOptions",
"Add a button to the title bar of plugin windows to open additional options"),
Loc.Localize(
"DalamudSettingEnablePluginUIAdditionalOptionsHint",
"This will allow you to pin certain plugin windows, make them clickthrough or adjust their opacity.\nThis may not be supported by all of your plugins. Contact the plugin author if you want them to support this feature."),
c => c.EnablePluginUiAdditionalOptions,
(v, c) => c.EnablePluginUiAdditionalOptions = v),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingEnablePluginUISoundEffects", "Enable sound effects for plugin windows"),
LazyLoc.Localize("DalamudSettingEnablePluginUISoundEffectsHint", "This will allow you to enable or disable sound effects generated by plugin user interfaces.\nThis is affected by your in-game `System Sounds` volume settings."),
Loc.Localize("DalamudSettingEnablePluginUISoundEffects", "Enable sound effects for plugin windows"),
Loc.Localize("DalamudSettingEnablePluginUISoundEffectsHint", "This will allow you to enable or disable sound effects generated by plugin user interfaces.\nThis is affected by your in-game `System Sounds` volume settings."),
c => c.EnablePluginUISoundEffects,
(v, c) => c.EnablePluginUISoundEffects = v),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingToggleGamepadNavigation", "Control plugins via gamepad"),
LazyLoc.Localize("DalamudSettingToggleGamepadNavigationHint", "This will allow you to toggle between game and plugin navigation via L1+L3.\nToggle the PluginInstaller window via R3 if ImGui navigation is enabled."),
Loc.Localize("DalamudSettingToggleGamepadNavigation", "Control plugins via gamepad"),
Loc.Localize("DalamudSettingToggleGamepadNavigationHint", "This will allow you to toggle between game and plugin navigation via L1+L3.\nToggle the PluginInstaller window via R3 if ImGui navigation is enabled."),
c => c.IsGamepadNavigationEnabled,
(v, c) => c.IsGamepadNavigationEnabled = v),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingToggleTsm", "Show title screen menu"),
LazyLoc.Localize("DalamudSettingToggleTsmHint", "This will allow you to access certain Dalamud and Plugin functionality from the title screen.\nDisabling this will also hide the Dalamud version text on the title screen."),
Loc.Localize("DalamudSettingToggleTsm", "Show title screen menu"),
Loc.Localize("DalamudSettingToggleTsmHint", "This will allow you to access certain Dalamud and Plugin functionality from the title screen.\nDisabling this will also hide the Dalamud version text on the title screen."),
c => c.ShowTsm,
(v, c) => c.ShowTsm = v),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingInstallerOpenDefault", "Open the Plugin Installer to the \"Installed Plugins\" tab by default"),
LazyLoc.Localize("DalamudSettingInstallerOpenDefaultHint", "This will allow you to open the Plugin Installer to the \"Installed Plugins\" tab by default, instead of the \"Available Plugins\" tab."),
Loc.Localize("DalamudSettingInstallerOpenDefault", "Open the Plugin Installer to the \"Installed Plugins\" tab by default"),
Loc.Localize("DalamudSettingInstallerOpenDefaultHint", "This will allow you to open the Plugin Installer to the \"Installed Plugins\" tab by default, instead of the \"Available Plugins\" tab."),
c => c.PluginInstallerOpen == PluginInstallerOpenKind.InstalledPlugins,
(v, c) => c.PluginInstallerOpen = v ? PluginInstallerOpenKind.InstalledPlugins : PluginInstallerOpenKind.AllPlugins),
new SettingsEntry<bool>(
LazyLoc.Localize("DalamudSettingReducedMotion", "Reduce motions"),
LazyLoc.Localize("DalamudSettingReducedMotionHint", "This will suppress certain animations from Dalamud, such as the notification popup."),
Loc.Localize("DalamudSettingReducedMotion", "Reduce motions"),
Loc.Localize("DalamudSettingReducedMotionHint", "This will suppress certain animations from Dalamud, such as the notification popup."),
c => c.ReduceMotions ?? false,
(v, c) => c.ReduceMotions = v),
new SettingsEntry<float>(
LazyLoc.Localize("DalamudSettingImeStateIndicatorOpacity", "IME State Indicator Opacity (CJK only)"),
LazyLoc.Localize("DalamudSettingImeStateIndicatorOpacityHint", "When any of CJK IMEs is in use, the state of IME will be shown with the opacity specified here."),
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)
{
@ -176,6 +176,8 @@ internal sealed class SettingsTabLook : SettingsTab
}
];
public override string Title => Loc.Localize("DalamudSettingsVisual", "Look & Feel");
public override void Draw()
{
var interfaceManager = Service<InterfaceManager>.Get();
@ -210,7 +212,7 @@ internal sealed class SettingsTabLook : SettingsTab
var p = stackalloc byte[len];
Encoding.UTF8.GetBytes(buildingFonts, new(p, len));
ImGui.Text(
new ReadOnlySpan<byte>(p, len)[..(len + (Environment.TickCount / 200 % 3) - 2)]);
new ReadOnlySpan<byte>(p, len)[..((len + ((Environment.TickCount / 200) % 3)) - 2)]);
}
}

View file

@ -1,18 +1,18 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Colors;
using Dalamud.Utility.Internal;
using Dalamud.Interface.Utility;
namespace Dalamud.Interface.Internal.Windows.Settings.Widgets;
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")]
internal sealed class ButtonSettingsEntry : SettingsEntry
public class ButtonSettingsEntry : SettingsEntry
{
private readonly LazyLoc description;
private readonly string description;
private readonly Action runs;
public ButtonSettingsEntry(LazyLoc name, LazyLoc description, Action runs)
public ButtonSettingsEntry(string name, string description, Action runs)
{
this.description = description;
this.runs = runs;

View file

@ -6,7 +6,6 @@ using System.Numerics;
using System.Threading.Tasks;
using CheapLoc;
using Dalamud.Bindings.ImGui;
using Dalamud.Configuration;
using Dalamud.Configuration.Internal;
@ -16,14 +15,13 @@ using Dalamud.Interface.ImGuiFileDialog;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Plugin.Internal;
using Dalamud.Utility.Internal;
namespace Dalamud.Interface.Internal.Windows.Settings.Widgets;
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")]
internal sealed class DevPluginsSettingsEntry : SettingsEntry
public class DevPluginsSettingsEntry : SettingsEntry
{
private List<DevPluginLocationSettings> devPluginLocations = [];
private List<DevPluginLocationSettings> devPluginLocations = new();
private bool devPluginLocationsChanged;
private string devPluginTempLocation = string.Empty;
private string devPluginLocationAddError = string.Empty;
@ -31,25 +29,25 @@ internal sealed class DevPluginsSettingsEntry : SettingsEntry
public DevPluginsSettingsEntry()
{
this.Name = LazyLoc.Localize("DalamudSettingsDevPluginLocation", "Dev Plugin Locations");
this.Name = Loc.Localize("DalamudSettingsDevPluginLocation", "Dev Plugin Locations");
}
public override void OnClose()
{
this.devPluginLocations =
[.. Service<DalamudConfiguration>.Get().DevPluginLoadLocations.Select(x => x.Clone())];
Service<DalamudConfiguration>.Get().DevPluginLoadLocations.Select(x => x.Clone()).ToList();
}
public override void Load()
{
this.devPluginLocations =
[.. Service<DalamudConfiguration>.Get().DevPluginLoadLocations.Select(x => x.Clone())];
Service<DalamudConfiguration>.Get().DevPluginLoadLocations.Select(x => x.Clone()).ToList();
this.devPluginLocationsChanged = false;
}
public override void Save()
{
Service<DalamudConfiguration>.Get().DevPluginLoadLocations = [.. this.devPluginLocations.Select(x => x.Clone())];
Service<DalamudConfiguration>.Get().DevPluginLoadLocations = this.devPluginLocations.Select(x => x.Clone()).ToList();
if (this.devPluginLocationsChanged)
{
@ -61,9 +59,7 @@ internal sealed class DevPluginsSettingsEntry : SettingsEntry
public override void Draw()
{
using var id = ImRaii.PushId("devPluginLocation"u8);
ImGui.Text(this.Name);
if (this.devPluginLocationsChanged)
{
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.HealerGreen))

View file

@ -1,15 +1,12 @@
using System.Diagnostics;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using CheapLoc;
using Dalamud.Bindings.ImGui;
using Dalamud.Configuration.Internal;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Utility.Internal;
namespace Dalamud.Interface.Internal.Windows.Settings.Widgets;
@ -26,8 +23,8 @@ internal sealed class EnumSettingsEntry<T> : SettingsEntry
private T valueBacking;
public EnumSettingsEntry(
LazyLoc name,
LazyLoc description,
string name,
string description,
LoadSettingDelegate load,
SaveSettingDelegate save,
Action<T>? change = null,
@ -64,7 +61,7 @@ internal sealed class EnumSettingsEntry<T> : SettingsEntry
}
}
public LazyLoc Description { get; }
public string Description { get; }
public Action<EnumSettingsEntry<T>>? CustomDraw { get; init; }
@ -82,10 +79,7 @@ internal sealed class EnumSettingsEntry<T> : SettingsEntry
public override void Draw()
{
var name = this.Name.ToString();
var description = this.Description.ToString();
Debug.Assert(!string.IsNullOrWhiteSpace(name), "Name is empty");
Debug.Assert(this.Name != null, "this.Name != null");
if (this.CustomDraw is not null)
{
@ -93,7 +87,7 @@ internal sealed class EnumSettingsEntry<T> : SettingsEntry
}
else
{
ImGui.TextWrapped(name);
ImGui.TextWrapped(this.Name);
var idx = this.valueBacking;
var values = Enum.GetValues<T>();
@ -123,14 +117,13 @@ internal sealed class EnumSettingsEntry<T> : SettingsEntry
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudGrey))
{
var desc = this.FriendlyEnumDescriptionGetter(this.valueBacking);
if (!string.IsNullOrWhiteSpace(desc))
{
ImGui.TextWrapped(desc);
ImGuiHelpers.ScaledDummy(2);
}
ImGui.TextWrapped(description);
ImGui.TextWrapped(this.Description);
}
if (this.CheckValidity != null)

View file

@ -1,4 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility;
@ -6,7 +6,7 @@ using Dalamud.Interface.Utility;
namespace Dalamud.Interface.Internal.Windows.Settings.Widgets;
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")]
internal sealed class GapSettingsEntry : SettingsEntry
public sealed class GapSettingsEntry : SettingsEntry
{
private readonly float size;
private readonly bool hr;

View file

@ -3,17 +3,16 @@ using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Colors;
using Dalamud.Utility.Internal;
namespace Dalamud.Interface.Internal.Windows.Settings.Widgets;
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")]
internal sealed class HintSettingsEntry : SettingsEntry
public class HintSettingsEntry : SettingsEntry
{
private readonly LazyLoc text;
private readonly string text;
private readonly Vector4 color;
public HintSettingsEntry(LazyLoc text, Vector4? color = null)
public HintSettingsEntry(string text, Vector4? color = null)
{
this.text = text;
this.color = color ?? ImGuiColors.DalamudGrey;

View file

@ -3,16 +3,15 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using CheapLoc;
using Dalamud.Bindings.ImGui;
using Dalamud.Configuration.Internal;
using Dalamud.Interface.Colors;
using Dalamud.Utility.Internal;
using Dalamud.Interface.Utility;
namespace Dalamud.Interface.Internal.Windows.Settings.Widgets;
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")]
internal sealed class LanguageChooserSettingsEntry : SettingsEntry
public sealed class LanguageChooserSettingsEntry : SettingsEntry
{
private readonly string[] languages;
private readonly string[] locLanguages;
@ -21,9 +20,9 @@ internal sealed class LanguageChooserSettingsEntry : SettingsEntry
public LanguageChooserSettingsEntry()
{
this.languages = [.. Localization.ApplicableLangCodes.Prepend("en")];
this.languages = Localization.ApplicableLangCodes.Prepend("en").ToArray();
this.Name = LazyLoc.Localize("DalamudSettingsLanguage", "Language");
this.Name = Loc.Localize("DalamudSettingsLanguage", "Language");
this.IsValid = true;
this.IsVisible = true;
@ -47,7 +46,7 @@ internal sealed class LanguageChooserSettingsEntry : SettingsEntry
}
}
this.locLanguages = [.. locLanguagesList];
this.locLanguages = locLanguagesList.ToArray();
}
catch (Exception)
{

View file

@ -1,12 +1,12 @@
using System.Diagnostics;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Dalamud.Bindings.ImGui;
using Dalamud.Configuration.Internal;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Utility.Internal;
namespace Dalamud.Interface.Internal.Windows.Settings.Widgets;
@ -20,8 +20,8 @@ internal sealed class SettingsEntry<T> : SettingsEntry
private object? valueBacking;
public SettingsEntry(
LazyLoc name,
LazyLoc description,
string name,
string description,
LoadSettingDelegate load,
SaveSettingDelegate save,
Action<T?>? change = null,
@ -55,7 +55,7 @@ internal sealed class SettingsEntry<T> : SettingsEntry
}
}
public LazyLoc Description { get; }
public string Description { get; }
public Action<SettingsEntry<T>>? CustomDraw { get; init; }
@ -69,10 +69,7 @@ internal sealed class SettingsEntry<T> : SettingsEntry
public override void Draw()
{
var name = this.Name.ToString();
var description = this.Description.ToString();
Debug.Assert(!string.IsNullOrWhiteSpace(name), "Name is empty");
Debug.Assert(this.Name != null, "this.Name != null");
var type = typeof(T);
@ -82,7 +79,7 @@ internal sealed class SettingsEntry<T> : SettingsEntry
}
else if (type == typeof(DirectoryInfo))
{
ImGui.TextWrapped(name);
ImGui.TextWrapped(this.Name);
var value = this.Value as DirectoryInfo;
var nativeBuffer = value?.FullName ?? string.Empty;
@ -94,7 +91,7 @@ internal sealed class SettingsEntry<T> : SettingsEntry
}
else if (type == typeof(string))
{
ImGui.TextWrapped(name);
ImGui.TextWrapped(this.Name);
var nativeBuffer = this.Value as string ?? string.Empty;
@ -107,19 +104,16 @@ internal sealed class SettingsEntry<T> : SettingsEntry
{
var nativeValue = this.Value as bool? ?? false;
if (ImGui.Checkbox($"{name}###{this.Id.ToString()}", ref nativeValue))
if (ImGui.Checkbox($"{this.Name}###{this.Id.ToString()}", ref nativeValue))
{
this.valueBacking = nativeValue;
this.change?.Invoke(this.Value);
}
}
if (!string.IsNullOrWhiteSpace(description))
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudGrey))
{
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudGrey))
{
ImGui.TextWrapped(this.Description);
}
ImGui.TextWrapped(this.Description);
}
if (this.CheckValidity != null)

View file

@ -18,9 +18,9 @@ using Dalamud.Utility;
namespace Dalamud.Interface.Internal.Windows.Settings.Widgets;
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")]
internal class ThirdRepoSettingsEntry : SettingsEntry
public class ThirdRepoSettingsEntry : SettingsEntry
{
private List<ThirdPartyRepoSettings> thirdRepoList = [];
private List<ThirdPartyRepoSettings> thirdRepoList = new();
private bool thirdRepoListChanged;
private string thirdRepoTempUrl = string.Empty;
private string thirdRepoAddError = string.Empty;
@ -34,20 +34,20 @@ internal class ThirdRepoSettingsEntry : SettingsEntry
public override void OnClose()
{
this.thirdRepoList =
[.. Service<DalamudConfiguration>.Get().ThirdRepoList.Select(x => x.Clone())];
Service<DalamudConfiguration>.Get().ThirdRepoList.Select(x => x.Clone()).ToList();
}
public override void Load()
{
this.thirdRepoList =
[.. Service<DalamudConfiguration>.Get().ThirdRepoList.Select(x => x.Clone())];
Service<DalamudConfiguration>.Get().ThirdRepoList.Select(x => x.Clone()).ToList();
this.thirdRepoListChanged = false;
}
public override void Save()
{
Service<DalamudConfiguration>.Get().ThirdRepoList =
[.. this.thirdRepoList.Select(x => x.Clone())];
this.thirdRepoList.Select(x => x.Clone()).ToList();
if (this.thirdRepoListChanged)
{

View file

@ -112,15 +112,27 @@ internal class ServiceContainer : IServiceProvider, IServiceType
errorStep = "property injection";
await this.InjectProperties(instance, scopedObjects, scope);
// Invoke ctor from a separate thread (LongRunning will spawn a new one)
// so that it does not count towards thread pool active threads cap.
// Plugin ctor can block to wait for Tasks, as we currently do not support asynchronous plugin init.
errorStep = "ctor invocation";
await Task.Factory.StartNew(
() => ctor.Invoke(instance, resolvedParams),
CancellationToken.None,
TaskCreationOptions.LongRunning,
TaskScheduler.Default).ConfigureAwait(false);
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
var thr = new Thread(
() =>
{
try
{
ctor.Invoke(instance, resolvedParams);
}
catch (Exception e)
{
tcs.SetException(e);
return;
}
tcs.SetResult();
});
thr.Start();
await tcs.Task.ConfigureAwait(false);
thr.Join();
return instance;
}

View file

@ -112,14 +112,11 @@ public class Localization : IServiceType
}
/// <summary>
/// Set up the UI language with "fallbacks" (original English text).
/// Set up the UI language with "fallbacks"(original English text).
/// </summary>
public void SetupWithFallbacks()
{
this.DalamudLanguageCultureInfo = CultureInfo.InvariantCulture;
Loc.SetupWithFallbacks(this.assembly);
foreach (var d in Delegate.EnumerateInvocationList(this.LocalizationChanged))
{
try
@ -131,6 +128,8 @@ public class Localization : IServiceType
Log.Error(ex, "Exception during raise of {handler}", d.Method);
}
}
Loc.SetupWithFallbacks(this.assembly);
}
/// <summary>
@ -146,17 +145,6 @@ public class Localization : IServiceType
}
this.DalamudLanguageCultureInfo = GetCultureInfoFromLangCode(langCode);
try
{
Loc.Setup(this.ReadLocData(langCode), this.assembly);
}
catch (Exception ex)
{
Log.Error(ex, "Could not load loc {0}. Setting up fallbacks.", langCode);
this.SetupWithFallbacks();
}
foreach (var d in Delegate.EnumerateInvocationList(this.LocalizationChanged))
{
try
@ -168,6 +156,16 @@ public class Localization : IServiceType
Log.Error(ex, "Exception during raise of {handler}", d.Method);
}
}
try
{
Loc.Setup(this.ReadLocData(langCode), this.assembly);
}
catch (Exception ex)
{
Log.Error(ex, "Could not load loc {0}. Setting up fallbacks.", langCode);
this.SetupWithFallbacks();
}
}
/// <summary>

View file

@ -1,5 +1,4 @@
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.SubKinds;
@ -30,26 +29,11 @@ public interface IClientState
/// <param name="code">The success/failure code.</param>
public delegate void LogoutDelegate(int type, int code);
/// <summary>
/// Event that gets fired when the game initializes a zone.
/// </summary>
public event Action<ZoneInitEventArgs> ZoneInit;
/// <summary>
/// Event that gets fired when the current Territory changes.
/// </summary>
public event Action<ushort> TerritoryChanged;
/// <summary>
/// Event that gets fired when the current Map changes.
/// </summary>
public event Action<uint> MapIdChanged;
/// <summary>
/// Event that gets fired when the current zone Instance changes.
/// </summary>
public event Action<uint> InstanceChanged;
/// <summary>
/// Event that fires when a characters ClassJob changed.
/// </summary>
@ -101,11 +85,6 @@ public interface IClientState
/// </summary>
public uint MapId { get; }
/// <summary>
/// Gets the instance number of the current zone, used when multiple copies of an area are active.
/// </summary>
public uint Instance { get; }
/// <summary>
/// Gets the local player character, if one is present.
/// </summary>

View file

@ -1,31 +0,0 @@
using CheapLoc;
using Dalamud.Bindings.ImGui;
namespace Dalamud.Utility.Internal;
/// <summary>
/// Represents a lazily localized string, consisting of a localization key and a fallback value.
/// </summary>
/// <param name="Key">The localization key used to retrieve the localized value.</param>
/// <param name="Fallback">The fallback text to use if the localization key is not found.</param>
internal readonly record struct LazyLoc(string Key, string Fallback)
{
public static implicit operator ImU8String(LazyLoc locRef)
=> new(locRef.ToString());
/// <summary>
/// Creates a new instance of <see cref="LazyLoc"/> with the specified localization key and fallback text.
/// </summary>
/// <param name="key">The localization key used to retrieve the localized value.</param>
/// <param name="fallback">The fallback text to use if the localization key is not found.</param>
/// <returns>A <see cref="LazyLoc"/> instance representing the localized value.</returns>
public static LazyLoc Localize(string key, string fallback)
=> new(key, fallback);
/// <inheritdoc/>
public override string ToString()
{
return Loc.Localize(this.Key, this.Fallback);
}
}

View file

@ -753,7 +753,7 @@ public static partial class Util
$"{actor.Address.ToInt64():X}:{actor.GameObjectId:X}[{tag}] - {actor.ObjectKind} - {actor.Name} - X{actor.Position.X} Y{actor.Position.Y} Z{actor.Position.Z} D{actor.YalmDistanceX} R{actor.Rotation} - Target: {actor.TargetObjectId:X}\n";
if (actor is Npc npc)
actorString += $" BaseId: {npc.BaseId} NameId:{npc.NameId}\n";
actorString += $" DataId: {npc.DataId} NameId:{npc.NameId}\n";
if (actor is ICharacter chara)
{
@ -787,7 +787,7 @@ public static partial class Util
$"{actor.Address.ToInt64():X}:{actor.GameObjectId:X}[{tag}] - {actor.ObjectKind} - {actor.Name} - X{actor.Position.X} Y{actor.Position.Y} Z{actor.Position.Z} D{actor.YalmDistanceX} R{actor.Rotation} - Target: {actor.TargetObjectId:X}\n";
if (actor is Npc npc)
actorString += $" BaseId: {npc.BaseId} NameId:{npc.NameId}\n";
actorString += $" DataId: {npc.DataId} NameId:{npc.NameId}\n";
if (actor is Character chara)
{

View file

@ -301,7 +301,7 @@ public ref struct ImU8String
{
var startingPos = this.Length;
this.AppendFormatted(value, format);
this.FixAlignment(startingPos, alignment);
FixAlignment(startingPos, alignment);
}
public void AppendFormatted(ReadOnlySpan<char> value) => this.AppendFormatted(value, null);
@ -322,7 +322,7 @@ public ref struct ImU8String
{
var startingPos = this.Length;
this.AppendFormatted(value, format);
this.FixAlignment(startingPos, alignment);
FixAlignment(startingPos, alignment);
}
public void AppendFormatted(object? value) => this.AppendFormatted<object>(value!);
@ -358,13 +358,13 @@ public ref struct ImU8String
{
var startingPos = this.Length;
this.AppendFormatted(value, format);
this.FixAlignment(startingPos, alignment);
FixAlignment(startingPos, alignment);
}
public void Reserve(int length)
{
if (length >= AllocFreeBufferSize)
this.IncreaseBuffer(out _, length);
IncreaseBuffer(out _, length);
}
private void FixAlignment(int startingPos, int alignment)

@ -1 +1 @@
Subproject commit c25482543246de695146c55ad7d571deca650919
Subproject commit ce64fdfe80e387289b46037deb7de6c514ed8592