From da25adf6c7d8746495515b3799d01509a2bc2e2e Mon Sep 17 00:00:00 2001 From: MTVirux Date: Sat, 9 May 2026 05:26:52 +0100 Subject: [PATCH] Port context menu to dalamud service --- Visibility/Handlers/ContextMenuHandler.cs | 214 ++++++++++++++++++++++ Visibility/Handlers/SettingsHandler.cs | 4 +- Visibility/Service.cs | 3 + Visibility/VisibilityPlugin.cs | 5 + 4 files changed, 223 insertions(+), 3 deletions(-) create mode 100644 Visibility/Handlers/ContextMenuHandler.cs diff --git a/Visibility/Handlers/ContextMenuHandler.cs b/Visibility/Handlers/ContextMenuHandler.cs new file mode 100644 index 0000000..5280009 --- /dev/null +++ b/Visibility/Handlers/ContextMenuHandler.cs @@ -0,0 +1,214 @@ +using System; +using System.Linq; + +using Dalamud.Game.Gui.ContextMenu; +using Dalamud.Game.Text; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Game.Text.SeStringHandling.Payloads; +using Dalamud.Game.ClientState.Objects.SubKinds; + +using Lumina.Excel.Sheets; + +using Visibility.Configuration; +using Visibility.Utils; +using Visibility.Void; + +namespace Visibility.Handlers; + +public class ContextMenuHandler: IDisposable +{ + private readonly VisibilityConfiguration configuration; + private readonly CommandManagerHandler commandManagerHandler; + private readonly FrameworkHandler frameworkHandler; + private readonly Localization pluginLocalization; + + public ContextMenuHandler( + VisibilityConfiguration configuration, + CommandManagerHandler commandManagerHandler, + FrameworkHandler frameworkHandler, + Localization pluginLocalization) + { + this.configuration = configuration; + this.commandManagerHandler = commandManagerHandler; + this.frameworkHandler = frameworkHandler; + this.pluginLocalization = pluginLocalization; + + Service.ContextMenu.OnMenuOpened += this.OnMenuOpened; + } + + private void OnMenuOpened(IMenuOpenedArgs args) + { + if (!this.configuration.EnableContextMenu) + { + return; + } + + if (args.Target is not MenuTargetDefault target) + { + return; + } + + if (target.TargetObject is not null and not IPlayerCharacter) + { + return; + } + + uint worldId = target.TargetHomeWorld.RowId; + if (worldId is 0u or ushort.MaxValue) + { + return; + } + + string targetName = target.TargetName; + if (string.IsNullOrEmpty(targetName)) + { + return; + } + + args.AddMenuItem(new MenuItem + { + Name = "Visibility", + Prefix = SeIconChar.BoxedLetterV, + PrefixColor = 539, + IsSubmenu = true, + OnClicked = clickArgs => this.OpenVisibilitySubmenu(clickArgs, targetName, worldId), + }); + } + + private void OpenVisibilitySubmenu(IMenuItemClickedArgs args, string targetName, uint worldId) + { + VoidItem? voidEntry = this.FindVoidEntry(targetName, worldId); + VoidItem? whitelistEntry = this.FindWhitelistEntry(targetName, worldId); + + MenuItem voidItem = voidEntry is null + ? new MenuItem + { + Name = BuildLabel(this.pluginLocalization.ContextMenuAdd(this.pluginLocalization.VoidListName)), + OnClicked = _ => this.AddToVoidList(targetName, worldId), + } + : new MenuItem + { + Name = BuildLabel(this.pluginLocalization.ContextMenuRemove(this.pluginLocalization.VoidListName)), + OnClicked = _ => this.RemoveFromVoidList(voidEntry), + }; + + MenuItem whitelistItem = whitelistEntry is null + ? new MenuItem + { + Name = BuildLabel(this.pluginLocalization.ContextMenuAdd(this.pluginLocalization.WhitelistName)), + OnClicked = _ => this.AddToWhitelist(targetName, worldId), + } + : new MenuItem + { + Name = BuildLabel(this.pluginLocalization.ContextMenuRemove(this.pluginLocalization.WhitelistName)), + OnClicked = _ => this.RemoveFromWhitelist(whitelistEntry), + }; + + args.OpenSubmenu("Visibility", new MenuItem[] { voidItem, whitelistItem }); + } + + private static SeString BuildLabel(string text) => + new( + new UIForegroundPayload(539), + new TextPayload($"{SeIconChar.BoxedLetterV.ToIconString()} "), + new UIForegroundPayload(0), + new TextPayload(text)); + + private VoidItem? FindVoidEntry(string name, uint worldId) => + this.configuration.VoidList.SingleOrDefault( + x => x.Name == name && x.HomeworldId == worldId); + + private VoidItem? FindWhitelistEntry(string name, uint worldId) => + this.configuration.Whitelist.SingleOrDefault( + x => x.Name == name && x.HomeworldId == worldId); + + private void AddToVoidList(string name, uint worldId) + { + if (!Service.DataManager.GetExcelSheet().TryGetRow(worldId, out World world)) + { + return; + } + + this.commandManagerHandler.VoidPlayer("ContextMenu", $"{name} {world.Name}"); + } + + private void RemoveFromVoidList(VoidItem entry) + { + SeString message = new( + new TextPayload("VoidList: "), + new PlayerPayload(entry.Name, entry.HomeworldId), + new IconPayload(BitmapFontIcon.CrossWorld), + new TextPayload($"{entry.HomeworldName} has been removed.")); + + this.configuration.VoidList.Remove(entry); + if (entry.Id != 0) + { + this.configuration.VoidDictionary.Remove(entry.Id); + } + this.configuration.Save(); + if (entry.ObjectId > 0) + { + this.frameworkHandler.RemoveChecked(entry.ObjectId); + this.frameworkHandler.ShowPlayer(entry.ObjectId); + } + else + { + this.frameworkHandler.RemoveChecked(entry.Name); + this.frameworkHandler.ShowPlayer(entry.Name); + } + + Service.ChatGui.Print(message); + } + + private void AddToWhitelist(string name, uint worldId) + { + if (!Service.DataManager.GetExcelSheet().TryGetRow(worldId, out World world)) + { + return; + } + + this.commandManagerHandler.WhitelistPlayer("ContextMenu", $"{name} {world.Name}"); + } + + private void RemoveFromWhitelist(VoidItem entry) + { + SeString message = new( + new TextPayload("Whitelist: "), + new PlayerPayload(entry.Name, entry.HomeworldId), + new IconPayload(BitmapFontIcon.CrossWorld), + new TextPayload($"{entry.HomeworldName} has been removed.")); + + this.configuration.Whitelist.Remove(entry); + if (entry.Id != 0) + { + this.configuration.WhitelistDictionary.Remove(entry.Id); + } + this.configuration.Save(); + if (entry.ObjectId > 0) + { + this.frameworkHandler.RemoveChecked(entry.ObjectId); + } + else + { + this.frameworkHandler.RemoveChecked(entry.Name); + } + + Service.ChatGui.Print(message); + } + + protected virtual void Dispose(bool disposing) + { + if (!disposing) + { + return; + } + + Service.ContextMenu.OnMenuOpened -= this.OnMenuOpened; + } + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } +} diff --git a/Visibility/Handlers/SettingsHandler.cs b/Visibility/Handlers/SettingsHandler.cs index cd35cfb..2ac91b6 100644 --- a/Visibility/Handlers/SettingsHandler.cs +++ b/Visibility/Handlers/SettingsHandler.cs @@ -121,9 +121,7 @@ private void InitializeSettingActions() this.settingActions[nameof(this.configurationInstance.EnableContextMenu)] = this.CreateDirectToggleAction((v, t) => - this.configurationInstance.EnableContextMenu.ToggleBool(v, t) - // afterToggleAction: () => { /* TODO: Switch to dalamud service */ } - ); + this.configurationInstance.EnableContextMenu.ToggleBool(v, t)); this.settingActions[nameof(this.configurationInstance.ShowTargetOfTarget)] = this.CreateDirectToggleAction((v, t) => diff --git a/Visibility/Service.cs b/Visibility/Service.cs index dcf1bf6..a454f11 100644 --- a/Visibility/Service.cs +++ b/Visibility/Service.cs @@ -1,6 +1,7 @@ using Dalamud.IoC; using Dalamud.Plugin; using Dalamud.Plugin.Services; +using Dalamud.Game.Gui.ContextMenu; namespace Visibility; @@ -27,4 +28,6 @@ public class Service [PluginService] public static IGameInteropProvider GameInteropProvider { get; set; } = null!; [PluginService] public static IPluginLog PluginLog { get; set; } = null!; + + [PluginService] public static IContextMenu ContextMenu { get; set; } = null!; } diff --git a/Visibility/VisibilityPlugin.cs b/Visibility/VisibilityPlugin.cs index ce4342f..088d349 100644 --- a/Visibility/VisibilityPlugin.cs +++ b/Visibility/VisibilityPlugin.cs @@ -25,6 +25,7 @@ public class VisibilityPlugin: IDalamudPlugin private readonly UiManager uiManager; private readonly VisibilityApi api; private readonly VisibilityProvider ipcProvider; + private readonly ContextMenuHandler contextMenuHandler; public VisibilityPlugin(IDalamudPluginInterface pluginInterface) { @@ -57,6 +58,9 @@ public VisibilityPlugin(IDalamudPluginInterface pluginInterface) this.api = new VisibilityApi(this.configuration, this.commandManagerHandler, this.frameworkHandler); this.ipcProvider = new VisibilityProvider(this.api); + + this.contextMenuHandler = new ContextMenuHandler( + this.configuration, this.commandManagerHandler, this.frameworkHandler, this.pluginLocalization); } protected virtual void Dispose(bool disposing) @@ -74,6 +78,7 @@ protected virtual void Dispose(bool disposing) this.frameworkUpdateHandler.Dispose(); this.uiManager.Dispose(); this.frameworkHandler.Dispose(); + this.contextMenuHandler.Dispose(); } public void Dispose()