Some cleanup, use OtterGui.

This commit is contained in:
Ottermandias 2022-07-03 16:35:56 +02:00
parent a36d1f1935
commit 0fc8992271
40 changed files with 2686 additions and 2792 deletions

View file

@ -2,6 +2,7 @@
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Logging;
using Dalamud.Plugin.Ipc;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Glamourer.Gui;
using ImGuiNET;
using Penumbra.GameData.Enums;
@ -17,10 +18,14 @@ public class PenumbraAttach : IDisposable
private ICallGateSubscriber<MouseButton, ChangedItemType, uint, object>? _clickSubscriber;
private ICallGateSubscriber<string, int, object>? _redrawSubscriberName;
private ICallGateSubscriber<GameObject, int, object>? _redrawSubscriberObject;
private ICallGateSubscriber<IntPtr, (IntPtr, string)>? _drawObjectInfo;
private ICallGateSubscriber<IntPtr, string, IntPtr, IntPtr, object?>? _creatingCharacterBase;
private readonly ICallGateSubscriber<object?> _initializedEvent;
private readonly ICallGateSubscriber<object?> _disposedEvent;
public event Action<IntPtr, IntPtr, IntPtr>? CreatingCharacterBase;
public PenumbraAttach(bool attach)
{
_initializedEvent = Dalamud.PluginInterface.GetIpcSubscriber<object?>("Penumbra.Initialized");
@ -47,6 +52,7 @@ public class PenumbraAttach : IDisposable
_redrawSubscriberName = Dalamud.PluginInterface.GetIpcSubscriber<string, int, object>("Penumbra.RedrawObjectByName");
_redrawSubscriberObject = Dalamud.PluginInterface.GetIpcSubscriber<GameObject, int, object>("Penumbra.RedrawObject");
_drawObjectInfo = Dalamud.PluginInterface.GetIpcSubscriber<IntPtr, (IntPtr, string)>("Penumbra.GetDrawObjectInfo");
if (!attach)
return;
@ -54,8 +60,11 @@ public class PenumbraAttach : IDisposable
_tooltipSubscriber = Dalamud.PluginInterface.GetIpcSubscriber<ChangedItemType, uint, object>("Penumbra.ChangedItemTooltip");
_clickSubscriber =
Dalamud.PluginInterface.GetIpcSubscriber<MouseButton, ChangedItemType, uint, object>("Penumbra.ChangedItemClick");
_creatingCharacterBase =
Dalamud.PluginInterface.GetIpcSubscriber<IntPtr, string, IntPtr, IntPtr, object?>("Penumbra.CreatingCharacterBase");
_tooltipSubscriber.Subscribe(PenumbraTooltip);
_clickSubscriber.Subscribe(PenumbraRightClick);
_creatingCharacterBase.Subscribe(SubscribeCharacterBase);
PluginLog.Debug("Glamourer attached to Penumbra.");
}
catch (Exception e)
@ -64,13 +73,19 @@ public class PenumbraAttach : IDisposable
}
}
private void SubscribeCharacterBase(IntPtr gameObject, string _, IntPtr customize, IntPtr equipment)
=> CreatingCharacterBase?.Invoke(gameObject, customize, equipment);
public void Unattach()
{
_tooltipSubscriber?.Unsubscribe(PenumbraTooltip);
_clickSubscriber?.Unsubscribe(PenumbraRightClick);
_tooltipSubscriber = null;
_clickSubscriber = null;
_redrawSubscriberName = null;
_creatingCharacterBase?.Unsubscribe(SubscribeCharacterBase);
_tooltipSubscriber = null;
_clickSubscriber = null;
_creatingCharacterBase = null;
_redrawSubscriberName = null;
_drawObjectInfo = null;
if (_redrawSubscriberObject != null)
{
PluginLog.Debug("Glamourer detached from Penumbra.");
@ -112,6 +127,9 @@ public class PenumbraAttach : IDisposable
}
}
public unsafe FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* GameObjectFromDrawObject(IntPtr drawObject)
=> (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)(_drawObjectInfo?.InvokeFunc(drawObject).Item1 ?? IntPtr.Zero);
public void RedrawObject(GameObject actor, RedrawType settings, bool repeat)
{
if (_redrawSubscriberObject != null)

View file

@ -1,68 +0,0 @@
using System;
namespace Glamourer
{
[Flags]
public enum CharacterFlag : ulong
{
MainHand = 1ul << 0,
OffHand = 1ul << 1,
Head = 1ul << 2,
Body = 1ul << 3,
Hands = 1ul << 4,
Legs = 1ul << 5,
Feet = 1ul << 6,
Ears = 1ul << 7,
Neck = 1ul << 8,
Wrists = 1ul << 9,
RFinger = 1ul << 10,
LFinger = 1ul << 11,
ModelMask = (1ul << 12) - 1,
StainMainHand = MainHand << 16,
StainOffHand = OffHand << 16,
StainHead = Head << 16,
StainBody = Body << 16,
StainHands = Hands << 16,
StainLegs = Legs << 16,
StainFeet = Feet << 16,
StainEars = Ears << 16,
StainNeck = Neck << 16,
StainWrists = Wrists << 16,
StainRFinger = RFinger << 16,
StainLFinger = LFinger << 16,
StainMask = ModelMask << 16,
EquipMask = ModelMask | StainMask,
Race = 1ul << 32,
Gender = 1ul << 33,
BodyType = 1ul << 34,
Height = 1ul << 35,
Clan = 1ul << 36,
Face = 1ul << 37,
Hairstyle = 1ul << 38,
Highlights = 1ul << 39,
SkinColor = 1ul << 40,
EyeColorRight = 1ul << 41,
HairColor = 1ul << 42,
HighlightsColor = 1ul << 43,
FacialFeatures = 1ul << 44,
TattooColor = 1ul << 45,
Eyebrows = 1ul << 46,
EyeColorLeft = 1ul << 47,
EyeShape = 1ul << 48,
IrisSize = 1ul << 49,
NoseShape = 1ul << 50,
JawShape = 1ul << 51,
MouthShape = 1ul << 52,
Lipstick = 1ul << 53,
LipColor = 1ul << 54,
MuscleMass = 1ul << 55,
TailShape = 1ul << 56,
BustSize = 1ul << 57,
FacePaint = 1ul << 58,
FacePaintReversed = 1ul << 59,
FacePaintColor = 1ul << 60,
CustomizeMask = ((1ul << 61) - 1) & ~EquipMask,
}
}

View file

@ -1,56 +1,29 @@
using Dalamud.Data;
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Buddy;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Fates;
using Dalamud.Game.ClientState.JobGauge;
using Dalamud.Game.ClientState.Keys;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Party;
using Dalamud.Game.Command;
using Dalamud.Game.Gui;
using Dalamud.Game.Gui.FlyText;
using Dalamud.Game.Gui.PartyFinder;
using Dalamud.Game.Gui.Toast;
using Dalamud.Game.Libc;
using Dalamud.Game.Network;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.IoC;
using Dalamud.Plugin;
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Local
namespace Glamourer
namespace Glamourer;
public class Dalamud
{
public class Dalamud
{
public static void Initialize(DalamudPluginInterface pluginInterface)
=> pluginInterface.Create<Dalamud>();
public static void Initialize(DalamudPluginInterface pluginInterface)
=> pluginInterface.Create<Dalamud>();
// @formatter:off
[PluginService][RequiredVersion("1.0")] public static DalamudPluginInterface PluginInterface { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static CommandManager Commands { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static SigScanner SigScanner { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static DataManager GameData { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static ClientState ClientState { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static ChatGui Chat { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static SeStringManager SeStrings { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static ChatHandlers ChatHandlers { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static Framework Framework { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static GameNetwork Network { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static Condition Conditions { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static KeyState Keys { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static GameGui GameGui { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static FlyTextGui FlyTexts { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static ToastGui Toasts { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static JobGauges Gauges { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static PartyFinderGui PartyFinder { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static BuddyList Buddies { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static PartyList Party { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static TargetManager Targets { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static ObjectTable Objects { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static FateTable Fates { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static LibcFunction LibC { get; private set; } = null!;
// @formatter:on
}
// @formatter:on
}

View file

@ -5,6 +5,7 @@ using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Logging;
using Glamourer.FileSystem;
using Glamourer.Structs;
using Penumbra.GameData.Enums;
using Penumbra.PlayerWatch;
@ -122,19 +123,19 @@ namespace Glamourer.Designs
private void OnPlayerChange(Character character)
{
var name = character.Name.ToString();
if (!EnabledDesigns.TryGetValue(name, out var designs))
return;
var design = designs.OrderBy(d => d.Jobs.Count).FirstOrDefault(d => d.Jobs.Fits(character.ClassJob.Id));
if (design == null)
return;
PluginLog.Debug("Redrawing {CharacterName} with {DesignName} for job {JobGroup}.", name, design.Design.FullName(),
design.Jobs.Name);
design.Design.Data.Apply(character);
Glamourer.PlayerWatcher.UpdatePlayerWithoutEvent(character);
Glamourer.Penumbra.RedrawObject(character, RedrawType.Redraw, false);
//var name = character.Name.ToString();
//if (!EnabledDesigns.TryGetValue(name, out var designs))
// return;
//
//var design = designs.OrderBy(d => d.Jobs.Count).FirstOrDefault(d => d.Jobs.Fits(character.ClassJob.Id));
//if (design == null)
// return;
//
//PluginLog.Debug("Redrawing {CharacterName} with {DesignName} for job {JobGroup}.", name, design.Design.FullName(),
// design.Jobs.Name);
//design.Design.Data.Apply(character);
//Glamourer.PlayerWatcher.UpdatePlayerWithoutEvent(character);
//Glamourer.Penumbra.RedrawObject(character, RedrawType.Redraw, false);
}
public void Add(string name, Design design, JobGroup group, bool enabled = false)

View file

@ -1,211 +1,337 @@
using System;
using System.Linq;
using System.Reflection;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.Command;
using Dalamud.Hooking;
using Dalamud.Plugin;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Glamourer.Api;
using Glamourer.Customization;
using Glamourer.Designs;
using Glamourer.FileSystem;
using Glamourer.Gui;
using ImGuiNET;
using Penumbra.GameData.ByteString;
using Penumbra.GameData.Enums;
using Penumbra.PlayerWatch;
namespace Glamourer
namespace Glamourer;
public unsafe class FixedDesignManager : IDisposable
{
public class Glamourer : IDalamudPlugin
public delegate ulong FlagSlotForUpdateDelegate(Human* drawObject, uint slot, uint* data);
[Signature("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 8B DA 49 8B F0 48 8B F9 83 FA 0A",
DetourName = nameof(FlagSlotForUpdateDetour))]
public Hook<FlagSlotForUpdateDelegate>? FlagSlotForUpdateHook;
public readonly FixedDesigns FixedDesigns;
public FixedDesignManager(DesignManager designs)
{
private const string HelpString = "[Copy|Apply|Save],[Name or PlaceHolder],<Name for Save>";
public string Name
=> "Glamourer";
public static GlamourerConfig Config = null!;
public static IPlayerWatcher PlayerWatcher = null!;
public static ICustomizationManager Customization = null!;
private readonly Interface _interface;
public readonly DesignManager Designs;
public readonly FixedDesigns FixedDesigns;
public static RevertableDesigns RevertableDesigns = new();
public readonly GlamourerIpc GlamourerIpc;
SignatureHelper.Initialise(this);
FixedDesigns = new FixedDesigns(designs);
public static string Version = string.Empty;
public static PenumbraAttach Penumbra = null!;
if (Glamourer.Config.ApplyFixedDesigns)
Enable();
}
public Glamourer(DalamudPluginInterface pluginInterface)
public void Enable()
{
FlagSlotForUpdateHook?.Enable();
Glamourer.Penumbra.CreatingCharacterBase += ApplyFixedDesign;
}
public void Disable()
{
FlagSlotForUpdateHook?.Disable();
Glamourer.Penumbra.CreatingCharacterBase -= ApplyFixedDesign;
}
public void Dispose()
{
FlagSlotForUpdateHook?.Dispose();
}
private void ApplyFixedDesign(IntPtr addr, IntPtr customize, IntPtr equipData)
{
var human = (FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)addr;
if (human->GameObject.ObjectKind is (byte)ObjectKind.EventNpc or (byte)ObjectKind.BattleNpc or (byte)ObjectKind.Player
&& human->ModelCharaId == 0)
{
Dalamud.Initialize(pluginInterface);
Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "";
Config = GlamourerConfig.Load();
Customization = CustomizationManager.Create(Dalamud.PluginInterface, Dalamud.GameData, Dalamud.ClientState.ClientLanguage);
Designs = new DesignManager();
Penumbra = new PenumbraAttach(Config.AttachToPenumbra);
PlayerWatcher = PlayerWatchFactory.Create(Dalamud.Framework, Dalamud.ClientState, Dalamud.Objects);
GlamourerIpc = new GlamourerIpc(Dalamud.ClientState, Dalamud.Objects, Dalamud.PluginInterface);
if (!Config.ApplyFixedDesigns)
PlayerWatcher.Disable();
FixedDesigns = new FixedDesigns(Designs);
Dalamud.Commands.AddHandler("/glamourer", new CommandInfo(OnGlamourer)
var name = new Utf8String(human->GameObject.Name).ToString();
if (FixedDesigns.EnabledDesigns.TryGetValue(name, out var designs))
{
HelpMessage = "Open or close the Glamourer window.",
});
Dalamud.Commands.AddHandler("/glamour", new CommandInfo(OnGlamour)
{
HelpMessage = $"Use Glamourer Functions: {HelpString}",
});
_interface = new Interface(this);
}
public void OnGlamourer(string command, string arguments)
=> _interface.ToggleVisibility();
private static GameObject? GetPlayer(string name)
{
var lowerName = name.ToLowerInvariant();
return lowerName switch
{
"" => null,
"<me>" => Dalamud.Objects[Interface.GPoseObjectId] ?? Dalamud.ClientState.LocalPlayer,
"self" => Dalamud.Objects[Interface.GPoseObjectId] ?? Dalamud.ClientState.LocalPlayer,
"<t>" => Dalamud.Targets.Target,
"target" => Dalamud.Targets.Target,
"<f>" => Dalamud.Targets.FocusTarget,
"focus" => Dalamud.Targets.FocusTarget,
"<mo>" => Dalamud.Targets.MouseOverTarget,
"mouseover" => Dalamud.Targets.MouseOverTarget,
_ => Dalamud.Objects.LastOrDefault(
a => string.Equals(a.Name.ToString(), lowerName, StringComparison.InvariantCultureIgnoreCase)),
};
}
public void CopyToClipboard(Character player)
{
var save = new CharacterSave();
save.LoadCharacter(player);
ImGui.SetClipboardText(save.ToBase64());
}
public void ApplyCommand(Character player, string target)
{
CharacterSave? save = null;
if (target.ToLowerInvariant() == "clipboard")
try
var design = designs.OrderBy(d => d.Jobs.Count).FirstOrDefault(d => d.Jobs.Fits(human->ClassJob));
if (design != null)
{
save = CharacterSave.FromString(ImGui.GetClipboardText());
}
catch (Exception)
{
Dalamud.Chat.PrintError("Clipboard does not contain a valid customization string.");
}
else if (!Designs.FileSystem.Find(target, out var child) || child is not Design d)
Dalamud.Chat.PrintError("The given path to a saved design does not exist or does not point to a design.");
else
save = d.Data;
if (design.Design.Data.WriteCustomizations)
*(CharacterCustomization*)customize = design.Design.Data.Customizations;
save?.Apply(player);
Penumbra.UpdateCharacters(player);
}
public void SaveCommand(Character player, string path)
{
var save = new CharacterSave();
save.LoadCharacter(player);
try
{
var (folder, name) = Designs.FileSystem.CreateAllFolders(path);
var design = new Design(folder, name) { Data = save };
folder.FindOrAddChild(design);
Designs.Designs.Add(design.FullName(), design.Data);
Designs.SaveToFile();
}
catch (Exception e)
{
Dalamud.Chat.PrintError("Could not save file:");
Dalamud.Chat.PrintError($" {e.Message}");
}
}
public void OnGlamour(string command, string arguments)
{
static void PrintHelp()
{
Dalamud.Chat.Print("Usage:");
Dalamud.Chat.Print($" {HelpString}");
}
arguments = arguments.Trim();
if (!arguments.Any())
{
PrintHelp();
return;
}
var split = arguments.Split(new[]
{
',',
}, 3, StringSplitOptions.RemoveEmptyEntries);
if (split.Length < 2)
{
PrintHelp();
return;
}
var player = GetPlayer(split[1]) as Character;
if (player == null)
{
Dalamud.Chat.Print($"Could not find object for {split[1]} or it was not a Character.");
return;
}
switch (split[0].ToLowerInvariant())
{
case "copy":
CopyToClipboard(player);
return;
case "apply":
{
if (split.Length < 3)
var data = (uint*)equipData;
for (var i = 0u; i < 10; ++i)
{
Dalamud.Chat.Print("Applying requires a name for the save to be applied or 'clipboard'.");
return;
var slot = i.ToEquipSlot();
if (design.Design.Data.WriteEquipment.Fits(slot))
data[i] = slot switch
{
EquipSlot.Head => design.Design.Data.Equipment.Head.Value,
EquipSlot.Body => design.Design.Data.Equipment.Body.Value,
EquipSlot.Hands => design.Design.Data.Equipment.Hands.Value,
EquipSlot.Legs => design.Design.Data.Equipment.Legs.Value,
EquipSlot.Feet => design.Design.Data.Equipment.Feet.Value,
EquipSlot.Ears => design.Design.Data.Equipment.Ears.Value,
EquipSlot.Neck => design.Design.Data.Equipment.Neck.Value,
EquipSlot.Wrists => design.Design.Data.Equipment.Wrists.Value,
EquipSlot.RFinger => design.Design.Data.Equipment.RFinger.Value,
EquipSlot.LFinger => design.Design.Data.Equipment.LFinger.Value,
_ => 0,
};
}
ApplyCommand(player, split[2]);
return;
}
case "save":
{
if (split.Length < 3)
{
Dalamud.Chat.Print("Saving requires a name for the save.");
return;
}
SaveCommand(player, split[2]);
return;
}
default:
PrintHelp();
return;
}
}
public void Dispose()
{
FixedDesigns.Dispose();
Penumbra.Dispose();
PlayerWatcher.Dispose();
_interface.Dispose();
GlamourerIpc.Dispose();
Dalamud.Commands.RemoveHandler("/glamour");
Dalamud.Commands.RemoveHandler("/glamourer");
}
}
private ulong FlagSlotForUpdateDetour(Human* drawObject, uint slotIdx, uint* data)
{
ulong ret;
var slot = slotIdx.ToEquipSlot();
try
{
if (slot != EquipSlot.Unknown)
{
var gameObject =
(FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)Glamourer.Penumbra.GameObjectFromDrawObject((IntPtr)drawObject);
if (gameObject != null)
{
var name = new Utf8String(gameObject->GameObject.Name).ToString();
if (FixedDesigns.EnabledDesigns.TryGetValue(name, out var designs))
{
var design = designs.OrderBy(d => d.Jobs.Count).FirstOrDefault(d => d.Jobs.Fits(gameObject->ClassJob));
if (design != null && design.Design.Data.WriteEquipment.Fits(slot))
*data = slot switch
{
EquipSlot.Head => design.Design.Data.Equipment.Head.Value,
EquipSlot.Body => design.Design.Data.Equipment.Body.Value,
EquipSlot.Hands => design.Design.Data.Equipment.Hands.Value,
EquipSlot.Legs => design.Design.Data.Equipment.Legs.Value,
EquipSlot.Feet => design.Design.Data.Equipment.Feet.Value,
EquipSlot.Ears => design.Design.Data.Equipment.Ears.Value,
EquipSlot.Neck => design.Design.Data.Equipment.Neck.Value,
EquipSlot.Wrists => design.Design.Data.Equipment.Wrists.Value,
EquipSlot.RFinger => design.Design.Data.Equipment.RFinger.Value,
EquipSlot.LFinger => design.Design.Data.Equipment.LFinger.Value,
_ => 0,
};
}
}
}
}
finally
{
ret = FlagSlotForUpdateHook!.Original(drawObject, slotIdx, data);
}
return ret;
}
}
public class Glamourer : IDalamudPlugin
{
private const string HelpString = "[Copy|Apply|Save],[Name or PlaceHolder],<Name for Save>";
public string Name
=> "Glamourer";
public static GlamourerConfig Config = null!;
public static IPlayerWatcher PlayerWatcher = null!;
public static ICustomizationManager Customization = null!;
public static FixedDesignManager FixedDesignManager = null!;
private readonly Interface _interface;
public readonly DesignManager Designs;
public static RevertableDesigns RevertableDesigns = new();
public readonly GlamourerIpc GlamourerIpc;
public static string Version = string.Empty;
public static PenumbraAttach Penumbra = null!;
public Glamourer(DalamudPluginInterface pluginInterface)
{
Dalamud.Initialize(pluginInterface);
Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "";
Config = GlamourerConfig.Load();
Customization = CustomizationManager.Create(Dalamud.PluginInterface, Dalamud.GameData, Dalamud.ClientState.ClientLanguage);
Designs = new DesignManager();
Penumbra = new PenumbraAttach(Config.AttachToPenumbra);
PlayerWatcher = PlayerWatchFactory.Create(Dalamud.Framework, Dalamud.ClientState, Dalamud.Objects);
GlamourerIpc = new GlamourerIpc(Dalamud.ClientState, Dalamud.Objects, Dalamud.PluginInterface);
FixedDesignManager = new FixedDesignManager(Designs);
if (!Config.ApplyFixedDesigns)
PlayerWatcher.Disable();
Dalamud.Commands.AddHandler("/glamourer", new CommandInfo(OnGlamourer)
{
HelpMessage = "Open or close the Glamourer window.",
});
Dalamud.Commands.AddHandler("/glamour", new CommandInfo(OnGlamour)
{
HelpMessage = $"Use Glamourer Functions: {HelpString}",
});
_interface = new Interface(this);
}
public void OnGlamourer(string command, string arguments)
=> _interface.ToggleVisibility();
private static GameObject? GetPlayer(string name)
{
var lowerName = name.ToLowerInvariant();
return lowerName switch
{
"" => null,
"<me>" => Dalamud.Objects[Interface.GPoseObjectId] ?? Dalamud.ClientState.LocalPlayer,
"self" => Dalamud.Objects[Interface.GPoseObjectId] ?? Dalamud.ClientState.LocalPlayer,
"<t>" => Dalamud.Targets.Target,
"target" => Dalamud.Targets.Target,
"<f>" => Dalamud.Targets.FocusTarget,
"focus" => Dalamud.Targets.FocusTarget,
"<mo>" => Dalamud.Targets.MouseOverTarget,
"mouseover" => Dalamud.Targets.MouseOverTarget,
_ => Dalamud.Objects.LastOrDefault(
a => string.Equals(a.Name.ToString(), lowerName, StringComparison.InvariantCultureIgnoreCase)),
};
}
public void CopyToClipboard(Character player)
{
var save = new CharacterSave();
save.LoadCharacter(player);
ImGui.SetClipboardText(save.ToBase64());
}
public void ApplyCommand(Character player, string target)
{
CharacterSave? save = null;
if (target.ToLowerInvariant() == "clipboard")
try
{
save = CharacterSave.FromString(ImGui.GetClipboardText());
}
catch (Exception)
{
Dalamud.Chat.PrintError("Clipboard does not contain a valid customization string.");
}
else if (!Designs.FileSystem.Find(target, out var child) || child is not Design d)
Dalamud.Chat.PrintError("The given path to a saved design does not exist or does not point to a design.");
else
save = d.Data;
save?.Apply(player);
Penumbra.UpdateCharacters(player);
}
public void SaveCommand(Character player, string path)
{
var save = new CharacterSave();
save.LoadCharacter(player);
try
{
var (folder, name) = Designs.FileSystem.CreateAllFolders(path);
var design = new Design(folder, name) { Data = save };
folder.FindOrAddChild(design);
Designs.Designs.Add(design.FullName(), design.Data);
Designs.SaveToFile();
}
catch (Exception e)
{
Dalamud.Chat.PrintError("Could not save file:");
Dalamud.Chat.PrintError($" {e.Message}");
}
}
public void OnGlamour(string command, string arguments)
{
static void PrintHelp()
{
Dalamud.Chat.Print("Usage:");
Dalamud.Chat.Print($" {HelpString}");
}
arguments = arguments.Trim();
if (!arguments.Any())
{
PrintHelp();
return;
}
var split = arguments.Split(new[]
{
',',
}, 3, StringSplitOptions.RemoveEmptyEntries);
if (split.Length < 2)
{
PrintHelp();
return;
}
var player = GetPlayer(split[1]) as Character;
if (player == null)
{
Dalamud.Chat.Print($"Could not find object for {split[1]} or it was not a Character.");
return;
}
switch (split[0].ToLowerInvariant())
{
case "copy":
CopyToClipboard(player);
return;
case "apply":
{
if (split.Length < 3)
{
Dalamud.Chat.Print("Applying requires a name for the save to be applied or 'clipboard'.");
return;
}
ApplyCommand(player, split[2]);
return;
}
case "save":
{
if (split.Length < 3)
{
Dalamud.Chat.Print("Saving requires a name for the save.");
return;
}
SaveCommand(player, split[2]);
return;
}
default:
PrintHelp();
return;
}
}
public void Dispose()
{
FixedDesignManager.Dispose();
Penumbra.Dispose();
PlayerWatcher.Dispose();
_interface.Dispose();
GlamourerIpc.Dispose();
Dalamud.Commands.RemoveHandler("/glamour");
Dalamud.Commands.RemoveHandler("/glamourer");
}
}

View file

@ -50,6 +50,10 @@
<HintPath>$(appdata)\XIVLauncher\addon\Hooks\dev\Dalamud.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="FFXIVClientStructs">
<HintPath>$(appdata)\XIVLauncher\addon\Hooks\dev\FFXIVClientStructs.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="ImGui.NET">
<HintPath>$(appdata)\XIVLauncher\addon\Hooks\dev\ImGui.NET.dll</HintPath>
<Private>False</Private>

View file

@ -4,205 +4,204 @@ using System.Linq;
using System.Numerics;
using ImGuiNET;
namespace Glamourer.Gui
namespace Glamourer.Gui;
public class ComboWithFilter<T>
{
public class ComboWithFilter<T>
private readonly string _label;
private readonly string _filterLabel;
private readonly string _listLabel;
private string _currentFilter = string.Empty;
private string _currentFilterLower = string.Empty;
private bool _focus;
private readonly float _size;
private float _previewSize;
private readonly IReadOnlyList<T> _items;
private readonly IReadOnlyList<(string, int)> _itemNamesLower;
private readonly Func<T, string> _itemToName;
private IReadOnlyList<(string, int)> _currentItemNames;
private bool _needsClear;
public Action? PrePreview;
public Action? PostPreview;
public Func<T, bool>? CreateSelectable;
public Action? PreList;
public Action? PostList;
public float? HeightPerItem;
private float _heightPerItem;
public ImGuiComboFlags Flags { get; set; } = ImGuiComboFlags.None;
public int ItemsAtOnce { get; set; } = 12;
private void UpdateFilter(string newFilter)
{
private readonly string _label;
private readonly string _filterLabel;
private readonly string _listLabel;
private string _currentFilter = string.Empty;
private string _currentFilterLower = string.Empty;
private bool _focus;
private readonly float _size;
private float _previewSize;
private readonly IReadOnlyList<T> _items;
private readonly IReadOnlyList<(string, int)> _itemNamesLower;
private readonly Func<T, string> _itemToName;
private IReadOnlyList<(string, int)> _currentItemNames;
private bool _needsClear;
if (newFilter == _currentFilter)
return;
public Action? PrePreview;
public Action? PostPreview;
public Func<T, bool>? CreateSelectable;
public Action? PreList;
public Action? PostList;
public float? HeightPerItem;
private float _heightPerItem;
public ImGuiComboFlags Flags { get; set; } = ImGuiComboFlags.None;
public int ItemsAtOnce { get; set; } = 12;
private void UpdateFilter(string newFilter)
{
if (newFilter == _currentFilter)
return;
var lower = newFilter.ToLowerInvariant();
if (_currentFilterLower.Any() && lower.Contains(_currentFilterLower))
_currentItemNames = _currentItemNames.Where(p => p.Item1.Contains(lower)).ToArray();
else if (lower.Any())
_currentItemNames = _itemNamesLower.Where(p => p.Item1.Contains(lower)).ToArray();
else
_currentItemNames = _itemNamesLower;
_currentFilter = newFilter;
_currentFilterLower = lower;
}
public ComboWithFilter(string label, float size, float previewSize, IReadOnlyList<T> items, Func<T, string> itemToName)
{
_label = label;
_filterLabel = $"##_{label}_filter";
_listLabel = $"##_{label}_list";
_itemToName = itemToName;
_items = items;
_size = size;
_previewSize = previewSize;
_itemNamesLower = _items.Select((i, idx) => (_itemToName(i).ToLowerInvariant(), idx)).ToArray();
var lower = newFilter.ToLowerInvariant();
if (_currentFilterLower.Any() && lower.Contains(_currentFilterLower))
_currentItemNames = _currentItemNames.Where(p => p.Item1.Contains(lower)).ToArray();
else if (lower.Any())
_currentItemNames = _itemNamesLower.Where(p => p.Item1.Contains(lower)).ToArray();
else
_currentItemNames = _itemNamesLower;
_currentFilter = newFilter;
_currentFilterLower = lower;
}
public ComboWithFilter(string label, float size, float previewSize, IReadOnlyList<T> items, Func<T, string> itemToName)
{
_label = label;
_filterLabel = $"##_{label}_filter";
_listLabel = $"##_{label}_list";
_itemToName = itemToName;
_items = items;
_size = size;
_previewSize = previewSize;
_itemNamesLower = _items.Select((i, idx) => (_itemToName(i).ToLowerInvariant(), idx)).ToArray();
_currentItemNames = _itemNamesLower;
}
public ComboWithFilter(string label, ComboWithFilter<T> other)
{
_label = label;
_filterLabel = $"##_{label}_filter";
_listLabel = $"##_{label}_list";
_itemToName = other._itemToName;
_items = other._items;
_itemNamesLower = other._itemNamesLower;
_currentItemNames = other._currentItemNames;
_size = other._size;
_previewSize = other._previewSize;
PrePreview = other.PrePreview;
PostPreview = other.PostPreview;
CreateSelectable = other.CreateSelectable;
PreList = other.PreList;
PostList = other.PostList;
HeightPerItem = other.HeightPerItem;
Flags = other.Flags;
}
private bool DrawList(string currentName, out int numItems, out int nodeIdx, ref T? value)
{
numItems = ItemsAtOnce;
nodeIdx = -1;
if (!ImGui.BeginChild(_listLabel, new Vector2(_size, ItemsAtOnce * _heightPerItem)))
{
ImGui.EndChild();
return false;
}
public ComboWithFilter(string label, ComboWithFilter<T> other)
var ret = false;
try
{
_label = label;
_filterLabel = $"##_{label}_filter";
_listLabel = $"##_{label}_list";
_itemToName = other._itemToName;
_items = other._items;
_itemNamesLower = other._itemNamesLower;
_currentItemNames = other._currentItemNames;
_size = other._size;
_previewSize = other._previewSize;
PrePreview = other.PrePreview;
PostPreview = other.PostPreview;
CreateSelectable = other.CreateSelectable;
PreList = other.PreList;
PostList = other.PostList;
HeightPerItem = other.HeightPerItem;
Flags = other.Flags;
}
private bool DrawList(string currentName, out int numItems, out int nodeIdx, ref T? value)
{
numItems = ItemsAtOnce;
nodeIdx = -1;
if (!ImGui.BeginChild(_listLabel, new Vector2(_size, ItemsAtOnce * _heightPerItem)))
if (!_focus)
{
ImGui.EndChild();
return false;
ImGui.SetScrollY(0);
_focus = true;
}
var ret = false;
try
var scrollY = Math.Max((int)(ImGui.GetScrollY() / _heightPerItem) - 1, 0);
var restHeight = scrollY * _heightPerItem;
numItems = 0;
nodeIdx = 0;
if (restHeight > 0)
ImGui.Dummy(Vector2.UnitY * restHeight);
for (var i = scrollY; i < _currentItemNames.Count; ++i)
{
if (!_focus)
if (++numItems > ItemsAtOnce + 2)
continue;
nodeIdx = _currentItemNames[i].Item2;
var item = _items[nodeIdx]!;
bool success;
if (CreateSelectable != null)
{
ImGui.SetScrollY(0);
_focus = true;
success = CreateSelectable(item);
}
else
{
var name = _itemToName(item);
success = ImGui.Selectable(name, name == currentName);
}
var scrollY = Math.Max((int) (ImGui.GetScrollY() / _heightPerItem) - 1, 0);
var restHeight = scrollY * _heightPerItem;
numItems = 0;
nodeIdx = 0;
if (restHeight > 0)
ImGui.Dummy(Vector2.UnitY * restHeight);
for (var i = scrollY; i < _currentItemNames.Count; ++i)
if (success)
{
if (++numItems > ItemsAtOnce + 2)
continue;
nodeIdx = _currentItemNames[i].Item2;
var item = _items[nodeIdx]!;
bool success;
if (CreateSelectable != null)
{
success = CreateSelectable(item);
}
else
{
var name = _itemToName(item);
success = ImGui.Selectable(name, name == currentName);
}
if (success)
{
value = item;
ImGui.CloseCurrentPopup();
ret = true;
}
}
if (_currentItemNames.Count > ItemsAtOnce + 2)
ImGui.Dummy(Vector2.UnitY * (_currentItemNames.Count - ItemsAtOnce - 2 - scrollY) * _heightPerItem);
}
finally
{
ImGui.EndChild();
}
return ret;
}
public bool Draw(string currentName, out T? value, float? size = null)
{
if (size.HasValue)
_previewSize = size.Value;
value = default;
ImGui.SetNextItemWidth(_previewSize);
PrePreview?.Invoke();
if (!ImGui.BeginCombo(_label, currentName, Flags))
{
if (_needsClear)
{
_needsClear = false;
_focus = false;
UpdateFilter(string.Empty);
}
PostPreview?.Invoke();
return false;
}
_needsClear = true;
PostPreview?.Invoke();
_heightPerItem = HeightPerItem ?? ImGui.GetTextLineHeightWithSpacing();
bool ret;
try
{
ImGui.SetNextItemWidth(-1);
var tmp = _currentFilter;
if (ImGui.InputTextWithHint(_filterLabel, "Filter...", ref tmp, 255))
UpdateFilter(tmp);
var isFocused = ImGui.IsItemActive();
if (!_focus)
ImGui.SetKeyboardFocusHere();
PreList?.Invoke();
ret = DrawList(currentName, out var numItems, out var nodeIdx, ref value);
PostList?.Invoke();
if (!isFocused && numItems <= 1 && nodeIdx >= 0)
{
value = _items[nodeIdx];
ret = true;
value = item;
ImGui.CloseCurrentPopup();
ret = true;
}
}
finally
if (_currentItemNames.Count > ItemsAtOnce + 2)
ImGui.Dummy(Vector2.UnitY * (_currentItemNames.Count - ItemsAtOnce - 2 - scrollY) * _heightPerItem);
}
finally
{
ImGui.EndChild();
}
return ret;
}
public bool Draw(string currentName, out T? value, float? size = null)
{
if (size.HasValue)
_previewSize = size.Value;
value = default;
ImGui.SetNextItemWidth(_previewSize);
PrePreview?.Invoke();
if (!ImGui.BeginCombo(_label, currentName, Flags))
{
if (_needsClear)
{
ImGui.EndCombo();
_needsClear = false;
_focus = false;
UpdateFilter(string.Empty);
}
return ret;
PostPreview?.Invoke();
return false;
}
_needsClear = true;
PostPreview?.Invoke();
_heightPerItem = HeightPerItem ?? ImGui.GetTextLineHeightWithSpacing();
bool ret;
try
{
ImGui.SetNextItemWidth(-1);
var tmp = _currentFilter;
if (ImGui.InputTextWithHint(_filterLabel, "Filter...", ref tmp, 255))
UpdateFilter(tmp);
var isFocused = ImGui.IsItemActive();
if (!_focus)
ImGui.SetKeyboardFocusHere();
PreList?.Invoke();
ret = DrawList(currentName, out var numItems, out var nodeIdx, ref value);
PostList?.Invoke();
if (!isFocused && numItems <= 1 && nodeIdx >= 0)
{
value = _items[nodeIdx];
ret = true;
ImGui.CloseCurrentPopup();
}
}
finally
{
ImGui.EndCombo();
}
return ret;
}
}

View file

@ -1,14 +0,0 @@
using System.Linq;
using ImGuiNET;
namespace Glamourer.Gui
{
public static partial class ImGuiCustom
{
public static void HoverTooltip(string text)
{
if (text.Any() && ImGui.IsItemHovered())
ImGui.SetTooltip(text);
}
}
}

View file

@ -1,155 +0,0 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using ImGuiNET;
namespace Glamourer.Gui
{
public sealed class ImGuiRaii : IDisposable
{
private int _colorStack;
private int _fontStack;
private int _styleStack;
private float _indentation;
private Stack<Action>? _onDispose;
public static ImGuiRaii NewGroup()
=> new ImGuiRaii().Group();
public ImGuiRaii Group()
=> Begin(ImGui.BeginGroup, ImGui.EndGroup);
public static ImGuiRaii NewTooltip()
=> new ImGuiRaii().Tooltip();
public ImGuiRaii Tooltip()
=> Begin(ImGui.BeginTooltip, ImGui.EndTooltip);
public ImGuiRaii PushColor(ImGuiCol which, uint color)
{
ImGui.PushStyleColor(which, color);
++_colorStack;
return this;
}
public ImGuiRaii PushColor(ImGuiCol which, Vector4 color)
{
ImGui.PushStyleColor(which, color);
++_colorStack;
return this;
}
public ImGuiRaii PopColors(int n = 1)
{
var actualN = Math.Min(n, _colorStack);
if (actualN > 0)
{
ImGui.PopStyleColor(actualN);
_colorStack -= actualN;
}
return this;
}
public ImGuiRaii PushStyle(ImGuiStyleVar style, Vector2 value)
{
ImGui.PushStyleVar(style, value);
++_styleStack;
return this;
}
public ImGuiRaii PushStyle(ImGuiStyleVar style, float value)
{
ImGui.PushStyleVar(style, value);
++_styleStack;
return this;
}
public ImGuiRaii PopStyles(int n = 1)
{
var actualN = Math.Min(n, _styleStack);
if (actualN > 0)
{
ImGui.PopStyleVar(actualN);
_styleStack -= actualN;
}
return this;
}
public ImGuiRaii PushFont(ImFontPtr font)
{
ImGui.PushFont(font);
++_fontStack;
return this;
}
public ImGuiRaii PopFonts(int n = 1)
{
var actualN = Math.Min(n, _fontStack);
while (actualN-- > 0)
{
ImGui.PopFont();
--_fontStack;
}
return this;
}
public ImGuiRaii Indent(float width)
{
if (width != 0)
{
ImGui.Indent(width);
_indentation += width;
}
return this;
}
public ImGuiRaii Unindent(float width)
=> Indent(-width);
public bool Begin(Func<bool> begin, Action end)
{
if (begin())
{
_onDispose ??= new Stack<Action>();
_onDispose.Push(end);
return true;
}
return false;
}
public ImGuiRaii Begin(Action begin, Action end)
{
begin();
_onDispose ??= new Stack<Action>();
_onDispose.Push(end);
return this;
}
public void End(int n = 1)
{
var actualN = Math.Min(n, _onDispose?.Count ?? 0);
while (actualN-- > 0)
_onDispose!.Pop()();
}
public void Dispose()
{
Unindent(_indentation);
PopColors(_colorStack);
PopStyles(_styleStack);
PopFonts(_fontStack);
if (_onDispose != null)
{
End(_onDispose.Count);
_onDispose = null;
}
}
}
}

View file

@ -7,104 +7,104 @@ using Dalamud.Game.ClientState.Objects.Types;
using Glamourer.Designs;
using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
using OtterGui.Raii;
using Penumbra.GameData;
using Penumbra.GameData.Enums;
namespace Glamourer.Gui
namespace Glamourer.Gui;
internal partial class Interface : IDisposable
{
internal partial class Interface : IDisposable
public const float SelectorWidth = 200;
public const float MinWindowWidth = 675;
public const int GPoseObjectId = 201;
private const string PluginName = "Glamourer";
private readonly string _glamourerHeader;
private readonly IReadOnlyDictionary<byte, Stain> _stains;
private readonly IReadOnlyDictionary<uint, ModelChara> _models;
private readonly IObjectIdentifier _identifier;
private readonly Dictionary<EquipSlot, (ComboWithFilter<Item>, ComboWithFilter<Stain>)> _combos;
private readonly ImGuiScene.TextureWrap? _legacyTattooIcon;
private readonly Dictionary<EquipSlot, string> _equipSlotNames;
private readonly DesignManager _designs;
private readonly Glamourer _plugin;
private bool _visible;
private bool _inGPose;
public Interface(Glamourer plugin)
{
public const float SelectorWidth = 200;
public const float MinWindowWidth = 675;
public const int GPoseObjectId = 201;
private const string PluginName = "Glamourer";
private readonly string _glamourerHeader;
_plugin = plugin;
_designs = plugin.Designs;
_glamourerHeader = Glamourer.Version.Length > 0
? $"{PluginName} v{Glamourer.Version}###{PluginName}Main"
: $"{PluginName}###{PluginName}Main";
Dalamud.PluginInterface.UiBuilder.DisableGposeUiHide = true;
Dalamud.PluginInterface.UiBuilder.Draw += Draw;
Dalamud.PluginInterface.UiBuilder.OpenConfigUi += ToggleVisibility;
private readonly IReadOnlyDictionary<byte, Stain> _stains;
private readonly IReadOnlyDictionary<uint, ModelChara> _models;
private readonly IObjectIdentifier _identifier;
private readonly Dictionary<EquipSlot, (ComboWithFilter<Item>, ComboWithFilter<Stain>)> _combos;
private readonly ImGuiScene.TextureWrap? _legacyTattooIcon;
private readonly Dictionary<EquipSlot, string> _equipSlotNames;
private readonly DesignManager _designs;
private readonly Glamourer _plugin;
_equipSlotNames = GetEquipSlotNames();
private bool _visible;
private bool _inGPose;
_stains = GameData.Stains(Dalamud.GameData);
_models = GameData.Models(Dalamud.GameData);
_identifier = Penumbra.GameData.GameData.GetIdentifier(Dalamud.GameData, Dalamud.ClientState.ClientLanguage);
public Interface(Glamourer plugin)
var stainCombo = CreateDefaultStainCombo(_stains.Values.ToArray());
var equip = GameData.ItemsBySlot(Dalamud.GameData);
_combos = equip.ToDictionary(kvp => kvp.Key, kvp => CreateCombos(kvp.Key, kvp.Value, stainCombo));
_legacyTattooIcon = GetLegacyTattooIcon();
}
public void ToggleVisibility()
=> _visible = !_visible;
public void Dispose()
{
_legacyTattooIcon?.Dispose();
Dalamud.PluginInterface.UiBuilder.Draw -= Draw;
Dalamud.PluginInterface.UiBuilder.OpenConfigUi -= ToggleVisibility;
}
private void Draw()
{
if (!_visible)
return;
ImGui.SetNextWindowSizeConstraints(Vector2.One * MinWindowWidth * ImGui.GetIO().FontGlobalScale,
Vector2.One * 5000 * ImGui.GetIO().FontGlobalScale);
if (!ImGui.Begin(_glamourerHeader, ref _visible))
{
_plugin = plugin;
_designs = plugin.Designs;
_glamourerHeader = Glamourer.Version.Length > 0
? $"{PluginName} v{Glamourer.Version}###{PluginName}Main"
: $"{PluginName}###{PluginName}Main";
Dalamud.PluginInterface.UiBuilder.DisableGposeUiHide = true;
Dalamud.PluginInterface.UiBuilder.Draw += Draw;
Dalamud.PluginInterface.UiBuilder.OpenConfigUi += ToggleVisibility;
_equipSlotNames = GetEquipSlotNames();
_stains = GameData.Stains(Dalamud.GameData);
_models = GameData.Models(Dalamud.GameData);
_identifier = Penumbra.GameData.GameData.GetIdentifier(Dalamud.GameData, Dalamud.ClientState.ClientLanguage);
var stainCombo = CreateDefaultStainCombo(_stains.Values.ToArray());
var equip = GameData.ItemsBySlot(Dalamud.GameData);
_combos = equip.ToDictionary(kvp => kvp.Key, kvp => CreateCombos(kvp.Key, kvp.Value, stainCombo));
_legacyTattooIcon = GetLegacyTattooIcon();
ImGui.End();
return;
}
public void ToggleVisibility()
=> _visible = !_visible;
public void Dispose()
try
{
_legacyTattooIcon?.Dispose();
Dalamud.PluginInterface.UiBuilder.Draw -= Draw;
Dalamud.PluginInterface.UiBuilder.OpenConfigUi -= ToggleVisibility;
using var tabBar = ImRaii.TabBar("##tabBar");
if (!tabBar)
return;
_inGPose = Dalamud.Objects[GPoseObjectId] != null;
_iconSize = Vector2.One * ImGui.GetTextLineHeightWithSpacing() * 2;
_actualIconSize = _iconSize + 2 * ImGui.GetStyle().FramePadding;
_comboSelectorSize = 4 * _actualIconSize.X + 3 * ImGui.GetStyle().ItemSpacing.X;
_percentageSize = _comboSelectorSize;
_inputIntSize = 2 * _actualIconSize.X + ImGui.GetStyle().ItemSpacing.X;
_raceSelectorWidth = _inputIntSize + _percentageSize - _actualIconSize.X;
_itemComboWidth = 6 * _actualIconSize.X + 4 * ImGui.GetStyle().ItemSpacing.X - ColorButtonWidth + 1;
DrawPlayerTab();
DrawSaves();
DrawFixedDesignsTab();
DrawConfigTab();
DrawRevertablesTab();
}
private void Draw()
finally
{
if (!_visible)
return;
ImGui.SetNextWindowSizeConstraints(Vector2.One * MinWindowWidth * ImGui.GetIO().FontGlobalScale,
Vector2.One * 5000 * ImGui.GetIO().FontGlobalScale);
if (!ImGui.Begin(_glamourerHeader, ref _visible))
{
ImGui.End();
return;
}
try
{
using var raii = new ImGuiRaii();
if (!raii.Begin(() => ImGui.BeginTabBar("##tabBar"), ImGui.EndTabBar))
return;
_inGPose = Dalamud.Objects[GPoseObjectId] != null;
_iconSize = Vector2.One * ImGui.GetTextLineHeightWithSpacing() * 2;
_actualIconSize = _iconSize + 2 * ImGui.GetStyle().FramePadding;
_comboSelectorSize = 4 * _actualIconSize.X + 3 * ImGui.GetStyle().ItemSpacing.X;
_percentageSize = _comboSelectorSize;
_inputIntSize = 2 * _actualIconSize.X + ImGui.GetStyle().ItemSpacing.X;
_raceSelectorWidth = _inputIntSize + _percentageSize - _actualIconSize.X;
_itemComboWidth = 6 * _actualIconSize.X + 4 * ImGui.GetStyle().ItemSpacing.X - ColorButtonWidth + 1;
DrawPlayerTab();
DrawSaves();
DrawFixedDesignsTab();
DrawConfigTab();
DrawRevertablesTab();
}
finally
{
ImGui.End();
}
ImGui.End();
}
}
}

View file

@ -11,300 +11,298 @@ using Glamourer.Customization;
using Glamourer.Designs;
using Glamourer.FileSystem;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Structs;
using Penumbra.PlayerWatch;
namespace Glamourer.Gui
namespace Glamourer.Gui;
internal partial class Interface
{
internal partial class Interface
private readonly CharacterSave _currentSave = new();
private string _newDesignName = string.Empty;
private bool _keyboardFocus;
private bool _holdShift;
private bool _holdCtrl;
private const string DesignNamePopupLabel = "Save Design As...";
private const uint RedHeaderColor = 0xFF1818C0;
private const uint GreenHeaderColor = 0xFF18C018;
private void DrawPlayerHeader()
{
private readonly CharacterSave _currentSave = new();
private string _newDesignName = string.Empty;
private bool _keyboardFocus;
private bool _holdShift;
private bool _holdCtrl;
private const string DesignNamePopupLabel = "Save Design As...";
private const uint RedHeaderColor = 0xFF1818C0;
private const uint GreenHeaderColor = 0xFF18C018;
var color = _player == null ? RedHeaderColor : GreenHeaderColor;
var buttonColor = ImGui.GetColorU32(ImGuiCol.FrameBg);
using var c = ImRaii.PushColor(ImGuiCol.Text, color)
.Push(ImGuiCol.Button, buttonColor)
.Push(ImGuiCol.ButtonHovered, buttonColor)
.Push(ImGuiCol.ButtonActive, buttonColor);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
ImGui.Button($"{_currentLabel}##playerHeader", -Vector2.UnitX * 0.0001f);
}
private void DrawPlayerHeader()
private static void DrawCopyClipboardButton(CharacterSave save)
{
ImGui.PushFont(UiBuilder.IconFont);
if (ImGui.Button(FontAwesomeIcon.Clipboard.ToIconString()))
ImGui.SetClipboardText(save.ToBase64());
ImGui.PopFont();
ImGuiUtil.HoverTooltip("Copy customization code to clipboard.");
}
private static void ConditionalApply(CharacterSave save, Character player)
{
if (ImGui.GetIO().KeyShift)
save.ApplyOnlyCustomizations(player);
else if (ImGui.GetIO().KeyCtrl)
save.ApplyOnlyEquipment(player);
else
save.Apply(player);
}
private static CharacterSave ConditionalCopy(CharacterSave save, bool shift, bool ctrl)
{
var copy = save.Copy();
if (shift)
{
var color = _player == null ? RedHeaderColor : GreenHeaderColor;
var buttonColor = ImGui.GetColorU32(ImGuiCol.FrameBg);
using var raii = new ImGuiRaii()
.PushColor(ImGuiCol.Text, color)
.PushColor(ImGuiCol.Button, buttonColor)
.PushColor(ImGuiCol.ButtonHovered, buttonColor)
.PushColor(ImGuiCol.ButtonActive, buttonColor)
.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.PushStyle(ImGuiStyleVar.FrameRounding, 0);
ImGui.Button($"{_currentLabel}##playerHeader", -Vector2.UnitX * 0.0001f);
copy.Load(new CharacterEquipment());
copy.SetHatState = false;
copy.SetVisorState = false;
copy.SetWeaponState = false;
copy.WriteEquipment = CharacterEquipMask.None;
}
else if (ctrl)
{
copy.Load(CharacterCustomization.Default);
copy.SetHatState = false;
copy.SetVisorState = false;
copy.SetWeaponState = false;
copy.WriteCustomizations = false;
}
private static void DrawCopyClipboardButton(CharacterSave save)
return copy;
}
private bool DrawApplyClipboardButton()
{
ImGui.PushFont(UiBuilder.IconFont);
var applyButton = ImGui.Button(FontAwesomeIcon.Paste.ToIconString()) && _player != null;
ImGui.PopFont();
ImGuiUtil.HoverTooltip(
"Apply customization code from clipboard.\nHold Shift to apply only customizations.\nHold Control to apply only equipment.");
if (!applyButton)
return false;
try
{
ImGui.PushFont(UiBuilder.IconFont);
if (ImGui.Button(FontAwesomeIcon.Clipboard.ToIconString()))
ImGui.SetClipboardText(save.ToBase64());
ImGui.PopFont();
ImGuiCustom.HoverTooltip("Copy customization code to clipboard.");
}
private static void ConditionalApply(CharacterSave save, Character player)
{
if (ImGui.GetIO().KeyShift)
save.ApplyOnlyCustomizations(player);
else if (ImGui.GetIO().KeyCtrl)
save.ApplyOnlyEquipment(player);
else
save.Apply(player);
}
private static CharacterSave ConditionalCopy(CharacterSave save, bool shift, bool ctrl)
{
var copy = save.Copy();
if (shift)
{
copy.Load(new CharacterEquipment());
copy.SetHatState = false;
copy.SetVisorState = false;
copy.SetWeaponState = false;
copy.WriteEquipment = CharacterEquipMask.None;
}
else if (ctrl)
{
copy.Load(CharacterCustomization.Default);
copy.SetHatState = false;
copy.SetVisorState = false;
copy.SetWeaponState = false;
copy.WriteCustomizations = false;
}
return copy;
}
private bool DrawApplyClipboardButton()
{
ImGui.PushFont(UiBuilder.IconFont);
var applyButton = ImGui.Button(FontAwesomeIcon.Paste.ToIconString()) && _player != null;
ImGui.PopFont();
ImGuiCustom.HoverTooltip("Apply customization code from clipboard.\nHold Shift to apply only customizations.\nHold Control to apply only equipment.");
if (!applyButton)
var text = ImGui.GetClipboardText();
if (!text.Any())
return false;
try
{
var text = ImGui.GetClipboardText();
if (!text.Any())
return false;
var save = CharacterSave.FromString(text);
ConditionalApply(save, _player!);
}
catch (Exception e)
{
PluginLog.Information($"{e}");
return false;
}
return true;
var save = CharacterSave.FromString(text);
ConditionalApply(save, _player!);
}
catch (Exception e)
{
PluginLog.Information($"{e}");
return false;
}
private void DrawSaveDesignButton()
{
ImGui.PushFont(UiBuilder.IconFont);
if (ImGui.Button(FontAwesomeIcon.Save.ToIconString()))
OpenDesignNamePopup(DesignNameUse.SaveCurrent);
return true;
}
ImGui.PopFont();
ImGuiCustom.HoverTooltip("Save the current design.\nHold Shift to save only customizations.\nHold Control to save only equipment.");
private void DrawSaveDesignButton()
{
ImGui.PushFont(UiBuilder.IconFont);
if (ImGui.Button(FontAwesomeIcon.Save.ToIconString()))
OpenDesignNamePopup(DesignNameUse.SaveCurrent);
DrawDesignNamePopup(DesignNameUse.SaveCurrent);
}
ImGui.PopFont();
ImGuiUtil.HoverTooltip("Save the current design.\nHold Shift to save only customizations.\nHold Control to save only equipment.");
private void DrawTargetPlayerButton()
{
if (ImGui.Button("Target Player"))
Dalamud.Targets.SetTarget(_player);
}
DrawDesignNamePopup(DesignNameUse.SaveCurrent);
}
private void DrawApplyToPlayerButton(CharacterSave save)
{
if (!ImGui.Button("Apply to Self"))
return;
private void DrawTargetPlayerButton()
{
if (ImGui.Button("Target Player"))
Dalamud.Targets.SetTarget(_player);
}
var player = _inGPose
? (Character?) Dalamud.Objects[GPoseObjectId]
: Dalamud.ClientState.LocalPlayer;
var fallback = _inGPose ? Dalamud.ClientState.LocalPlayer : null;
if (player == null)
return;
private void DrawApplyToPlayerButton(CharacterSave save)
{
if (!ImGui.Button("Apply to Self"))
return;
ConditionalApply(save, player);
if (_inGPose)
ConditionalApply(save, fallback!);
Glamourer.Penumbra.UpdateCharacters(player, fallback);
}
var player = _inGPose
? (Character?)Dalamud.Objects[GPoseObjectId]
: Dalamud.ClientState.LocalPlayer;
var fallback = _inGPose ? Dalamud.ClientState.LocalPlayer : null;
if (player == null)
return;
private static Character? TransformToCustomizable(Character? actor)
{
if (actor == null)
return null;
ConditionalApply(save, player);
if (_inGPose)
ConditionalApply(save, fallback!);
Glamourer.Penumbra.UpdateCharacters(player, fallback);
}
if (actor.ModelType() == 0)
return actor;
actor.SetModelType(0);
CharacterCustomization.Default.Write(actor.Address);
private static Character? TransformToCustomizable(Character? actor)
{
if (actor == null)
return null;
if (actor.ModelType() == 0)
return actor;
}
private void DrawApplyToTargetButton(CharacterSave save)
actor.SetModelType(0);
CharacterCustomization.Default.Write(actor.Address);
return actor;
}
private void DrawApplyToTargetButton(CharacterSave save)
{
if (!ImGui.Button("Apply to Target"))
return;
var player = TransformToCustomizable(CharacterFactory.Convert(Dalamud.Targets.Target));
if (player == null)
return;
var fallBackCharacter = _gPoseActors.TryGetValue(player.Name.ToString(), out var f) ? f : null;
ConditionalApply(save, player);
if (fallBackCharacter != null)
ConditionalApply(save, fallBackCharacter!);
Glamourer.Penumbra.UpdateCharacters(player, fallBackCharacter);
}
private void DrawRevertButton()
{
if (!ImGuiUtil.DrawDisabledButton("Revert", Vector2.Zero, string.Empty, _player == null))
return;
Glamourer.RevertableDesigns.Revert(_player!);
var fallBackCharacter = _gPoseActors.TryGetValue(_player!.Name.ToString(), out var f) ? f : null;
if (fallBackCharacter != null)
Glamourer.RevertableDesigns.Revert(fallBackCharacter);
Glamourer.Penumbra.UpdateCharacters(_player, fallBackCharacter);
}
private void SaveNewDesign(CharacterSave save)
{
try
{
if (!ImGui.Button("Apply to Target"))
var (folder, name) = _designs.FileSystem.CreateAllFolders(_newDesignName);
if (!name.Any())
return;
var player = TransformToCustomizable(CharacterFactory.Convert(Dalamud.Targets.Target));
if (player == null)
return;
var newDesign = new Design(folder, name) { Data = save };
folder.AddChild(newDesign);
_designs.Designs[newDesign.FullName()] = save;
_designs.SaveToFile();
}
catch (Exception e)
{
PluginLog.Error($"Could not save new design {_newDesignName}:\n{e}");
}
}
var fallBackCharacter = _gPoseActors.TryGetValue(player.Name.ToString(), out var f) ? f : null;
ConditionalApply(save, player);
if (fallBackCharacter != null)
ConditionalApply(save, fallBackCharacter!);
Glamourer.Penumbra.UpdateCharacters(player, fallBackCharacter);
private void DrawMonsterPanel()
{
if (DrawApplyClipboardButton())
Glamourer.Penumbra.UpdateCharacters(_player!);
ImGui.SameLine();
if (ImGui.Button("Convert to Character"))
{
TransformToCustomizable(_player);
_currentLabel = _currentLabel.Replace("(Monster)", "(NPC)");
Glamourer.Penumbra.UpdateCharacters(_player!);
}
private void DrawRevertButton()
if (!_inGPose)
{
if (!DrawDisableButton("Revert", _player == null))
return;
Glamourer.RevertableDesigns.Revert(_player!);
var fallBackCharacter = _gPoseActors.TryGetValue(_player!.Name.ToString(), out var f) ? f : null;
if (fallBackCharacter != null)
Glamourer.RevertableDesigns.Revert(fallBackCharacter);
Glamourer.Penumbra.UpdateCharacters(_player, fallBackCharacter);
}
private void SaveNewDesign(CharacterSave save)
{
try
{
var (folder, name) = _designs.FileSystem.CreateAllFolders(_newDesignName);
if (!name.Any())
return;
var newDesign = new Design(folder, name) { Data = save };
folder.AddChild(newDesign);
_designs.Designs[newDesign.FullName()] = save;
_designs.SaveToFile();
}
catch (Exception e)
{
PluginLog.Error($"Could not save new design {_newDesignName}:\n{e}");
}
}
private void DrawMonsterPanel()
{
if (DrawApplyClipboardButton())
Glamourer.Penumbra.UpdateCharacters(_player!);
ImGui.SameLine();
if (ImGui.Button("Convert to Character"))
{
TransformToCustomizable(_player);
_currentLabel = _currentLabel.Replace("(Monster)", "(NPC)");
Glamourer.Penumbra.UpdateCharacters(_player!);
}
DrawTargetPlayerButton();
}
if (!_inGPose)
var currentModel = _player!.ModelType();
using var combo = ImRaii.Combo("Model Id", currentModel.ToString());
if (!combo)
return;
foreach (var (id, _) in _models.Skip(1))
{
if (!ImGui.Selectable($"{id:D6}##models", id == currentModel) || id == currentModel)
continue;
_player!.SetModelType((int)id);
Glamourer.Penumbra.UpdateCharacters(_player!);
}
}
private void DrawPlayerPanel()
{
DrawCopyClipboardButton(_currentSave);
ImGui.SameLine();
var changes = !_currentSave.WriteProtected && DrawApplyClipboardButton();
ImGui.SameLine();
DrawSaveDesignButton();
ImGui.SameLine();
DrawApplyToPlayerButton(_currentSave);
if (!_inGPose)
{
ImGui.SameLine();
DrawApplyToTargetButton(_currentSave);
if (_player != null && !_currentSave.WriteProtected)
{
ImGui.SameLine();
DrawTargetPlayerButton();
}
var currentModel = _player!.ModelType();
using var raii = new ImGuiRaii();
if (!raii.Begin(() => ImGui.BeginCombo("Model Id", currentModel.ToString()), ImGui.EndCombo))
return;
foreach (var (id, _) in _models.Skip(1))
{
if (!ImGui.Selectable($"{id:D6}##models", id == currentModel) || id == currentModel)
continue;
_player!.SetModelType((int) id);
Glamourer.Penumbra.UpdateCharacters(_player!);
}
}
private void DrawPlayerPanel()
var data = _currentSave;
if (!_currentSave.WriteProtected)
{
DrawCopyClipboardButton(_currentSave);
ImGui.SameLine();
var changes = !_currentSave.WriteProtected && DrawApplyClipboardButton();
ImGui.SameLine();
DrawSaveDesignButton();
ImGui.SameLine();
DrawApplyToPlayerButton(_currentSave);
if (!_inGPose)
{
ImGui.SameLine();
DrawApplyToTargetButton(_currentSave);
if (_player != null && !_currentSave.WriteProtected)
{
ImGui.SameLine();
DrawTargetPlayerButton();
}
}
var data = _currentSave;
if (!_currentSave.WriteProtected)
{
ImGui.SameLine();
DrawRevertButton();
}
else
{
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.8f);
data = data.Copy();
}
if (DrawCustomization(ref data.Customizations) && _player != null)
{
Glamourer.RevertableDesigns.Add(_player);
_currentSave.Customizations.Write(_player.Address);
changes = true;
}
changes |= DrawEquip(data.Equipment);
changes |= DrawMiscellaneous(data, _player);
if (_player != null && changes)
Glamourer.Penumbra.UpdateCharacters(_player);
if (_currentSave.WriteProtected)
ImGui.PopStyleVar();
DrawRevertButton();
}
private void DrawActorPanel()
else
{
using var raii = ImGuiRaii.NewGroup();
DrawPlayerHeader();
if (!ImGui.BeginChild("##playerData", -Vector2.One, true))
{
ImGui.EndChild();
return;
}
if (_player == null || _player.ModelType() == 0)
DrawPlayerPanel();
else
DrawMonsterPanel();
ImGui.EndChild();
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.8f);
data = data.Copy();
}
if (DrawCustomization(ref data.Customizations) && _player != null)
{
Glamourer.RevertableDesigns.Add(_player);
_currentSave.Customizations.Write(_player.Address);
changes = true;
}
changes |= DrawEquip(data.Equipment);
changes |= DrawMiscellaneous(data, _player);
if (_player != null && changes)
Glamourer.Penumbra.UpdateCharacters(_player);
if (_currentSave.WriteProtected)
ImGui.PopStyleVar();
}
private void DrawActorPanel()
{
using var group = ImRaii.Group();
DrawPlayerHeader();
using var child = ImRaii.Child("##playerData", -Vector2.One, true);
if (!child)
return;
if (_player == null || _player.ModelType() == 0)
DrawPlayerPanel();
else
DrawMonsterPanel();
}
}

View file

@ -7,6 +7,8 @@ using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Interface;
using Dalamud.Logging;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.PlayerWatch;
namespace Glamourer.Gui;
@ -27,9 +29,8 @@ internal partial class Interface
private void DrawPlayerFilter()
{
using var raii = new ImGuiRaii()
.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.PushStyle(ImGuiStyleVar.FrameRounding, 0);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
ImGui.SetNextItemWidth(SelectorWidth * ImGui.GetIO().FontGlobalScale);
if (ImGui.InputTextWithHint("##playerFilter", "Filter Players...", ref _playerFilter, 32))
_playerFilterLower = _playerFilter.ToLowerInvariant();
@ -115,23 +116,22 @@ internal partial class Interface
private void DrawSelectionButtons()
{
using var raii = new ImGuiRaii()
.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.PushStyle(ImGuiStyleVar.FrameRounding, 0)
.PushFont(UiBuilder.IconFont);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
using var font = ImRaii.PushFont(UiBuilder.IconFont);
Character? select = null;
var buttonWidth = Vector2.UnitX * SelectorWidth / 2;
if (ImGui.Button(FontAwesomeIcon.UserCircle.ToIconString(), buttonWidth))
select = Dalamud.ClientState.LocalPlayer;
raii.PopFonts();
ImGuiCustom.HoverTooltip("Select the local player character.");
font.Pop();
ImGuiUtil.HoverTooltip("Select the local player character.");
ImGui.SameLine();
raii.PushFont(UiBuilder.IconFont);
font.Push(UiBuilder.IconFont);
if (_inGPose)
{
raii.PushStyle(ImGuiStyleVar.Alpha, 0.5f);
style.Push(ImGuiStyleVar.Alpha, 0.5f);
ImGui.Button(FontAwesomeIcon.HandPointer.ToIconString(), buttonWidth);
raii.PopStyles();
style.Pop();
}
else
{
@ -139,8 +139,8 @@ internal partial class Interface
select = CharacterFactory.Convert(Dalamud.Targets.Target);
}
raii.PopFonts();
ImGuiCustom.HoverTooltip("Select the current target, if it is in the list.");
font.Pop();
ImGuiUtil.HoverTooltip("Select the current target, if it is in the list.");
if (select == null)
return;
@ -196,7 +196,7 @@ internal partial class Interface
}
using (var _ = new ImGuiRaii().PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero))
using (var _ = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero))
{
ImGui.EndChild();
}
@ -207,14 +207,14 @@ internal partial class Interface
private void DrawPlayerTab()
{
using var raii = new ImGuiRaii();
using var tab = ImRaii.TabItem("Current Players");
_player = null;
if (!raii.Begin(() => ImGui.BeginTabItem("Current Players"), ImGui.EndTabItem))
if (!tab)
return;
DrawPlayerSelector();
if (!_currentLabel.Any())
if (_currentLabel.Length == 0)
return;
ImGui.SameLine();

View file

@ -1,6 +1,8 @@
using System;
using System.Numerics;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
namespace Glamourer.Gui
{
@ -11,7 +13,7 @@ namespace Glamourer.Gui
if (DrawCheckMark(label, value, setter))
Glamourer.Config.Save();
ImGuiCustom.HoverTooltip(tooltip);
ImGuiUtil.HoverTooltip(tooltip);
}
private static void ChangeAndSave<T>(T value, T currentValue, Action<T> setter) where T : IEquatable<T>
@ -33,19 +35,19 @@ namespace Glamourer.Gui
ImGui.SameLine();
if (ImGui.Button($"Default##{name}"))
ChangeAndSave(defaultValue, value, setter);
ImGuiCustom.HoverTooltip(
ImGuiUtil.HoverTooltip(
$"Reset to default: #{defaultValue & 0xFF:X2}{(defaultValue >> 8) & 0xFF:X2}{(defaultValue >> 16) & 0xFF:X2}{defaultValue >> 24:X2}");
ImGui.SameLine();
ImGui.Text(name);
ImGuiCustom.HoverTooltip(tooltip);
ImGuiUtil.HoverTooltip(tooltip);
}
private void DrawRestorePenumbraButton()
private static void DrawRestorePenumbraButton()
{
const string buttonLabel = "Re-Register Penumbra";
if (!Glamourer.Config.AttachToPenumbra)
{
using var raii = new ImGuiRaii().PushStyle(ImGuiStyleVar.Alpha, 0.5f);
using var style = ImRaii.PushStyle(ImGuiStyleVar.Alpha, 0.5f);
ImGui.Button(buttonLabel);
return;
}
@ -53,14 +55,14 @@ namespace Glamourer.Gui
if (ImGui.Button(buttonLabel))
Glamourer.Penumbra.Reattach(true);
ImGuiCustom.HoverTooltip(
ImGuiUtil.HoverTooltip(
"If Penumbra did not register the functions for some reason, pressing this button might help restore functionality.");
}
private void DrawConfigTab()
private static void DrawConfigTab()
{
using var raii = new ImGuiRaii();
if (!raii.Begin(() => ImGui.BeginTabItem("Config"), ImGui.EndTabItem))
using var tab = ImRaii.TabItem("Config");
if (!tab)
return;
var cfg = Glamourer.Config;

View file

@ -5,6 +5,8 @@ using Dalamud.Interface;
using Dalamud.Logging;
using Glamourer.Customization;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Enums;
namespace Glamourer.Gui
@ -14,13 +16,14 @@ namespace Glamourer.Gui
private static bool DrawColorPickerPopup(string label, CustomizationSet set, CustomizationId id, out Customization.Customization value)
{
value = default;
if (!ImGui.BeginPopup(label, ImGuiWindowFlags.AlwaysAutoResize))
using var popup = ImRaii.Popup(label, ImGuiWindowFlags.AlwaysAutoResize);
if (!popup)
return false;
var ret = false;
var count = set.Count(id);
using var raii = new ImGuiRaii().PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.PushStyle(ImGuiStyleVar.FrameRounding, 0);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
for (var i = 0; i < count; ++i)
{
var custom = set.Data(id, i);
@ -35,7 +38,6 @@ namespace Glamourer.Gui
ImGui.SameLine();
}
ImGui.EndPopup();
return ret;
}
@ -58,7 +60,7 @@ namespace Glamourer.Gui
ret = true;
}
ImGuiCustom.HoverTooltip($"Input Range: [{minValue}, {maxValue}]");
ImGuiUtil.HoverTooltip($"Input Range: [{minValue}, {maxValue}]");
return ret;
}
@ -92,7 +94,7 @@ namespace Glamourer.Gui
ImGui.SameLine();
using (var _ = ImGuiRaii.NewGroup())
using (var _ = ImRaii.Group())
{
if (InputInt($"##text_{id}", ref current, 1, count))
{
@ -102,7 +104,7 @@ namespace Glamourer.Gui
ImGui.Text(label);
ImGuiCustom.HoverTooltip(tooltip);
ImGuiUtil.HoverTooltip(tooltip);
}
if (!DrawColorPickerPopup(popupName, set, id, out var newCustom))
@ -117,7 +119,7 @@ namespace Glamourer.Gui
private bool DrawListSelector(string label, string tooltip, ref CharacterCustomization customization, CustomizationId id,
CustomizationSet set)
{
using var bigGroup = ImGuiRaii.NewGroup();
using var bigGroup = ImRaii.Group();
var ret = false;
int current = customization[id];
var count = set.Count(id);
@ -146,7 +148,7 @@ namespace Glamourer.Gui
ImGui.SameLine();
ImGui.Text(label);
ImGuiCustom.HoverTooltip(tooltip);
ImGuiUtil.HoverTooltip(tooltip);
return ret;
}
@ -157,10 +159,10 @@ namespace Glamourer.Gui
private bool DrawMultiSelector(ref CharacterCustomization customization, CustomizationSet set)
{
using var bigGroup = ImGuiRaii.NewGroup();
using var bigGroup = ImRaii.Group();
var ret = false;
var count = set.Count(CustomizationId.FacialFeaturesTattoos);
using (var _ = ImGuiRaii.NewGroup())
using (var _ = ImRaii.Group())
{
var face = customization.Face;
if (set.Faces.Count < face)
@ -180,11 +182,7 @@ namespace Glamourer.Gui
customization.FacialFeature(i, !enabled);
}
if (ImGui.IsItemHovered())
{
using var tt = ImGuiRaii.NewTooltip();
ImGui.Image(icon.ImGuiHandle, new Vector2(icon.Width, icon.Height));
}
ImGuiUtil.HoverIconTooltip(icon, _iconSize);
if (i % 4 != 3)
ImGui.SameLine();
@ -192,7 +190,7 @@ namespace Glamourer.Gui
}
ImGui.SameLine();
using var group = ImGuiRaii.NewGroup();
using var group = ImRaii.Group();
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + ImGui.GetTextLineHeightWithSpacing() + 3 * ImGui.GetStyle().ItemSpacing.Y / 2);
int value = customization[CustomizationId.FacialFeaturesTattoos];
if (InputInt($"##{CustomizationId.FacialFeaturesTattoos}", ref value, 1, 256))
@ -201,7 +199,7 @@ namespace Glamourer.Gui
ret = true;
}
ImGui.Text(set.Option(CustomizationId.FacialFeaturesTattoos));
ImGui.TextUnformatted(set.Option(CustomizationId.FacialFeaturesTattoos));
return ret;
}
@ -210,13 +208,14 @@ namespace Glamourer.Gui
private bool DrawIconPickerPopup(string label, CustomizationSet set, CustomizationId id, out Customization.Customization value)
{
value = default;
if (!ImGui.BeginPopup(label, ImGuiWindowFlags.AlwaysAutoResize))
using var popup = ImRaii.Popup(label, ImGuiWindowFlags.AlwaysAutoResize);
if (!popup)
return false;
var ret = false;
var count = set.Count(id);
using var raii = new ImGuiRaii().PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.PushStyle(ImGuiStyleVar.FrameRounding, 0);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
for (var i = 0; i < count; ++i)
{
var custom = set.Data(id, i);
@ -229,11 +228,7 @@ namespace Glamourer.Gui
ImGui.CloseCurrentPopup();
}
if (ImGui.IsItemHovered())
{
using var tt = ImGuiRaii.NewTooltip();
ImGui.Image(icon.ImGuiHandle, new Vector2(icon.Width, icon.Height));
}
ImGuiUtil.HoverIconTooltip(icon, _iconSize);
var text = custom.Value.ToString();
var textWidth = ImGui.CalcTextSize(text).X;
@ -244,14 +239,13 @@ namespace Glamourer.Gui
ImGui.SameLine();
}
ImGui.EndPopup();
return ret;
}
private bool DrawIconSelector(string label, string tooltip, ref CharacterCustomization customization, CustomizationId id,
CustomizationSet set)
{
using var bigGroup = ImGuiRaii.NewGroup();
using var bigGroup = ImRaii.Group();
var ret = false;
var count = set.Count(id);
@ -268,14 +262,10 @@ namespace Glamourer.Gui
if (ImGui.ImageButton(icon.ImGuiHandle, _iconSize))
ImGui.OpenPopup(popupName);
if (ImGui.IsItemHovered())
{
using var tt = ImGuiRaii.NewTooltip();
ImGui.Image(icon.ImGuiHandle, new Vector2(icon.Width, icon.Height));
}
ImGuiUtil.HoverIconTooltip(icon, _iconSize);
ImGui.SameLine();
using var group = ImGuiRaii.NewGroup();
using var group = ImRaii.Group();
if (InputInt($"##text_{id}", ref current, 1, count))
{
customization[id] = set.Data(id, current).Value;
@ -288,8 +278,8 @@ namespace Glamourer.Gui
ret = true;
}
ImGui.Text($"{label} ({custom.Value.Value})");
ImGuiCustom.HoverTooltip(tooltip);
ImGui.TextUnformatted($"{label} ({custom.Value.Value})");
ImGuiUtil.HoverTooltip(tooltip);
return ret;
}
@ -298,7 +288,7 @@ namespace Glamourer.Gui
private bool DrawPercentageSelector(string label, string tooltip, ref CharacterCustomization customization, CustomizationId id,
CustomizationSet set)
{
using var bigGroup = ImGuiRaii.NewGroup();
using var bigGroup = ImRaii.Group();
var ret = false;
int value = customization[id];
var count = set.Count(id);
@ -318,15 +308,15 @@ namespace Glamourer.Gui
}
ImGui.SameLine();
ImGui.Text(label);
ImGuiCustom.HoverTooltip(tooltip);
ImGui.TextUnformatted(label);
ImGuiUtil.HoverTooltip(tooltip);
return ret;
}
private bool DrawRaceSelector(ref CharacterCustomization customization)
{
using var group = ImGuiRaii.NewGroup();
using var group = ImRaii.Group();
var ret = false;
ImGui.SetNextItemWidth(_raceSelectorWidth);
if (ImGui.BeginCombo("##subRaceCombo", ClanName(customization.Clan, customization.Gender)))
@ -343,7 +333,7 @@ namespace Glamourer.Gui
ImGui.EndCombo();
}
ImGui.Text(
ImGui.TextUnformatted(
$"{Glamourer.Customization.GetName(CustomName.Gender)} & {Glamourer.Customization.GetName(CustomName.Clan)}");
return ret;
@ -352,7 +342,7 @@ namespace Glamourer.Gui
private bool DrawGenderSelector(ref CharacterCustomization customization)
{
var ret = false;
ImGui.PushFont(UiBuilder.IconFont);
using var font = ImRaii.PushFont(UiBuilder.IconFont);
var icon = customization.Gender == Gender.Male ? FontAwesomeIcon.Mars : FontAwesomeIcon.Venus;
var restricted = false;
if (customization.Race == Race.Hrothgar)
@ -370,7 +360,6 @@ namespace Glamourer.Gui
if (restricted)
ImGui.PopStyleVar();
ImGui.PopFont();
return ret;
}

View file

@ -6,369 +6,369 @@ using Dalamud.Logging;
using Glamourer.Designs;
using Glamourer.FileSystem;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
namespace Glamourer.Gui
namespace Glamourer.Gui;
internal partial class Interface
{
internal partial class Interface
private int _totalObject;
private bool _inDesignMode;
private Design? _selection;
private string _newChildName = string.Empty;
private void DrawDesignSelector()
{
private int _totalObject;
private bool _inDesignMode;
private Design? _selection;
private string _newChildName = string.Empty;
private void DrawDesignSelector()
_totalObject = 0;
ImGui.BeginGroup();
if (ImGui.BeginChild("##selector", new Vector2(SelectorWidth * ImGui.GetIO().FontGlobalScale, -ImGui.GetFrameHeight() - 1), true))
{
_totalObject = 0;
ImGui.BeginGroup();
if (ImGui.BeginChild("##selector", new Vector2(SelectorWidth * ImGui.GetIO().FontGlobalScale, -ImGui.GetFrameHeight() - 1), true))
{
DrawFolderContent(_designs.FileSystem.Root, Glamourer.Config.FoldersFirst ? SortMode.FoldersFirst : SortMode.Lexicographical);
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
ImGui.EndChild();
ImGui.PopStyleVar();
}
DrawDesignSelectorButtons();
ImGui.EndGroup();
DrawFolderContent(_designs.FileSystem.Root, Glamourer.Config.FoldersFirst ? SortMode.FoldersFirst : SortMode.Lexicographical);
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
ImGui.EndChild();
ImGui.PopStyleVar();
}
private void DrawPasteClipboardButton()
{
if (_selection!.Data.WriteProtected)
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.5f);
DrawDesignSelectorButtons();
ImGui.EndGroup();
}
ImGui.PushFont(UiBuilder.IconFont);
var applyButton = ImGui.Button(FontAwesomeIcon.Paste.ToIconString());
ImGui.PopFont();
if (_selection!.Data.WriteProtected)
private void DrawPasteClipboardButton()
{
if (_selection!.Data.WriteProtected)
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.5f);
ImGui.PushFont(UiBuilder.IconFont);
var applyButton = ImGui.Button(FontAwesomeIcon.Paste.ToIconString());
ImGui.PopFont();
if (_selection!.Data.WriteProtected)
ImGui.PopStyleVar();
ImGuiUtil.HoverTooltip("Overwrite with customization code from clipboard.");
if (_selection!.Data.WriteProtected || !applyButton)
return;
var text = ImGui.GetClipboardText();
if (!text.Any())
return;
try
{
_selection!.Data = CharacterSave.FromString(text);
_designs.SaveToFile();
}
catch (Exception e)
{
PluginLog.Information($"{e}");
}
}
private void DrawNewFolderButton()
{
ImGui.PushFont(UiBuilder.IconFont);
if (ImGui.Button(FontAwesomeIcon.FolderPlus.ToIconString(), Vector2.UnitX * SelectorWidth / 5))
OpenDesignNamePopup(DesignNameUse.NewFolder);
ImGui.PopFont();
ImGuiUtil.HoverTooltip("Create a new, empty Folder.");
DrawDesignNamePopup(DesignNameUse.NewFolder);
}
private void DrawNewDesignButton()
{
ImGui.PushFont(UiBuilder.IconFont);
if (ImGui.Button(FontAwesomeIcon.Plus.ToIconString(), Vector2.UnitX * SelectorWidth / 5))
OpenDesignNamePopup(DesignNameUse.NewDesign);
ImGui.PopFont();
ImGuiUtil.HoverTooltip("Create a new, empty Design.");
DrawDesignNamePopup(DesignNameUse.NewDesign);
}
private void DrawClipboardDesignButton()
{
ImGui.PushFont(UiBuilder.IconFont);
if (ImGui.Button(FontAwesomeIcon.Paste.ToIconString(), Vector2.UnitX * SelectorWidth / 5))
OpenDesignNamePopup(DesignNameUse.FromClipboard);
ImGui.PopFont();
ImGuiUtil.HoverTooltip("Create a new design from the customization string in your clipboard.");
DrawDesignNamePopup(DesignNameUse.FromClipboard);
}
private void DrawDeleteDesignButton()
{
ImGui.PushFont(UiBuilder.IconFont);
var style = _selection == null;
if (style)
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.5f);
if (ImGui.Button(FontAwesomeIcon.Trash.ToIconString(), Vector2.UnitX * SelectorWidth / 5) && _selection != null)
{
_designs.DeleteAllChildren(_selection, false);
_selection = null;
}
ImGui.PopFont();
if (style)
ImGui.PopStyleVar();
ImGuiUtil.HoverTooltip("Delete the currently selected Design.");
}
private void DrawDuplicateDesignButton()
{
ImGui.PushFont(UiBuilder.IconFont);
if (_selection == null)
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.5f);
if (ImGui.Button(FontAwesomeIcon.Clone.ToIconString(), Vector2.UnitX * SelectorWidth / 5) && _selection != null)
OpenDesignNamePopup(DesignNameUse.DuplicateDesign);
ImGui.PopFont();
if (_selection == null)
ImGui.PopStyleVar();
ImGuiUtil.HoverTooltip(
"Clone the currently selected Design.\nHold Shift to only clone the customizations.\nHold Control to only clone the equipment.");
DrawDesignNamePopup(DesignNameUse.DuplicateDesign);
}
private void DrawDesignSelectorButtons()
{
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0f);
DrawNewFolderButton();
ImGui.SameLine();
DrawNewDesignButton();
ImGui.SameLine();
DrawClipboardDesignButton();
ImGui.SameLine();
DrawDuplicateDesignButton();
ImGui.SameLine();
DrawDeleteDesignButton();
}
private void DrawDesignHeaderButtons()
{
DrawCopyClipboardButton(_selection!.Data);
ImGui.SameLine();
DrawPasteClipboardButton();
ImGui.SameLine();
DrawApplyToPlayerButton(_selection!.Data);
if (!_inGPose)
{
ImGui.SameLine();
DrawApplyToTargetButton(_selection!.Data);
}
ImGui.SameLine();
DrawCheckbox("Write Protected", _selection!.Data.WriteProtected, v => _selection!.Data.WriteProtected = v, false);
}
private void DrawDesignPanel()
{
if (ImGui.BeginChild("##details", -Vector2.One * 0.001f, true))
{
DrawDesignHeaderButtons();
var data = _selection!.Data;
var prot = _selection!.Data.WriteProtected;
if (prot)
{
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.8f);
data = data.Copy();
}
DrawGeneralSettings(data, prot);
var mask = data.WriteEquipment;
if (DrawEquip(data.Equipment, ref mask) && !prot)
{
data.WriteEquipment = mask;
_designs.SaveToFile();
}
if (DrawCustomization(ref data.Customizations) && !prot)
_designs.SaveToFile();
if (DrawMiscellaneous(data, null) && !prot)
_designs.SaveToFile();
if (prot)
ImGui.PopStyleVar();
ImGuiCustom.HoverTooltip("Overwrite with customization code from clipboard.");
ImGui.EndChild();
}
}
if (_selection!.Data.WriteProtected || !applyButton)
return;
private void DrawSaves()
{
using var style = ImRaii.PushStyle(ImGuiStyleVar.IndentSpacing, 12.5f * ImGui.GetIO().FontGlobalScale);
using var tab = ImRaii.TabItem("Designs");
_inDesignMode = tab.Success;
if (!_inDesignMode)
return;
var text = ImGui.GetClipboardText();
if (!text.Any())
return;
DrawDesignSelector();
if (_selection != null)
{
ImGui.SameLine();
DrawDesignPanel();
}
}
private void DrawCheckbox(string label, bool value, Action<bool> setter, bool prot)
{
var tmp = value;
if (ImGui.Checkbox(label, ref tmp) && tmp != value)
{
setter(tmp);
if (!prot)
_designs.SaveToFile();
}
}
private void DrawGeneralSettings(CharacterSave data, bool prot)
{
ImGui.BeginGroup();
DrawCheckbox("Apply Customizations", data.WriteCustomizations, v => data.WriteCustomizations = v, prot);
DrawCheckbox("Write Weapon State", data.SetWeaponState, v => data.SetWeaponState = v, prot);
ImGui.EndGroup();
ImGui.SameLine();
ImGui.BeginGroup();
DrawCheckbox("Write Hat State", data.SetHatState, v => data.SetHatState = v, prot);
DrawCheckbox("Write Visor State", data.SetVisorState, v => data.SetVisorState = v, prot);
ImGui.EndGroup();
}
private void RenameChildInput(IFileSystemBase child)
{
ImGui.SetNextItemWidth(150);
if (!ImGui.InputTextWithHint("##fsNewName", "Rename...", ref _newChildName, 64,
ImGuiInputTextFlags.EnterReturnsTrue))
return;
if (_newChildName.Any() && _newChildName != child.Name)
try
{
_selection!.Data = CharacterSave.FromString(text);
_designs.SaveToFile();
var oldPath = child.FullName();
if (_designs.FileSystem.Rename(child, _newChildName))
_designs.UpdateAllChildren(oldPath, child);
}
catch (Exception e)
{
PluginLog.Information($"{e}");
PluginLog.Error($"Could not rename {child.Name} to {_newChildName}:\n{e}");
}
}
private void DrawNewFolderButton()
{
ImGui.PushFont(UiBuilder.IconFont);
if (ImGui.Button(FontAwesomeIcon.FolderPlus.ToIconString(), Vector2.UnitX * SelectorWidth / 5))
OpenDesignNamePopup(DesignNameUse.NewFolder);
ImGui.PopFont();
ImGuiCustom.HoverTooltip("Create a new, empty Folder.");
DrawDesignNamePopup(DesignNameUse.NewFolder);
}
private void DrawNewDesignButton()
{
ImGui.PushFont(UiBuilder.IconFont);
if (ImGui.Button(FontAwesomeIcon.Plus.ToIconString(), Vector2.UnitX * SelectorWidth / 5))
OpenDesignNamePopup(DesignNameUse.NewDesign);
ImGui.PopFont();
ImGuiCustom.HoverTooltip("Create a new, empty Design.");
DrawDesignNamePopup(DesignNameUse.NewDesign);
}
private void DrawClipboardDesignButton()
{
ImGui.PushFont(UiBuilder.IconFont);
if (ImGui.Button(FontAwesomeIcon.Paste.ToIconString(), Vector2.UnitX * SelectorWidth / 5))
OpenDesignNamePopup(DesignNameUse.FromClipboard);
ImGui.PopFont();
ImGuiCustom.HoverTooltip("Create a new design from the customization string in your clipboard.");
DrawDesignNamePopup(DesignNameUse.FromClipboard);
}
private void DrawDeleteDesignButton()
{
ImGui.PushFont(UiBuilder.IconFont);
var style = _selection == null;
if (style)
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.5f);
if (ImGui.Button(FontAwesomeIcon.Trash.ToIconString(), Vector2.UnitX * SelectorWidth / 5) && _selection != null)
else if (child is Folder f)
try
{
_designs.DeleteAllChildren(_selection, false);
_selection = null;
var oldPath = child.FullName();
if (_designs.FileSystem.Merge(f, f.Parent, true))
_designs.UpdateAllChildren(oldPath, f.Parent);
}
ImGui.PopFont();
if (style)
ImGui.PopStyleVar();
ImGuiCustom.HoverTooltip("Delete the currently selected Design.");
}
private void DrawDuplicateDesignButton()
{
ImGui.PushFont(UiBuilder.IconFont);
if (_selection == null)
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.5f);
if (ImGui.Button(FontAwesomeIcon.Clone.ToIconString(), Vector2.UnitX * SelectorWidth / 5) && _selection != null)
OpenDesignNamePopup(DesignNameUse.DuplicateDesign);
ImGui.PopFont();
if (_selection == null)
ImGui.PopStyleVar();
ImGuiCustom.HoverTooltip("Clone the currently selected Design.\nHold Shift to only clone the customizations.\nHold Control to only clone the equipment.");
DrawDesignNamePopup(DesignNameUse.DuplicateDesign);
}
private void DrawDesignSelectorButtons()
{
using var raii = new ImGuiRaii()
.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.PushStyle(ImGuiStyleVar.FrameRounding, 0f);
DrawNewFolderButton();
ImGui.SameLine();
DrawNewDesignButton();
ImGui.SameLine();
DrawClipboardDesignButton();
ImGui.SameLine();
DrawDuplicateDesignButton();
ImGui.SameLine();
DrawDeleteDesignButton();
}
private void DrawDesignHeaderButtons()
{
DrawCopyClipboardButton(_selection!.Data);
ImGui.SameLine();
DrawPasteClipboardButton();
ImGui.SameLine();
DrawApplyToPlayerButton(_selection!.Data);
if (!_inGPose)
catch (Exception e)
{
ImGui.SameLine();
DrawApplyToTargetButton(_selection!.Data);
PluginLog.Error($"Could not merge folder {child.Name} into parent:\n{e}");
}
ImGui.SameLine();
DrawCheckbox("Write Protected", _selection!.Data.WriteProtected, v => _selection!.Data.WriteProtected = v, false);
_newChildName = string.Empty;
}
private void ContextMenu(IFileSystemBase child)
{
var label = $"##fsPopup{child.FullName()}";
if (ImGui.BeginPopup(label))
{
if (ImGui.MenuItem("Delete") && ImGui.GetIO().KeyCtrl && ImGui.GetIO().KeyShift)
_designs.DeleteAllChildren(child, false);
ImGuiUtil.HoverTooltip("Hold Control and Shift to delete.");
RenameChildInput(child);
if (child is Design d && ImGui.MenuItem("Copy to Clipboard"))
ImGui.SetClipboardText(d.Data.ToBase64());
ImGui.EndPopup();
}
private void DrawDesignPanel()
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
if (ImGui.BeginChild("##details", -Vector2.One * 0.001f, true))
{
DrawDesignHeaderButtons();
var data = _selection!.Data;
var prot = _selection!.Data.WriteProtected;
if (prot)
{
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.8f);
data = data.Copy();
}
DrawGeneralSettings(data, prot);
var mask = data.WriteEquipment;
if (DrawEquip(data.Equipment, ref mask) && !prot)
{
data.WriteEquipment = mask;
_designs.SaveToFile();
}
if (DrawCustomization(ref data.Customizations) && !prot)
_designs.SaveToFile();
if (DrawMiscellaneous(data, null) && !prot)
_designs.SaveToFile();
if (prot)
ImGui.PopStyleVar();
ImGui.EndChild();
}
}
private void DrawSaves()
{
using var raii = new ImGuiRaii();
raii.PushStyle(ImGuiStyleVar.IndentSpacing, 12.5f * ImGui.GetIO().FontGlobalScale);
_inDesignMode = raii.Begin(() => ImGui.BeginTabItem("Designs"), ImGui.EndTabItem);
if (!_inDesignMode)
return;
DrawDesignSelector();
if (_selection != null)
{
ImGui.SameLine();
DrawDesignPanel();
}
}
private void DrawCheckbox(string label, bool value, Action<bool> setter, bool prot)
{
var tmp = value;
if (ImGui.Checkbox(label, ref tmp) && tmp != value)
{
setter(tmp);
if (!prot)
_designs.SaveToFile();
}
}
private void DrawGeneralSettings(CharacterSave data, bool prot)
{
ImGui.BeginGroup();
DrawCheckbox("Apply Customizations", data.WriteCustomizations, v => data.WriteCustomizations = v, prot);
DrawCheckbox("Write Weapon State", data.SetWeaponState, v => data.SetWeaponState = v, prot);
ImGui.EndGroup();
ImGui.SameLine();
ImGui.BeginGroup();
DrawCheckbox("Write Hat State", data.SetHatState, v => data.SetHatState = v, prot);
DrawCheckbox("Write Visor State", data.SetVisorState, v => data.SetVisorState = v, prot);
ImGui.EndGroup();
}
private void RenameChildInput(IFileSystemBase child)
{
ImGui.SetNextItemWidth(150);
if (!ImGui.InputTextWithHint("##fsNewName", "Rename...", ref _newChildName, 64,
ImGuiInputTextFlags.EnterReturnsTrue))
return;
if (_newChildName.Any() && _newChildName != child.Name)
try
{
var oldPath = child.FullName();
if (_designs.FileSystem.Rename(child, _newChildName))
_designs.UpdateAllChildren(oldPath, child);
}
catch (Exception e)
{
PluginLog.Error($"Could not rename {child.Name} to {_newChildName}:\n{e}");
}
else if (child is Folder f)
try
{
var oldPath = child.FullName();
if (_designs.FileSystem.Merge(f, f.Parent, true))
_designs.UpdateAllChildren(oldPath, f.Parent);
}
catch (Exception e)
{
PluginLog.Error($"Could not merge folder {child.Name} into parent:\n{e}");
}
_newChildName = string.Empty;
}
private void ContextMenu(IFileSystemBase child)
{
var label = $"##fsPopup{child.FullName()}";
if (ImGui.BeginPopup(label))
{
if (ImGui.MenuItem("Delete") && ImGui.GetIO().KeyCtrl && ImGui.GetIO().KeyShift)
_designs.DeleteAllChildren(child, false);
ImGuiCustom.HoverTooltip("Hold Control and Shift to delete.");
RenameChildInput(child);
if (child is Design d && ImGui.MenuItem("Copy to Clipboard"))
ImGui.SetClipboardText(d.Data.ToBase64());
ImGui.EndPopup();
}
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
_newChildName = child.Name;
ImGui.OpenPopup(label);
}
}
private static uint GetDesignColor(CharacterSave save)
{
const uint white = 0xFFFFFFFF;
const uint grey = 0xFF808080;
if (!Glamourer.Config.ColorDesigns)
return white;
var changesStates = save.SetHatState || save.SetVisorState || save.SetWeaponState || save.IsWet || save.Alpha != 1.0f;
if (save.WriteCustomizations)
if (save.WriteEquipment != CharacterEquipMask.None)
return white;
else
return changesStates ? white : Glamourer.Config.CustomizationColor;
if (save.WriteEquipment != CharacterEquipMask.None)
return changesStates ? white : Glamourer.Config.EquipmentColor;
return changesStates ? Glamourer.Config.StateColor : grey;
}
private void DrawFolderContent(Folder folder, SortMode mode)
{
foreach (var child in folder.AllChildren(mode).ToArray())
{
if (child.IsFolder(out var subFolder))
{
var treeNode = ImGui.TreeNodeEx($"{subFolder.Name}##{_totalObject}");
DrawOrnaments(child);
if (treeNode)
{
DrawFolderContent(subFolder, mode);
ImGui.TreePop();
}
else
{
_totalObject += subFolder.TotalDescendantLeaves();
}
}
else
{
if (child is not Design d)
continue;
++_totalObject;
var color = GetDesignColor(d.Data);
using var raii = new ImGuiRaii()
.PushColor(ImGuiCol.Text, color);
var selected = ImGui.Selectable($"{child.Name}##{_totalObject}", ReferenceEquals(child, _selection));
raii.PopColors();
DrawOrnaments(child);
if (Glamourer.Config.ShowLocks && d.Data.WriteProtected)
{
ImGui.SameLine();
raii.PushFont(UiBuilder.IconFont)
.PushColor(ImGuiCol.Text, color);
ImGui.Text(FontAwesomeIcon.Lock.ToIconString());
}
if (selected)
_selection = d;
}
}
}
private void DrawOrnaments(IFileSystemBase child)
{
FileSystemImGui.DragDropSource(child);
if (FileSystemImGui.DragDropTarget(_designs.FileSystem, child, out var oldPath, out var draggedFolder))
_designs.UpdateAllChildren(oldPath, draggedFolder!);
ContextMenu(child);
_newChildName = child.Name;
ImGui.OpenPopup(label);
}
}
private static uint GetDesignColor(CharacterSave save)
{
const uint white = 0xFFFFFFFF;
const uint grey = 0xFF808080;
if (!Glamourer.Config.ColorDesigns)
return white;
var changesStates = save.SetHatState || save.SetVisorState || save.SetWeaponState || save.IsWet || save.Alpha != 1.0f;
if (save.WriteCustomizations)
if (save.WriteEquipment != CharacterEquipMask.None)
return white;
else
return changesStates ? white : Glamourer.Config.CustomizationColor;
if (save.WriteEquipment != CharacterEquipMask.None)
return changesStates ? white : Glamourer.Config.EquipmentColor;
return changesStates ? Glamourer.Config.StateColor : grey;
}
private void DrawFolderContent(Folder folder, SortMode mode)
{
foreach (var child in folder.AllChildren(mode).ToArray())
{
if (child.IsFolder(out var subFolder))
{
var treeNode = ImGui.TreeNodeEx($"{subFolder.Name}##{_totalObject}");
DrawOrnaments(child);
if (treeNode)
{
DrawFolderContent(subFolder, mode);
ImGui.TreePop();
}
else
{
_totalObject += subFolder.TotalDescendantLeaves();
}
}
else
{
if (child is not Design d)
continue;
++_totalObject;
var color = GetDesignColor(d.Data);
using var c = ImRaii.PushColor(ImGuiCol.Text, color);
var selected = ImGui.Selectable($"{child.Name}##{_totalObject}", ReferenceEquals(child, _selection));
c.Pop();
DrawOrnaments(child);
if (Glamourer.Config.ShowLocks && d.Data.WriteProtected)
{
ImGui.SameLine();
using var font = ImRaii.PushFont(UiBuilder.IconFont);
c.Push(ImGuiCol.Text, color);
ImGui.TextUnformatted(FontAwesomeIcon.Lock.ToIconString());
}
if (selected)
_selection = d;
}
}
}
private void DrawOrnaments(IFileSystemBase child)
{
FileSystemImGui.DragDropSource(child);
if (FileSystemImGui.DragDropTarget(_designs.FileSystem, child, out var oldPath, out var draggedFolder))
_designs.UpdateAllChildren(oldPath, draggedFolder!);
ContextMenu(child);
}
}

View file

@ -1,6 +1,7 @@
using Dalamud.Interface;
using ImGuiNET;
using Lumina.Text;
using OtterGui;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -20,7 +21,7 @@ namespace Glamourer.Gui
var change = stainCombo.Draw(string.Empty, out var newStain) && !newStain.RowIndex.Equals(stainIdx);
if (!change && (byte) stainIdx != 0)
{
ImGuiCustom.HoverTooltip("Right-click to clear.");
ImGuiUtil.HoverTooltip("Right-click to clear.");
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
change = true;
@ -46,7 +47,7 @@ namespace Glamourer.Gui
var change = equipCombo.Draw(currentName, out var newItem, _itemComboWidth) && newItem.Base.RowId != item.RowId;
if (!change && !ReferenceEquals(item, SmallClothes))
{
ImGuiCustom.HoverTooltip("Right-click to clear.");
ImGuiUtil.HoverTooltip("Right-click to clear.");
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
change = true;

View file

@ -6,161 +6,163 @@ using Dalamud.Interface;
using Glamourer.Designs;
using Glamourer.FileSystem;
using ImGuiNET;
using OtterGui.Raii;
namespace Glamourer.Gui
namespace Glamourer.Gui;
internal partial class Interface
{
internal partial class Interface
private const string FixDragDropLabel = "##FixDragDrop";
private List<string>? _fullPathCache;
private string _newFixCharacterName = string.Empty;
private string _newFixDesignPath = string.Empty;
private JobGroup? _newFixDesignGroup;
private Design? _newFixDesign;
private int _fixDragDropIdx = -1;
private static unsafe bool IsDropping()
=> ImGui.AcceptDragDropPayload(FixDragDropLabel).NativePtr != null;
private void DrawFixedDesignsTab()
{
private const string FixDragDropLabel = "##FixDragDrop";
_newFixDesignGroup ??= Glamourer.FixedDesignManager.FixedDesigns.JobGroups[1];
private List<string>? _fullPathCache;
private string _newFixCharacterName = string.Empty;
private string _newFixDesignPath = string.Empty;
private JobGroup? _newFixDesignGroup;
private Design? _newFixDesign;
private int _fixDragDropIdx = -1;
private static unsafe bool IsDropping()
=> ImGui.AcceptDragDropPayload(FixDragDropLabel).NativePtr != null;
private void DrawFixedDesignsTab()
using var tabItem = ImRaii.TabItem("Fixed Designs");
if (!tabItem)
{
_newFixDesignGroup ??= _plugin.FixedDesigns.JobGroups[1];
_fullPathCache = null;
_newFixDesign = null;
_newFixDesignPath = string.Empty;
_newFixDesignGroup = Glamourer.FixedDesignManager.FixedDesigns.JobGroups[1];
return;
}
using var raii = new ImGuiRaii();
if (!raii.Begin(() => ImGui.BeginTabItem("Fixed Designs"), ImGui.EndTabItem))
{
_fullPathCache = null;
_newFixDesign = null;
_newFixDesignPath = string.Empty;
_newFixDesignGroup = _plugin.FixedDesigns.JobGroups[1];
return;
}
_fullPathCache ??= Glamourer.FixedDesignManager.FixedDesigns.Data.Select(d => d.Design.FullName()).ToList();
_fullPathCache ??= _plugin.FixedDesigns.Data.Select(d => d.Design.FullName()).ToList();
using var table = ImRaii.Table("##FixedTable", 4);
var buttonWidth = 23.5f * ImGuiHelpers.GlobalScale;
raii.Begin(() => ImGui.BeginTable("##FixedTable", 4), ImGui.EndTable);
ImGui.TableSetupColumn("##DeleteColumn", ImGuiTableColumnFlags.WidthFixed, 2 * buttonWidth);
ImGui.TableSetupColumn("Character", ImGuiTableColumnFlags.WidthFixed, 200 * ImGuiHelpers.GlobalScale);
ImGui.TableSetupColumn("Jobs", ImGuiTableColumnFlags.WidthFixed, 175 * ImGuiHelpers.GlobalScale);
ImGui.TableSetupColumn("Design", ImGuiTableColumnFlags.WidthStretch);
ImGui.TableHeadersRow();
var xPos = 0f;
var buttonWidth = 23.5f * ImGuiHelpers.GlobalScale;
ImGui.TableSetupColumn("##DeleteColumn", ImGuiTableColumnFlags.WidthFixed, 2 * buttonWidth);
ImGui.TableSetupColumn("Character", ImGuiTableColumnFlags.WidthFixed, 200 * ImGuiHelpers.GlobalScale);
ImGui.TableSetupColumn("Jobs", ImGuiTableColumnFlags.WidthFixed, 175 * ImGuiHelpers.GlobalScale);
ImGui.TableSetupColumn("Design", ImGuiTableColumnFlags.WidthStretch);
ImGui.TableHeadersRow();
var xPos = 0f;
for (var i = 0; i < _fullPathCache.Count; ++i)
{
var path = _fullPathCache[i];
var name = _plugin.FixedDesigns.Data[i];
ImGui.TableNextRow();
ImGui.TableNextColumn();
raii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing / 2);
raii.PushFont(UiBuilder.IconFont);
if (ImGui.Button($"{FontAwesomeIcon.Trash.ToIconChar()}##{i}"))
{
_fullPathCache.RemoveAt(i--);
_plugin.FixedDesigns.Remove(name);
continue;
}
var tmp = name.Enabled;
ImGui.SameLine();
xPos = ImGui.GetCursorPosX();
if (ImGui.Checkbox($"##Enabled{i}", ref tmp))
if (tmp && _plugin.FixedDesigns.EnableDesign(name)
|| !tmp && _plugin.FixedDesigns.DisableDesign(name))
{
Glamourer.Config.FixedDesigns[i].Enabled = tmp;
Glamourer.Config.Save();
}
raii.PopStyles();
raii.PopFonts();
ImGui.TableNextColumn();
ImGui.Selectable($"{name.Name}##Fix{i}");
if (ImGui.BeginDragDropSource())
{
_fixDragDropIdx = i;
ImGui.SetDragDropPayload("##FixDragDrop", IntPtr.Zero, 0);
ImGui.Text($"Dragging {name.Name} ({path})...");
ImGui.EndDragDropSource();
}
if (ImGui.BeginDragDropTarget())
{
if (IsDropping() && _fixDragDropIdx >= 0)
{
var d = _plugin.FixedDesigns.Data[_fixDragDropIdx];
_plugin.FixedDesigns.Move(d, i);
var p = _fullPathCache[_fixDragDropIdx];
_fullPathCache.RemoveAt(_fixDragDropIdx);
_fullPathCache.Insert(i, p);
_fixDragDropIdx = -1;
}
ImGui.EndDragDropTarget();
}
ImGui.TableNextColumn();
ImGui.Text(_plugin.FixedDesigns.Data[i].Jobs.Name);
ImGui.TableNextColumn();
ImGui.Text(path);
}
using var style = new ImRaii.Style();
using var font = new ImRaii.Font();
for (var i = 0; i < _fullPathCache.Count; ++i)
{
var path = _fullPathCache[i];
var name = Glamourer.FixedDesignManager.FixedDesigns.Data[i];
ImGui.TableNextRow();
ImGui.TableNextColumn();
raii.PushFont(UiBuilder.IconFont);
ImGui.SetCursorPosX(xPos);
if (_newFixDesign == null || _newFixCharacterName == string.Empty)
style.Push(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing / 2);
font.Push(UiBuilder.IconFont);
if (ImGui.Button($"{FontAwesomeIcon.Trash.ToIconChar()}##{i}"))
{
raii.PushStyle(ImGuiStyleVar.Alpha, 0.5f);
ImGui.Button($"{FontAwesomeIcon.Plus.ToIconChar()}##NewFix");
raii.PopStyles();
}
else if (ImGui.Button($"{FontAwesomeIcon.Plus.ToIconChar()}##NewFix"))
{
_fullPathCache.Add(_newFixDesignPath);
_plugin.FixedDesigns.Add(_newFixCharacterName, _newFixDesign, _newFixDesignGroup.Value, false);
_newFixCharacterName = string.Empty;
_newFixDesignPath = string.Empty;
_newFixDesign = null;
_newFixDesignGroup = _plugin.FixedDesigns.JobGroups[1];
_fullPathCache.RemoveAt(i--);
Glamourer.FixedDesignManager.FixedDesigns.Remove(name);
continue;
}
raii.PopFonts();
ImGui.TableNextColumn();
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
ImGui.InputTextWithHint("##NewFix", "Enter new Character", ref _newFixCharacterName, 32);
ImGui.TableNextColumn();
ImGui.SetNextItemWidth(-1);
if (raii.Begin(() => ImGui.BeginCombo("##NewFixDesignGroup", _newFixDesignGroup.Value.Name), ImGui.EndCombo))
{
foreach (var (id, group) in _plugin.FixedDesigns.JobGroups)
var tmp = name.Enabled;
ImGui.SameLine();
xPos = ImGui.GetCursorPosX();
if (ImGui.Checkbox($"##Enabled{i}", ref tmp))
if (tmp && Glamourer.FixedDesignManager.FixedDesigns.EnableDesign(name)
|| !tmp && Glamourer.FixedDesignManager.FixedDesigns.DisableDesign(name))
{
ImGui.SetNextItemWidth(-1);
if (ImGui.Selectable($"{group.Name}##NewFixDesignGroup", group.Name == _newFixDesignGroup.Value.Name))
_newFixDesignGroup = group;
Glamourer.Config.FixedDesigns[i].Enabled = tmp;
Glamourer.Config.Save();
}
raii.End();
style.Pop();
font.Pop();
ImGui.TableNextColumn();
ImGui.Selectable($"{name.Name}##Fix{i}");
if (ImGui.BeginDragDropSource())
{
_fixDragDropIdx = i;
ImGui.SetDragDropPayload("##FixDragDrop", IntPtr.Zero, 0);
ImGui.Text($"Dragging {name.Name} ({path})...");
ImGui.EndDragDropSource();
}
if (ImGui.BeginDragDropTarget())
{
if (IsDropping() && _fixDragDropIdx >= 0)
{
var d = Glamourer.FixedDesignManager.FixedDesigns.Data[_fixDragDropIdx];
Glamourer.FixedDesignManager.FixedDesigns.Move(d, i);
var p = _fullPathCache[_fixDragDropIdx];
_fullPathCache.RemoveAt(_fixDragDropIdx);
_fullPathCache.Insert(i, p);
_fixDragDropIdx = -1;
}
ImGui.EndDragDropTarget();
}
ImGui.TableNextColumn();
ImGui.SetNextItemWidth(-1);
if (!raii.Begin(() => ImGui.BeginCombo("##NewFixPath", _newFixDesignPath), ImGui.EndCombo))
return;
ImGui.Text(Glamourer.FixedDesignManager.FixedDesigns.Data[i].Jobs.Name);
ImGui.TableNextColumn();
ImGui.Text(path);
}
foreach (var design in _plugin.Designs.FileSystem.Root.AllLeaves(SortMode.Lexicographical).Cast<Design>())
ImGui.TableNextRow();
ImGui.TableNextColumn();
font.Push(UiBuilder.IconFont);
ImGui.SetCursorPosX(xPos);
if (_newFixDesign == null || _newFixCharacterName == string.Empty)
{
style.Push(ImGuiStyleVar.Alpha, 0.5f);
ImGui.Button($"{FontAwesomeIcon.Plus.ToIconChar()}##NewFix");
style.Pop();
}
else if (ImGui.Button($"{FontAwesomeIcon.Plus.ToIconChar()}##NewFix"))
{
_fullPathCache.Add(_newFixDesignPath);
Glamourer.FixedDesignManager.FixedDesigns.Add(_newFixCharacterName, _newFixDesign, _newFixDesignGroup.Value, false);
_newFixCharacterName = string.Empty;
_newFixDesignPath = string.Empty;
_newFixDesign = null;
_newFixDesignGroup = Glamourer.FixedDesignManager.FixedDesigns.JobGroups[1];
}
font.Pop();
ImGui.TableNextColumn();
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
ImGui.InputTextWithHint("##NewFix", "Enter new Character", ref _newFixCharacterName, 32);
ImGui.TableNextColumn();
ImGui.SetNextItemWidth(-1);
using var combo = ImRaii.Combo("##NewFixDesignGroup", _newFixDesignGroup.Value.Name);
if (combo)
foreach (var (id, group) in Glamourer.FixedDesignManager.FixedDesigns.JobGroups)
{
var fullName = design.FullName();
ImGui.SetNextItemWidth(-1);
if (!ImGui.Selectable($"{fullName}##NewFixDesign", fullName == _newFixDesignPath))
continue;
_newFixDesignPath = fullName;
_newFixDesign = design;
if (ImGui.Selectable($"{group.Name}##NewFixDesignGroup", group.Name == _newFixDesignGroup.Value.Name))
_newFixDesignGroup = group;
}
ImGui.TableNextColumn();
ImGui.SetNextItemWidth(-1);
using var combo2 = ImRaii.Combo("##NewFixPath", _newFixDesignPath);
if (!combo2)
return;
foreach (var design in _plugin.Designs.FileSystem.Root.AllLeaves(SortMode.Lexicographical).Cast<Design>())
{
var fullName = design.FullName();
ImGui.SetNextItemWidth(-1);
if (!ImGui.Selectable($"{fullName}##NewFixDesign", fullName == _newFixDesignPath))
continue;
_newFixDesignPath = fullName;
_newFixDesign = design;
}
}
}

View file

@ -2,6 +2,7 @@
using System.Linq;
using Dalamud.Logging;
using Glamourer.Customization;
using Glamourer.Structs;
using ImGuiNET;
using Penumbra.GameData.Enums;

View file

@ -4,6 +4,7 @@ using System.Reflection;
using ImGuiNET;
using Penumbra.GameData.Enums;
using Lumina.Excel.GeneratedSheets;
using Glamourer.Structs;
namespace Glamourer.Gui
{

View file

@ -2,74 +2,62 @@
using Dalamud.Game.ClientState.Objects.Types;
using ImGuiNET;
namespace Glamourer.Gui
namespace Glamourer.Gui;
internal partial class Interface
{
internal partial class Interface
private static bool DrawCheckMark(string label, bool value, Action<bool> setter)
{
private static bool DrawCheckMark(string label, bool value, Action<bool> setter)
var startValue = value;
if (ImGui.Checkbox(label, ref startValue) && startValue != value)
{
var startValue = value;
if (ImGui.Checkbox(label, ref startValue) && startValue != value)
{
setter(startValue);
return true;
}
return false;
setter(startValue);
return true;
}
private static bool DrawDisableButton(string label, bool disabled)
{
if (!disabled)
return ImGui.Button(label);
using var raii = new ImGuiRaii();
raii.PushStyle(ImGuiStyleVar.Alpha, 0.5f);
ImGui.Button(label);
return false;
}
private static bool DrawMiscellaneous(CharacterSave save, Character? player)
{
var ret = false;
if (!ImGui.CollapsingHeader("Miscellaneous"))
return ret;
ret |= DrawCheckMark("Hat Visible", save.HatState, v =>
{
save.HatState = v;
player?.SetHatVisible(v);
});
ret |= DrawCheckMark("Weapon Visible", save.WeaponState, v =>
{
save.WeaponState = v;
player?.SetWeaponHidden(!v);
});
ret |= DrawCheckMark("Visor Toggled", save.VisorState, v =>
{
save.VisorState = v;
player?.SetVisorToggled(v);
});
ret |= DrawCheckMark("Is Wet", save.IsWet, v =>
{
save.IsWet = v;
player?.SetWetness(v);
});
var alpha = save.Alpha;
if (ImGui.DragFloat("Alpha", ref alpha, 0.01f, 0f, 1f, "%.2f") && alpha != save.Alpha)
{
alpha = (float) Math.Round(alpha > 1 ? 1 : alpha < 0 ? 0 : alpha, 2);
save.Alpha = alpha;
ret = true;
if (player != null)
player.Alpha() = alpha;
}
return false;
}
private static bool DrawMiscellaneous(CharacterSave save, Character? player)
{
var ret = false;
if (!ImGui.CollapsingHeader("Miscellaneous"))
return ret;
ret |= DrawCheckMark("Hat Visible", save.HatState, v =>
{
save.HatState = v;
player?.SetHatVisible(v);
});
ret |= DrawCheckMark("Weapon Visible", save.WeaponState, v =>
{
save.WeaponState = v;
player?.SetWeaponHidden(!v);
});
ret |= DrawCheckMark("Visor Toggled", save.VisorState, v =>
{
save.VisorState = v;
player?.SetVisorToggled(v);
});
ret |= DrawCheckMark("Is Wet", save.IsWet, v =>
{
save.IsWet = v;
player?.SetWetness(v);
});
var alpha = save.Alpha;
if (ImGui.DragFloat("Alpha", ref alpha, 0.01f, 0f, 1f, "%.2f") && alpha != save.Alpha)
{
alpha = (float)Math.Round(alpha > 1 ? 1 : alpha < 0 ? 0 : alpha, 2);
save.Alpha = alpha;
ret = true;
if (player != null)
player.Alpha() = alpha;
}
return ret;
}
}

View file

@ -2,87 +2,86 @@
using System.Linq;
using System.Numerics;
using ImGuiNET;
using OtterGui.Raii;
namespace Glamourer.Gui
namespace Glamourer.Gui;
internal partial class Interface
{
internal partial class Interface
private string? _currentRevertableName;
private CharacterSave? _currentRevertable;
private void DrawRevertablesSelector()
{
private string? _currentRevertableName;
private CharacterSave? _currentRevertable;
private void DrawRevertablesSelector()
{
ImGui.BeginGroup();
DrawPlayerFilter();
if (!ImGui.BeginChild("##playerSelector",
ImGui.BeginGroup();
DrawPlayerFilter();
if (!ImGui.BeginChild("##playerSelector",
new Vector2(SelectorWidth * ImGui.GetIO().FontGlobalScale, -ImGui.GetFrameHeight() - 1), true))
{
ImGui.EndChild();
ImGui.EndGroup();
return;
}
foreach (var (name, save) in Glamourer.RevertableDesigns.Saves)
{
if (name.ToLowerInvariant().Contains(_playerFilterLower) && ImGui.Selectable(name, name == _currentRevertableName))
{
_currentRevertableName = name;
_currentRevertable = save;
}
}
using (var _ = new ImGuiRaii().PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero))
{
ImGui.EndChild();
}
DrawSelectionButtons();
{
ImGui.EndChild();
ImGui.EndGroup();
return;
}
private void DrawRevertablePanel()
foreach (var (name, save) in Glamourer.RevertableDesigns.Saves)
{
using var group = ImGuiRaii.NewGroup();
if (name.ToLowerInvariant().Contains(_playerFilterLower) && ImGui.Selectable(name, name == _currentRevertableName))
{
var buttonColor = ImGui.GetColorU32(ImGuiCol.FrameBg);
using var raii = new ImGuiRaii()
.PushColor(ImGuiCol.Text, GreenHeaderColor)
.PushColor(ImGuiCol.Button, buttonColor)
.PushColor(ImGuiCol.ButtonHovered, buttonColor)
.PushColor(ImGuiCol.ButtonActive, buttonColor)
.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.PushStyle(ImGuiStyleVar.FrameRounding, 0);
ImGui.Button($"{_currentRevertableName}##playerHeader", -Vector2.UnitX * 0.0001f);
_currentRevertableName = name;
_currentRevertable = save;
}
}
if (!ImGui.BeginChild("##revertableData", -Vector2.One, true))
{
ImGui.EndChild();
return;
}
var save = _currentRevertable!.Copy();
DrawCustomization(ref save.Customizations);
DrawEquip(save.Equipment);
DrawMiscellaneous(save, null);
using (var _ = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero))
{
ImGui.EndChild();
}
[Conditional("DEBUG")]
private void DrawRevertablesTab()
DrawSelectionButtons();
ImGui.EndGroup();
}
private void DrawRevertablePanel()
{
using var group = ImRaii.Group();
{
using var raii = new ImGuiRaii();
if (!raii.Begin(() => ImGui.BeginTabItem("Revertables"), ImGui.EndTabItem))
return;
DrawRevertablesSelector();
if (_currentRevertableName == null)
return;
ImGui.SameLine();
DrawRevertablePanel();
var buttonColor = ImGui.GetColorU32(ImGuiCol.FrameBg);
using var color = ImRaii.PushColor(ImGuiCol.Text, GreenHeaderColor)
.Push(ImGuiCol.Button, buttonColor)
.Push(ImGuiCol.ButtonHovered, buttonColor)
.Push(ImGuiCol.ButtonActive, buttonColor);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
ImGui.Button($"{_currentRevertableName}##playerHeader", -Vector2.UnitX * 0.0001f);
}
if (!ImGui.BeginChild("##revertableData", -Vector2.One, true))
{
ImGui.EndChild();
return;
}
var save = _currentRevertable!.Copy();
DrawCustomization(ref save.Customizations);
DrawEquip(save.Equipment);
DrawMiscellaneous(save, null);
ImGui.EndChild();
}
[Conditional("DEBUG")]
private void DrawRevertablesTab()
{
using var tabItem = ImRaii.TabItem("Revertables");
if (!tabItem)
return;
DrawRevertablesSelector();
if (_currentRevertableName == null)
return;
ImGui.SameLine();
DrawRevertablePanel();
}
}