Some more work.

This commit is contained in:
Ottermandias 2026-01-28 14:07:05 +01:00
parent a29631e963
commit 161cd4f0d1
13 changed files with 317 additions and 316 deletions

View file

@ -3,12 +3,12 @@ using Glamourer.Designs;
using Glamourer.State; using Glamourer.State;
using Luna; using Luna;
using OtterGui.Extensions; using OtterGui.Extensions;
using OtterGui.Log;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using Penumbra.String; using Penumbra.String;
using LazyString = Luna.LazyString;
namespace Glamourer.Api; namespace Glamourer.Api;
@ -84,15 +84,15 @@ public class ApiHelpers(ActorObjectManager objects, StateManager stateManager, A
return []; return [];
return stateManager.Values.Where(state => state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString) return stateManager.Values.Where(state => state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString)
.Concat(ArrayExtensions.SelectWhere(objects .Concat(ArrayExtensions.SelectWhere(objects
.Where(kvp => kvp.Key is { IsValid: true, Type: IdentifierType.Player } && kvp.Key.PlayerName == byteString), kvp => .Where(kvp => kvp.Key is { IsValid: true, Type: IdentifierType.Player } && kvp.Key.PlayerName == byteString), kvp =>
{ {
if (stateManager.ContainsKey(kvp.Key)) if (stateManager.ContainsKey(kvp.Key))
return (false, null); return (false, null);
var ret = stateManager.GetOrCreate(kvp.Key, kvp.Value.Objects[0], out var state); var ret = stateManager.GetOrCreate(kvp.Key, kvp.Value.Objects[0], out var state);
return (ret, state); return (ret, state);
})); }));
} }
@ -109,8 +109,8 @@ public class ApiHelpers(ActorObjectManager objects, StateManager stateManager, A
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal static LazyString Args(params object[] arguments) internal static LazyString Args(params object[] arguments)
{ {
if (arguments.Length == 0) if (arguments.Length is 0)
return new LazyString(() => "no arguments"); return new LazyString("no arguments");
return new LazyString(() => return new LazyString(() =>
{ {

View file

@ -1,26 +1,58 @@
namespace Glamourer.GameData; using Luna.Generators;
namespace Glamourer.GameData;
[Flags] [Flags]
[NamedEnum(Utf16: false)]
public enum CustomizeParameterFlag : ushort public enum CustomizeParameterFlag : ushort
{ {
SkinDiffuse = 0x0001, [Name("Skin Color")]
MuscleTone = 0x0002, SkinDiffuse = 0x0001,
SkinSpecular = 0x0004,
LipDiffuse = 0x0008, [Name("Muscle Tone")]
HairDiffuse = 0x0010, MuscleTone = 0x0002,
HairSpecular = 0x0020,
HairHighlight = 0x0040, [Name("Skin Shine")]
LeftEye = 0x0080, SkinSpecular = 0x0004,
RightEye = 0x0100,
FeatureColor = 0x0200, [Name("Lip Color")]
LipDiffuse = 0x0008,
[Name("Hair Color")]
HairDiffuse = 0x0010,
[Name("Hair Shine")]
HairSpecular = 0x0020,
[Name("Hair Highlights")]
HairHighlight = 0x0040,
[Name("Left Eye Color")]
LeftEye = 0x0080,
[Name("Right Eye Color")]
RightEye = 0x0100,
[Name("Feature Color")]
FeatureColor = 0x0200,
[Name("Multiplier for Face Paint")]
FacePaintUvMultiplier = 0x0400, FacePaintUvMultiplier = 0x0400,
FacePaintUvOffset = 0x0800,
DecalColor = 0x1000, [Name("Offset of Face Paint")]
LeftLimbalIntensity = 0x2000, FacePaintUvOffset = 0x0800,
RightLimbalIntensity = 0x4000,
[Name("Face Paint Color")]
DecalColor = 0x1000,
[Name("Left Limbal Ring Intensity")]
LeftLimbalIntensity = 0x2000,
[Name("Right Limbal Ring Intensity")]
RightLimbalIntensity = 0x4000,
} }
public static class CustomizeParameterExtensions public static partial class CustomizeParameterExtensions
{ {
// Speculars are not available anymore. // Speculars are not available anymore.
public const CustomizeParameterFlag All = (CustomizeParameterFlag)0x7FDB; public const CustomizeParameterFlag All = (CustomizeParameterFlag)0x7FDB;
@ -36,7 +68,9 @@ public static class CustomizeParameterExtensions
public const CustomizeParameterFlag Values = CustomizeParameterFlag.FacePaintUvOffset | CustomizeParameterFlag.FacePaintUvMultiplier; public const CustomizeParameterFlag Values = CustomizeParameterFlag.FacePaintUvOffset | CustomizeParameterFlag.FacePaintUvMultiplier;
public static readonly IReadOnlyList<CustomizeParameterFlag> AllFlags = [.. Enum.GetValues<CustomizeParameterFlag>().Where(f => All.HasFlag(f))]; public static readonly IReadOnlyList<CustomizeParameterFlag> AllFlags =
[.. Enum.GetValues<CustomizeParameterFlag>().Where(f => All.HasFlag(f))];
public static readonly IReadOnlyList<CustomizeParameterFlag> RgbaFlags = AllFlags.Where(f => RgbaQuadruples.HasFlag(f)).ToArray(); public static readonly IReadOnlyList<CustomizeParameterFlag> RgbaFlags = AllFlags.Where(f => RgbaQuadruples.HasFlag(f)).ToArray();
public static readonly IReadOnlyList<CustomizeParameterFlag> RgbFlags = AllFlags.Where(f => RgbTriples.HasFlag(f)).ToArray(); public static readonly IReadOnlyList<CustomizeParameterFlag> RgbFlags = AllFlags.Where(f => RgbTriples.HasFlag(f)).ToArray();
public static readonly IReadOnlyList<CustomizeParameterFlag> PercentageFlags = AllFlags.Where(f => Percentages.HasFlag(f)).ToArray(); public static readonly IReadOnlyList<CustomizeParameterFlag> PercentageFlags = AllFlags.Where(f => Percentages.HasFlag(f)).ToArray();
@ -50,25 +84,4 @@ public static class CustomizeParameterExtensions
public static int ToInternalIndex(this CustomizeParameterFlag flag) public static int ToInternalIndex(this CustomizeParameterFlag flag)
=> BitOperations.TrailingZeroCount((uint)flag); => BitOperations.TrailingZeroCount((uint)flag);
public static string ToName(this CustomizeParameterFlag flag)
=> flag switch
{
CustomizeParameterFlag.SkinDiffuse => "Skin Color",
CustomizeParameterFlag.MuscleTone => "Muscle Tone",
CustomizeParameterFlag.SkinSpecular => "Skin Shine",
CustomizeParameterFlag.LipDiffuse => "Lip Color",
CustomizeParameterFlag.HairDiffuse => "Hair Color",
CustomizeParameterFlag.HairSpecular => "Hair Shine",
CustomizeParameterFlag.HairHighlight => "Hair Highlights",
CustomizeParameterFlag.LeftEye => "Left Eye Color",
CustomizeParameterFlag.RightEye => "Right Eye Color",
CustomizeParameterFlag.FeatureColor => "Feature Color",
CustomizeParameterFlag.FacePaintUvMultiplier => "Multiplier for Face Paint",
CustomizeParameterFlag.FacePaintUvOffset => "Offset of Face Paint",
CustomizeParameterFlag.DecalColor => "Face Paint Color",
CustomizeParameterFlag.LeftLimbalIntensity => "Left Limbal Ring Intensity",
CustomizeParameterFlag.RightLimbalIntensity => "Right Limbal Ring Intensity",
_ => string.Empty,
};
} }

View file

@ -1,4 +1,4 @@
using OtterGui; using ImSharp;
using OtterGui.Extensions; using OtterGui.Extensions;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
@ -27,7 +27,7 @@ public class CustomizeSet
public SubRace Clan { get; } public SubRace Clan { get; }
public Race Race { get; } public Race Race { get; }
public string Name { get; internal init; } = string.Empty; public StringU8 Name { get; internal init; } = StringU8.Empty;
public CustomizeFlag SettingAvailable { get; internal set; } public CustomizeFlag SettingAvailable { get; internal set; }
@ -38,9 +38,9 @@ public class CustomizeSet
=> SettingAvailable.HasFlag(index.ToFlag()); => SettingAvailable.HasFlag(index.ToFlag());
// Meta // Meta
public IReadOnlyList<string> OptionName { get; internal init; } = null!; public IReadOnlyList<StringU8> OptionName { get; internal init; } = null!;
public string Option(CustomizeIndex index) public StringU8 Option(CustomizeIndex index)
=> OptionName[(int)index]; => OptionName[(int)index];
public IReadOnlyList<byte> Voices { get; internal init; } = null!; public IReadOnlyList<byte> Voices { get; internal init; } = null!;

View file

@ -1,4 +1,5 @@
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using ImSharp;
namespace Glamourer.Gui; namespace Glamourer.Gui;
@ -79,7 +80,7 @@ public static class Colors
private static IReadOnlyDictionary<ColorId, uint> _colors = new Dictionary<ColorId, uint>(); private static IReadOnlyDictionary<ColorId, uint> _colors = new Dictionary<ColorId, uint>();
/// <summary> Obtain the configured value for a color. </summary> /// <summary> Obtain the configured value for a color. </summary>
public static uint Value(this ColorId color) public static Rgba32 Value(this ColorId color)
=> _colors.TryGetValue(color, out var value) ? value : color.Data().DefaultColor; => _colors.TryGetValue(color, out var value) ? value : color.Data().DefaultColor;
/// <summary> Set the configurable colors dictionary to a value. </summary> /// <summary> Set the configurable colors dictionary to a value. </summary>

View file

@ -1,8 +1,6 @@
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using ImSharp; using ImSharp;
using OtterGui; using Luna;
using OtterGui.Raii;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
@ -10,15 +8,19 @@ namespace Glamourer.Gui.Customization;
public partial class CustomizationDrawer public partial class CustomizationDrawer
{ {
private static readonly AwesomeIcon Male = FontAwesomeIcon.Mars;
private static readonly AwesomeIcon Female = FontAwesomeIcon.Venus;
private static readonly AwesomeIcon Unknown = FontAwesomeIcon.Question;
private void DrawRaceGenderSelector() private void DrawRaceGenderSelector()
{ {
DrawGenderSelector(); DrawGenderSelector();
Im.Line.Same(); Im.Line.Same();
using var group = ImRaii.Group(); using var group = Im.Group();
DrawRaceCombo(); DrawRaceCombo();
if (_withApply) if (_withApply)
{ {
using var disabled = ImRaii.Disabled(_locked); using var disabled = Im.Disabled(_locked);
if (UiHelpers.DrawCheckbox("##applyGender", "Apply gender of this design.", ChangeApply.HasFlag(CustomizeFlag.Gender), if (UiHelpers.DrawCheckbox("##applyGender", "Apply gender of this design.", ChangeApply.HasFlag(CustomizeFlag.Gender),
out var applyGender, _locked)) out var applyGender, _locked))
ChangeApply = applyGender ? ChangeApply | CustomizeFlag.Gender : ChangeApply & ~CustomizeFlag.Gender; ChangeApply = applyGender ? ChangeApply | CustomizeFlag.Gender : ChangeApply & ~CustomizeFlag.Gender;
@ -29,61 +31,60 @@ public partial class CustomizationDrawer
Im.Line.Same(); Im.Line.Same();
} }
ImGui.AlignTextToFramePadding(); ImEx.TextFrameAligned("Gender & Clan"u8);
ImGui.TextUnformatted("Gender & Clan");
} }
private void DrawGenderSelector() private void DrawGenderSelector()
{ {
using (ImRaii.Disabled(_locked || _lockedRedraw)) using (Im.Disabled(_locked || _lockedRedraw))
{ {
var icon = _customize.Gender switch var icon = _customize.Gender switch
{ {
Gender.Male => FontAwesomeIcon.Mars, Gender.Male => Male,
Gender.Female => FontAwesomeIcon.Venus, Gender.Female => Female,
_ => FontAwesomeIcon.Question, _ => Unknown,
}; };
if (ImGuiUtil.DrawDisabledButton(icon.ToIconString(), _framedIconSize, string.Empty, if (ImEx.Icon.Button(icon, StringU8.Empty, icon != Unknown, _framedIconSize))
icon is not FontAwesomeIcon.Mars and not FontAwesomeIcon.Venus, true)) Changed |= service.ChangeGender(ref _customize, icon == Male ? Gender.Female : Gender.Male);
Changed |= service.ChangeGender(ref _customize, icon is FontAwesomeIcon.Mars ? Gender.Female : Gender.Male);
} }
if (_lockedRedraw && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) if (_lockedRedraw)
ImGui.SetTooltip( Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled,
"The gender can not be changed as this requires a redraw of the character, which is not supported for this actor."); "The gender can not be changed as this requires a redraw of the character, which is not supported for this actor."u8);
} }
private void DrawRaceCombo() private void DrawRaceCombo()
{ {
using (ImRaii.Disabled(_locked || _lockedRedraw)) using (Im.Disabled(_locked || _lockedRedraw))
{ {
ImGui.SetNextItemWidth(_raceSelectorWidth); Im.Item.SetNextWidth(_raceSelectorWidth);
using (var combo = ImRaii.Combo("##subRaceCombo", service.ClanName(_customize.Clan, _customize.Gender))) using (var combo = Im.Combo.Begin("##subRaceCombo"u8, service.ClanName(_customize.Clan, _customize.Gender)))
{ {
if (combo) if (combo)
foreach (var subRace in Enum.GetValues<SubRace>().Skip(1)) // Skip Unknown foreach (var subRace in SubRace.Values.Skip(1)) // Skip Unknown
{ {
if (ImGui.Selectable(service.ClanName(subRace, _customize.Gender), subRace == _customize.Clan)) if (Im.Selectable(service.ClanName(subRace, _customize.Gender), subRace == _customize.Clan))
Changed |= service.ChangeClan(ref _customize, subRace); Changed |= service.ChangeClan(ref _customize, subRace);
} }
} }
} }
if (_lockedRedraw && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) if (_lockedRedraw)
ImGui.SetTooltip("The race can not be changed as this requires a redraw of the character, which is not supported for this actor."); Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled,
"The race can not be changed as this requires a redraw of the character, which is not supported for this actor."u8);
} }
private void DrawBodyType() private void DrawBodyType()
{ {
if (_customize.BodyType.Value == 1) if (_customize.BodyType.Value is 1)
return; return;
var label = _lockedRedraw if (!ImEx.Button(_lockedRedraw
? $"Body Type {_customize.BodyType.Value}" ? $"Body Type {_customize.BodyType.Value}"
: $"Reset Body Type {_customize.BodyType.Value} to Default"; : $"Reset Body Type {_customize.BodyType.Value} to Default",
if (!ImGuiUtil.DrawDisabledButton(label, new Vector2(_raceSelectorWidth + _framedIconSize.X + ImGui.GetStyle().ItemSpacing.X, 0), new Vector2(_raceSelectorWidth + _framedIconSize.X + Im.Style.ItemSpacing.X, 0),
string.Empty, _lockedRedraw)) StringU8.Empty, _lockedRedraw))
return; return;
Changed |= CustomizeFlag.BodyType; Changed |= CustomizeFlag.BodyType;

View file

@ -1,11 +1,8 @@
using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Interface.Textures.TextureWraps;
using Glamourer.GameData; using Glamourer.GameData;
using Glamourer.Unlocks; using Glamourer.Unlocks;
using Dalamud.Bindings.ImGui;
using ImSharp; using ImSharp;
using OtterGui; using Luna;
using OtterGui.Extensions;
using OtterGui.Raii;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
@ -13,12 +10,13 @@ namespace Glamourer.Gui.Customization;
public partial class CustomizationDrawer public partial class CustomizationDrawer
{ {
private const string IconSelectorPopup = "Style Picker"; private static ReadOnlySpan<byte> IconSelectorPopup
=> "Style Picker"u8;
private void DrawIconSelector(CustomizeIndex index) private void DrawIconSelector(CustomizeIndex index)
{ {
using var id = SetId(index); using var id = SetId(index);
using var bigGroup = ImRaii.Group(); using var bigGroup = Im.Group();
var label = _currentOption; var label = _currentOption;
var current = _set.DataByValue(index, _currentByte, out var custom, _customize.Face); var current = _set.DataByValue(index, _currentByte, out var custom, _customize.Face);
@ -26,7 +24,7 @@ public partial class CustomizationDrawer
var npc = false; var npc = false;
if (current < 0) if (current < 0)
{ {
label = $"{_currentOption} (NPC)"; label = new StringU8($"{_currentOption} (NPC)");
current = 0; current = 0;
custom = _set.Data(index, 0); custom = _set.Data(index, 0);
npc = true; npc = true;
@ -34,11 +32,11 @@ public partial class CustomizationDrawer
var icon = service.Manager.GetIcon(custom!.Value.IconId); var icon = service.Manager.GetIcon(custom!.Value.IconId);
var hasIcon = icon.TryGetWrap(out var wrap, out _); var hasIcon = icon.TryGetWrap(out var wrap, out _);
using (_ = ImRaii.Disabled(_locked || _currentIndex is CustomizeIndex.Face && _lockedRedraw)) using (Im.Disabled(_locked || _currentIndex is CustomizeIndex.Face && _lockedRedraw))
{ {
if (ImGui.ImageButton(wrap?.Handle ?? icon.GetWrapOrEmpty().Handle, _iconSize)) if (Im.Image.Button(wrap?.Id ?? icon.GetWrapOrEmpty().Id, _iconSize))
{ {
ImGui.OpenPopup(IconSelectorPopup); Im.Popup.Open(IconSelectorPopup);
} }
else if (originalCurrent >= 0 && CaptureMouseWheel(ref current, 0, _currentCount)) else if (originalCurrent >= 0 && CaptureMouseWheel(ref current, 0, _currentCount))
{ {
@ -48,15 +46,15 @@ public partial class CustomizationDrawer
} }
if (hasIcon) if (hasIcon)
ImGuiUtil.HoverIconTooltip(wrap!, _iconSize); Im.Tooltip.ImageOnHover(wrap!.Id, _iconSize);
Im.Line.Same(); Im.Line.Same();
using (_ = ImRaii.Group()) using (Im.Group())
{ {
DataInputInt(current, npc); DataInputInt(current, npc);
if (_lockedRedraw && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) if (_lockedRedraw)
ImGui.SetTooltip( Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled,
"The face can not be changed as this requires a redraw of the character, which is not supported for this actor."); "The face can not be changed as this requires a redraw of the character, which is not supported for this actor."u8);
if (_withApply) if (_withApply)
{ {
@ -64,7 +62,7 @@ public partial class CustomizationDrawer
Im.Line.Same(); Im.Line.Same();
} }
ImGui.TextUnformatted(label); Im.Text(label);
} }
DrawIconPickerPopup(current); DrawIconPickerPopup(current);
@ -72,47 +70,47 @@ public partial class CustomizationDrawer
private void DrawIconPickerPopup(int current) private void DrawIconPickerPopup(int current)
{ {
using var popup = ImRaii.Popup(IconSelectorPopup, ImGuiWindowFlags.AlwaysAutoResize); using var popup = Im.Popup.Begin(IconSelectorPopup, WindowFlags.AlwaysAutoResize);
if (!popup) if (!popup)
return; return;
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero) using var style = ImStyleDouble.ItemSpacing.Push(Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0); .Push(ImStyleSingle.FrameRounding, 0);
for (var i = 0; i < _currentCount; ++i) for (var i = 0; i < _currentCount; ++i)
{ {
var custom = _set.Data(_currentIndex, i, _customize.Face); var custom = _set.Data(_currentIndex, i, _customize.Face);
var icon = service.Manager.GetIcon(custom.IconId); var icon = service.Manager.GetIcon(custom.IconId);
using (var _ = ImRaii.Group()) using (Im.Group())
{ {
var isFavorite = favorites.Contains(_set.Gender, _set.Clan, _currentIndex, custom.Value); var isFavorite = favorites.Contains(_set.Gender, _set.Clan, _currentIndex, custom.Value);
using var frameColor = current == i using var frameColor = current == i
? ImRaii.PushColor(ImGuiCol.Button, Colors.SelectedRed) ? ImGuiColor.Button.Push(Colors.SelectedRed)
: ImRaii.PushColor(ImGuiCol.Button, ColorId.FavoriteStarOn.Value(), isFavorite); : ImGuiColor.Button.Push(ColorId.FavoriteStarOn.Value(), isFavorite);
var hasIcon = icon.TryGetWrap(out var wrap, out var _); var hasIcon = icon.TryGetWrap(out var wrap, out _);
if (ImGui.ImageButton(wrap?.Handle ?? icon.GetWrapOrEmpty().Handle, _iconSize)) if (Im.Image.Button(wrap?.Id ?? icon.GetWrapOrEmpty().Id, _iconSize))
{ {
UpdateValue(custom.Value); UpdateValue(custom.Value);
ImGui.CloseCurrentPopup(); Im.Popup.CloseCurrent();
} }
if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) if (Im.Item.RightClicked())
if (isFavorite) if (isFavorite)
favorites.Remove(_set.Gender, _set.Clan, _currentIndex, custom.Value); favorites.Remove(_set.Gender, _set.Clan, _currentIndex, custom.Value);
else else
favorites.TryAdd(_set.Gender, _set.Clan, _currentIndex, custom.Value); favorites.TryAdd(_set.Gender, _set.Clan, _currentIndex, custom.Value);
if (hasIcon) if (hasIcon)
ImGuiUtil.HoverIconTooltip(wrap!, _iconSize, Im.Tooltip.ImageOnHover(wrap!.Id, _iconSize,
FavoriteManager.TypeAllowed(_currentIndex) ? "Right-Click to toggle favorite." : string.Empty); FavoriteManager.TypeAllowed(_currentIndex) ? "Right-Click to toggle favorite."u8 : StringU8.Empty);
var text = custom.Value.ToString(); var text = new StringU8($"{custom.Value.Value}");
var textWidth = ImGui.CalcTextSize(text).X; var textWidth = Im.Font.CalculateButtonSize(text).X;
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + (_iconSize.X - textWidth + 2 * ImGui.GetStyle().FramePadding.X) / 2); Im.Cursor.X += +(_iconSize.X - textWidth) / 2;
ImGui.TextUnformatted(text); Im.Text(text);
} }
if (i % 8 != 7) if (i % 8 is not 7)
Im.Line.Same(); Im.Line.Same();
} }
} }
@ -121,18 +119,18 @@ public partial class CustomizationDrawer
// Only used for facial features, so fixed ID. // Only used for facial features, so fixed ID.
private void DrawMultiIconSelector() private void DrawMultiIconSelector()
{ {
using var bigGroup = ImRaii.Group(); using var bigGroup = Im.Group();
using var disabled = ImRaii.Disabled(_locked); using var disabled = Im.Disabled(_locked);
DrawMultiIcons(); DrawMultiIcons();
Im.Line.Same(); Im.Line.Same();
using var group = ImRaii.Group(); using var group = Im.Group();
_currentCount = 256; _currentCount = 256;
if (_withApply) if (_withApply)
{ {
ApplyCheckbox(CustomizeIndex.FacialFeature1); ApplyCheckbox(CustomizeIndex.FacialFeature1);
Im.Line.Same(); Im.Line.Same();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + _spacing.X); Im.Cursor.X += _spacing.X;
ApplyCheckbox(CustomizeIndex.FacialFeature2); ApplyCheckbox(CustomizeIndex.FacialFeature2);
Im.Line.Same(); Im.Line.Same();
ApplyCheckbox(CustomizeIndex.FacialFeature3); ApplyCheckbox(CustomizeIndex.FacialFeature3);
@ -141,64 +139,62 @@ public partial class CustomizationDrawer
} }
else else
{ {
ImGui.Dummy(new Vector2(ImGui.GetFrameHeight())); Im.FrameDummy();
} }
var oldValue = _customize.AtIndex(_currentIndex.ToByteAndMask().ByteIdx); var oldValue = _customize.AtIndex(_currentIndex.ToByteAndMask().ByteIdx);
var tmp = (int)oldValue.Value; var tmp = (int)oldValue.Value;
ImGui.SetNextItemWidth(_inputIntSize); Im.Item.SetNextWidth(_inputIntSize);
if (ImGui.InputInt("##text", ref tmp, 1, 1)) if (Im.Input.Scalar("##text"u8, ref tmp, 1, 1))
{ {
tmp = Math.Clamp(tmp, 0, byte.MaxValue); tmp = Math.Clamp(tmp, 0, byte.MaxValue);
if (tmp != oldValue.Value) if (tmp != oldValue.Value)
{ {
_customize.SetByIndex(_currentIndex.ToByteAndMask().ByteIdx, (CustomizeValue)tmp); _customize.SetByIndex(_currentIndex.ToByteAndMask().ByteIdx, (CustomizeValue)tmp);
var changes = (byte)tmp ^ oldValue.Value; var changes = (byte)tmp ^ oldValue.Value;
Changed |= ((changes & 0x01) == 0x01 ? CustomizeFlag.FacialFeature1 : 0) Changed |= ((changes & 0x01) is 0x01 ? CustomizeFlag.FacialFeature1 : 0)
| ((changes & 0x02) == 0x02 ? CustomizeFlag.FacialFeature2 : 0) | ((changes & 0x02) is 0x02 ? CustomizeFlag.FacialFeature2 : 0)
| ((changes & 0x04) == 0x04 ? CustomizeFlag.FacialFeature3 : 0) | ((changes & 0x04) is 0x04 ? CustomizeFlag.FacialFeature3 : 0)
| ((changes & 0x08) == 0x08 ? CustomizeFlag.FacialFeature4 : 0) | ((changes & 0x08) is 0x08 ? CustomizeFlag.FacialFeature4 : 0)
| ((changes & 0x10) == 0x10 ? CustomizeFlag.FacialFeature5 : 0) | ((changes & 0x10) is 0x10 ? CustomizeFlag.FacialFeature5 : 0)
| ((changes & 0x20) == 0x20 ? CustomizeFlag.FacialFeature6 : 0) | ((changes & 0x20) is 0x20 ? CustomizeFlag.FacialFeature6 : 0)
| ((changes & 0x40) == 0x40 ? CustomizeFlag.FacialFeature7 : 0) | ((changes & 0x40) is 0x40 ? CustomizeFlag.FacialFeature7 : 0)
| ((changes & 0x80) == 0x80 ? CustomizeFlag.LegacyTattoo : 0); | ((changes & 0x80) is 0x80 ? CustomizeFlag.LegacyTattoo : 0);
} }
} }
if (_set.DataByValue(CustomizeIndex.Face, _customize.Face, out _, _customize.Face) < 0) if (_set.DataByValue(CustomizeIndex.Face, _customize.Face, out _, _customize.Face) < 0)
{ {
Im.Line.Same(); Im.Line.Same();
ImGui.AlignTextToFramePadding(); using var _ = Im.Enabled();
using var _ = ImRaii.Enabled(); ImEx.TextFrameAligned("(Using Face 1)"u8);
ImGui.TextUnformatted("(Using Face 1)");
} }
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + _spacing.Y); Im.Cursor.Y += _spacing.Y;
ImGui.AlignTextToFramePadding(); using (Im.Enabled())
using (var _ = ImRaii.Enabled())
{ {
ImGui.TextUnformatted("Facial Features & Tattoos"); ImEx.TextFrameAligned("Facial Features & Tattoos"u8);
} }
if (_withApply) if (!_withApply)
{ return;
ApplyCheckbox(CustomizeIndex.FacialFeature5);
Im.Line.Same(); ApplyCheckbox(CustomizeIndex.FacialFeature5);
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + _spacing.X); Im.Line.Same();
ApplyCheckbox(CustomizeIndex.FacialFeature6); Im.Cursor.X += _spacing.X;
Im.Line.Same(); ApplyCheckbox(CustomizeIndex.FacialFeature6);
ApplyCheckbox(CustomizeIndex.FacialFeature7); Im.Line.Same();
Im.Line.Same(); ApplyCheckbox(CustomizeIndex.FacialFeature7);
ApplyCheckbox(CustomizeIndex.LegacyTattoo); Im.Line.Same();
} ApplyCheckbox(CustomizeIndex.LegacyTattoo);
} }
private void DrawMultiIcons() private void DrawMultiIcons()
{ {
var options = _set.Order[MenuType.IconCheckmark]; var options = _set.Order[MenuType.IconCheckmark];
using var group = ImRaii.Group(); using var group = Im.Group();
var face = _set.DataByValue(CustomizeIndex.Face, _customize.Face, out _, _customize.Face) < 0 ? _set.Faces[0].Value : _customize.Face; var face = _set.DataByValue(CustomizeIndex.Face, _customize.Face, out _, _customize.Face) < 0 ? _set.Faces[0].Value : _customize.Face;
foreach (var (featureIdx, idx) in options.WithIndex()) foreach (var (idx, featureIdx) in options.Index())
{ {
using var id = SetId(featureIdx); using var id = SetId(featureIdx);
var enabled = _customize.Get(featureIdx) != CustomizeValue.Zero; var enabled = _customize.Get(featureIdx) != CustomizeValue.Zero;
@ -209,23 +205,24 @@ public partial class CustomizationDrawer
if (featureIdx is CustomizeIndex.LegacyTattoo) if (featureIdx is CustomizeIndex.LegacyTattoo)
{ {
wrap = _legacyTattoo; wrap = _legacyTattoo;
hasIcon = wrap != null; hasIcon = wrap is not null;
} }
else else
{ {
hasIcon = icon.TryGetWrap(out wrap, out _); hasIcon = icon.TryGetWrap(out wrap, out _);
} }
if (ImGui.ImageButton(wrap?.Handle ?? icon.GetWrapOrEmpty().Handle, _iconSize, Vector2.Zero, Vector2.One, if (Im.Image.Button(wrap?.Id ?? icon.GetWrapOrEmpty().Id, _iconSize, Vector2.Zero, Vector2.One, Vector4.Zero,
(int)ImGui.GetStyle().FramePadding.X, Vector4.Zero, enabled ? Vector4.One : _redTint)) enabled ? Vector4.One : _redTint,
(int)Im.Style.FramePadding.X))
{ {
_customize.Set(featureIdx, enabled ? CustomizeValue.Zero : CustomizeValue.Max); _customize.Set(featureIdx, enabled ? CustomizeValue.Zero : CustomizeValue.Max);
Changed |= _currentFlag; Changed |= _currentFlag;
} }
if (hasIcon) if (hasIcon)
ImGuiUtil.HoverIconTooltip(wrap!, _iconSize); Im.Tooltip.ImageOnHover(wrap!.Id, _iconSize);
if (idx % 4 != 3) if (idx % 4 is not 3)
Im.Line.Same(); Im.Line.Same();
} }
} }

View file

@ -1,8 +1,5 @@
using Dalamud.Bindings.ImGui; using System.Text.Unicode;
using ImSharp; using ImSharp;
using OtterGui;
using OtterGui.Raii;
using OtterGuiInternal;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
@ -13,9 +10,9 @@ public partial class CustomizationDrawer
private void PercentageSelector(CustomizeIndex index) private void PercentageSelector(CustomizeIndex index)
{ {
using var _ = SetId(index); using var _ = SetId(index);
using var bigGroup = ImRaii.Group(); using var bigGroup = Im.Group();
using (var disabled = ImRaii.Disabled(_locked)) using (Im.Disabled(_locked))
{ {
DrawPercentageSlider(); DrawPercentageSlider();
Im.Line.Same(); Im.Line.Same();
@ -28,8 +25,7 @@ public partial class CustomizationDrawer
} }
Im.Line.Same(); Im.Line.Same();
ImGui.AlignTextToFramePadding(); ImEx.TextFrameAligned(_currentOption);
ImGui.TextUnformatted(_currentOption);
if (_currentIndex is CustomizeIndex.Height) if (_currentIndex is CustomizeIndex.Height)
DrawHeight(); DrawHeight();
} }
@ -42,24 +38,26 @@ public partial class CustomizationDrawer
var height = heightService.Height(_customize); var height = heightService.Height(_customize);
Im.Line.Same(); Im.Line.Same();
var heightString = config.HeightDisplayType switch Span<byte> t = stackalloc byte[64];
{ var ic = CultureInfo.InvariantCulture;
HeightDisplayType.Centimetre => FormattableString.Invariant($"({height * 100:F1} cm)"), if (config.HeightDisplayType switch
HeightDisplayType.Metre => FormattableString.Invariant($"({height:F2} m)"), {
HeightDisplayType.Wrong => FormattableString.Invariant($"({height * 100 / 2.539:F1} in)"), HeightDisplayType.Centimetre => Utf8.TryWrite(t, ic, $"({height * 100:F1} cm)", out _),
HeightDisplayType.WrongFoot => $"({(int)(height * 100 / 2.539 / 12)}'{(int)(height * 100 / 2.539) % 12}'')", HeightDisplayType.Metre => Utf8.TryWrite(t, ic, $"({height:F2} m)", out _),
HeightDisplayType.Corgi => FormattableString.Invariant($"({height * 100 / 40.0:F1} Corgis)"), HeightDisplayType.Wrong => Utf8.TryWrite(t, ic, $"({height * 100 / 2.539:F1} in)", out _),
HeightDisplayType.OlympicPool => FormattableString.Invariant($"({height / 3.0:F3} Pools)"), HeightDisplayType.WrongFoot => Utf8.TryWrite(t, ic, $"({(int)(height * 3.2821)}'{(int)(height * 39.3856) % 12}'')", out _),
_ => FormattableString.Invariant($"({height})"), HeightDisplayType.Corgi => Utf8.TryWrite(t, ic, $"({height * 100 / 40.0:F1} Corgis)", out _),
}; HeightDisplayType.OlympicPool => Utf8.TryWrite(t, ic, $"({height / 3.0:F3} Pools)", out _),
ImGui.TextUnformatted(heightString); _ => Utf8.TryWrite(t, ic, $"({height})", out _),
})
Im.Text(t);
} }
private void DrawPercentageSlider() private void DrawPercentageSlider()
{ {
var tmp = (int)_currentByte.Value; var tmp = (int)_currentByte.Value;
ImGui.SetNextItemWidth(_comboSelectorSize); Im.Item.SetNextWidth(_comboSelectorSize);
if (ImGui.SliderInt("##slider", ref tmp, 0, _currentCount - 1, "%i", ImGuiSliderFlags.AlwaysClamp) if (Im.Slider("##slider"u8, ref tmp, "%i"u8, 0, _currentCount - 1, SliderFlags.AlwaysClamp)
|| CaptureMouseWheel(ref tmp, 0, _currentCount)) || CaptureMouseWheel(ref tmp, 0, _currentCount))
UpdateValue((CustomizeValue)tmp); UpdateValue((CustomizeValue)tmp);
} }
@ -67,15 +65,15 @@ public partial class CustomizationDrawer
private void PercentageInputInt() private void PercentageInputInt()
{ {
var tmp = (int)_currentByte.Value; var tmp = (int)_currentByte.Value;
ImGui.SetNextItemWidth(_inputIntSize); Im.Item.SetNextWidth(_inputIntSize);
var cap = ImGui.GetIO().KeyCtrl ? byte.MaxValue : _currentCount - 1; var cap = Im.Io.KeyControl ? byte.MaxValue : _currentCount - 1;
if (ImGui.InputInt("##text", ref tmp, 1, 1)) if (Im.Input.Scalar("##text"u8, ref tmp, 1, 1))
{ {
var newValue = (CustomizeValue)Math.Clamp(tmp, 0, cap); var newValue = (CustomizeValue)Math.Clamp(tmp, 0, cap);
UpdateValue(newValue); UpdateValue(newValue);
} }
ImGuiUtil.HoverTooltip($"Input Range: [0, {_currentCount - 1}]\n" Im.Tooltip.OnHover($"Input Range: [0, {_currentCount - 1}]\n"
+ "Hold Control to force updates with invalid/unknown options at your own risk."); + "Hold Control to force updates with invalid/unknown options at your own risk.");
} }
@ -87,15 +85,15 @@ public partial class CustomizationDrawer
if (_currentIndex is CustomizeIndex.Face && _set.Race is Race.Hrothgar && value is > 4 and < 9) if (_currentIndex is CustomizeIndex.Face && _set.Race is Race.Hrothgar && value is > 4 and < 9)
value -= 4; value -= 4;
using var group = ImRaii.Group(); using var group = Im.Group();
using var disabled = ImRaii.Disabled(_locked || _currentIndex is CustomizeIndex.Face && _lockedRedraw); using var disabled = Im.Disabled(_locked || _currentIndex is CustomizeIndex.Face && _lockedRedraw);
ImGui.SetNextItemWidth(_inputIntSizeNoButtons); Im.Item.SetNextWidth(_inputIntSizeNoButtons);
if (ImGui.InputInt("##text", ref value, 0, 0)) if (Im.Input.Scalar("##text"u8, ref value))
{ {
var index = _set.DataByValue(_currentIndex, (CustomizeValue)value, out var data, _customize.Face); var index = _set.DataByValue(_currentIndex, (CustomizeValue)value, out var data, _customize.Face);
if (index >= 0) if (index >= 0)
UpdateValue(data!.Value.Value); UpdateValue(data!.Value.Value);
else if (ImGui.GetIO().KeyCtrl) else if (Im.Io.KeyControl)
UpdateValue((CustomizeValue)value); UpdateValue((CustomizeValue)value);
} }
else else
@ -104,17 +102,16 @@ public partial class CustomizationDrawer
} }
if (!_withApply) if (!_withApply)
ImGuiUtil.HoverTooltip("Hold Control to force updates with invalid/unknown options at your own risk."); Im.Tooltip.OnHover("Hold Control to force updates with invalid/unknown options at your own risk.");
var size = new Vector2(Im.Style.FrameHeight);
Im.Line.Same(); Im.Line.Same();
if (ImGuiUtil.DrawDisabledButton("-", new Vector2(ImGui.GetFrameHeight()), "Select the previous available option in order.", if (ImEx.Button("-"u8, size, "Select the previous available option in order."u8, currentIndex <= 0))
currentIndex <= 0))
UpdateValue(_set.Data(_currentIndex, currentIndex - 1, _customize.Face).Value); UpdateValue(_set.Data(_currentIndex, currentIndex - 1, _customize.Face).Value);
else else
CheckWheel(); CheckWheel();
Im.Line.Same(); Im.Line.Same();
if (ImGuiUtil.DrawDisabledButton("+", new Vector2(ImGui.GetFrameHeight()), "Select the next available option in order.", if (ImEx.Button("+"u8, size, "Select the next available option in order."u8, currentIndex >= _currentCount - 1 || npc))
currentIndex >= _currentCount - 1 || npc))
UpdateValue(_set.Data(_currentIndex, currentIndex + 1, _customize.Face).Value); UpdateValue(_set.Data(_currentIndex, currentIndex + 1, _customize.Face).Value);
else else
CheckWheel(); CheckWheel();
@ -133,9 +130,9 @@ public partial class CustomizationDrawer
private void DrawListSelector(CustomizeIndex index, bool indexedBy1) private void DrawListSelector(CustomizeIndex index, bool indexedBy1)
{ {
using var id = SetId(index); using var id = SetId(index);
using var bigGroup = ImRaii.Group(); using var bigGroup = Im.Group();
using (_ = ImRaii.Disabled(_locked)) using (Im.Disabled(_locked))
{ {
if (indexedBy1) if (indexedBy1)
{ {
@ -158,21 +155,20 @@ public partial class CustomizationDrawer
} }
Im.Line.Same(); Im.Line.Same();
ImGui.AlignTextToFramePadding(); ImEx.TextFrameAligned(_currentOption);
ImGui.TextUnformatted(_currentOption);
} }
private void ListCombo0() private void ListCombo0()
{ {
ImGui.SetNextItemWidth(_comboSelectorSize * ImGui.GetIO().FontGlobalScale); Im.Item.SetNextWidth(_comboSelectorSize * Im.Io.GlobalScale);
var current = (int)_currentByte.Value; var current = (int)_currentByte.Value;
using (var combo = ImRaii.Combo("##combo", $"{_currentOption} #{current + 1}")) using (var combo = Im.Combo.Begin("##combo"u8, $"{_currentOption} #{current + 1}"))
{ {
if (combo) if (combo)
for (var i = 0; i < _currentCount; ++i) for (var i = 0; i < _currentCount; ++i)
{ {
if (ImGui.Selectable($"{_currentOption} #{i + 1}##combo", i == current)) if (Im.Selectable($"{_currentOption} #{i + 1}##combo", i == current))
UpdateValue((CustomizeValue)i); UpdateValue((CustomizeValue)i);
} }
} }
@ -184,28 +180,28 @@ public partial class CustomizationDrawer
private void ListInputInt0() private void ListInputInt0()
{ {
var tmp = _currentByte.Value + 1; var tmp = _currentByte.Value + 1;
ImGui.SetNextItemWidth(_inputIntSize); Im.Item.SetNextWidth(_inputIntSize);
var cap = ImGui.GetIO().KeyCtrl ? byte.MaxValue + 1 : _currentCount; var cap = Im.Io.KeyControl ? byte.MaxValue + 1 : _currentCount;
if (ImGui.InputInt("##text", ref tmp, 1, 1)) if (Im.Input.Scalar("##text"u8, ref tmp, 1, 1))
{ {
var newValue = Math.Clamp(tmp, 1, cap); var newValue = Math.Clamp(tmp, 1, cap);
UpdateValue((CustomizeValue)(newValue - 1)); UpdateValue((CustomizeValue)(newValue - 1));
} }
ImGuiUtil.HoverTooltip($"Input Range: [1, {_currentCount}]\n" Im.Tooltip.OnHover($"Input Range: [1, {_currentCount}]\n"
+ "Hold Control to force updates with invalid/unknown options at your own risk."); + "Hold Control to force updates with invalid/unknown options at your own risk.");
} }
private void ListCombo1() private void ListCombo1()
{ {
ImGui.SetNextItemWidth(_comboSelectorSize * ImGui.GetIO().FontGlobalScale); Im.Item.SetNextWidth(_comboSelectorSize * Im.Io.GlobalScale);
var current = (int)_currentByte.Value; var current = (int)_currentByte.Value;
using (var combo = ImRaii.Combo("##combo", $"{_currentOption} #{current}")) using (var combo = Im.Combo.Begin("##combo"u8, $"{_currentOption} #{current}"))
{ {
if (combo) if (combo)
for (var i = 1; i <= _currentCount; ++i) for (var i = 1; i <= _currentCount; ++i)
{ {
if (ImGui.Selectable($"{_currentOption} #{i}##combo", i == current)) if (Im.Selectable($"{_currentOption} #{i}##combo", i == current))
UpdateValue((CustomizeValue)i); UpdateValue((CustomizeValue)i);
} }
} }
@ -217,27 +213,27 @@ public partial class CustomizationDrawer
private void ListInputInt1() private void ListInputInt1()
{ {
var tmp = (int)_currentByte.Value; var tmp = (int)_currentByte.Value;
ImGui.SetNextItemWidth(_inputIntSize); Im.Item.SetNextWidth(_inputIntSize);
var (offset, cap) = ImGui.GetIO().KeyCtrl ? (0, byte.MaxValue) : (1, _currentCount); var (offset, cap) = Im.Io.KeyControl ? (0, byte.MaxValue) : (1, _currentCount);
if (ImGui.InputInt("##text", ref tmp, 1, 1)) if (Im.Input.Scalar("##text"u8, ref tmp, 1, 1))
{ {
var newValue = (CustomizeValue)Math.Clamp(tmp, offset, cap); var newValue = (CustomizeValue)Math.Clamp(tmp, offset, cap);
UpdateValue(newValue); UpdateValue(newValue);
} }
ImGuiUtil.HoverTooltip($"Input Range: [1, {_currentCount}]\n" Im.Tooltip.OnHover($"Input Range: [1, {_currentCount}]\n"
+ "Hold Control to force updates with invalid/unknown options at your own risk."); + "Hold Control to force updates with invalid/unknown options at your own risk.");
} }
private static bool CaptureMouseWheel(ref int value, int offset, int cap) private static bool CaptureMouseWheel(ref int value, int offset, int cap)
{ {
if (!ImGui.IsItemHovered() || !ImGui.GetIO().KeyCtrl) if (!Im.Item.Hovered() || !Im.Io.KeyControl)
return false; return false;
ImGuiInternal.ItemSetUsingMouseWheel(); Im.Item.SetUsingMouseWheel();
var mw = (int)ImGui.GetIO().MouseWheel; var mw = (int)Im.Io.MouseWheel;
if (mw == 0) if (mw is 0)
return false; return false;
value -= offset; value -= offset;
@ -262,9 +258,7 @@ public partial class CustomizationDrawer
_customize.Set(idx, newValue ? CustomizeValue.Max : CustomizeValue.Zero); _customize.Set(idx, newValue ? CustomizeValue.Max : CustomizeValue.Zero);
Changed |= _currentFlag; Changed |= _currentFlag;
break; break;
case (false, true): case (false, true): ChangeApply = newApply ? ChangeApply | _currentFlag : ChangeApply & ~_currentFlag; break;
ChangeApply = newApply ? ChangeApply | _currentFlag : ChangeApply & ~_currentFlag;
break;
case (true, true): case (true, true):
ChangeApply = newApply ? ChangeApply | _currentFlag : ChangeApply & ~_currentFlag; ChangeApply = newApply ? ChangeApply | _currentFlag : ChangeApply & ~_currentFlag;
_customize.Set(idx, newValue ? CustomizeValue.Max : CustomizeValue.Zero); _customize.Set(idx, newValue ? CustomizeValue.Max : CustomizeValue.Zero);
@ -274,9 +268,9 @@ public partial class CustomizationDrawer
} }
else else
{ {
using (_ = ImRaii.Disabled(_locked)) using (Im.Disabled(_locked))
{ {
if (ImGui.Checkbox("##toggle", ref tmp)) if (Im.Checkbox("##toggle"u8, ref tmp))
{ {
_customize.Set(idx, tmp ? CustomizeValue.Max : CustomizeValue.Zero); _customize.Set(idx, tmp ? CustomizeValue.Max : CustomizeValue.Zero);
Changed |= _currentFlag; Changed |= _currentFlag;
@ -284,7 +278,7 @@ public partial class CustomizationDrawer
} }
Im.Line.Same(); Im.Line.Same();
ImGui.TextUnformatted(_currentIndex.ToDefaultName()); Im.Text(_currentIndex.ToDefaultName());
} }
} }

View file

@ -81,7 +81,7 @@ public partial class CustomizationDrawer(
private CustomizeValue _currentByte = CustomizeValue.Zero; private CustomizeValue _currentByte = CustomizeValue.Zero;
private bool _currentApply; private bool _currentApply;
private int _currentCount; private int _currentCount;
private string _currentOption = string.Empty; private StringU8 _currentOption = StringU8.Empty;
// Prepare a new customization option. // Prepare a new customization option.
private Im.IdDisposable SetId(CustomizeIndex index) private Im.IdDisposable SetId(CustomizeIndex index)

View file

@ -1,20 +1,16 @@
using Dalamud.Interface; using Glamourer.Designs;
using Glamourer.Designs;
using Glamourer.GameData; using Glamourer.GameData;
using Glamourer.Interop.PalettePlus; using Glamourer.Interop.PalettePlus;
using Glamourer.State; using Glamourer.State;
using Dalamud.Bindings.ImGui;
using ImSharp; using ImSharp;
using Luna; using Luna;
using OtterGui;
using OtterGui.Raii;
namespace Glamourer.Gui.Customization; namespace Glamourer.Gui.Customization;
public class CustomizeParameterDrawer(Configuration config, PaletteImport import) : IService public class CustomizeParameterDrawer(Configuration config, PaletteImport import) : IService
{ {
private readonly Dictionary<Design, CustomizeParameterData> _lastData = []; private readonly Dictionary<Design, CustomizeParameterData> _lastData = [];
private string _paletteName = string.Empty; private StringU8 _paletteName = StringU8.Empty;
private CustomizeParameterData _data; private CustomizeParameterData _data;
private CustomizeParameterFlag _flags; private CustomizeParameterFlag _flags;
private float _width; private float _width;
@ -26,7 +22,7 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
DrawPaletteImport(designManager, design); DrawPaletteImport(designManager, design);
DrawConfig(true); DrawConfig(true);
using (_ = ImRaii.ItemWidth(_width - 2 * ImGui.GetFrameHeight() - 2 * ImGui.GetStyle().ItemInnerSpacing.X)) using (Im.Item.PushWidth(_width - 2 * Im.Style.FrameHeight - 2 * Im.Style.ItemInnerSpacing.X))
{ {
foreach (var flag in CustomizeParameterExtensions.RgbFlags) foreach (var flag in CustomizeParameterExtensions.RgbFlags)
DrawColorInput3(CustomizeParameterDrawData.FromDesign(designManager, design, flag), true); DrawColorInput3(CustomizeParameterDrawData.FromDesign(designManager, design, flag), true);
@ -46,7 +42,7 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
{ {
using var generalSize = EnsureSize(); using var generalSize = EnsureSize();
DrawConfig(false); DrawConfig(false);
using (_ = ImRaii.ItemWidth(_width - 2 * ImGui.GetFrameHeight() - 2 * ImGui.GetStyle().ItemInnerSpacing.X)) using (Im.Item.PushWidth(_width - 2 * Im.Style.FrameHeight - 2 * Im.Style.ItemInnerSpacing.X))
{ {
foreach (var flag in CustomizeParameterExtensions.RgbFlags) foreach (var flag in CustomizeParameterExtensions.RgbFlags)
DrawColorInput3(CustomizeParameterDrawData.FromState(stateManager, state, flag), state.ModelData.Customize.Highlights); DrawColorInput3(CustomizeParameterDrawData.FromState(stateManager, state, flag), state.ModelData.Customize.Highlights);
@ -64,14 +60,14 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
private void DrawPaletteCombo() private void DrawPaletteCombo()
{ {
using var id = ImRaii.PushId("Palettes"); using var id = Im.Id.Push("Palettes"u8);
using var combo = ImRaii.Combo("##import", _paletteName.Length > 0 ? _paletteName : "Select Palette..."); using var combo = Im.Combo.Begin("##import"u8, _paletteName.Length > 0 ? _paletteName : "Select Palette..."u8);
if (!combo) if (!combo)
return; return;
foreach (var (name, (palette, flags)) in import.Data) foreach (var (name, (palette, flags)) in import.Data)
{ {
if (!ImGui.Selectable(name, _paletteName == name)) if (!Im.Selectable(name, _paletteName == name))
continue; continue;
_paletteName = name; _paletteName = name;
@ -85,37 +81,33 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
if (!config.ShowPalettePlusImport) if (!config.ShowPalettePlusImport)
return; return;
var spacing = ImGui.GetStyle().ItemInnerSpacing.X;
DrawPaletteCombo(); DrawPaletteCombo();
ImGui.SameLine(0, spacing); Im.Line.SameInner();
var value = true; var value = true;
if (ImGui.Checkbox("Show Import", ref value)) if (Im.Checkbox("Show Import"u8, ref value))
{ {
config.ShowPalettePlusImport = false; config.ShowPalettePlusImport = false;
config.Save(); config.Save();
} }
ImGuiUtil.HoverTooltip("Hide the Palette+ Import bar from all designs. You can re-enable it in Glamourers interface settings."); Im.Tooltip.OnHover("Hide the Palette+ Import bar from all designs. You can re-enable it in Glamourers interface settings."u8);
var buttonWidth = new Vector2((_width - spacing) / 2, 0); var buttonWidth = new Vector2((_width - Im.Style.ItemInnerSpacing.X) / 2, 0);
var tt = _paletteName.Length > 0 if (ImEx.Button("Apply Import"u8, buttonWidth, _paletteName.Length > 0
? $"Apply the imported data from the Palette+ palette [{_paletteName}] to this design." ? $"Apply the imported data from the Palette+ palette [{_paletteName}] to this design."
: "Please select a palette first."; : "Please select a palette first.", _paletteName.Length is 0 || design.WriteProtected()))
if (ImGuiUtil.DrawDisabledButton("Apply Import", buttonWidth, tt, _paletteName.Length == 0 || design.WriteProtected()))
{ {
_lastData[design] = design.DesignData.Parameters; _lastData[design] = design.DesignData.Parameters;
foreach (var parameter in _flags.Iterate()) foreach (var parameter in _flags.Iterate())
manager.ChangeCustomizeParameter(design, parameter, _data[parameter]); manager.ChangeCustomizeParameter(design, parameter, _data[parameter]);
} }
ImGui.SameLine(0, spacing); Im.Line.SameInner();
var enabled = _lastData.TryGetValue(design, out var oldData); var enabled = _lastData.TryGetValue(design, out var oldData);
tt = enabled if (ImEx.Button("Revert Import"u8, buttonWidth, enabled
? $"Revert to the last set of advanced customization parameters of [{design.Name}] before importing." ? $"Revert to the last set of advanced customization parameters of [{design.Name}] before importing."
: $"You have not imported any data that could be reverted for [{design.Name}]."; : $"You have not imported any data that could be reverted for [{design.Name}].", !enabled || design.WriteProtected()))
if (ImGuiUtil.DrawDisabledButton("Revert Import", buttonWidth, tt, !enabled || design.WriteProtected()))
{ {
_lastData.Remove(design); _lastData.Remove(design);
foreach (var parameter in CustomizeParameterExtensions.AllFlags) foreach (var parameter in CustomizeParameterExtensions.AllFlags)
@ -133,27 +125,27 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
DrawColorFormatOptions(withApply); DrawColorFormatOptions(withApply);
var value = config.ShowColorConfig; var value = config.ShowColorConfig;
Im.Line.Same(); Im.Line.Same();
if (ImGui.Checkbox("Show Config", ref value)) if (Im.Checkbox("Show Config"u8, ref value))
{ {
config.ShowColorConfig = value; config.ShowColorConfig = value;
config.Save(); config.Save();
} }
ImGuiUtil.HoverTooltip( Im.Tooltip.OnHover(
"Hide the color configuration options from the Advanced Customization panel. You can re-enable it in Glamourers interface settings."); "Hide the color configuration options from the Advanced Customization panel. You can re-enable it in Glamourers interface settings."u8);
} }
private void DrawColorDisplayOptions() private void DrawColorDisplayOptions()
{ {
using var group = ImRaii.Group(); using var group = Im.Group();
if (ImGui.RadioButton("RGB", config.UseRgbForColors) && !config.UseRgbForColors) if (Im.RadioButton("RGB"u8, config.UseRgbForColors) && !config.UseRgbForColors)
{ {
config.UseRgbForColors = true; config.UseRgbForColors = true;
config.Save(); config.Save();
} }
Im.Line.Same(); Im.Line.Same();
if (ImGui.RadioButton("HSV", !config.UseRgbForColors) && config.UseRgbForColors) if (Im.RadioButton("HSV"u8, !config.UseRgbForColors) && config.UseRgbForColors)
{ {
config.UseRgbForColors = false; config.UseRgbForColors = false;
config.Save(); config.Save();
@ -163,23 +155,23 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
private void DrawColorFormatOptions(bool withApply) private void DrawColorFormatOptions(bool withApply)
{ {
var width = _width var width = _width
- (ImGui.CalcTextSize("Float").X - (Im.Font.CalculateSize("Float"u8).X
+ ImGui.CalcTextSize("Integer").X + Im.Font.CalculateButtonSize("Integer"u8).X
+ 2 * (ImGui.GetFrameHeight() + ImGui.GetStyle().ItemSpacing.X) + 2 * Im.Style.ItemSpacing.X)
+ ImGui.GetStyle().ItemInnerSpacing.X + Im.Style.ItemInnerSpacing.X
+ ImGui.GetItemRectSize().X); + Im.Item.Size.X;
if (!withApply) if (!withApply)
width -= ImGui.GetFrameHeight() + ImGui.GetStyle().ItemInnerSpacing.X; width -= Im.Style.FrameHeight + Im.Style.ItemInnerSpacing.X;
ImGui.SameLine(0, width); Im.Line.Same(0, width);
if (ImGui.RadioButton("Float", config.UseFloatForColors) && !config.UseFloatForColors) if (Im.RadioButton("Float"u8, config.UseFloatForColors) && !config.UseFloatForColors)
{ {
config.UseFloatForColors = true; config.UseFloatForColors = true;
config.Save(); config.Save();
} }
Im.Line.Same(); Im.Line.Same();
if (ImGui.RadioButton("Integer", !config.UseFloatForColors) && config.UseFloatForColors) if (Im.RadioButton("Integer"u8, !config.UseFloatForColors) && config.UseFloatForColors)
{ {
config.UseFloatForColors = false; config.UseFloatForColors = false;
config.Save(); config.Save();
@ -188,19 +180,19 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
private void DrawColorInput3(in CustomizeParameterDrawData data, bool allowHighlights) private void DrawColorInput3(in CustomizeParameterDrawData data, bool allowHighlights)
{ {
using var id = ImRaii.PushId((int)data.Flag); using var id = Im.Id.Push((int)data.Flag);
var value = data.CurrentValue.InternalTriple; var value = data.CurrentValue.InternalTriple;
var noHighlights = !allowHighlights && data.Flag is CustomizeParameterFlag.HairHighlight; var noHighlights = !allowHighlights && data.Flag is CustomizeParameterFlag.HairHighlight;
DrawCopyPasteButtons(data, data.Locked || noHighlights); DrawCopyPasteButtons(data, data.Locked || noHighlights);
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X); Im.Line.SameInner();
using (_ = ImRaii.Disabled(data.Locked || noHighlights)) using (Im.Disabled(data.Locked || noHighlights))
{ {
if (ImGui.ColorEdit3("##value", ref value, GetFlags())) if (Im.Color.Editor("##value"u8, ref value, GetFlags()))
data.ChangeParameter(new CustomizeParameterValue(value)); data.ChangeParameter(new CustomizeParameterValue(value));
} }
if (noHighlights) if (noHighlights)
ImGuiUtil.HoverTooltip("Highlights are disabled in your regular customizations.", ImGuiHoveredFlags.AllowWhenDisabled); Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, "Highlights are disabled in your regular customizations."u8);
DrawRevert(data); DrawRevert(data);
@ -209,13 +201,13 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
private void DrawColorInput4(in CustomizeParameterDrawData data) private void DrawColorInput4(in CustomizeParameterDrawData data)
{ {
using var id = ImRaii.PushId((int)data.Flag); using var id = Im.Id.Push((int)data.Flag);
var value = data.CurrentValue.InternalQuadruple; var value = data.CurrentValue.InternalQuadruple;
DrawCopyPasteButtons(data, data.Locked); DrawCopyPasteButtons(data, data.Locked);
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X); Im.Line.SameInner();
using (_ = ImRaii.Disabled(data.Locked)) using (Im.Disabled(data.Locked))
{ {
if (ImGui.ColorEdit4("##value", ref value, GetFlags() | ImGuiColorEditFlags.AlphaPreviewHalf)) if (Im.Color.Editor("##value"u8, ref value, GetFlags() | ColorEditorFlags.AlphaPreviewHalf))
data.ChangeParameter(new CustomizeParameterValue(value)); data.ChangeParameter(new CustomizeParameterValue(value));
} }
@ -226,12 +218,12 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
private void DrawValueInput(in CustomizeParameterDrawData data) private void DrawValueInput(in CustomizeParameterDrawData data)
{ {
using var id = ImRaii.PushId((int)data.Flag); using var id = Im.Id.Push((int)data.Flag);
var value = data.CurrentValue[0]; var value = data.CurrentValue[0];
using (_ = ImRaii.Disabled(data.Locked)) using (Im.Disabled(data.Locked))
{ {
if (ImGui.InputFloat("##value", ref value, 0.1f, 0.5f)) if (Im.Input.Scalar("##value"u8, ref value, 0.1f, 0.5f))
data.ChangeParameter(new CustomizeParameterValue(value)); data.ChangeParameter(new CustomizeParameterValue(value));
} }
@ -242,14 +234,14 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
private void DrawPercentageInput(in CustomizeParameterDrawData data) private void DrawPercentageInput(in CustomizeParameterDrawData data)
{ {
using var id = ImRaii.PushId((int)data.Flag); using var id = Im.Id.Push((int)data.Flag);
var value = data.CurrentValue[0] * 100f; var value = data.CurrentValue[0] * 100f;
using (_ = ImRaii.Disabled(data.Locked)) using (Im.Disabled(data.Locked))
{ {
if (ImGui.SliderFloat("##value", ref value, -100f, 300, "%.2f")) if (Im.Slider("##value"u8, ref value, "%.2f"u8, -100f, 300))
data.ChangeParameter(new CustomizeParameterValue(value / 100f)); data.ChangeParameter(new CustomizeParameterValue(value / 100f));
ImGuiUtil.HoverTooltip("You can control-click this to enter arbitrary values by hand instead of dragging."); Im.Tooltip.OnHover("You can control-click this to enter arbitrary values by hand instead of dragging."u8);
} }
DrawRevert(data); DrawRevert(data);
@ -262,10 +254,10 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
if (data.Locked || !data.AllowRevert) if (data.Locked || !data.AllowRevert)
return; return;
if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && ImGui.GetIO().KeyCtrl) if (Im.Item.RightClicked() && Im.Io.KeyControl)
data.ChangeParameter(data.GameValue); data.ChangeParameter(data.GameValue);
ImGuiUtil.HoverTooltip("Hold Control and Right-click to revert to game values."); Im.Tooltip.OnHover("Hold Control and Right-click to revert to game values."u8);
} }
private static void DrawApply(in CustomizeParameterDrawData data) private static void DrawApply(in CustomizeParameterDrawData data)
@ -279,38 +271,37 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
{ {
if (data.DisplayApplication && !config.HideApplyCheckmarks) if (data.DisplayApplication && !config.HideApplyCheckmarks)
{ {
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X); Im.Line.SameInner();
DrawApply(data); DrawApply(data);
} }
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X); Im.Line.SameInner();
ImGui.TextUnformatted(data.Flag.ToName()); Im.Text(data.Flag.ToNameU8());
} }
private ImGuiColorEditFlags GetFlags() private ColorEditorFlags GetFlags()
=> Format | Display | ImGuiColorEditFlags.Hdr | ImGuiColorEditFlags.NoOptions; => Format | Display | ColorEditorFlags.Hdr | ColorEditorFlags.NoOptions;
private ImGuiColorEditFlags Format private ColorEditorFlags Format
=> config.UseFloatForColors ? ImGuiColorEditFlags.Float : ImGuiColorEditFlags.Uint8; => config.UseFloatForColors ? ColorEditorFlags.Float : ColorEditorFlags.Uint8;
private ImGuiColorEditFlags Display private ColorEditorFlags Display
=> config.UseRgbForColors ? ImGuiColorEditFlags.DisplayRgb : ImGuiColorEditFlags.DisplayHsv; => config.UseRgbForColors ? ColorEditorFlags.DisplayRgb : ColorEditorFlags.DisplayHsv;
private ImRaii.IEndObject EnsureSize() private Im.ItemWidthDisposable EnsureSize()
{ {
var iconSize = ImGui.GetTextLineHeight() * 2 + ImGui.GetStyle().ItemSpacing.Y + 4 * ImGui.GetStyle().FramePadding.Y; var iconSize = Im.Style.TextHeight * 2 + Im.Style.ItemSpacing.Y + 4 * Im.Style.FramePadding.Y;
_width = 7 * iconSize + 4 * ImGui.GetStyle().ItemInnerSpacing.X; _width = 7 * iconSize + 4 * Im.Style.ItemInnerSpacing.X;
return ImRaii.ItemWidth(_width); return Im.Item.PushWidth(_width);
} }
private void DrawCopyPasteButtons(in CustomizeParameterDrawData data, bool locked) private void DrawCopyPasteButtons(in CustomizeParameterDrawData data, bool locked)
{ {
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Copy.ToIconString(), new Vector2(ImGui.GetFrameHeight()), if (ImEx.Icon.Button(LunaStyle.ToClipboardIcon, "Copy this color for later use."u8))
"Copy this color for later use.", false, true))
_copy = data.CurrentValue; _copy = data.CurrentValue;
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X); Im.Line.SameInner();
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Paste.ToIconString(), new Vector2(ImGui.GetFrameHeight()), if (ImEx.Icon.Button(LunaStyle.FromClipboardIcon, _copy.HasValue ? "Paste the currently copied value."u8 : "No value copied yet."u8,
_copy.HasValue ? "Paste the currently copied value." : "No value copied yet.", locked || !_copy.HasValue, true)) locked || !_copy.HasValue))
data.ChangeParameter(_copy!.Value); data.ChangeParameter(_copy!.Value);
} }
} }

View file

@ -1,6 +1,7 @@
using Dalamud.Plugin; using Dalamud.Plugin;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.GameData; using Glamourer.GameData;
using ImSharp;
using Luna; using Luna;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@ -11,9 +12,9 @@ public class PaletteImport(IDalamudPluginInterface pluginInterface, DesignManage
private string ConfigFile private string ConfigFile
=> Path.Combine(Path.GetDirectoryName(pluginInterface.GetPluginConfigDirectory())!, "PalettePlus.json"); => Path.Combine(Path.GetDirectoryName(pluginInterface.GetPluginConfigDirectory())!, "PalettePlus.json");
private readonly Dictionary<string, (CustomizeParameterData, CustomizeParameterFlag)> _data = []; private readonly Dictionary<StringU8, (CustomizeParameterData, CustomizeParameterFlag)> _data = [];
public IReadOnlyDictionary<string, (CustomizeParameterData, CustomizeParameterFlag)> Data public IReadOnlyDictionary<StringU8, (CustomizeParameterData, CustomizeParameterFlag)> Data
{ {
get get
{ {
@ -59,24 +60,24 @@ public class PaletteImport(IDalamudPluginInterface pluginInterface, DesignManage
var text = File.ReadAllText(path); var text = File.ReadAllText(path);
var obj = JObject.Parse(text); var obj = JObject.Parse(text);
var palettes = obj["SavedPalettes"]; var palettes = obj["SavedPalettes"];
if (palettes == null) if (palettes is null)
return; return;
foreach (var child in palettes.Children()) foreach (var child in palettes.Children())
{ {
var name = child["Name"]?.ToObject<string>() ?? string.Empty; var name = new StringU8(child["Name"]?.ToObject<string>() ?? string.Empty);
if (name.Length == 0) if (name.Length is 0)
continue; continue;
var conditions = child["Conditions"]?.ToObject<int>() ?? 0; var conditions = child["Conditions"]?.ToObject<int>() ?? 0;
var parameters = child["ShaderParams"]; var parameters = child["ShaderParams"];
if (parameters == null) if (parameters is null)
continue; continue;
var orig = name; var orig = name;
var counter = 1; var counter = 1;
while (_data.ContainsKey(name)) while (_data.ContainsKey(name))
name = $"{orig} #{++counter}"; name = new StringU8($"{orig} #{++counter}");
var data = new CustomizeParameterData(); var data = new CustomizeParameterData();
CustomizeParameterFlag flags = 0; CustomizeParameterFlag flags = 0;
@ -118,14 +119,14 @@ public class PaletteImport(IDalamudPluginInterface pluginInterface, DesignManage
ParseSingle("MuscleTone", CustomizeParameterFlag.MuscleTone, ref data.MuscleTone); ParseSingle("MuscleTone", CustomizeParameterFlag.MuscleTone, ref data.MuscleTone);
while (!_data.TryAdd(name, (data, flags))) while (!_data.TryAdd(name, (data, flags)))
name = $"{orig} ({++counter})"; name = new StringU8($"{orig} ({++counter})");
continue; continue;
void Parse(string attribute, CustomizeParameterFlag flag, ref float x, ref float y, ref float z, ref float w) void Parse(string attribute, CustomizeParameterFlag flag, ref float x, ref float y, ref float z, ref float w)
{ {
var node = parameters![attribute]; var node = parameters[attribute];
if (node == null) if (node is null)
return; return;
flags |= flag; flags |= flag;
@ -145,7 +146,7 @@ public class PaletteImport(IDalamudPluginInterface pluginInterface, DesignManage
void ParseSingle(string attribute, CustomizeParameterFlag flag, ref float value) void ParseSingle(string attribute, CustomizeParameterFlag flag, ref float value)
{ {
var node = parameters![attribute]?.ToObject<float>(); var node = parameters[attribute]?.ToObject<float>();
if (!node.HasValue) if (!node.HasValue)
return; return;

View file

@ -1,4 +1,5 @@
using Glamourer.GameData; using Glamourer.GameData;
using ImSharp;
using Luna; using Luna;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
@ -73,12 +74,14 @@ public sealed class CustomizeService(
} }
/// <summary> In languages other than english the actual clan name may depend on gender. </summary> /// <summary> In languages other than english the actual clan name may depend on gender. </summary>
public string ClanName(SubRace race, Gender gender) public StringU8 ClanName(SubRace race, Gender gender)
{ {
if (gender == Gender.FemaleNpc) gender = gender switch
gender = Gender.Female; {
if (gender == Gender.MaleNpc) Gender.FemaleNpc => Gender.Female,
gender = Gender.Male; Gender.MaleNpc => Gender.Male,
_ => gender,
};
return Manager.GetSet(race, gender).Name; return Manager.GetSet(race, gender).Name;
} }

2
Luna

@ -1 +1 @@
Subproject commit 4f9ff17be4a1235a3bf66d9a80932e59e97d7e99 Subproject commit 4660929a2d463820b5ac1685a57687df677adafc

@ -1 +1 @@
Subproject commit d13caf68759521f2b89e74eae79698a5fa346ad5 Subproject commit b5213e2dee715bb6c53dc15dee61392b183a34a1