From 5c7a5295d119c65510f9cc0b3fb4b8c4e418ea20 Mon Sep 17 00:00:00 2001 From: MidoriKami <9083275+MidoriKami@users.noreply.github.com> Date: Tue, 27 Jan 2026 13:49:35 -0800 Subject: [PATCH] Misc Fixes (#2584) * Disable default logging, remove log message * Add IDtrBarEntry.MinimumWidth * Fix Addon/Agent Lifecycle Register/Unregister * Rename Agent.ReceiveEvent2 * Add to IReadOnlyDtrBarEntry * Fix autoformat being terrible * More style fixes * Add focused changed lifecycle event * Fix for obsolete renames --- .../AddonArgTypes/AddonFocusChangedArgs.cs | 22 ++++ Dalamud/Game/Addon/Lifecycle/AddonArgsType.cs | 5 + Dalamud/Game/Addon/Lifecycle/AddonEvent.cs | 10 ++ .../Game/Addon/Lifecycle/AddonLifecycle.cs | 97 ++++++++++------ .../Game/Addon/Lifecycle/AddonVirtualTable.cs | 34 ++++++ Dalamud/Game/Agent/AgentEvent.cs | 4 +- Dalamud/Game/Agent/AgentLifecycle.cs | 105 +++++++++++------- Dalamud/Game/Agent/AgentVirtualTable.cs | 20 ++-- Dalamud/Game/Gui/Dtr/DtrBar.cs | 10 +- Dalamud/Game/Gui/Dtr/DtrBarEntry.cs | 29 +++++ 10 files changed, 245 insertions(+), 91 deletions(-) create mode 100644 Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonFocusChangedArgs.cs diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonFocusChangedArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonFocusChangedArgs.cs new file mode 100644 index 000000000..8936a233b --- /dev/null +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonFocusChangedArgs.cs @@ -0,0 +1,22 @@ +namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; + +/// +/// Addon argument data for OnFocusChanged events. +/// +public class AddonFocusChangedArgs : AddonArgs +{ + /// + /// Initializes a new instance of the class. + /// + internal AddonFocusChangedArgs() + { + } + + /// + public override AddonArgsType Type => AddonArgsType.FocusChanged; + + /// + /// Gets or sets a value indicating whether the window is being focused or unfocused. + /// + public bool ShouldFocus { get; set; } +} diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgsType.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgsType.cs index 46ee479ac..bc48eeed0 100644 --- a/Dalamud/Game/Addon/Lifecycle/AddonArgsType.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgsType.cs @@ -44,4 +44,9 @@ public enum AddonArgsType /// Contains argument data for Close. /// Close, + + /// + /// Contains argument data for OnFocusChanged. + /// + FocusChanged, } diff --git a/Dalamud/Game/Addon/Lifecycle/AddonEvent.cs b/Dalamud/Game/Addon/Lifecycle/AddonEvent.cs index 3b9c6e867..74c84d754 100644 --- a/Dalamud/Game/Addon/Lifecycle/AddonEvent.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonEvent.cs @@ -203,4 +203,14 @@ public enum AddonEvent /// Be aware this is only called for certain popup windows, it is not triggered when clicking on windows. /// PostFocus, + + /// + /// An event that is fired before an addon processes its FocusChanged method. + /// + PreFocusChanged, + + /// + /// An event that is fired after a addon processes its FocusChanged method. + /// + PostFocusChanged, } diff --git a/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs b/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs index c70c0c10f..6520ee4cf 100644 --- a/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs @@ -31,7 +31,7 @@ internal unsafe class AddonLifecycle : IInternalDisposableService private readonly Framework framework = Service.Get(); private Hook? onInitializeAddonHook; - private bool isInvokingListeners = false; + private bool isInvokingListeners; [ServiceManager.ServiceConstructor] private AddonLifecycle() @@ -56,29 +56,36 @@ internal unsafe class AddonLifecycle : IInternalDisposableService AllocatedTables.Clear(); } + /// + /// Resolves a virtual table address to the original virtual table address. + /// + /// The modified address to resolve. + /// The original address. + internal static AtkUnitBase.AtkUnitBaseVirtualTable* GetOriginalVirtualTable(AtkUnitBase.AtkUnitBaseVirtualTable* tableAddress) + { + var matchedTable = AllocatedTables.FirstOrDefault(table => table.ModifiedVirtualTable == tableAddress); + if (matchedTable == null) + { + return null; + } + + return matchedTable.OriginalVirtualTable; + } + /// /// Register a listener for the target event and addon. /// /// The listener to register. internal void RegisterListener(AddonLifecycleEventListener listener) { - this.framework.RunOnTick(() => + if (this.isInvokingListeners) { - if (!this.EventListeners.ContainsKey(listener.EventType)) - { - if (!this.EventListeners.TryAdd(listener.EventType, [])) - return; - } - - // Note: string.Empty is a valid addon name, as that will trigger on any addon for this event type - if (!this.EventListeners[listener.EventType].ContainsKey(listener.AddonName)) - { - if (!this.EventListeners[listener.EventType].TryAdd(listener.AddonName, [])) - return; - } - - this.EventListeners[listener.EventType][listener.AddonName].Add(listener); - }, delayTicks: this.isInvokingListeners ? 1 : 0); + this.framework.RunOnTick(() => this.RegisterListenerMethod(listener)); + } + else + { + this.framework.RunOnFrameworkThread(() => this.RegisterListenerMethod(listener)); + } } /// @@ -87,16 +94,14 @@ internal unsafe class AddonLifecycle : IInternalDisposableService /// The listener to unregister. internal void UnregisterListener(AddonLifecycleEventListener listener) { - this.framework.RunOnTick(() => + if (this.isInvokingListeners) { - if (this.EventListeners.TryGetValue(listener.EventType, out var addonListeners)) - { - if (addonListeners.TryGetValue(listener.AddonName, out var addonListener)) - { - addonListener.Remove(listener); - } - } - }, delayTicks: this.isInvokingListeners ? 1 : 0); + this.framework.RunOnTick(() => this.UnregisterListenerMethod(listener)); + } + else + { + this.framework.RunOnFrameworkThread(() => this.UnregisterListenerMethod(listener)); + } } /// @@ -147,17 +152,37 @@ internal unsafe class AddonLifecycle : IInternalDisposableService this.isInvokingListeners = false; } - /// - /// Resolves a virtual table address to the original virtual table address. - /// - /// The modified address to resolve. - /// The original address. - internal AtkUnitBase.AtkUnitBaseVirtualTable* GetOriginalVirtualTable(AtkUnitBase.AtkUnitBaseVirtualTable* tableAddress) + private void RegisterListenerMethod(AddonLifecycleEventListener listener) { - var matchedTable = AllocatedTables.FirstOrDefault(table => table.ModifiedVirtualTable == tableAddress); - if (matchedTable == null) return null; + if (!this.EventListeners.ContainsKey(listener.EventType)) + { + if (!this.EventListeners.TryAdd(listener.EventType, [])) + { + return; + } + } - return matchedTable.OriginalVirtualTable; + // Note: string.Empty is a valid addon name, as that will trigger on any addon for this event type + if (!this.EventListeners[listener.EventType].ContainsKey(listener.AddonName)) + { + if (!this.EventListeners[listener.EventType].TryAdd(listener.AddonName, [])) + { + return; + } + } + + this.EventListeners[listener.EventType][listener.AddonName].Add(listener); + } + + private void UnregisterListenerMethod(AddonLifecycleEventListener listener) + { + if (this.EventListeners.TryGetValue(listener.EventType, out var addonListeners)) + { + if (addonListeners.TryGetValue(listener.AddonName, out var addonListener)) + { + addonListener.Remove(listener); + } + } } private void OnAddonInitialize(AtkUnitBase* addon) @@ -277,5 +302,5 @@ internal class AddonLifecyclePluginScoped : IInternalDisposableService, IAddonLi /// public unsafe nint GetOriginalVirtualTable(nint virtualTableAddress) - => (nint)this.addonLifecycleService.GetOriginalVirtualTable((AtkUnitBase.AtkUnitBaseVirtualTable*)virtualTableAddress); + => (nint)AddonLifecycle.GetOriginalVirtualTable((AtkUnitBase.AtkUnitBaseVirtualTable*)virtualTableAddress); } diff --git a/Dalamud/Game/Addon/Lifecycle/AddonVirtualTable.cs b/Dalamud/Game/Addon/Lifecycle/AddonVirtualTable.cs index 736415738..1b2c828f8 100644 --- a/Dalamud/Game/Addon/Lifecycle/AddonVirtualTable.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonVirtualTable.cs @@ -42,6 +42,7 @@ internal unsafe class AddonVirtualTable : IDisposable private readonly AddonArgs onMouseOverArgs = new(); private readonly AddonArgs onMouseOutArgs = new(); private readonly AddonArgs focusArgs = new(); + private readonly AddonFocusChangedArgs focusChangedArgs = new(); private readonly AtkUnitBase* atkUnitBase; @@ -63,6 +64,7 @@ internal unsafe class AddonVirtualTable : IDisposable private readonly AtkUnitBase.Delegates.OnMouseOver onMouseOverFunction; private readonly AtkUnitBase.Delegates.OnMouseOut onMouseOutFunction; private readonly AtkUnitBase.Delegates.Focus focusFunction; + private readonly AtkUnitBase.Delegates.OnFocusChange onFocusChangeFunction; /// /// Initializes a new instance of the class. @@ -103,6 +105,7 @@ internal unsafe class AddonVirtualTable : IDisposable this.onMouseOverFunction = this.OnAddonMouseOver; this.onMouseOutFunction = this.OnAddonMouseOut; this.focusFunction = this.OnAddonFocus; + this.onFocusChangeFunction = this.OnAddonFocusChange; // Overwrite specific virtual table entries this.ModifiedVirtualTable->Dtor = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.destructorFunction); @@ -121,6 +124,7 @@ internal unsafe class AddonVirtualTable : IDisposable this.ModifiedVirtualTable->OnMouseOver = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.onMouseOverFunction); this.ModifiedVirtualTable->OnMouseOut = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.onMouseOutFunction); this.ModifiedVirtualTable->Focus = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.focusFunction); + this.ModifiedVirtualTable->OnFocusChange = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.onFocusChangeFunction); } /// @@ -630,6 +634,36 @@ internal unsafe class AddonVirtualTable : IDisposable } } + private void OnAddonFocusChange(AtkUnitBase* thisPtr, bool isFocused) + { + try + { + this.LogEvent(EnableLogging); + + this.focusChangedArgs.Addon = thisPtr; + this.focusChangedArgs.ShouldFocus = isFocused; + + this.lifecycleService.InvokeListenersSafely(AddonEvent.PreFocusChanged, this.focusChangedArgs); + + isFocused = this.focusChangedArgs.ShouldFocus; + + try + { + this.OriginalVirtualTable->OnFocusChange(thisPtr, isFocused); + } + catch (Exception e) + { + Log.Error(e, "Caught exception when calling original Addon OnFocusChanged. This may be a bug in the game or another plugin hooking this method."); + } + + this.lifecycleService.InvokeListenersSafely(AddonEvent.PostFocusChanged, this.focusChangedArgs); + } + catch (Exception e) + { + Log.Error(e, "Caught exception from Dalamud when attempting to process OnAddonFocusChange."); + } + } + [Conditional("DEBUG")] private void LogEvent(bool loggingEnabled, [CallerMemberName] string caller = "") { diff --git a/Dalamud/Game/Agent/AgentEvent.cs b/Dalamud/Game/Agent/AgentEvent.cs index 2a3002daa..e9c9a1b85 100644 --- a/Dalamud/Game/Agent/AgentEvent.cs +++ b/Dalamud/Game/Agent/AgentEvent.cs @@ -18,12 +18,12 @@ public enum AgentEvent /// /// An event that is fired before the agent processes its Filtered Receive Event Function. /// - PreReceiveFilteredEvent, + PreReceiveEventWithResult, /// /// An event that is fired after the agent has processed its Filtered Receive Event Function. /// - PostReceiveFilteredEvent, + PostReceiveEventWithResult, /// /// An event that is fired before the agent processes its Show Function. diff --git a/Dalamud/Game/Agent/AgentLifecycle.cs b/Dalamud/Game/Agent/AgentLifecycle.cs index 75ed47d86..45f0dec5c 100644 --- a/Dalamud/Game/Agent/AgentLifecycle.cs +++ b/Dalamud/Game/Agent/AgentLifecycle.cs @@ -69,30 +69,36 @@ internal unsafe class AgentLifecycle : IInternalDisposableService AllocatedTables.Clear(); } + /// + /// Resolves a virtual table address to the original virtual table address. + /// + /// The modified address to resolve. + /// The original address. + internal static AgentInterface.AgentInterfaceVirtualTable* GetOriginalVirtualTable(AgentInterface.AgentInterfaceVirtualTable* tableAddress) + { + var matchedTable = AllocatedTables.FirstOrDefault(table => table.ModifiedVirtualTable == tableAddress); + if (matchedTable == null) + { + return null; + } + + return matchedTable.OriginalVirtualTable; + } + /// /// Register a listener for the target event and agent. /// /// The listener to register. internal void RegisterListener(AgentLifecycleEventListener listener) { - this.framework.RunOnTick(() => + if (this.isInvokingListeners) { - if (!this.EventListeners.ContainsKey(listener.EventType)) - { - if (!this.EventListeners.TryAdd(listener.EventType, [])) - return; - } - - // Note: uint.MaxValue is a valid agent id, as that will trigger on any agent for this event type - if (!this.EventListeners[listener.EventType].ContainsKey(listener.AgentId)) - { - if (!this.EventListeners[listener.EventType].TryAdd(listener.AgentId, [])) - return; - } - - this.EventListeners[listener.EventType][listener.AgentId].Add(listener); - }, - delayTicks: this.isInvokingListeners ? 1 : 0); + this.framework.RunOnTick(() => this.RegisterListenerMethod(listener)); + } + else + { + this.framework.RunOnFrameworkThread(() => this.RegisterListenerMethod(listener)); + } } /// @@ -101,17 +107,14 @@ internal unsafe class AgentLifecycle : IInternalDisposableService /// The listener to unregister. internal void UnregisterListener(AgentLifecycleEventListener listener) { - this.framework.RunOnTick(() => + if (this.isInvokingListeners) { - if (this.EventListeners.TryGetValue(listener.EventType, out var agentListeners)) - { - if (agentListeners.TryGetValue(listener.AgentId, out var agentListener)) - { - agentListener.Remove(listener); - } - } - }, - delayTicks: this.isInvokingListeners ? 1 : 0); + this.framework.RunOnTick(() => this.UnregisterListenerMethod(listener)); + } + else + { + this.framework.RunOnFrameworkThread(() => this.UnregisterListenerMethod(listener)); + } } /// @@ -162,19 +165,6 @@ internal unsafe class AgentLifecycle : IInternalDisposableService this.isInvokingListeners = false; } - /// - /// Resolves a virtual table address to the original virtual table address. - /// - /// The modified address to resolve. - /// The original address. - internal AgentInterface.AgentInterfaceVirtualTable* GetOriginalVirtualTable(AgentInterface.AgentInterfaceVirtualTable* tableAddress) - { - var matchedTable = AllocatedTables.FirstOrDefault(table => table.ModifiedVirtualTable == tableAddress); - if (matchedTable == null) return null; - - return matchedTable.OriginalVirtualTable; - } - private void OnAgentModuleInitialize(AgentModule* thisPtr, UIModule* uiModule) { this.onInitializeAgentsHook!.Original(thisPtr, uiModule); @@ -193,6 +183,39 @@ internal unsafe class AgentLifecycle : IInternalDisposableService } } + private void RegisterListenerMethod(AgentLifecycleEventListener listener) + { + if (!this.EventListeners.ContainsKey(listener.EventType)) + { + if (!this.EventListeners.TryAdd(listener.EventType, [])) + { + return; + } + } + + // Note: uint.MaxValue is a valid agent id, as that will trigger on any agent for this event type + if (!this.EventListeners[listener.EventType].ContainsKey(listener.AgentId)) + { + if (!this.EventListeners[listener.EventType].TryAdd(listener.AgentId, [])) + { + return; + } + } + + this.EventListeners[listener.EventType][listener.AgentId].Add(listener); + } + + private void UnregisterListenerMethod(AgentLifecycleEventListener listener) + { + if (this.EventListeners.TryGetValue(listener.EventType, out var agentListeners)) + { + if (agentListeners.TryGetValue(listener.AgentId, out var agentListener)) + { + agentListener.Remove(listener); + } + } + } + private void ReplaceVirtualTables(AgentModule* agentModule) { foreach (uint index in Enumerable.Range(0, agentModule->Agents.Length)) @@ -311,5 +334,5 @@ internal class AgentLifecyclePluginScoped : IInternalDisposableService, IAgentLi /// public unsafe nint GetOriginalVirtualTable(nint virtualTableAddress) - => (nint)this.agentLifecycleService.GetOriginalVirtualTable((AgentInterface.AgentInterfaceVirtualTable*)virtualTableAddress); + => (nint)AgentLifecycle.GetOriginalVirtualTable((AgentInterface.AgentInterfaceVirtualTable*)virtualTableAddress); } diff --git a/Dalamud/Game/Agent/AgentVirtualTable.cs b/Dalamud/Game/Agent/AgentVirtualTable.cs index e7f9a2f6e..99f613137 100644 --- a/Dalamud/Game/Agent/AgentVirtualTable.cs +++ b/Dalamud/Game/Agent/AgentVirtualTable.cs @@ -21,7 +21,7 @@ internal unsafe class AgentVirtualTable : IDisposable // Copying extra entries is not problematic, and is considered safe. private const int VirtualTableEntryCount = 60; - private const bool EnableLogging = true; + private const bool EnableLogging = false; private static readonly ModuleLog Log = new("AgentVT"); @@ -44,7 +44,7 @@ internal unsafe class AgentVirtualTable : IDisposable // Pinned Function Delegates, as these functions get assigned to an unmanaged virtual table, // the CLR needs to know they are in use, or it will invalidate them causing random crashing. private readonly AgentInterface.Delegates.ReceiveEvent receiveEventFunction; - private readonly AgentInterface.Delegates.ReceiveEvent2 filteredReceiveEventFunction; + private readonly AgentInterface.Delegates.ReceiveEventWithResult receiveEventWithResultFunction; private readonly AgentInterface.Delegates.Show showFunction; private readonly AgentInterface.Delegates.Hide hideFunction; private readonly AgentInterface.Delegates.Update updateFunction; @@ -60,8 +60,6 @@ internal unsafe class AgentVirtualTable : IDisposable /// Reference to AgentLifecycle service to callback and invoke listeners. internal AgentVirtualTable(AgentInterface* agent, AgentId agentId, AgentLifecycle lifecycleService) { - Log.Debug($"Initializing AgentVirtualTable for {agentId}, Address: {(nint)agent:X}"); - this.agentInterface = agent; this.agentId = agentId; this.lifecycleService = lifecycleService; @@ -80,7 +78,7 @@ internal unsafe class AgentVirtualTable : IDisposable // Pin each of our listener functions this.receiveEventFunction = this.OnAgentReceiveEvent; - this.filteredReceiveEventFunction = this.OnAgentFilteredReceiveEvent; + this.receiveEventWithResultFunction = this.OnAgentReceiveEventWithResult; this.showFunction = this.OnAgentShow; this.hideFunction = this.OnAgentHide; this.updateFunction = this.OnAgentUpdate; @@ -90,7 +88,7 @@ internal unsafe class AgentVirtualTable : IDisposable // Overwrite specific virtual table entries this.ModifiedVirtualTable->ReceiveEvent = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.receiveEventFunction); - this.ModifiedVirtualTable->ReceiveEvent2 = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.filteredReceiveEventFunction); + this.ModifiedVirtualTable->ReceiveEventWithResult = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.receiveEventWithResultFunction); this.ModifiedVirtualTable->Show = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.showFunction); this.ModifiedVirtualTable->Hide = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.hideFunction); this.ModifiedVirtualTable->Update = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.updateFunction); @@ -158,7 +156,7 @@ internal unsafe class AgentVirtualTable : IDisposable return result; } - private AtkValue* OnAgentFilteredReceiveEvent(AgentInterface* thisPtr, AtkValue* returnValue, AtkValue* values, uint valueCount, ulong eventKind) + private AtkValue* OnAgentReceiveEventWithResult(AgentInterface* thisPtr, AtkValue* returnValue, AtkValue* values, uint valueCount, ulong eventKind) { AtkValue* result = null; @@ -173,7 +171,7 @@ internal unsafe class AgentVirtualTable : IDisposable this.filteredReceiveEventArgs.ValueCount = valueCount; this.filteredReceiveEventArgs.EventKind = eventKind; - this.lifecycleService.InvokeListenersSafely(AgentEvent.PreReceiveFilteredEvent, this.filteredReceiveEventArgs); + this.lifecycleService.InvokeListenersSafely(AgentEvent.PreReceiveEventWithResult, this.filteredReceiveEventArgs); returnValue = (AtkValue*)this.filteredReceiveEventArgs.ReturnValue; values = (AtkValue*)this.filteredReceiveEventArgs.AtkValues; @@ -182,18 +180,18 @@ internal unsafe class AgentVirtualTable : IDisposable try { - result = this.OriginalVirtualTable->ReceiveEvent2(thisPtr, returnValue, values, valueCount, eventKind); + result = this.OriginalVirtualTable->ReceiveEventWithResult(thisPtr, returnValue, values, valueCount, eventKind); } catch (Exception e) { Log.Error(e, "Caught exception when calling original Agent FilteredReceiveEvent. This may be a bug in the game or another plugin hooking this method."); } - this.lifecycleService.InvokeListenersSafely(AgentEvent.PostReceiveFilteredEvent, this.filteredReceiveEventArgs); + this.lifecycleService.InvokeListenersSafely(AgentEvent.PostReceiveEventWithResult, this.filteredReceiveEventArgs); } catch (Exception e) { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAgentFilteredReceiveEvent."); + Log.Error(e, "Caught exception from Dalamud when attempting to process OnAgentReceiveEventWithResult."); } return result; diff --git a/Dalamud/Game/Gui/Dtr/DtrBar.cs b/Dalamud/Game/Gui/Dtr/DtrBar.cs index 5663d0748..e5de6b2bd 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBar.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs @@ -397,7 +397,15 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar ushort w = 0, h = 0; node->GetTextDrawSize(&w, &h, node->NodeText.StringPtr); - node->SetWidth(w); + + if (data.MinimumWidth > 0) + { + node->SetWidth(Math.Max(data.MinimumWidth, w)); + } + else + { + node->SetWidth(w); + } } var elementWidth = data.TextNode->Width + this.configuration.DtrSpacing; diff --git a/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs b/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs index e0bd8fd49..47e86fde1 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs @@ -40,6 +40,11 @@ public interface IReadOnlyDtrBarEntry /// public bool Shown { get; } + /// + /// Gets a value indicating this entry's minimum width. + /// + public ushort MinimumWidth { get; } + /// /// Gets a value indicating whether the user has hidden this entry from view through the Dalamud settings. /// @@ -76,6 +81,11 @@ public interface IDtrBarEntry : IReadOnlyDtrBarEntry /// public new bool Shown { get; set; } + /// + /// Gets or sets a value specifying the requested minimum width to make this entry. + /// + public new ushort MinimumWidth { get; set; } + /// /// Gets or sets an action to be invoked when the user clicks on the dtr entry. /// @@ -128,6 +138,25 @@ internal sealed unsafe class DtrBarEntry : IDisposable, IDtrBarEntry /// public SeString? Tooltip { get; set; } + /// + public ushort MinimumWidth + { + get; + set + { + field = value; + if (this.TextNode is not null) + { + if (this.TextNode->GetWidth() < value) + { + this.TextNode->SetWidth(value); + } + } + + this.Dirty = true; + } + } + /// public Action? OnClick { get; set; }