mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 18:27:24 +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 HideApplyCheckmarks { get; set; } = false;
|
||||||
public bool SmallEquip { get; set; } = false;
|
public bool SmallEquip { get; set; } = false;
|
||||||
public bool UnlockedItemMode { 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 MainWindow.TabType SelectedTab { get; set; } = MainWindow.TabType.Settings;
|
||||||
public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift);
|
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.Api\Penumbra.Api.csproj" />
|
||||||
<ProjectReference Include="..\..\Penumbra\Penumbra.GameData\Penumbra.GameData.csproj" />
|
<ProjectReference Include="..\..\Penumbra\Penumbra.GameData\Penumbra.GameData.csproj" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Dalamud.ContextMenu" Version="1.2.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Glamourer.Gui.Tabs.DesignTab;
|
using Glamourer.Gui.Tabs.DesignTab;
|
||||||
|
using Glamourer.Interop;
|
||||||
using Glamourer.Interop.Penumbra;
|
using Glamourer.Interop.Penumbra;
|
||||||
using Glamourer.Services;
|
using Glamourer.Services;
|
||||||
using Glamourer.State;
|
using Glamourer.State;
|
||||||
|
|
@ -19,15 +20,17 @@ public class SettingsTab : ITab
|
||||||
private readonly StateListener _stateListener;
|
private readonly StateListener _stateListener;
|
||||||
private readonly CodeService _codeService;
|
private readonly CodeService _codeService;
|
||||||
private readonly PenumbraAutoRedraw _autoRedraw;
|
private readonly PenumbraAutoRedraw _autoRedraw;
|
||||||
|
private readonly ContextMenuService _contextMenuService;
|
||||||
|
|
||||||
public SettingsTab(Configuration config, DesignFileSystemSelector selector, StateListener stateListener,
|
public SettingsTab(Configuration config, DesignFileSystemSelector selector, StateListener stateListener,
|
||||||
CodeService codeService, PenumbraAutoRedraw autoRedraw)
|
CodeService codeService, PenumbraAutoRedraw autoRedraw, ContextMenuService contextMenuService)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
_selector = selector;
|
_selector = selector;
|
||||||
_stateListener = stateListener;
|
_stateListener = stateListener;
|
||||||
_codeService = codeService;
|
_codeService = codeService;
|
||||||
_autoRedraw = autoRedraw;
|
_autoRedraw = autoRedraw;
|
||||||
|
_contextMenuService = contextMenuService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> Label
|
public ReadOnlySpan<byte> Label
|
||||||
|
|
@ -59,6 +62,15 @@ public class SettingsTab : ITab
|
||||||
Checkbox("Hide Application Checkboxes",
|
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.",
|
"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);
|
_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",
|
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,
|
"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))
|
_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<CustomizeUnlockManager>()
|
||||||
.AddSingleton<ItemUnlockManager>()
|
.AddSingleton<ItemUnlockManager>()
|
||||||
.AddSingleton<DatFileService>()
|
.AddSingleton<DatFileService>()
|
||||||
.AddSingleton<InventoryService>();
|
.AddSingleton<InventoryService>()
|
||||||
|
.AddSingleton<ContextMenuService>();
|
||||||
|
|
||||||
private static IServiceCollection AddDesigns(this IServiceCollection services)
|
private static IServiceCollection AddDesigns(this IServiceCollection services)
|
||||||
=> services.AddSingleton<DesignManager>()
|
=> services.AddSingleton<DesignManager>()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue