mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 10:17:23 +01:00
Add context menu support.
This commit is contained in:
parent
ec27c3e931
commit
662ec617dc
5 changed files with 180 additions and 7 deletions
|
|
@ -27,6 +27,7 @@ public class Configuration : IPluginConfiguration, ISavable
|
|||
public bool HideApplyCheckmarks { get; set; } = false;
|
||||
public bool SmallEquip { get; set; } = false;
|
||||
public bool UnlockedItemMode { get; set; } = false;
|
||||
public bool EnableGameContextMenu { get; set; } = true;
|
||||
public MainWindow.TabType SelectedTab { get; set; } = MainWindow.TabType.Settings;
|
||||
public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift);
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@
|
|||
<ProjectReference Include="..\..\Penumbra\Penumbra.Api\Penumbra.Api.csproj" />
|
||||
<ProjectReference Include="..\..\Penumbra\Penumbra.GameData\Penumbra.GameData.csproj" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||
<PackageReference Include="Dalamud.ContextMenu" Version="1.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
using Dalamud.Interface;
|
||||
using Glamourer.Gui.Tabs.DesignTab;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
|
|
@ -19,15 +20,17 @@ public class SettingsTab : ITab
|
|||
private readonly StateListener _stateListener;
|
||||
private readonly CodeService _codeService;
|
||||
private readonly PenumbraAutoRedraw _autoRedraw;
|
||||
private readonly ContextMenuService _contextMenuService;
|
||||
|
||||
public SettingsTab(Configuration config, DesignFileSystemSelector selector, StateListener stateListener,
|
||||
CodeService codeService, PenumbraAutoRedraw autoRedraw)
|
||||
CodeService codeService, PenumbraAutoRedraw autoRedraw, ContextMenuService contextMenuService)
|
||||
{
|
||||
_config = config;
|
||||
_selector = selector;
|
||||
_stateListener = stateListener;
|
||||
_codeService = codeService;
|
||||
_autoRedraw = autoRedraw;
|
||||
_config = config;
|
||||
_selector = selector;
|
||||
_stateListener = stateListener;
|
||||
_codeService = codeService;
|
||||
_autoRedraw = autoRedraw;
|
||||
_contextMenuService = contextMenuService;
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> Label
|
||||
|
|
@ -59,6 +62,15 @@ public class SettingsTab : ITab
|
|||
Checkbox("Hide Application Checkboxes",
|
||||
"Hide the application checkboxes in the Customization and Equipment panels of the design tab, and only show them under Application Rules.",
|
||||
_config.HideApplyCheckmarks, v => _config.HideApplyCheckmarks = v);
|
||||
Checkbox("Enable Game Context Menus", "Whether to show a Try On via Glamourer button on context menus for equippable items.",
|
||||
_config.EnableGameContextMenu, v =>
|
||||
{
|
||||
_config.EnableGameContextMenu = v;
|
||||
if (v)
|
||||
_contextMenuService.Enable();
|
||||
else
|
||||
_contextMenuService.Disable();
|
||||
});
|
||||
if (Widget.DoubleModifierSelector("Design Deletion Modifier",
|
||||
"A modifier you need to hold while clicking the Delete Design button for it to take effect.", 100 * ImGuiHelpers.GlobalScale,
|
||||
_config.DeleteDesignModifier, v => _config.DeleteDesignModifier = v))
|
||||
|
|
|
|||
158
Glamourer/Interop/ContextMenuService.cs
Normal file
158
Glamourer/Interop/ContextMenuService.cs
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
using System;
|
||||
using Dalamud.ContextMenu;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
public class ContextMenuService : IDisposable
|
||||
{
|
||||
public const int ItemSearchContextItemId = 0x1738;
|
||||
public const int ChatLogContextItemId = 0x948;
|
||||
|
||||
private readonly ItemManager _items;
|
||||
private readonly DalamudContextMenu _contextMenu = new();
|
||||
private readonly StateManager _state;
|
||||
private readonly ObjectManager _objects;
|
||||
private readonly GameGui _gameGui;
|
||||
private readonly Configuration _config;
|
||||
|
||||
public ContextMenuService(ItemManager items, StateManager state, ObjectManager objects, GameGui gameGui, Configuration config)
|
||||
{
|
||||
_items = items;
|
||||
_state = state;
|
||||
_objects = objects;
|
||||
_gameGui = gameGui;
|
||||
_config = config;
|
||||
Enable();
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
_contextMenu.OnOpenGameObjectContextMenu += AddGameObjectItem;
|
||||
_contextMenu.OnOpenInventoryContextMenu += AddInventoryItem;
|
||||
}
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
_contextMenu.OnOpenGameObjectContextMenu -= AddGameObjectItem;
|
||||
_contextMenu.OnOpenInventoryContextMenu -= AddInventoryItem;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disable();
|
||||
_contextMenu.Dispose();
|
||||
}
|
||||
|
||||
private static readonly SeString TryOnString = new SeStringBuilder().AddUiForeground(SeIconChar.BoxedLetterG.ToIconString(), 541)
|
||||
.AddText(" Try On").AddUiForegroundOff().BuiltString;
|
||||
|
||||
private void AddInventoryItem(InventoryContextMenuOpenArgs args)
|
||||
{
|
||||
var item = CheckInventoryItem(args.ItemId);
|
||||
if (item != null)
|
||||
args.AddCustomItem(item);
|
||||
}
|
||||
|
||||
private InventoryContextMenuItem? CheckInventoryItem(uint itemId)
|
||||
{
|
||||
if (itemId > 500000)
|
||||
itemId -= 500000;
|
||||
|
||||
if (!_items.ItemService.AwaitedService.TryGetValue(itemId, EquipSlot.MainHand, out var item))
|
||||
return null;
|
||||
|
||||
return new InventoryContextMenuItem(TryOnString, GetInventoryAction(item));
|
||||
}
|
||||
|
||||
|
||||
private GameObjectContextMenuItem? CheckGameObjectItem(uint itemId)
|
||||
{
|
||||
if (itemId > 500000)
|
||||
itemId -= 500000;
|
||||
|
||||
if (!_items.ItemService.AwaitedService.TryGetValue(itemId, EquipSlot.MainHand, out var item))
|
||||
return null;
|
||||
|
||||
return new GameObjectContextMenuItem(TryOnString, GetGameObjectAction(item));
|
||||
}
|
||||
|
||||
private unsafe GameObjectContextMenuItem? CheckGameObjectItem(IntPtr agent, int offset, Func<nint, bool> validate)
|
||||
=> agent != IntPtr.Zero && validate(agent) ? CheckGameObjectItem(*(uint*)(agent + offset)) : null;
|
||||
|
||||
private unsafe GameObjectContextMenuItem? CheckGameObjectItem(IntPtr agent, int offset)
|
||||
=> agent != IntPtr.Zero ? CheckGameObjectItem(*(uint*)(agent + offset)) : null;
|
||||
|
||||
private GameObjectContextMenuItem? CheckGameObjectItem(string name, int offset, Func<nint, bool> validate)
|
||||
=> CheckGameObjectItem(_gameGui.FindAgentInterface(name), offset, validate);
|
||||
|
||||
private void AddGameObjectItem(GameObjectContextMenuOpenArgs args)
|
||||
{
|
||||
var item = args.ParentAddonName switch
|
||||
{
|
||||
"ItemSearch" => CheckGameObjectItem(args.Agent, ItemSearchContextItemId),
|
||||
"ChatLog" => CheckGameObjectItem("ChatLog", ChatLogContextItemId, ValidateChatLogContext),
|
||||
_ => null,
|
||||
};
|
||||
if (item != null)
|
||||
args.AddCustomItem(item);
|
||||
}
|
||||
|
||||
private DalamudContextMenu.InventoryContextMenuItemSelectedDelegate GetInventoryAction(EquipItem item)
|
||||
{
|
||||
return _ =>
|
||||
{
|
||||
var (id, playerData) = _objects.PlayerData;
|
||||
if (!playerData.Valid)
|
||||
return;
|
||||
|
||||
if (!_state.GetOrCreate(id, playerData.Objects[0], out var state))
|
||||
return;
|
||||
|
||||
var slot = item.Type.ToSlot();
|
||||
_state.ChangeEquip(state, slot, item, 0, StateChanged.Source.Manual);
|
||||
if (item.Type.ValidOffhand().IsOffhandType())
|
||||
{
|
||||
if (item.ModelId.Value is > 1600 and < 1651
|
||||
&& _items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets))
|
||||
_state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, StateChanged.Source.Manual);
|
||||
if (_items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
|
||||
_state.ChangeEquip(state, EquipSlot.OffHand, offhand, 0, StateChanged.Source.Manual);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private DalamudContextMenu.GameObjectContextMenuItemSelectedDelegate GetGameObjectAction(EquipItem item)
|
||||
{
|
||||
return _ =>
|
||||
{
|
||||
var (id, playerData) = _objects.PlayerData;
|
||||
if (!playerData.Valid)
|
||||
return;
|
||||
|
||||
if (!_state.GetOrCreate(id, playerData.Objects[0], out var state))
|
||||
return;
|
||||
|
||||
var slot = item.Type.ToSlot();
|
||||
_state.ChangeEquip(state, slot, item, 0, StateChanged.Source.Manual);
|
||||
if (item.Type.ValidOffhand().IsOffhandType())
|
||||
{
|
||||
if (item.ModelId.Value is > 1600 and < 1651
|
||||
&& _items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets))
|
||||
_state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, StateChanged.Source.Manual);
|
||||
if (_items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
|
||||
_state.ChangeEquip(state, EquipSlot.OffHand, offhand, 0, StateChanged.Source.Manual);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static unsafe bool ValidateChatLogContext(nint agent)
|
||||
=> *(uint*)(agent + ChatLogContextItemId + 8) == 3;
|
||||
}
|
||||
|
|
@ -92,7 +92,8 @@ public static class ServiceManager
|
|||
.AddSingleton<CustomizeUnlockManager>()
|
||||
.AddSingleton<ItemUnlockManager>()
|
||||
.AddSingleton<DatFileService>()
|
||||
.AddSingleton<InventoryService>();
|
||||
.AddSingleton<InventoryService>()
|
||||
.AddSingleton<ContextMenuService>();
|
||||
|
||||
private static IServiceCollection AddDesigns(this IServiceCollection services)
|
||||
=> services.AddSingleton<DesignManager>()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue