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/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 = "")
{