Current state.

This commit is contained in:
Ottermandias 2026-02-06 23:45:06 +01:00
parent 0d4f7777f0
commit 06e1fb2a3b
33 changed files with 934 additions and 928 deletions

View file

@ -2,23 +2,27 @@
using Glamourer.Gui;
using Glamourer.Services;
using Luna;
using Luna.Generators;
using Newtonsoft.Json;
using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
namespace Glamourer;
public class EphemeralConfig : ISavable
public partial class EphemeralConfig : ISavable
{
public int Version { get; set; } = Configuration.Constants.CurrentVersion;
public bool IncognitoMode { get; set; } = false;
public bool UnlockDetailMode { get; set; } = true;
public bool ShowDesignQuickBar { get; set; } = false;
public bool LockDesignQuickBar { get; set; } = false;
public bool LockMainWindow { get; set; } = false;
public MainTabType SelectedMainTab { get; set; } = MainTabType.Settings;
public Guid SelectedDesign { get; set; } = Guid.Empty;
public Guid SelectedQuickDesign { get; set; } = Guid.Empty;
public int LastSeenVersion { get; set; } = GlamourerChangelog.LastChangelogVersion;
public int Version { get; set; } = Configuration.Constants.CurrentVersion;
[ConfigProperty]
private bool _incognitoMode;
public bool UnlockDetailMode { get; set; } = true;
public bool ShowDesignQuickBar { get; set; } = false;
public bool LockDesignQuickBar { get; set; } = false;
public bool LockMainWindow { get; set; } = false;
public MainTabType SelectedMainTab { get; set; } = MainTabType.Settings;
public Guid SelectedDesign { get; set; } = Guid.Empty;
public Guid SelectedQuickDesign { get; set; } = Guid.Empty;
public int LastSeenVersion { get; set; } = GlamourerChangelog.LastChangelogVersion;
public float CurrentDesignSelectorWidth { get; set; } = 200f;
public float DesignSelectorMinimumScale { get; set; } = 0.1f;
@ -70,9 +74,9 @@ public class EphemeralConfig : ISavable
public void Save(StreamWriter writer)
{
using var jWriter = new JsonTextWriter(writer);
using var jWriter = new JsonTextWriter(writer);
jWriter.Formatting = Formatting.Indented;
var serializer = new JsonSerializer { Formatting = Formatting.Indented };
var serializer = new JsonSerializer { Formatting = Formatting.Indented };
serializer.Serialize(jWriter, this);
}
}

View file

@ -46,7 +46,7 @@ public partial class CustomizationDrawer
}
if (hasIcon)
Im.Tooltip.ImageOnHover(wrap!.Id, wrap.Size);
Im.Tooltip.ImageOnHover(wrap!.Id, wrap!.Size);
Im.Line.Same();
using (Im.Group())
@ -221,7 +221,7 @@ public partial class CustomizationDrawer
}
if (hasIcon)
Im.Tooltip.ImageOnHover(wrap!.Id, wrap.Size);
Im.Tooltip.ImageOnHover(wrap!.Id, wrap!.Size);
if (idx % 4 is not 3)
Im.Line.Same();
}

View file

@ -92,9 +92,6 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
DesignChanged.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignCombo);
}
public bool Incognito
=> Config.IncognitoMode;
void IDisposable.Dispose()
{
DesignChanged.Unsubscribe(OnDesignChanged);
@ -168,7 +165,7 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
}
protected override string ToString(Tuple<IDesignStandIn, string> obj)
=> obj.Item1.ResolveName(Incognito);
=> obj.Item1.ResolveName(Config.IncognitoMode);
protected override float GetFilterWidth()
=> InnerWidth - 2 * Im.Style.FramePadding.X;
@ -294,7 +291,7 @@ public abstract class DesignCombo : DesignComboBase
=> CurrentSelection?.Item1;
public void Draw(float width)
=> Draw(Design, Design?.ResolveName(Incognito) ?? string.Empty, width);
=> Draw(Design, Design?.ResolveName(Config.IncognitoMode) ?? string.Empty, width);
}
public sealed class QuickDesignCombo : DesignCombo
@ -392,11 +389,11 @@ public sealed class RandomDesignCombo(
public bool Draw(RandomPredicate.Exact exact, float width)
{
var design = GetDesign(exact);
return Draw(design, design?.ResolveName(Incognito) ?? $"Not Found [{exact.Value.Text}]", width);
return Draw(design, design?.ResolveName(Config.IncognitoMode) ?? $"Not Found [{exact.Value.Text}]", width);
}
public bool Draw(IDesignStandIn? design, float width)
=> Draw(design, design?.ResolveName(Incognito) ?? string.Empty, width);
=> Draw(design, design?.ResolveName(Config.IncognitoMode) ?? string.Empty, width);
}
public sealed class SpecialDesignCombo(
@ -419,7 +416,7 @@ public sealed class SpecialDesignCombo(
{
public void Draw(AutoDesignSet set, AutoDesign? design, int autoDesignIndex)
{
if (!Draw(design?.Design, design?.Design.ResolveName(Incognito), Im.ContentRegion.Available.X))
if (!Draw(design?.Design, design?.Design.ResolveName(Config.IncognitoMode), Im.ContentRegion.Available.X))
return;
if (autoDesignIndex >= 0)

View file

@ -0,0 +1,119 @@
using Glamourer.Services;
using Glamourer.Unlocks;
using ImSharp;
using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Equipment;
public abstract class BaseItemCombo(FavoriteManager favorites, ItemManager items) : FilterComboBase<BaseItemCombo.CacheItem>(new ItemFilter())
{
public abstract StringU8 Label { get; }
protected readonly FavoriteManager Favorites = favorites;
protected readonly ItemManager Items = items;
protected EquipItem CurrentItem;
protected PrimaryId CustomSetId;
protected SecondaryId CustomWeaponId;
protected Variant CustomVariant;
public bool Draw(in EquipItem item, out EquipItem newItem, float width)
{
using var id = Im.Id.Push(Label);
CurrentItem = item;
CustomVariant = 0;
if (Draw(StringU8.Empty, item.Name, StringU8.Empty, width, out var cache))
{
newItem = cache.Item;
return true;
}
if (CustomVariant.Id is not 0 && Identify(out newItem))
return true;
newItem = item;
return false;
}
public readonly struct CacheItem(EquipItem item) : IDisposable
{
public readonly EquipItem Item = item;
public readonly StringPair Name = new(item.Name);
public readonly SizedStringPair Model = new($"({item.PrimaryId.Id}-{item.Variant.Id})");
public void Dispose()
=> Model.Dispose();
}
protected sealed class ItemFilter : PartwiseFilterBase<CacheItem>
{
public override bool WouldBeVisible(in CacheItem item, int globalIndex)
=> base.WouldBeVisible(in item, globalIndex) || WouldBeVisible(item.Model.Utf16);
protected override string ToFilterString(in CacheItem item, int globalIndex)
=> item.Name.Utf16;
}
protected override FilterComboBaseCache<CacheItem> CreateCache()
=> new Cache(this);
protected sealed class Cache(FilterComboBase<CacheItem> parent) : FilterComboBaseCache<CacheItem>(parent)
{
private static EquipItem _longestItem;
protected override void ComputeWidth()
{
if (!_longestItem.Valid)
{
var data = ((BaseItemCombo)Parent).Items.ItemData;
_longestItem = data.AllItems(true).Concat(data.AllItems(false))
.MaxBy(i => Im.Font.CalculateSize($"{i.Item2.Name} ({i.Item2.ModelString})").X).Item2;
}
ComboWidth = Im.Font.CalculateSize($"{_longestItem.Name} ({_longestItem.Name})").X
+ Im.Style.FrameHeight
+ Im.Style.ItemSpacing.X * 3;
}
}
protected override float ItemHeight
=> Im.Style.FrameHeightWithSpacing;
protected override bool DrawItem(in CacheItem item, int globalIndex, bool selected)
{
UiHelpers.DrawFavoriteStar(Favorites, item.Item);
Im.Line.Same();
var ret = Im.Selectable(item.Name.Utf8, selected);
Im.Line.Same();
using var color = ImGuiColor.Text.Push(Rgba32.Gray);
ImEx.TextRightAligned(item.Model);
return ret;
}
protected override void EnterPressed()
{
if (!Im.Io.KeyControl)
return;
var split = ((ItemFilter)Filter).Text.Split('-', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
switch (split.Length)
{
case 2 when ushort.TryParse(split[0], out var setId) && byte.TryParse(split[1], out var variant):
CustomSetId = setId;
CustomVariant = variant;
break;
case 3 when ushort.TryParse(split[0], out var setId)
&& ushort.TryParse(split[1], out var weaponId)
&& byte.TryParse(split[2], out var variant):
CustomSetId = setId;
CustomWeaponId = weaponId;
CustomVariant = variant;
break;
default: return;
}
}
protected abstract bool Identify(out EquipItem item);
protected override bool IsSelected(CacheItem item, int globalIndex)
=> item.Item.Id == CurrentItem.Id;
}

View file

@ -1,66 +1,32 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Plugin.Services;
using Dalamud.Plugin.Services;
using Glamourer.Services;
using Glamourer.Unlocks;
using ImSharp;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Extensions;
using OtterGui.Log;
using OtterGui.Widgets;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Addon = Lumina.Excel.Sheets.Addon;
using MouseWheelType = OtterGui.Widgets.MouseWheelType;
namespace Glamourer.Gui.Equipment;
public sealed class BonusItemCombo2(IDataManager gameData, ItemManager items, FavoriteManager favorites, BonusItemFlag slot)
: ImSharp.FilterComboBase<BonusItemCombo2.CacheItem>(new ItemFilter())
public sealed class BonusItemCombo(FavoriteManager favorites, ItemManager items, IDataManager gameData, BonusItemFlag slot)
: BaseItemCombo(favorites, items)
{
public readonly StringU8 Label = GetLabel(gameData, slot);
public readonly BonusItemFlag Slot = slot;
public override StringU8 Label { get; } = GetLabel(gameData, slot);
public readonly BonusItemFlag Slot = slot;
public readonly struct CacheItem(EquipItem item) : IDisposable
protected override bool Identify(out EquipItem item)
{
public readonly EquipItem Item = item;
public readonly StringPair Name = new(item.Name);
public readonly SizedString Model = new($"({item.PrimaryId.Id}-{item.Variant.Id})");
public void Dispose()
=> Model.Dispose();
}
private sealed class ItemFilter : PartwiseFilterBase<CacheItem>
{
protected override string ToFilterString(in CacheItem item, int globalIndex)
=> item.Name.Utf16;
item = Items.Identify(Slot, CustomSetId, CustomVariant);
return true;
}
protected override IEnumerable<CacheItem> GetItems()
{
var nothing = EquipItem.BonusItemNothing(Slot);
return items.ItemData.ByType[Slot.ToEquipType()].OrderByDescending(favorites.Contains).ThenBy(i => i.Id.Id).Prepend(nothing)
return Items.ItemData.ByType[Slot.ToEquipType()].OrderByDescending(Favorites.Contains).ThenBy(i => i.Id.Id).Prepend(nothing)
.Select(i => new CacheItem(i));
}
protected override float ItemHeight
=> Im.Style.TextHeightWithSpacing;
protected override bool DrawItem(in CacheItem item, int globalIndex, bool selected)
{
UiHelpers.DrawFavoriteStar(favorites, item.Item);
Im.Line.Same();
var ret = Im.Selectable(item.Name.Utf8, selected);
Im.Line.Same();
using var color = ImGuiColor.Text.Push(Rgba32.Gray);
ImEx.TextRightAligned(item.Model);
return ret;
}
protected override bool IsSelected(CacheItem item, int globalIndex)
=> throw new NotImplementedException();
private static StringU8 GetLabel(IDataManager gameData, BonusItemFlag slot)
{
var sheet = gameData.GetExcelSheet<Addon>()!;
@ -74,109 +40,3 @@ public sealed class BonusItemCombo2(IDataManager gameData, ItemManager items, Fa
};
}
}
public sealed class BonusItemCombo : FilterComboCache<EquipItem>
{
private readonly FavoriteManager _favorites;
public readonly string Label;
private CustomItemId _currentItem;
private float _innerWidth;
public PrimaryId CustomSetId { get; private set; }
public Variant CustomVariant { get; private set; }
public BonusItemCombo(IDataManager gameData, ItemManager items, BonusItemFlag slot, Logger log, FavoriteManager favorites)
: base(() => GetItems(favorites, items, slot), MouseWheelType.Control, log)
{
_favorites = favorites;
Label = GetLabel(gameData, slot);
_currentItem = 0;
SearchByParts = true;
}
protected override void DrawList(float width, float itemHeight)
{
base.DrawList(width, itemHeight);
if (NewSelection != null && Items.Count > NewSelection.Value)
CurrentSelection = Items[NewSelection.Value];
}
protected override int UpdateCurrentSelected(int currentSelected)
{
if (CurrentSelection.Id == _currentItem)
return currentSelected;
CurrentSelectionIdx = Items.IndexOf(i => i.Id == _currentItem);
CurrentSelection = CurrentSelectionIdx >= 0 ? Items[CurrentSelectionIdx] : default;
return base.UpdateCurrentSelected(CurrentSelectionIdx);
}
public bool Draw(string previewName, BonusItemId previewIdx, float width, float innerWidth)
{
_innerWidth = innerWidth;
_currentItem = previewIdx;
CustomVariant = 0;
return Draw($"##{Label}", previewName, string.Empty, width, Im.Style.TextHeightWithSpacing);
}
protected override float GetFilterWidth()
=> _innerWidth - 2 * Im.Style.FramePadding.X;
protected override bool DrawSelectable(int globalIdx, bool selected)
{
var obj = Items[globalIdx];
var name = ToString(obj);
if (UiHelpers.DrawFavoriteStar(_favorites, obj) && CurrentSelectionIdx == globalIdx)
{
CurrentSelectionIdx = -1;
_currentItem = obj.Id;
CurrentSelection = default;
}
Im.Line.Same();
var ret = ImGui.Selectable(name, selected);
Im.Line.Same();
using var color = ImGuiColor.Text.Push(0xFF808080);
ImGuiUtil.RightAlign($"({obj.PrimaryId.Id}-{obj.Variant.Id})");
return ret;
}
protected override bool IsVisible(int globalIndex, LowerString filter)
=> base.IsVisible(globalIndex, filter) || filter.IsContained(Items[globalIndex].PrimaryId.Id.ToString());
protected override string ToString(EquipItem obj)
=> obj.Name;
private static string GetLabel(IDataManager gameData, BonusItemFlag slot)
{
var sheet = gameData.GetExcelSheet<Addon>()!;
return slot switch
{
BonusItemFlag.Glasses => sheet.TryGetRow(16050, out var text) ? text.Text.ToString() : "Facewear",
BonusItemFlag.UnkSlot => sheet.TryGetRow(16051, out var text) ? text.Text.ToString() : "Facewear",
_ => string.Empty,
};
}
private static List<EquipItem> GetItems(FavoriteManager favorites, ItemManager items, BonusItemFlag slot)
{
var nothing = EquipItem.BonusItemNothing(slot);
return items.ItemData.ByType[slot.ToEquipType()].OrderByDescending(favorites.Contains).ThenBy(i => i.Id.Id).Prepend(nothing).ToList();
}
protected override void OnClosePopup()
{
// If holding control while the popup closes, try to parse the input as a full pair of set id and variant, and set a custom item for that.
if (!Im.Io.KeyControl)
return;
var split = Filter.Text.Split('-', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (split.Length != 2 || !ushort.TryParse(split[0], out var setId) || !byte.TryParse(split[1], out var variant))
return;
CustomSetId = setId;
CustomVariant = variant;
}
}

View file

@ -5,7 +5,6 @@ using Glamourer.Services;
using Glamourer.Unlocks;
using ImSharp;
using Luna;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -19,7 +18,7 @@ public class EquipmentDrawer
private readonly ItemManager _items;
private readonly GlamourerColorCombo _stainCombo;
private readonly DictStain _stainData;
private readonly ItemCombo[] _itemCombo;
private readonly EquipCombo[] _equipCombo;
private readonly BonusItemCombo[] _bonusItemCombo;
private readonly Dictionary<FullEquipType, WeaponCombo> _weaponCombo;
private readonly TextureService _textures;
@ -28,9 +27,6 @@ public class EquipmentDrawer
private readonly AdvancedDyePopup _advancedDyes;
private readonly ItemCopyService _itemCopy;
private float _requiredComboWidthUnscaled;
private float _requiredComboWidth;
private Stain? _draggedStain;
private EquipItemSlotCache _draggedItem;
private EquipSlot _dragTarget;
@ -46,18 +42,16 @@ public class EquipmentDrawer
_itemCopy = itemCopy;
_stainData = items.Stains;
_stainCombo = new GlamourerColorCombo(_stainData, favorites);
_itemCombo = EquipSlotExtensions.EqdpSlots.Select(e => new ItemCombo(gameData, items, e, Glamourer.Log, favorites)).ToArray();
_bonusItemCombo = BonusExtensions.AllFlags.Select(f => new BonusItemCombo(gameData, items, f, Glamourer.Log, favorites)).ToArray();
_equipCombo = EquipSlotExtensions.EqdpSlots.Select(e => new EquipCombo(favorites, items, gameData, e)).ToArray();
_bonusItemCombo = BonusExtensions.AllFlags.Select(f => new BonusItemCombo(favorites, items, gameData, f)).ToArray();
_weaponCombo = new Dictionary<FullEquipType, WeaponCombo>(FullEquipTypeExtensions.WeaponTypes.Count * 2);
foreach (var type in FullEquipType.Values)
{
if (type.ToSlot() is EquipSlot.MainHand)
_weaponCombo.TryAdd(type, new WeaponCombo(items, type, Glamourer.Log, favorites));
else if (type.ToSlot() is EquipSlot.OffHand)
_weaponCombo.TryAdd(type, new WeaponCombo(items, type, Glamourer.Log, favorites));
if (type.ToSlot() is EquipSlot.MainHand or EquipSlot.OffHand)
_weaponCombo.TryAdd(type, new WeaponCombo(favorites, items, type));
}
_weaponCombo.Add(FullEquipType.Unknown, new WeaponCombo(items, FullEquipType.Unknown, Glamourer.Log, favorites));
_weaponCombo.Add(FullEquipType.Unknown, new WeaponCombo(favorites, items, FullEquipType.Unknown));
}
private Vector2 _iconSize;
@ -66,15 +60,8 @@ public class EquipmentDrawer
public void Prepare()
{
_iconSize = new Vector2(2 * Im.Style.FrameHeight + Im.Style.ItemSpacing.Y);
_comboLength = DefaultWidth * Im.Style.GlobalScale;
if (_requiredComboWidthUnscaled is 0)
_requiredComboWidthUnscaled = _items.ItemData.AllItems(true)
.Concat(_items.ItemData.AllItems(false))
.Max(i => Im.Font.CalculateSize($"{i.Item2.Name} ({i.Item2.ModelString})").X)
/ Im.Style.GlobalScale;
_requiredComboWidth = _requiredComboWidthUnscaled * Im.Style.GlobalScale;
_iconSize = new Vector2(2 * Im.Style.FrameHeight + Im.Style.ItemSpacing.Y);
_comboLength = DefaultWidth * Im.Style.GlobalScale;
_advancedMaterialColor = ColorId.AdvancedDyeActive.Value();
_dragTarget = EquipSlot.Unknown;
}
@ -194,11 +181,11 @@ public class EquipmentDrawer
}
else if (equipDrawData.IsState)
{
_advancedDyes.DrawButton(equipDrawData.Slot, equipDrawData.HasAdvancedDyes ? _advancedMaterialColor : 0u);
_advancedDyes.DrawButton(equipDrawData.Slot, equipDrawData.HasAdvancedDyes ? _advancedMaterialColor : ColorParameter.Default);
}
if (VerifyRestrictedGear(equipDrawData))
label += " (Restricted)";
label += " (Restricted)"u8;
DrawEquipLabel(equipDrawData is { IsDesign: true, HasAdvancedDyes: true }, label);
}
@ -216,7 +203,7 @@ public class EquipmentDrawer
}
else if (bonusDrawData.IsState)
{
_advancedDyes.DrawButton(bonusDrawData.Slot, bonusDrawData.HasAdvancedDyes ? _advancedMaterialColor : 0u);
_advancedDyes.DrawButton(bonusDrawData.Slot, bonusDrawData.HasAdvancedDyes ? _advancedMaterialColor : ColorParameter.Default);
}
DrawEquipLabel(bonusDrawData is { IsDesign: true, HasAdvancedDyes: true }, label);
@ -236,11 +223,11 @@ public class EquipmentDrawer
}
else if (mainhand.IsState)
{
_advancedDyes.DrawButton(EquipSlot.MainHand, mainhand.HasAdvancedDyes ? _advancedMaterialColor : 0u);
_advancedDyes.DrawButton(EquipSlot.MainHand, mainhand.HasAdvancedDyes ? _advancedMaterialColor : ColorParameter.Default);
}
if (allWeapons)
mainhandLabel += $" ({mainhand.CurrentItem.Type.ToName()})";
mainhandLabel = new StringU8($"{mainhandLabel} ({mainhand.CurrentItem.Type.ToName()})");
WeaponHelpMarker(mainhand is { IsDesign: true, HasAdvancedDyes: true }, mainhandLabel);
if (offhand.CurrentItem.Type is FullEquipType.Unknown)
@ -258,7 +245,7 @@ public class EquipmentDrawer
}
else if (offhand.IsState)
{
_advancedDyes.DrawButton(EquipSlot.OffHand, offhand.HasAdvancedDyes ? _advancedMaterialColor : 0u);
_advancedDyes.DrawButton(EquipSlot.OffHand, offhand.HasAdvancedDyes ? _advancedMaterialColor : ColorParameter.Default);
}
WeaponHelpMarker(offhand is { IsDesign: true, HasAdvancedDyes: true }, offhandLabel);
@ -292,7 +279,7 @@ public class EquipmentDrawer
}
else if (equipDrawData.IsState)
{
_advancedDyes.DrawButton(equipDrawData.Slot, equipDrawData.HasAdvancedDyes ? _advancedMaterialColor : 0u);
_advancedDyes.DrawButton(equipDrawData.Slot, equipDrawData.HasAdvancedDyes ? _advancedMaterialColor : ColorParameter.Default);
}
if (VerifyRestrictedGear(equipDrawData))
@ -316,7 +303,7 @@ public class EquipmentDrawer
}
else if (bonusDrawData.IsState)
{
_advancedDyes.DrawButton(bonusDrawData.Slot, bonusDrawData.HasAdvancedDyes ? _advancedMaterialColor : 0u);
_advancedDyes.DrawButton(bonusDrawData.Slot, bonusDrawData.HasAdvancedDyes ? _advancedMaterialColor : ColorParameter.Default);
}
DrawEquipLabel(bonusDrawData is { IsDesign: true, HasAdvancedDyes: true }, label);
@ -339,7 +326,7 @@ public class EquipmentDrawer
}
WeaponHelpMarker(mainhand is { IsDesign: true, HasAdvancedDyes: true }, mainhandLabel,
allWeapons ? mainhand.CurrentItem.Type.ToName() : null);
allWeapons ? new StringU8(mainhand.CurrentItem.Type.ToName()) : null);
DrawStain(mainhand, false);
if (mainhand.DisplayApplication)
@ -349,7 +336,7 @@ public class EquipmentDrawer
}
else if (mainhand.IsState)
{
_advancedDyes.DrawButton(EquipSlot.MainHand, mainhand.HasAdvancedDyes ? _advancedMaterialColor : 0u);
_advancedDyes.DrawButton(EquipSlot.MainHand, mainhand.HasAdvancedDyes ? _advancedMaterialColor : ColorParameter.Default);
}
}
@ -379,7 +366,7 @@ public class EquipmentDrawer
}
else if (offhand.IsState)
{
_advancedDyes.DrawButton(EquipSlot.OffHand, offhand.HasAdvancedDyes ? _advancedMaterialColor : 0u);
_advancedDyes.DrawButton(EquipSlot.OffHand, offhand.HasAdvancedDyes ? _advancedMaterialColor : ColorParameter.Default);
}
}
}
@ -434,23 +421,20 @@ public class EquipmentDrawer
}
}
private void DrawItem(in EquipDrawData data, out string label, bool small, bool clear, bool open)
private void DrawItem(in EquipDrawData data, out StringU8 label, bool small, bool clear, bool open)
{
Debug.Assert(data.Slot.IsEquipment() || data.Slot.IsAccessory(), $"Called {nameof(DrawItem)} on {data.Slot}.");
var combo = _itemCombo[data.Slot.ToIndex()];
var combo = _equipCombo[data.Slot.ToIndex()];
label = combo.Label;
if (!data.Locked && open)
UiHelpers.OpenCombo($"##{combo.Label}");
using var disabled = Im.Disabled(data.Locked);
var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.ItemId, small ? _comboLength - Im.Style.FrameHeight : _comboLength,
_requiredComboWidth);
var change = combo.Draw(data.CurrentItem, out var newItem, small ? _comboLength - Im.Style.FrameHeight : _comboLength);
DrawGearDragDrop(data);
if (change)
data.SetItem(combo.CurrentSelection);
else if (combo.CustomVariant.Id > 0)
data.SetItem(_items.Identify(data.Slot, combo.CustomSetId, combo.CustomVariant));
data.SetItem(newItem);
_itemCopy.HandleCopyPaste(data);
if (ResetOrClear(data.Locked, clear, data.AllowRevert, true, data.CurrentItem, data.GameItem, ItemManager.NothingItem(data.Slot),
@ -458,7 +442,7 @@ public class EquipmentDrawer
data.SetItem(item);
}
private void DrawBonusItem(in BonusDrawData data, out string label, bool small, bool clear, bool open)
private void DrawBonusItem(in BonusDrawData data, out StringU8 label, bool small, bool clear, bool open)
{
var combo = _bonusItemCombo[data.Slot.ToIndex()];
label = combo.Label;
@ -466,21 +450,17 @@ public class EquipmentDrawer
UiHelpers.OpenCombo($"##{combo.Label}");
using var disabled = Im.Disabled(data.Locked);
var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.Id.BonusItem,
small ? _comboLength - Im.Style.FrameHeight : _comboLength,
_requiredComboWidth);
var change = combo.Draw(data.CurrentItem, out var newItem, small ? _comboLength - Im.Style.FrameHeight : _comboLength);
if (Im.Item.Hovered() && Im.Io.KeyControl)
{
if (Im.Keyboard.IsPressed(Key.C))
_itemCopy.Copy(combo.CurrentSelection);
_itemCopy.Copy(newItem);
else if (Im.Keyboard.IsPressed(Key.V))
_itemCopy.Paste(data.Slot.ToEquipType(), data.SetItem);
}
if (change)
data.SetItem(combo.CurrentSelection);
else if (combo.CustomVariant.Id > 0)
data.SetItem(_items.Identify(data.Slot, combo.CustomSetId, combo.CustomVariant));
data.SetItem(newItem);
if (ResetOrClear(data.Locked, clear, data.AllowRevert, true, data.CurrentItem, data.GameItem, EquipItem.BonusItemNothing(data.Slot),
out var item))
@ -563,12 +543,12 @@ public class EquipmentDrawer
return clicked && valid;
}
private void DrawMainhand(ref EquipDrawData mainhand, ref EquipDrawData offhand, out string label, bool drawAll, bool small,
private void DrawMainhand(ref EquipDrawData mainhand, ref EquipDrawData offhand, out StringU8 label, bool drawAll, bool small,
bool open)
{
if (!_weaponCombo.TryGetValue(drawAll ? FullEquipType.Unknown : mainhand.CurrentItem.Type, out var combo))
{
label = string.Empty;
label = StringU8.Empty;
return;
}
@ -580,11 +560,8 @@ public class EquipmentDrawer
{
if (!mainhand.Locked && open)
UiHelpers.OpenCombo($"##{label}");
if (combo.Draw(mainhand.CurrentItem.Name, mainhand.CurrentItem.ItemId, small ? _comboLength - Im.Style.FrameHeight : _comboLength,
_requiredComboWidth))
changedItem = combo.CurrentSelection;
else if (combo.CustomVariant.Id > 0 && (drawAll || ItemData.ConvertWeaponId(combo.CustomSetId) == mainhand.CurrentItem.Type))
changedItem = _items.Identify(mainhand.Slot, combo.CustomSetId, combo.CustomWeaponId, combo.CustomVariant);
if (combo.Draw(mainhand.CurrentItem, out var newItem, small ? _comboLength - Im.Style.FrameHeight : _comboLength))
changedItem = newItem;
_itemCopy.HandleCopyPaste(mainhand);
DrawGearDragDrop(mainhand);
@ -610,11 +587,11 @@ public class EquipmentDrawer
"The weapon type could not be identified, thus changing it to other weapons of that type is not possible."u8);
}
private void DrawOffhand(in EquipDrawData mainhand, in EquipDrawData offhand, out string label, bool small, bool clear, bool open)
private void DrawOffhand(in EquipDrawData mainhand, in EquipDrawData offhand, out StringU8 label, bool small, bool clear, bool open)
{
if (!_weaponCombo.TryGetValue(offhand.CurrentItem.Type, out var combo))
{
label = string.Empty;
label = StringU8.Empty;
return;
}
@ -624,11 +601,8 @@ public class EquipmentDrawer
using var disabled = Im.Disabled(locked);
if (!locked && open)
UiHelpers.OpenCombo($"##{combo.Label}");
if (combo.Draw(offhand.CurrentItem.Name, offhand.CurrentItem.ItemId, small ? _comboLength - Im.Style.FrameHeight : _comboLength,
_requiredComboWidth))
offhand.SetItem(combo.CurrentSelection);
else if (combo.CustomVariant.Id > 0 && ItemData.ConvertWeaponId(combo.CustomSetId) == offhand.CurrentItem.Type)
offhand.SetItem(_items.Identify(mainhand.Slot, combo.CustomSetId, combo.CustomWeaponId, combo.CustomVariant));
if (combo.Draw(offhand.CurrentItem, out var newItem, small ? _comboLength - Im.Style.FrameHeight : _comboLength))
offhand.SetItem(newItem);
_itemCopy.HandleCopyPaste(offhand);
DrawGearDragDrop(offhand);
@ -664,8 +638,9 @@ public class EquipmentDrawer
#endregion
private void WeaponHelpMarker(bool hasAdvancedDyes, string label, string? type = null)
private void WeaponHelpMarker(bool hasAdvancedDyes, StringU8 label, StringU8? type = null)
{
Im.Line.SameInner();
LunaStyle.DrawAlignedHelpMarker(
"Changing weapons to weapons of different types can cause crashes, freezes, soft- and hard locks and cheating, "u8
+ "thus it is only allowed to change weapons to other weapons of the same type."u8);
@ -680,7 +655,7 @@ public class EquipmentDrawer
}
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
private void DrawEquipLabel(bool hasAdvancedDyes, string label)
private void DrawEquipLabel(bool hasAdvancedDyes, StringU8 label)
{
Im.Line.Same();
using (ImGuiColor.Text.Push(_advancedMaterialColor, hasAdvancedDyes))

View file

@ -9,7 +9,7 @@ namespace Glamourer.Gui.Equipment;
public sealed class GlamourerColorCombo(DictStain stains, FavoriteManager favorites) : FilterComboColors
{
protected override float AdditionalSpace
=> AwesomeIcon.Font.CalculateTextSize(LunaStyle.FavoriteIcon.Span).X + 4 * Im.Style.GlobalScale;
=> AwesomeIcon.Font.CalculateTextSize(LunaStyle.FavoriteIcon.Span).X + 8 * Im.Style.GlobalScale;
protected override bool DrawItem(in Item item, int globalIndex, bool selected)
{
@ -17,7 +17,7 @@ public sealed class GlamourerColorCombo(DictStain stains, FavoriteManager favori
Im.Dummy(AwesomeIcon.Font.CalculateTextSize(LunaStyle.FavoriteIcon.Span));
else
UiHelpers.DrawFavoriteStar(favorites, item.Id);
Im.Line.Same(0, 4 * Im.Style.GlobalScale);
Im.Line.Same(0, 8 * Im.Style.GlobalScale);
var buttonWidth = Im.ContentRegion.Available.X;
var totalWidth = Im.ContentRegion.Maximum.X;

View file

@ -1,135 +1,54 @@
using Dalamud.Plugin.Services;
using Glamourer.Services;
using Glamourer.Unlocks;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Lumina.Excel.Sheets;
using OtterGui.Classes;
using OtterGui.Extensions;
using OtterGui.Log;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using MouseWheelType = OtterGui.Widgets.MouseWheelType;
namespace Glamourer.Gui.Equipment;
public sealed class ItemCombo : FilterComboCache<EquipItem>
public sealed class EquipCombo(FavoriteManager favorites, ItemManager items, IDataManager gameData, EquipSlot slot)
: BaseItemCombo(favorites, items)
{
private readonly FavoriteManager _favorites;
public readonly string Label;
private ItemId _currentItem;
private float _innerWidth;
public override StringU8 Label { get; } = GetLabel(gameData, slot);
public readonly EquipSlot Slot = slot;
public PrimaryId CustomSetId { get; private set; }
public Variant CustomVariant { get; private set; }
public ItemCombo(IDataManager gameData, ItemManager items, EquipSlot slot, Logger log, FavoriteManager favorites)
: base(() => GetItems(favorites, items, slot), MouseWheelType.Control, log)
protected override bool Identify(out EquipItem item)
{
_favorites = favorites;
Label = GetLabel(gameData, slot);
_currentItem = ItemManager.NothingId(slot);
SearchByParts = true;
item = Items.Identify(Slot, CustomSetId, CustomVariant);
return true;
}
protected override void DrawList(float width, float itemHeight)
protected override IEnumerable<CacheItem> GetItems()
{
base.DrawList(width, itemHeight);
if (NewSelection != null && Items.Count > NewSelection.Value)
CurrentSelection = Items[NewSelection.Value];
var nothing = ItemManager.NothingItem(Slot);
if (!Items.ItemData.ByType.TryGetValue(Slot.ToEquipType(), out var list))
return [new CacheItem(nothing)];
var enumerable = list.AsEnumerable();
if (Slot.IsEquipment())
enumerable = enumerable.Append(ItemManager.SmallClothesItem(Slot));
return enumerable.OrderByDescending(Favorites.Contains).ThenBy(i => i.Name).Prepend(nothing).Select(e => new CacheItem(e));
}
protected override int UpdateCurrentSelected(int currentSelected)
{
if (CurrentSelection.ItemId == _currentItem)
return currentSelected;
CurrentSelectionIdx = Items.IndexOf(i => i.ItemId == _currentItem);
CurrentSelection = CurrentSelectionIdx >= 0 ? Items[CurrentSelectionIdx] : default;
return base.UpdateCurrentSelected(CurrentSelectionIdx);
}
public bool Draw(string previewName, ItemId previewIdx, float width, float innerWidth)
{
_innerWidth = innerWidth;
_currentItem = previewIdx;
CustomVariant = 0;
return Draw($"##{Label}", previewName, string.Empty, width, Im.Style.TextHeightWithSpacing);
}
protected override float GetFilterWidth()
=> _innerWidth - 2 * Im.Style.FramePadding.X;
protected override bool DrawSelectable(int globalIdx, bool selected)
{
var obj = Items[globalIdx];
var name = ToString(obj);
if (UiHelpers.DrawFavoriteStar(_favorites, obj) && CurrentSelectionIdx == globalIdx)
{
CurrentSelectionIdx = -1;
_currentItem = obj.ItemId;
CurrentSelection = default;
}
Im.Line.Same();
var ret = ImGui.Selectable(name, selected);
Im.Line.Same();
using var color = ImGuiColor.Text.Push(0xFF808080);
ImUtf8.TextRightAligned($"({obj.PrimaryId.Id}-{obj.Variant.Id})");
return ret;
}
protected override bool IsVisible(int globalIndex, LowerString filter)
=> base.IsVisible(globalIndex, filter) || Items[globalIndex].ModelString.StartsWith(filter.Lower);
protected override string ToString(EquipItem obj)
=> obj.Name;
private static string GetLabel(IDataManager gameData, EquipSlot slot)
private static StringU8 GetLabel(IDataManager gameData, EquipSlot slot)
{
var sheet = gameData.GetExcelSheet<Addon>();
return slot switch
{
EquipSlot.Head => sheet.TryGetRow(740, out var text) ? text.Text.ToString() : "Head",
EquipSlot.Body => sheet.TryGetRow(741, out var text) ? text.Text.ToString() : "Body",
EquipSlot.Hands => sheet.TryGetRow(742, out var text) ? text.Text.ToString() : "Hands",
EquipSlot.Legs => sheet.TryGetRow(744, out var text) ? text.Text.ToString() : "Legs",
EquipSlot.Feet => sheet.TryGetRow(745, out var text) ? text.Text.ToString() : "Feet",
EquipSlot.Ears => sheet.TryGetRow(746, out var text) ? text.Text.ToString() : "Ears",
EquipSlot.Neck => sheet.TryGetRow(747, out var text) ? text.Text.ToString() : "Neck",
EquipSlot.Wrists => sheet.TryGetRow(748, out var text) ? text.Text.ToString() : "Wrists",
EquipSlot.RFinger => sheet.TryGetRow(749, out var text) ? text.Text.ToString() : "Right Ring",
EquipSlot.LFinger => sheet.TryGetRow(750, out var text) ? text.Text.ToString() : "Left Ring",
_ => string.Empty,
EquipSlot.Head => sheet.TryGetRow(740, out var text) ? new StringU8(text.Text.Data, false) : new StringU8("Head"u8),
EquipSlot.Body => sheet.TryGetRow(741, out var text) ? new StringU8(text.Text.Data, false) : new StringU8("Body"u8),
EquipSlot.Hands => sheet.TryGetRow(742, out var text) ? new StringU8(text.Text.Data, false) : new StringU8("Hands"u8),
EquipSlot.Legs => sheet.TryGetRow(744, out var text) ? new StringU8(text.Text.Data, false) : new StringU8("Legs"u8),
EquipSlot.Feet => sheet.TryGetRow(745, out var text) ? new StringU8(text.Text.Data, false) : new StringU8("Feet"u8),
EquipSlot.Ears => sheet.TryGetRow(746, out var text) ? new StringU8(text.Text.Data, false) : new StringU8("Ears"u8),
EquipSlot.Neck => sheet.TryGetRow(747, out var text) ? new StringU8(text.Text.Data, false) : new StringU8("Neck"u8),
EquipSlot.Wrists => sheet.TryGetRow(748, out var text) ? new StringU8(text.Text.Data, false) : new StringU8("Wrists"u8),
EquipSlot.RFinger => sheet.TryGetRow(749, out var text) ? new StringU8(text.Text.Data, false) : new StringU8("Right Ring"u8),
EquipSlot.LFinger => sheet.TryGetRow(750, out var text) ? new StringU8(text.Text.Data, false) : new StringU8("Left Ring"u8),
_ => StringU8.Empty,
};
}
private static List<EquipItem> GetItems(FavoriteManager favorites, ItemManager items, EquipSlot slot)
{
var nothing = ItemManager.NothingItem(slot);
if (!items.ItemData.ByType.TryGetValue(slot.ToEquipType(), out var list))
return [nothing];
var enumerable = list.AsEnumerable();
if (slot.IsEquipment())
enumerable = enumerable.Append(ItemManager.SmallClothesItem(slot));
return enumerable.OrderByDescending(favorites.Contains).ThenBy(i => i.Name).Prepend(nothing).ToList();
}
protected override void OnClosePopup()
{
// If holding control while the popup closes, try to parse the input as a full pair of set id and variant, and set a custom item for that.
if (!Im.Io.KeyControl)
return;
var split = Filter.Text.Split('-', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (split.Length != 2 || !ushort.TryParse(split[0], out var setId) || !byte.TryParse(split[1], out var variant))
return;
CustomSetId = setId;
CustomVariant = variant;
}
}

View file

@ -1,115 +1,30 @@
using Glamourer.Services;
using Glamourer.Unlocks;
using Dalamud.Bindings.ImGui;
using ImSharp;
using OtterGui.Classes;
using OtterGui.Extensions;
using OtterGui.Log;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using MouseWheelType = OtterGui.Widgets.MouseWheelType;
using Luna;
namespace Glamourer.Gui.Equipment;
public sealed class WeaponCombo : FilterComboCache<EquipItem>
public sealed class WeaponCombo(FavoriteManager favorites, ItemManager items, FullEquipType slot)
: BaseItemCombo(favorites, items)
{
private readonly FavoriteManager _favorites;
public readonly string Label;
private ItemId _currentItem;
private float _innerWidth;
public override StringU8 Label { get; } = GetLabel(slot);
public readonly FullEquipType Slot = slot;
public PrimaryId CustomSetId { get; private set; }
public SecondaryId CustomWeaponId { get; private set; }
public Variant CustomVariant { get; private set; }
public WeaponCombo(ItemManager items, FullEquipType type, OtterGui.Log.Logger log, FavoriteManager favorites)
: base(() => GetWeapons(favorites, items, type), MouseWheelType.Control, log)
protected override bool Identify(out EquipItem item)
{
_favorites = favorites;
Label = GetLabel(type);
SearchByParts = true;
}
protected override void DrawList(float width, float itemHeight)
{
base.DrawList(width, itemHeight);
if (NewSelection != null && Items.Count > NewSelection.Value)
CurrentSelection = Items[NewSelection.Value];
}
protected override int UpdateCurrentSelected(int currentSelected)
{
if (CurrentSelection.ItemId == _currentItem)
return currentSelected;
CurrentSelectionIdx = Items.IndexOf(i => i.ItemId == _currentItem);
CurrentSelection = CurrentSelectionIdx >= 0 ? Items[CurrentSelectionIdx] : default;
return base.UpdateCurrentSelected(CurrentSelectionIdx);
}
public bool Draw(string previewName, ItemId previewIdx, float width, float innerWidth)
{
_innerWidth = innerWidth;
_currentItem = previewIdx;
CustomVariant = 0;
return Draw($"##{Label}", previewName, string.Empty, width, Im.Style.TextHeightWithSpacing);
}
protected override float GetFilterWidth()
=> _innerWidth - 2 * Im.Style.FramePadding.X;
protected override bool DrawSelectable(int globalIdx, bool selected)
{
var obj = Items[globalIdx];
var name = ToString(obj);
if (UiHelpers.DrawFavoriteStar(_favorites, obj) && CurrentSelectionIdx == globalIdx)
if (Slot is not FullEquipType.Unknown && ItemData.ConvertWeaponId(CustomSetId) != CurrentItem.Type)
{
CurrentSelectionIdx = -1;
_currentItem = obj.ItemId;
CurrentSelection = default;
item = default;
return false;
}
Im.Line.Same();
var ret = ImGui.Selectable(name, selected);
Im.Line.Same();
using var color = ImGuiColor.Text.Push(0xFF808080);
ImUtf8.TextRightAligned($"({obj.PrimaryId.Id}-{obj.SecondaryId.Id}-{obj.Variant.Id})");
return ret;
item = Items.Identify(Slot.ToSlot(), CustomSetId, CustomWeaponId, CustomVariant);
return true;
}
protected override void OnClosePopup()
{
// If holding control while the popup closes, try to parse the input as a full tuple of set id, weapon id and variant, and set a custom item for that.
if (!Im.Io.KeyControl)
return;
var split = Filter.Text.Split('-', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (split.Length != 3
|| !ushort.TryParse(split[0], out var setId)
|| !ushort.TryParse(split[1], out var weaponId)
|| !byte.TryParse(split[2], out var variant))
return;
CustomSetId = setId;
CustomWeaponId = weaponId;
CustomVariant = variant;
}
protected override bool IsVisible(int globalIndex, LowerString filter)
=> base.IsVisible(globalIndex, filter) || Items[globalIndex].ModelString.StartsWith(filter.Lower);
protected override string ToString(EquipItem obj)
=> obj.Name;
private static string GetLabel(FullEquipType type)
=> type.IsUnknown() ? "Mainhand" : type.ToName();
private static IReadOnlyList<EquipItem> GetWeapons(FavoriteManager favorites, ItemManager items, FullEquipType type)
private static IReadOnlyList<EquipItem> GetItems(FavoriteManager favorites, ItemManager items, FullEquipType type)
{
if (type is FullEquipType.Unknown)
{
@ -131,4 +46,30 @@ public sealed class WeaponCombo : FilterComboCache<EquipItem>
return [.. list.OrderByDescending(favorites.Contains).ThenBy(e => e.Name)];
}
}
protected override IEnumerable<CacheItem> GetItems()
{
if (Slot is FullEquipType.Unknown)
{
var enumerable = Array.Empty<EquipItem>().AsEnumerable();
foreach (var t in FullEquipType.Values.Where(e => e.ToSlot() is EquipSlot.MainHand))
{
if (Items.ItemData.ByType.TryGetValue(t, out var l))
enumerable = enumerable.Concat(l);
}
return enumerable.OrderByDescending(Favorites.Contains).ThenBy(e => e.Name).Select(e => new CacheItem(e));
}
if (!Items.ItemData.ByType.TryGetValue(Slot, out var list))
return [];
IEnumerable<EquipItem> ret = list.OrderByDescending(Favorites.Contains).ThenBy(e => e.Name);
if (Slot.AllowsNothing())
ret = ret.Prepend(ItemManager.NothingItem(Slot));
return ret.Select(e => new CacheItem(e));
}
private static StringU8 GetLabel(FullEquipType type)
=> type.IsUnknown() ? new StringU8("Mainhand"u8) : new StringU8(type.ToName());
}

View file

@ -45,13 +45,13 @@ public sealed unsafe class AdvancedDyePopup(
return true;
}
public void DrawButton(EquipSlot slot, Rgba32 color)
public void DrawButton(EquipSlot slot, ColorParameter color)
=> DrawButton(MaterialValueIndex.FromSlot(slot), color);
public void DrawButton(BonusItemFlag slot, Rgba32 color)
public void DrawButton(BonusItemFlag slot, ColorParameter color)
=> DrawButton(MaterialValueIndex.FromSlot(slot), color);
private void DrawButton(MaterialValueIndex index, Rgba32 color)
private void DrawButton(MaterialValueIndex index, ColorParameter color)
{
if (config.HideDesignPanel.HasFlag(DesignPanelFlag.AdvancedDyes))
return;
@ -62,7 +62,7 @@ public sealed unsafe class AdvancedDyePopup(
var (textColor, buttonColor) = isOpen
? (ColorId.HeaderButtons.Value(), ImGuiColor.ButtonActive.Get())
: (color, 0u);
: (color, ColorParameter.Default);
using (ImStyleBorder.Frame.Push(textColor, 2 * Im.Style.GlobalScale, isOpen))
{

View file

@ -0,0 +1,100 @@
using Dalamud.Plugin.Services;
using ImSharp;
using Luna;
using Penumbra.GameData.Enums;
namespace Glamourer.Gui.Tabs.ActorTab;
public sealed class ActorFilter : TextFilterBase<ActorCacheItem>, IUiService
{
private readonly IPlayerState _playerState;
private enum FilterMethod
{
Player,
Owned,
Npc,
Retainer,
Special,
Homeworld,
Text,
Empty,
};
private FilterMethod _method = FilterMethod.Empty;
public ActorFilter(IPlayerState playerState)
{
_playerState = playerState;
FilterChanged += () =>
{
_method = Text switch
{
"" => FilterMethod.Empty,
"<p>" or "<P>" => FilterMethod.Player,
"<o>" or "<O>" => FilterMethod.Owned,
"<n>" or "<N>" => FilterMethod.Npc,
"<r>" or "<R>" => FilterMethod.Retainer,
"<s>" or "<S>" => FilterMethod.Special,
"<w>" or "<W>" => FilterMethod.Homeworld,
_ => FilterMethod.Text,
};
};
}
public override bool DrawFilter(ReadOnlySpan<byte> label, Vector2 availableRegion)
{
var ret = base.DrawFilter(label, availableRegion);
if (!Im.Item.Hovered())
return ret;
using var tt = Im.Tooltip.Begin();
Im.Text("Filter for names containing the input."u8);
Im.Dummy(new Vector2(0, Im.Style.TextHeight / 2));
Im.Text("Special filters are:"u8);
var color = ColorId.HeaderButtons.Value();
Im.Text("<p>"u8, color);
Im.Line.NoSpacing();
Im.Text(": show only player characters."u8);
Im.Text("<o>"u8, color);
Im.Line.NoSpacing();
Im.Text(": show only owned game objects."u8);
Im.Text("<n>"u8, color);
Im.Line.NoSpacing();
Im.Text(": show only NPCs."u8);
Im.Text("<r>"u8, color);
Im.Line.NoSpacing();
Im.Text(": show only retainers."u8);
Im.Text("<s>"u8, color);
Im.Line.NoSpacing();
Im.Text(": show only special screen characters."u8);
Im.Text("<w>"u8, color);
Im.Line.NoSpacing();
Im.Text(": show only players from your world."u8);
return ret;
}
protected override string ToFilterString(in ActorCacheItem item, int globalIndex)
=> item.DisplayText.Utf16;
public override bool WouldBeVisible(in ActorCacheItem item, int globalIndex)
=> _method switch
{
FilterMethod.Player => item.Identifier.Type is IdentifierType.Player,
FilterMethod.Owned => item.Identifier.Type is IdentifierType.Owned,
FilterMethod.Npc => item.Identifier.Type is IdentifierType.Npc,
FilterMethod.Retainer => item.Identifier.Type is IdentifierType.Retainer,
FilterMethod.Special => item.Identifier.Type is IdentifierType.Special,
FilterMethod.Homeworld => item.Identifier.Type is IdentifierType.Player
&& item.Identifier.HomeWorld == _playerState.HomeWorld.RowId,
FilterMethod.Text => base.WouldBeVisible(item, globalIndex),
_ => true,
};
}

View file

@ -1,8 +1,4 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Interface;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Plugin.Services;
using Dalamud.Interface.ImGuiNotification;
using FFXIVClientStructs.FFXIV.Client.Game;
using Glamourer.Automation;
using Glamourer.Designs;
@ -14,20 +10,16 @@ using Glamourer.Interop;
using Glamourer.State;
using ImSharp;
using Luna;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.GameData.Actors;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
using TextStringHandlerBuffer = OtterGui.Text.HelperObjects.TextStringHandlerBuffer;
namespace Glamourer.Gui.Tabs.ActorTab;
public class ActorPanel
public sealed class ActorPanel : IPanel
{
private readonly ActorSelector _selector;
private readonly ActorSelection _selection;
private readonly StateManager _stateManager;
private readonly CustomizationDrawer _customizationDrawer;
private readonly EquipmentDrawer _equipmentDrawer;
@ -35,18 +27,12 @@ public class ActorPanel
private readonly Configuration _config;
private readonly DesignConverter _converter;
private readonly ActorObjectManager _objects;
private readonly DesignManager _designManager;
private readonly ImportService _importService;
private readonly ICondition _conditions;
private readonly DictModelChara _modelChara;
private readonly CustomizeParameterDrawer _parameterDrawer;
private readonly AdvancedDyePopup _advancedDyes;
private readonly EditorHistory _editorHistory;
private readonly HeaderDrawer.Button[] _leftButtons;
private readonly HeaderDrawer.Button[] _rightButtons;
public ActorPanel(ActorSelector selector,
StateManager stateManager,
public ActorPanel(StateManager stateManager,
CustomizationDrawer customizationDrawer,
EquipmentDrawer equipmentDrawer,
AutoDesignApplier autoDesignApplier,
@ -55,13 +41,11 @@ public class ActorPanel
ActorObjectManager objects,
DesignManager designManager,
ImportService importService,
ICondition conditions,
DictModelChara modelChara,
CustomizeParameterDrawer parameterDrawer,
AdvancedDyePopup advancedDyes,
EditorHistory editorHistory)
EditorHistory editorHistory, ActorSelection selection)
{
_selector = selector;
_stateManager = stateManager;
_customizationDrawer = customizationDrawer;
_equipmentDrawer = equipmentDrawer;
@ -69,61 +53,40 @@ public class ActorPanel
_config = config;
_converter = converter;
_objects = objects;
_designManager = designManager;
_importService = importService;
_conditions = conditions;
_modelChara = modelChara;
_parameterDrawer = parameterDrawer;
_advancedDyes = advancedDyes;
_editorHistory = editorHistory;
_leftButtons =
[
new SetFromClipboardButton(this),
new ExportToClipboardButton(this),
new SaveAsDesignButton(this),
new UndoButton(this),
];
_rightButtons =
[
new LockedButton(this),
new HeaderDrawer.IncognitoButton(_config),
];
_selection = selection;
}
private ActorIdentifier _identifier;
private string _actorName = string.Empty;
private Actor _actor = Actor.Null;
private ActorData _data;
private ActorState? _state;
private bool _lockedRedraw;
private CustomizeFlag CustomizeApplicationFlags
=> _lockedRedraw ? CustomizeFlagExtensions.AllRelevant & ~CustomizeFlagExtensions.RedrawRequired : CustomizeFlagExtensions.AllRelevant;
=> _selection.LockedRedraw
? CustomizeFlagExtensions.AllRelevant & ~CustomizeFlagExtensions.RedrawRequired
: CustomizeFlagExtensions.AllRelevant;
public ReadOnlySpan<byte> Id
=> "ActorPanel"u8;
public void Draw()
{
using var group = ImRaii.Group();
(_identifier, _data) = _selector.Selection;
_lockedRedraw = _identifier.Type is IdentifierType.Special || _objects.IsInLobby
|| _conditions[ConditionFlag.OccupiedInCutSceneEvent];
(_actorName, _actor) = GetHeaderName();
DrawHeader();
DrawPanel();
if (_state is not { IsLocked: false })
if (_selection.State is not { IsLocked: false })
return;
if (_importService.CreateDatTarget(out var dat))
{
_stateManager.ChangeEntireCustomize(_state!, dat.Customize, CustomizeApplicationFlags, ApplySettings.Manual);
Glamourer.Messager.NotificationMessage($"Applied games .dat file {dat.Description} customizations to {_state.Identifier}.",
_stateManager.ChangeEntireCustomize(_selection.State!, dat.Customize, CustomizeApplicationFlags, ApplySettings.Manual);
Glamourer.Messager.NotificationMessage(
$"Applied games .dat file {dat.Description} customizations to {_selection.State.Identifier}.",
NotificationType.Success, false);
}
else if (_importService.CreateCharaTarget(out var designBase, out var name))
{
_stateManager.ApplyDesign(_state!, designBase, ApplySettings.Manual);
Glamourer.Messager.NotificationMessage($"Applied Anamnesis .chara file {name} to {_state.Identifier}.", NotificationType.Success,
_stateManager.ApplyDesign(_selection.State!, designBase, ApplySettings.Manual);
Glamourer.Messager.NotificationMessage($"Applied Anamnesis .chara file {name} to {_selection.State.Identifier}.",
NotificationType.Success,
false);
}
@ -131,39 +94,19 @@ public class ActorPanel
_importService.CreateCharaSource();
}
private void DrawHeader()
{
var textColor = !_identifier.IsValid ? ImGuiColor.Text.Get() :
_data.Valid ? ColorId.ActorAvailable.Value() : ColorId.ActorUnavailable.Value();
HeaderDrawer.Draw(_actorName, textColor.Color, ImGuiColor.FrameBackground.Get().Color, _leftButtons, _rightButtons);
SaveDesignDrawPopup();
}
private (string, Actor) GetHeaderName()
{
if (!_identifier.IsValid)
return ("No Selection", Actor.Null);
if (_data.Valid)
return (_selector.IncognitoMode ? _identifier.Incognito(_data.Label) : _data.Label, _data.Objects[0]);
return (_selector.IncognitoMode ? _identifier.Incognito(null) : _identifier.ToString(), Actor.Null);
}
private unsafe void DrawPanel()
{
using var table = Im.Table.Begin("##Panel"u8, 1, TableFlags.BordersOuter | TableFlags.ScrollY, Im.ContentRegion.Available);
if (!table || !_selector.HasSelection || !_stateManager.GetOrCreate(_identifier, _actor, out _state))
using var table = Im.Table.Begin("##Panel"u8, 1, TableFlags.ScrollY, Im.ContentRegion.Available);
if (!table || _selection.State is null)
return;
table.SetupScrollFreeze(0, 1);
table.NextColumn();
Im.Dummy(Vector2.Zero);
var transformationId = _actor.IsCharacter ? _actor.AsCharacter->CharacterData.TransformationId : 0;
var transformationId = _selection.Actor.IsCharacter ? _selection.Actor.AsCharacter->CharacterData.TransformationId : 0;
if (transformationId is not 0)
ImGuiUtil.DrawTextButton($"Currently transformed to Transformation {transformationId}.",
-Vector2.UnitX, Colors.SelectedRed);
ImEx.TextFramed($"Currently transformed to Transformation {transformationId}.", Im.ContentRegion.Available with { Y = 0 },
Colors.SelectedRed);
DrawApplyToSelf();
Im.Line.Same();
@ -173,12 +116,12 @@ public class ActorPanel
table.NextColumn();
using var disabled = Im.Disabled(transformationId is not 0);
if (_state.ModelData.IsHuman)
if (_selection.State.ModelData.IsHuman)
DrawHumanPanel();
else
DrawMonsterPanel();
if (_data.Objects.Count > 0)
_advancedDyes.Draw(_data.Objects.Last(), _state);
if (_selection.Data.Objects.Count > 0)
_advancedDyes.Draw(_selection.Data.Objects.Last(), _selection.State);
}
private void DrawHumanPanel()
@ -194,18 +137,19 @@ public class ActorPanel
if (_config.HideDesignPanel.HasFlag(DesignPanelFlag.Customization))
return;
var header = _state!.ModelData.ModelId == 0
? "Customization"
: $"Customization (Model Id #{_state.ModelData.ModelId})###Customization";
var expand = _config.AutoExpandDesignPanel.HasFlag(DesignPanelFlag.Customization);
using var h = Im.Tree.HeaderId(header, expand ? TreeNodeFlags.DefaultOpen : TreeNodeFlags.None);
var expand = _config.AutoExpandDesignPanel.HasFlag(DesignPanelFlag.Customization);
using var h = Im.Tree.HeaderId(_selection.State!.ModelData.ModelId is 0
? "Customization"u8
: $"Customization (Model Id #{_selection.State.ModelData.ModelId})###Customization",
expand ? TreeNodeFlags.DefaultOpen : TreeNodeFlags.None);
if (!h)
return;
if (_customizationDrawer.Draw(_state!.ModelData.Customize, _state.IsLocked, _lockedRedraw))
_stateManager.ChangeEntireCustomize(_state, _customizationDrawer.Customize, _customizationDrawer.Changed, ApplySettings.Manual);
if (_customizationDrawer.Draw(_selection.State!.ModelData.Customize, _selection.State.IsLocked, _selection.LockedRedraw))
_stateManager.ChangeEntireCustomize(_selection.State, _customizationDrawer.Customize, _customizationDrawer.Changed,
ApplySettings.Manual);
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.Wetness, _stateManager, _state));
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.Wetness, _stateManager, _selection.State));
Im.Dummy(new Vector2(Im.Style.TextHeight / 2));
}
@ -217,22 +161,22 @@ public class ActorPanel
_equipmentDrawer.Prepare();
var usedAllStain = _equipmentDrawer.DrawAllStain(out var newAllStain, _state!.IsLocked);
var usedAllStain = _equipmentDrawer.DrawAllStain(out var newAllStain, _selection.State!.IsLocked);
foreach (var slot in EquipSlotExtensions.EqdpSlots)
{
var data = EquipDrawData.FromState(_stateManager, _state!, slot);
var data = EquipDrawData.FromState(_stateManager, _selection.State!, slot);
_equipmentDrawer.DrawEquip(data);
if (usedAllStain)
_stateManager.ChangeStains(_state, slot, newAllStain, ApplySettings.Manual);
_stateManager.ChangeStains(_selection.State, slot, newAllStain, ApplySettings.Manual);
}
var mainhand = EquipDrawData.FromState(_stateManager, _state, EquipSlot.MainHand);
var offhand = EquipDrawData.FromState(_stateManager, _state, EquipSlot.OffHand);
var mainhand = EquipDrawData.FromState(_stateManager, _selection.State, EquipSlot.MainHand);
var offhand = EquipDrawData.FromState(_stateManager, _selection.State, EquipSlot.OffHand);
_equipmentDrawer.DrawWeapons(mainhand, offhand, GameMain.IsInGPose());
foreach (var slot in BonusExtensions.AllFlags)
{
var data = BonusDrawData.FromState(_stateManager, _state!, slot);
var data = BonusDrawData.FromState(_stateManager, _selection.State!, slot);
_equipmentDrawer.DrawBonusItem(data);
}
@ -248,7 +192,7 @@ public class ActorPanel
if (!h)
return;
_parameterDrawer.Draw(_stateManager, _state!);
_parameterDrawer.Draw(_stateManager, _selection.State!);
}
private unsafe void DrawDebugData()
@ -260,63 +204,64 @@ public class ActorPanel
if (!h)
return;
using var t = Im.Table.Begin("table"u8, 2, TableFlags.SizingFixedFit);
if (!t)
using var table = Im.Table.Begin("table"u8, 2, TableFlags.SizingFixedFit);
if (!table)
return;
ImUtf8.DrawTableColumn("Object Index"u8);
DrawCopyColumn($"{string.Join(", ", _data.Objects.Select(d => d.AsObject->ObjectIndex))}");
ImUtf8.DrawTableColumn("Name ID"u8);
DrawCopyColumn($"{string.Join(", ", _data.Objects.Select(d => d.AsObject->GetNameId()))}");
ImUtf8.DrawTableColumn("Base ID"u8);
DrawCopyColumn($"{string.Join(", ", _data.Objects.Select(d => d.AsObject->BaseId))}");
ImUtf8.DrawTableColumn("Entity ID"u8);
DrawCopyColumn($"{string.Join(", ", _data.Objects.Select(d => d.AsObject->EntityId))}");
ImUtf8.DrawTableColumn("Owner ID"u8);
DrawCopyColumn($"{string.Join(", ", _data.Objects.Select(d => d.AsObject->OwnerId))}");
ImUtf8.DrawTableColumn("Game Object ID"u8);
DrawCopyColumn($"{string.Join(", ", _data.Objects.Select(d => d.AsObject->GetGameObjectId().ObjectId))}");
table.DrawColumn("Object Index"u8);
DrawCopyColumn(table, StringU8.Join(", "u8, _selection.Data.Objects.Select(d => d.AsObject->ObjectIndex)));
table.DrawColumn("Name ID"u8);
DrawCopyColumn(table, StringU8.Join(", "u8, _selection.Data.Objects.Select(d => d.AsObject->GetNameId())));
table.DrawColumn("Base ID"u8);
DrawCopyColumn(table, StringU8.Join(", "u8, _selection.Data.Objects.Select(d => d.AsObject->BaseId)));
table.DrawColumn("Entity ID"u8);
DrawCopyColumn(table, StringU8.Join(", "u8, _selection.Data.Objects.Select(d => d.AsObject->EntityId)));
table.DrawColumn("Owner ID"u8);
DrawCopyColumn(table, StringU8.Join(", "u8, _selection.Data.Objects.Select(d => d.AsObject->OwnerId)));
table.DrawColumn("Game Object ID"u8);
DrawCopyColumn(table, StringU8.Join(", "u8, _selection.Data.Objects.Select(d => d.AsObject->GetGameObjectId().ObjectId)));
return;
static void DrawCopyColumn(ref OtterGui.Text.HelperObjects.Utf8StringHandler<TextStringHandlerBuffer> text)
static void DrawCopyColumn(Im.TableDisposable table, Utf8StringHandler<TextStringHandlerBuffer> text)
{
ImUtf8.DrawTableColumn(ref text);
table.DrawColumn(ref text);
if (Im.Item.RightClicked())
ImUtf8.SetClipboardText(TextStringHandlerBuffer.Span);
Im.Clipboard.Set(ref text);
}
}
private void DrawEquipmentMetaToggles()
{
using (_ = ImRaii.Group())
using (Im.Group())
{
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.HatState, _stateManager, _state!));
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromState(CrestFlag.Head, _stateManager, _state!));
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.HatState, _stateManager, _selection.State!));
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromState(CrestFlag.Head, _stateManager, _selection.State!));
}
Im.Line.Same();
using (_ = ImRaii.Group())
using (Im.Group())
{
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.VisorState, _stateManager, _state!));
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromState(CrestFlag.Body, _stateManager, _state!));
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.VisorState, _stateManager, _selection.State!));
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromState(CrestFlag.Body, _stateManager, _selection.State!));
}
Im.Line.Same();
using (_ = ImRaii.Group())
using (Im.Group())
{
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.WeaponState, _stateManager, _state!));
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromState(CrestFlag.OffHand, _stateManager, _state!));
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.WeaponState, _stateManager, _selection.State!));
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromState(CrestFlag.OffHand, _stateManager, _selection.State!));
}
Im.Line.Same();
using (_ = ImRaii.Group())
using (Im.Group())
{
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.EarState, _stateManager, _state!));
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.EarState, _stateManager, _selection.State!));
}
}
private void DrawMonsterPanel()
{
var names = _modelChara[_state!.ModelData.ModelId];
var names = _modelChara[_selection.State!.ModelData.ModelId];
var turnHuman = Im.Button("Turn Human"u8);
Im.Separator();
using (Im.ListBox.Begin("##MonsterList"u8, Im.ContentRegion.Available with { Y = 10 * Im.Style.TextHeightWithSpacing }))
@ -324,15 +269,14 @@ public class ActorPanel
if (names.Count is 0)
Im.Text("Unknown Monster"u8);
else
ImGuiClip.ClippedDraw(names, p => Im.Text($"{p.Name} ({p.Kind.ToName()} #{p.Id})"),
Im.Style.TextHeightWithSpacing);
Im.ListClipper.Draw(names, p => Im.Text($"{p.Name} ({p.Kind.ToName()} #{p.Id})"), Im.Style.TextHeightWithSpacing);
}
Im.Separator();
Im.Text("Customization Data"u8);
using (Im.Font.PushMono())
{
foreach (var b in _state.ModelData.Customize)
foreach (var b in _selection.State.ModelData.Customize)
{
using (Im.Group())
{
@ -353,7 +297,7 @@ public class ActorPanel
Im.Text("Equipment Data"u8);
using (Im.Font.PushMono())
{
foreach (var b in _state.ModelData.GetEquipmentBytes())
foreach (var b in _selection.State.ModelData.GetEquipmentBytes())
{
using (Im.Group())
{
@ -371,65 +315,53 @@ public class ActorPanel
}
if (turnHuman)
_stateManager.TurnHuman(_state, StateSource.Manual);
_stateManager.TurnHuman(_selection.State, StateSource.Manual);
}
private string _newName = string.Empty;
private DesignBase? _newDesign;
private void SaveDesignDrawPopup()
{
if (!ImGuiUtil.OpenNameField("Save as Design", ref _newName))
return;
if (_newDesign != null && _newName.Length > 0)
_designManager.CreateClone(_newDesign, _newName, true);
_newDesign = null;
_newName = string.Empty;
}
private void RevertButtons()
{
if (ImGuiUtil.DrawDisabledButton("Revert to Game", Vector2.Zero, "Revert the character to its actual state in the game.",
_state!.IsLocked))
_stateManager.ResetState(_state!, StateSource.Manual, isFinal: true);
if (ImEx.Button("Revert to Game"u8, Vector2.Zero, "Revert the character to its actual state in the game."u8,
_selection.State!.IsLocked))
_stateManager.ResetState(_selection.State!, StateSource.Manual, isFinal: true);
Im.Line.Same();
if (ImGuiUtil.DrawDisabledButton("Reapply Automation", Vector2.Zero,
"Reapply the current automation state for the character on top of its current state..",
!_config.EnableAutoDesigns || _state!.IsLocked))
if (ImEx.Button("Reapply Automation"u8, Vector2.Zero,
"Reapply the current automation state for the character on top of its current state.."u8,
!_config.EnableAutoDesigns || _selection.State!.IsLocked))
{
_autoDesignApplier.ReapplyAutomation(_actor, _identifier, _state!, false, false, out var forcedRedraw);
_stateManager.ReapplyAutomationState(_actor, forcedRedraw, false, StateSource.Manual);
_autoDesignApplier.ReapplyAutomation(_selection.Actor, _selection.Identifier, _selection.State!, false, false,
out var forcedRedraw);
_stateManager.ReapplyAutomationState(_selection.Actor, forcedRedraw, false, StateSource.Manual);
}
Im.Line.Same();
if (ImGuiUtil.DrawDisabledButton("Revert to Automation", Vector2.Zero,
"Try to revert the character to the state it would have using automated designs.",
!_config.EnableAutoDesigns || _state!.IsLocked))
if (ImEx.Button("Revert to Automation"u8, Vector2.Zero,
"Try to revert the character to the state it would have using automated designs."u8,
!_config.EnableAutoDesigns || _selection.State!.IsLocked))
{
_autoDesignApplier.ReapplyAutomation(_actor, _identifier, _state!, true, false, out var forcedRedraw);
_stateManager.ReapplyAutomationState(_actor, forcedRedraw, true, StateSource.Manual);
_autoDesignApplier.ReapplyAutomation(_selection.Actor, _selection.Identifier, _selection.State!, true, false, out var forcedRedraw);
_stateManager.ReapplyAutomationState(_selection.Actor, forcedRedraw, true, StateSource.Manual);
}
Im.Line.Same();
if (ImGuiUtil.DrawDisabledButton("Reapply", Vector2.Zero,
"Try to reapply the configured state if something went wrong. Should generally not be necessary.",
_state!.IsLocked))
_stateManager.ReapplyState(_actor, false, StateSource.Manual, true);
if (ImEx.Button("Reapply"u8, Vector2.Zero,
"Try to reapply the configured state if something went wrong. Should generally not be necessary."u8,
_selection.State!.IsLocked))
_stateManager.ReapplyState(_selection.Actor, false, StateSource.Manual, true);
}
private void DrawApplyToSelf()
{
var (id, data) = _objects.PlayerData;
if (!ImGuiUtil.DrawDisabledButton("Apply to Yourself", Vector2.Zero,
"Apply the current state to your own character.\nHold Control to only apply gear.\nHold Shift to only apply customizations.",
!data.Valid || id == _identifier || _state!.ModelData.ModelId != 0))
if (!ImEx.Button("Apply to Yourself"u8, Vector2.Zero,
"Apply the current state to your own character.\nHold Control to only apply gear.\nHold Shift to only apply customizations."u8,
!data.Valid || id == _selection.Identifier || _selection.State!.ModelData.ModelId is not 0))
return;
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
_stateManager.ApplyDesign(state, _converter.Convert(_state!, ApplicationRules.FromModifiers(_state!)),
_stateManager.ApplyDesign(state, _converter.Convert(_selection.State!, ApplicationRules.FromModifiers(_selection.State!)),
ApplySettings.Manual with { IsFinal = true });
}
@ -438,136 +370,15 @@ public class ActorPanel
var (id, data) = _objects.TargetData;
var tt = id.IsValid
? data.Valid
? "Apply the current state to your current target.\nHold Control to only apply gear.\nHold Shift to only apply customizations."
: "The current target can not be manipulated."
: "No valid target selected.";
if (!ImGuiUtil.DrawDisabledButton("Apply to Target", Vector2.Zero, tt,
!data.Valid || id == _identifier || _state!.ModelData.ModelId != 0))
? "Apply the current state to your current target.\nHold Control to only apply gear.\nHold Shift to only apply customizations."u8
: "The current target can not be manipulated."u8
: "No valid target selected."u8;
if (!ImEx.Button("Apply to Target"u8, Vector2.Zero, tt,
!data.Valid || id == _selection.Identifier || _selection.State!.ModelData.ModelId is not 0))
return;
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
_stateManager.ApplyDesign(state, _converter.Convert(_state!, ApplicationRules.FromModifiers(_state!)),
_stateManager.ApplyDesign(state, _converter.Convert(_selection.State!, ApplicationRules.FromModifiers(_selection.State!)),
ApplySettings.Manual with { IsFinal = true });
}
private sealed class SetFromClipboardButton(ActorPanel panel)
: HeaderDrawer.Button
{
protected override string Description
=> "Try to apply a design from your clipboard.\nHold Control to only apply gear.\nHold Shift to only apply customizations.";
protected override FontAwesomeIcon Icon
=> FontAwesomeIcon.Clipboard;
public override bool Visible
=> panel._state != null;
protected override bool Disabled
=> panel._state?.IsLocked ?? true;
protected override void OnClick()
{
try
{
var (applyGear, applyCustomize) = UiHelpers.ConvertKeysToBool();
var text = ImGui.GetClipboardText();
var design = panel._converter.FromBase64(text, applyCustomize, applyGear, out _)
?? throw new Exception("The clipboard did not contain valid data.");
panel._stateManager.ApplyDesign(panel._state!, design, ApplySettings.ManualWithLinks with { IsFinal = true });
}
catch (Exception ex)
{
Glamourer.Messager.NotificationMessage(ex, $"Could not apply clipboard to {panel._identifier}.",
$"Could not apply clipboard to design {panel._identifier.Incognito(null)}", NotificationType.Error, false);
}
}
}
private sealed class ExportToClipboardButton(ActorPanel panel) : HeaderDrawer.Button
{
protected override string Description
=> "Copy the current design to your clipboard.\nHold Control to disable applying of customizations for the copied design.\nHold Shift to disable applying of gear for the copied design.";
protected override FontAwesomeIcon Icon
=> FontAwesomeIcon.Copy;
public override bool Visible
=> panel._state?.ModelData.ModelId == 0;
protected override void OnClick()
{
try
{
var text = panel._converter.ShareBase64(panel._state!, ApplicationRules.FromModifiers(panel._state!));
ImGui.SetClipboardText(text);
}
catch (Exception ex)
{
Glamourer.Messager.NotificationMessage(ex, $"Could not copy {panel._identifier} data to clipboard.",
$"Could not copy data from design {panel._identifier.Incognito(null)} to clipboard", NotificationType.Error);
}
}
}
private sealed class SaveAsDesignButton(ActorPanel panel) : HeaderDrawer.Button
{
protected override string Description
=> "Save the current state as a design.\nHold Control to disable applying of customizations for the saved design.\nHold Shift to disable applying of gear for the saved design.";
protected override FontAwesomeIcon Icon
=> FontAwesomeIcon.Save;
public override bool Visible
=> panel._state?.ModelData.ModelId == 0;
protected override void OnClick()
{
ImGui.OpenPopup("Save as Design");
panel._newName = panel._state!.Identifier.ToName();
panel._newDesign = panel._converter.Convert(panel._state, ApplicationRules.FromModifiers(panel._state));
}
}
private sealed class UndoButton(ActorPanel panel) : HeaderDrawer.Button
{
protected override string Description
=> "Undo the last change.";
protected override FontAwesomeIcon Icon
=> FontAwesomeIcon.Undo;
public override bool Visible
=> panel._state != null;
protected override bool Disabled
=> (panel._state?.IsLocked ?? true) || !panel._editorHistory.CanUndo(panel._state);
protected override void OnClick()
=> panel._editorHistory.Undo(panel._state!);
}
private sealed class LockedButton(ActorPanel panel) : HeaderDrawer.Button
{
protected override string Description
=> "The current state of this actor is locked by external tools.";
protected override FontAwesomeIcon Icon
=> FontAwesomeIcon.Lock;
public override bool Visible
=> panel._state?.IsLocked ?? false;
protected override bool Disabled
=> true;
protected override Rgba32 BorderColor
=> ColorId.ActorUnavailable.Value();
protected override Rgba32 TextColor
=> ColorId.ActorUnavailable.Value();
protected override void OnClick()
{ }
}
}

View file

@ -0,0 +1,64 @@
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Plugin.Services;
using Glamourer.State;
using ImSharp;
using Luna;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
namespace Glamourer.Gui.Tabs.ActorTab;
public sealed class ActorSelection(StateManager manager, ActorObjectManager objects, ICondition conditions) : IUiService
{
private static readonly StringU8 NoSelection = new("No Selection"u8);
public ActorIdentifier Identifier { get; private set; }
public ActorState? State { get; private set; }
public StringU8 ActorName { get; private set; } = NoSelection;
public StringU8 IncognitoName { get; private set; } = NoSelection;
public ActorData Data { get; private set; } = ActorData.Invalid;
public Actor Actor { get; private set; } = Actor.Null;
public bool LockedRedraw { get; private set; } = false;
public void Select(ActorIdentifier identifier, ActorData data)
{
Identifier = identifier.CreatePermanent();
if (Identifier.IsValid)
{
ActorName = new StringU8(data.Label);
IncognitoName = new StringU8(Identifier.Incognito(data.Label));
State = data.Valid && manager.GetOrCreate(Identifier, data.Objects[0], out var s) ? s : null;
}
else
{
ActorName = NoSelection;
IncognitoName = NoSelection;
}
}
public void Update()
{
if (Identifier.IsValid)
{
if (objects.TryGetValue(Identifier, out var data))
{
Data = data;
Actor = Data.Objects[0];
}
else
{
Data = ActorData.Invalid;
Actor = Actor.Null;
}
LockedRedraw = Identifier.Type is IdentifierType.Special || objects.IsInLobby || conditions[ConditionFlag.OccupiedInCutSceneEvent];
}
else
{
Data = ActorData.Invalid;
Actor = Actor.Null;
LockedRedraw = false;
}
}
}

View file

@ -1,135 +1,68 @@
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using Glamourer.Interop.Penumbra;
using ImSharp;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
using OtterGui.Text;
using Luna;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Tabs.ActorTab;
public class ActorSelector(ActorObjectManager objects, ActorManager actors, EphemeralConfig config)
public readonly struct ActorCacheItem(ActorIdentifier identifier, ActorData data)
{
private ActorIdentifier _identifier = ActorIdentifier.Invalid;
public readonly ActorIdentifier Identifier = identifier;
public readonly ActorData Data = data;
public readonly StringPair DisplayText = new(data.Label);
public readonly StringU8 IncognitoText = new(identifier.Incognito(data.Label));
}
public bool IncognitoMode
public sealed class ActorSelector(ActorSelection selection, ActorObjectManager objects, ActorFilter filter, PenumbraService penumbra, EphemeralConfig config) : IPanel
{
public ReadOnlySpan<byte> Id
=> "ActorSelector"u8;
public void Draw()
{
get => config.IncognitoMode;
set
Im.Cursor.Y += Im.Style.FramePadding.Y;
var cache = CacheManager.Instance.GetOrCreateCache(Im.Id.Current, () => new ActorSelectorCache(objects, filter, penumbra));
using var clip = new Im.ListClipper(cache.Count, Im.Style.TextHeightWithSpacing);
foreach (var actor in clip.Iterate(cache))
{
config.IncognitoMode = value;
config.Save();
Im.Cursor.X += Im.Style.FramePadding.X;
var selected = actor.Identifier.Equals(selection.Identifier);
if (Im.Selectable(config.IncognitoMode ? actor.IncognitoText : actor.DisplayText.Utf8, selected) && !selected)
selection.Select(actor.Identifier, actor.Data);
}
}
private LowerString _actorFilter = LowerString.Empty;
private Vector2 _defaultItemSpacing;
private WorldId _world;
private float _width;
public (ActorIdentifier Identifier, ActorData Data) Selection
=> objects.TryGetValue(_identifier, out var data) ? (_identifier, data) : (_identifier, ActorData.Invalid);
public bool HasSelection
=> _identifier.IsValid;
public void Draw(float width)
private sealed class ActorSelectorCache : BasicFilterCache<ActorCacheItem>
{
_width = width;
using var group = Im.Group();
_defaultItemSpacing = Im.Style.ItemSpacing;
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
ImGui.SetNextItemWidth(_width);
LowerString.InputWithHint("##actorFilter", "Filter...", ref _actorFilter, 64);
if (ImGui.IsItemHovered())
private readonly ActorObjectManager _objects;
private readonly PenumbraService _penumbra;
public ActorSelectorCache(ActorObjectManager objects, ActorFilter filter, PenumbraService penumbra)
: base(filter)
{
using var tt = ImUtf8.Tooltip();
ImUtf8.Text("Filter for names containing the input."u8);
Im.Dummy(new Vector2(0, Im.Style.TextHeight / 2));
ImUtf8.Text("Special filters are:"u8);
var color = ColorId.HeaderButtons.Value();
Im.Text("<p>"u8, color);
ImGui.SameLine(0, 0);
ImUtf8.Text(": show only player characters."u8);
Im.Text("<o>"u8, color);
ImGui.SameLine(0, 0);
ImUtf8.Text(": show only owned game objects."u8);
Im.Text("<n>"u8, color);
ImGui.SameLine(0, 0);
ImUtf8.Text(": show only NPCs."u8);
Im.Text("<r>"u8, color);
ImGui.SameLine(0, 0);
ImUtf8.Text(": show only retainers."u8);
Im.Text("<s>"u8, color);
ImGui.SameLine(0, 0);
ImUtf8.Text(": show only special screen characters."u8);
Im.Text("<w>"u8, color);
ImGui.SameLine(0, 0);
ImUtf8.Text(": show only players from your world."u8);
_objects = objects;
_penumbra = penumbra;
_objects.Objects.OnUpdateRequired += OnUpdateRequired;
_penumbra.CreatedCharacterBase += OnCreatedCharacterBase;
}
DrawSelector();
DrawSelectionButtons();
}
/// <summary> Update actors when models are created since visible models are required. </summary>
private void OnCreatedCharacterBase(nint _1, Guid _2, IntPtr _3)
=> Dirty |= IManagedCache.DirtyFlags.Custom;
private void DrawSelector()
{
using var child = ImUtf8.Child("##Selector"u8, new Vector2(_width, -Im.Style.FrameHeight), true);
if (!child)
return;
/// <summary> Update actors when anything changes in the object table. </summary>
private void OnUpdateRequired()
=> Dirty |= IManagedCache.DirtyFlags.Custom;
_world = new WorldId(objects.Player.Valid ? objects.Player.HomeWorld : (ushort)0);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, _defaultItemSpacing);
var skips = ImGuiClip.GetNecessarySkips(Im.Style.TextHeight);
var remainder = ImGuiClip.FilteredClippedDraw(objects.Where(p => p.Value.Objects.Any(a => a.Model)), skips, CheckFilter,
DrawSelectable);
ImGuiClip.DrawEndDummy(remainder, Im.Style.TextHeight);
}
private bool CheckFilter(KeyValuePair<ActorIdentifier, ActorData> pair)
=> _actorFilter.Lower switch
protected override void Dispose(bool disposing)
{
"" => true,
"<p>" => pair.Key.Type is IdentifierType.Player,
"<o>" => pair.Key.Type is IdentifierType.Owned,
"<n>" => pair.Key.Type is IdentifierType.Npc,
"<r>" => pair.Key.Type is IdentifierType.Retainer,
"<s>" => pair.Key.Type is IdentifierType.Special,
"<w>" => pair.Key.Type is IdentifierType.Player && pair.Key.HomeWorld == _world,
_ => _actorFilter.IsContained(pair.Value.Label),
};
base.Dispose(disposing);
_objects.Objects.OnUpdateRequired -= OnUpdateRequired;
_penumbra.CreatedCharacterBase -= OnCreatedCharacterBase;
}
private void DrawSelectable(KeyValuePair<ActorIdentifier, ActorData> pair)
{
var equals = pair.Key.Equals(_identifier);
if (ImUtf8.Selectable(IncognitoMode ? pair.Key.Incognito(pair.Value.Label) : pair.Value.Label, equals) && !equals)
_identifier = pair.Key.CreatePermanent();
}
private void DrawSelectionButtons()
{
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
var buttonWidth = new Vector2(_width / 2, 0);
if (ImUtf8.IconButton(FontAwesomeIcon.UserCircle, "Select the local player character."u8, buttonWidth, !objects.Player))
_identifier = objects.Player.GetIdentifier(actors);
Im.Line.Same();
var (id, data) = objects.TargetData;
var tt = data.Valid ? $"Select the current target {id} in the list." :
id.IsValid ? $"The target {id} is not in the list." : "No target selected.";
if (ImUtf8.IconButton(FontAwesomeIcon.HandPointer, tt, buttonWidth, objects.IsInGPose || !data.Valid))
_identifier = id;
protected override IEnumerable<ActorCacheItem> GetItems()
=> _objects.Where(p => p.Value.Objects.Any(a => a.Model)).Select(a => new ActorCacheItem(a.Key, a.Value));
}
}

View file

@ -3,18 +3,35 @@ using Luna;
namespace Glamourer.Gui.Tabs.ActorTab;
public sealed class ActorTab(ActorSelector selector, ActorPanel panel) : ITab<MainTabType>
public sealed class ActorTab : TwoPanelLayout, ITab<MainTabType>
{
public ReadOnlySpan<byte> Label
=> "Actors"u8;
private readonly ActorSelection _selection;
public MainTabType Identifier
=> MainTabType.Actors;
public ActorTab(ActorSelector selector, ActorPanel panel, ActorFilter filter, SelectPlayerButton selectPlayer,
SelectTargetButton selectTarget, ActorsHeader header, ActorSelection selection)
{
_selection = selection;
LeftPanel = selector;
LeftHeader = new FilterHeader<ActorCacheItem>(filter, new StringU8("Filter..."u8));
var footer = new ButtonFooter();
footer.Buttons.AddButton(selectPlayer, 100);
footer.Buttons.AddButton(selectTarget, 0);
LeftFooter = footer;
RightHeader = header;
RightPanel = panel;
RightFooter = NopHeaderFooter.Instance;
}
public override ReadOnlySpan<byte> Label
=> "Actors"u8;
public void DrawContent()
{
selector.Draw(200 * Im.Style.GlobalScale);
Im.Line.Same();
panel.Draw();
_selection.Update();
Draw(TwoPanelWidth.IndeterminateRelative);
}
public MainTabType Identifier
=> MainTabType.Actors;
}

View file

@ -0,0 +1,38 @@
using ImSharp;
using Luna;
namespace Glamourer.Gui.Tabs.ActorTab;
public sealed class ActorsHeader : SplitButtonHeader
{
private readonly ActorSelection _selection;
private readonly EphemeralConfig _config;
public ActorsHeader(SetFromClipboardButton setFromClipboard, ExportToClipboardButton exportToClipboard, SaveAsDesignButton save,
UndoButton undo, LockedButton locked, IncognitoButton incognito, ActorSelection selection, EphemeralConfig config)
{
_selection = selection;
_config = config;
LeftButtons.AddButton(setFromClipboard, 100);
LeftButtons.AddButton(exportToClipboard, 90);
LeftButtons.AddButton(save, 80);
LeftButtons.AddButton(undo, 70);
RightButtons.AddButton(locked, 100);
RightButtons.AddButton(incognito, 90);
}
public override ReadOnlySpan<byte> Text
=> _config.IncognitoMode ? _selection.IncognitoName : _selection.ActorName;
public override ColorParameter TextColor
=> _selection.State is null ? ColorParameter.Default :
_selection.Data.Valid ? ColorId.ActorAvailable.Value() : ColorId.ActorUnavailable.Value();
public override void Draw(Vector2 size)
{
var color = ColorId.HeaderButtons.Value();
using var _ = ImGuiColor.Text.Push(color).Push(ImGuiColor.Border, color);
base.Draw(size with { Y = Im.Style.FrameHeight });
}
}

View file

@ -0,0 +1,41 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.ImGuiNotification;
using Glamourer.Designs;
using ImSharp;
using Luna;
namespace Glamourer.Gui.Tabs.ActorTab;
public sealed class ExportToClipboardButton(ActorSelection selection, DesignConverter converter) : BaseIconButton<AwesomeIcon>, IUiService
{
public override AwesomeIcon Icon
=> LunaStyle.ToClipboardIcon;
public override bool IsVisible
=> selection.State?.ModelData.ModelId is 0;
public override bool Enabled
=> !(selection.State?.IsLocked ?? true);
public override bool HasTooltip
=> true;
public override void DrawTooltip()
=> Im.Text(
"Copy the current design to your clipboard.\nHold Control to disable applying of customizations for the copied design.\nHold Shift to disable applying of gear for the copied design."u8);
public override void OnClick()
{
try
{
var text = converter.ShareBase64(selection.State!, ApplicationRules.FromModifiers(selection.State!));
ImGui.SetClipboardText(text);
}
catch (Exception ex)
{
Glamourer.Messager.NotificationMessage(ex, $"Could not copy {selection.Identifier} data to clipboard.",
$"Could not copy data from design {selection.Identifier.Incognito(null)} to clipboard", NotificationType.Error);
}
}
}

View file

@ -0,0 +1,32 @@
using ImSharp;
using Luna;
namespace Glamourer.Gui.Tabs.ActorTab;
public sealed class LockedButton(ActorSelection selection) : BaseIconButton<AwesomeIcon>, IUiService
{
public override AwesomeIcon Icon
=> LunaStyle.LockedIcon;
public override bool IsVisible
=> selection.State?.IsLocked ?? false;
public override bool Enabled
=> true;
public override bool HasTooltip
=> true;
public override void DrawTooltip()
=> Im.Text("The current state of this actor is locked by external tools."u8);
protected override void PreDraw()
{
var color = ColorId.ActorAvailable.Value();
ImGuiColor.Border.Push(color)
.Push(ImGuiColor.Text, color);
}
protected override void PostDraw()
=> Im.ColorDisposable.PopUnsafe(2);
}

View file

@ -0,0 +1,47 @@
using Glamourer.Designs;
using ImSharp;
using Luna;
namespace Glamourer.Gui.Tabs.ActorTab;
public sealed class SaveAsDesignButton(ActorSelection selection, DesignConverter converter, DesignManager designManager)
: BaseIconButton<AwesomeIcon>, IUiService
{
public override AwesomeIcon Icon
=> LunaStyle.SaveIcon;
public override bool IsVisible
=> selection.State?.ModelData.ModelId is 0;
public override bool Enabled
=> !(selection.State?.IsLocked ?? true);
public override bool HasTooltip
=> true;
public override void DrawTooltip()
=> Im.Text(
"Save the current state as a design.\nHold Control to disable applying of customizations for the saved design.\nHold Shift to disable applying of gear for the saved design."u8);
private string _newName = string.Empty;
private DesignBase? _newDesign;
public override void OnClick()
{
Im.Popup.Open("Save as Design"u8);
_newName = selection.State!.Identifier.ToName();
_newDesign = converter.Convert(selection.State, ApplicationRules.FromModifiers(selection.State));
}
protected override void PostDraw()
{
if (!InputPopup.Open("Save as Design"u8, _newName, out var newName, "Enter Design Name..."u8))
return;
if (_newDesign is not null && newName.Length > 0)
designManager.CreateClone(_newDesign, newName, true);
_newDesign = null;
_newName = string.Empty;
}
}

View file

@ -0,0 +1,27 @@
using Dalamud.Interface;
using ImSharp;
using Luna;
using Penumbra.GameData.Interop;
namespace Glamourer.Gui.Tabs.ActorTab;
public sealed class SelectPlayerButton(ActorObjectManager objects, ActorSelection selection) : BaseIconButton<AwesomeIcon>, IUiService
{
public override AwesomeIcon Icon
=> FontAwesomeIcon.UserCircle;
public override void DrawTooltip()
=> Im.Text("Select the local player character."u8);
public override bool HasTooltip
=> true;
public override bool Enabled
=> objects.Player;
public override void OnClick()
{
var (identifier, data) = objects.PlayerData;
selection.Select(identifier, data);
}
}

View file

@ -0,0 +1,35 @@
using Dalamud.Interface;
using ImSharp;
using Luna;
using Penumbra.GameData.Interop;
namespace Glamourer.Gui.Tabs.ActorTab;
public sealed class SelectTargetButton(ActorObjectManager objects, ActorSelection selection) : BaseIconButton<AwesomeIcon>, IUiService
{
public override AwesomeIcon Icon
=> FontAwesomeIcon.HandPointer;
public override void DrawTooltip()
{
var (id, data) = objects.TargetData;
if (data.Valid)
Im.Text($"Select the current target {id} in the list.");
else if (id.IsValid)
Im.Text($"The target {id} is not in the list.");
else
Im.Text("No target selected."u8);
}
public override bool HasTooltip
=> true;
public override bool Enabled
=> objects.IsInGPose || !objects.TargetData.Data.Valid;
public override void OnClick()
{
var (identifier, data) = objects.TargetData;
selection.Select(identifier, data);
}
}

View file

@ -0,0 +1,44 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.ImGuiNotification;
using Glamourer.Designs;
using Glamourer.State;
using ImSharp;
using Luna;
namespace Glamourer.Gui.Tabs.ActorTab;
public sealed class SetFromClipboardButton(ActorSelection selection, DesignConverter converter, StateManager stateManager) : BaseIconButton<AwesomeIcon>, IUiService
{
public override AwesomeIcon Icon
=> LunaStyle.FromClipboardIcon;
public override bool IsVisible
=> selection.State is not null;
public override bool Enabled
=> !(selection.State?.IsLocked ?? true);
public override bool HasTooltip
=> true;
public override void DrawTooltip()
=> Im.Text("Try to apply a design from your clipboard.\nHold Control to only apply gear.\nHold Shift to only apply customizations."u8);
public override void OnClick()
{
try
{
var (applyGear, applyCustomize) = UiHelpers.ConvertKeysToBool();
var text = ImGui.GetClipboardText();
var design = converter.FromBase64(text, applyCustomize, applyGear, out _)
?? throw new Exception("The clipboard did not contain valid data.");
stateManager.ApplyDesign(selection.State!, design, ApplySettings.ManualWithLinks with { IsFinal = true });
}
catch (Exception ex)
{
Glamourer.Messager.NotificationMessage(ex, $"Could not apply clipboard to {selection.Identifier}.",
$"Could not apply clipboard to design {selection.Identifier.Incognito(null)}", NotificationType.Error, false);
}
}
}

View file

@ -0,0 +1,26 @@
using Glamourer.Designs.History;
using ImSharp;
using Luna;
namespace Glamourer.Gui.Tabs.ActorTab;
public sealed class UndoButton(ActorSelection selection, EditorHistory editorHistory) : BaseIconButton<AwesomeIcon>, IUiService
{
public override AwesomeIcon Icon
=> LunaStyle.UndoIcon;
public override bool IsVisible
=> selection.State is not null;
public override bool Enabled
=> !(selection.State?.IsLocked ?? true) && editorHistory.CanUndo(selection.State);
public override bool HasTooltip
=> true;
public override void DrawTooltip()
=> Im.Text("Undo the last change."u8);
public override void OnClick()
=> editorHistory.Undo(selection.State!);
}

View file

@ -27,7 +27,7 @@ public class SetPanel(
RandomRestrictionDrawer randomDrawer)
{
private readonly JobGroupCombo _jobGroupCombo = new(manager, jobs, Glamourer.Log);
private readonly HeaderDrawer.Button[] _rightButtons = [new HeaderDrawer.IncognitoButton(config)];
private readonly HeaderDrawer.Button[] _rightButtons = []; // [new IncognitoButton(config)];
private string? _tempName;
private int _dragIndex = -1;
@ -96,7 +96,7 @@ public class SetPanel(
Im.Dummy(Vector2.Zero);
var name = _tempName ?? Selection.Name;
var flags = selector.IncognitoMode ? InputTextFlags.ReadOnly | InputTextFlags.Password : InputTextFlags.None;
var flags = config.Ephemeral.IncognitoMode ? InputTextFlags.ReadOnly | InputTextFlags.Password : InputTextFlags.None;
Im.Item.SetNextWidthScaled(330);
if (Im.Input.Text("Rename Set##Name"u8, ref name, StringU8.Empty, flags))
_tempName = name;

View file

@ -25,16 +25,6 @@ public class SetSelector : IDisposable
public AutoDesignSet? Selection { get; private set; }
public int SelectionIndex { get; private set; } = -1;
public bool IncognitoMode
{
get => _config.Ephemeral.IncognitoMode;
set
{
_config.Ephemeral.IncognitoMode = value;
_config.Ephemeral.Save();
}
}
private int _dragIndex = -1;
private Action? _endAction;
@ -58,7 +48,7 @@ public class SetSelector : IDisposable
=> GetSetName(Selection, SelectionIndex);
public string GetSetName(AutoDesignSet? set, int index)
=> set == null ? "No Selection" : IncognitoMode ? $"Auto Design Set #{index + 1}" : set.Name;
=> set == null ? "No Selection" : _config.Ephemeral.IncognitoMode ? $"Auto Design Set #{index + 1}" : set.Name;
private void OnAutomationChange(AutomationChanged.Type type, AutoDesignSet? set, object? data)
{
@ -200,7 +190,7 @@ public class SetSelector : IDisposable
DrawDragDrop(pair.Set, pair.Index);
var text = pair.Set.Identifiers[0].ToString();
if (IncognitoMode)
if (_config.Ephemeral.IncognitoMode)
text = pair.Set.Identifiers[0].Incognito(text);
var textSize = ImGui.CalcTextSize(text);
var textColor = pair.Set.Identifiers.Any(_objects.ContainsKey) ? ColorId.AutomationActorAvailable : ColorId.AutomationActorUnavailable;

View file

@ -32,16 +32,6 @@ public sealed class DesignFileSystemSelector : FileSystemSelector<Design, Design
private Design? _cloneDesign;
private string _newName = string.Empty;
public bool IncognitoMode
{
get => _config.Ephemeral.IncognitoMode;
set
{
_config.Ephemeral.IncognitoMode = value;
_config.Ephemeral.Save();
}
}
public new DesignFileSystem.Leaf? SelectedLeaf
=> base.SelectedLeaf;
@ -175,7 +165,7 @@ public sealed class DesignFileSystemSelector : FileSystemSelector<Design, Design
protected override void DrawLeafName(FileSystem<Design>.Leaf leaf, in DesignState state, bool selected)
{
var flag = selected ? ImGuiTreeNodeFlags.Selected | LeafFlags : LeafFlags;
var name = IncognitoMode ? leaf.Value.Incognito : leaf.Value.Name.Text;
var name = _config.Ephemeral.IncognitoMode ? leaf.Value.Incognito : leaf.Value.Name.Text;
using var color = ImGuiColor.Text.Push(state.Color);
using var _ = ImUtf8.TreeNode(name, flag);
if (_config.AllowDoubleClickToApply && ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(ImGuiMouseButton.Left))

View file

@ -103,7 +103,7 @@ public class DesignLinkDrawer(
using (ImGuiColor.Text.Push(color))
{
Im.Cursor.FrameAlign();
Im.Selectable(selector.IncognitoMode ? selector.Selected!.Incognito : selector.Selected!.Name.Text);
Im.Selectable(config.Ephemeral.IncognitoMode ? selector.Selected!.Incognito : selector.Selected!.Name.Text);
}
Im.Tooltip.OnHover("Current Design"u8);
@ -133,7 +133,7 @@ public class DesignLinkDrawer(
using (ImGuiColor.Text.Push(colorManager.GetColor(design)))
{
Im.Cursor.FrameAlign();
Im.Selectable(selector.IncognitoMode ? design.Incognito : design.Name.Text);
Im.Selectable(config.Ephemeral.IncognitoMode ? design.Incognito : design.Name.Text);
}
DrawDragDrop(design, order, i);

View file

@ -91,7 +91,7 @@ public class DesignPanel
_rightButtons =
[
new LockButton(this),
new IncognitoButton(_config),
//new IncognitoButton(_config),
];
}
@ -99,7 +99,7 @@ public class DesignPanel
=> HeaderDrawer.Draw(SelectionName, 0, ImGuiColor.FrameBackground.Get().Color, _leftButtons, _rightButtons);
private string SelectionName
=> _selector.Selected == null ? "No Selection" : _selector.IncognitoMode ? _selector.Selected.Incognito : _selector.Selected.Name.Text;
=> _selector.Selected == null ? "No Selection" : _config.Ephemeral.IncognitoMode ? _selector.Selected.Incognito : _selector.Selected.Name.Text;
private void DrawEquipment()
{

View file

@ -18,7 +18,7 @@ public class MultiDesignPanel(
Configuration config)
{
private readonly Button[] _leftButtons = [];
private readonly Button[] _rightButtons = [new IncognitoButton(config)];
private readonly Button[] _rightButtons = []; //[new IncognitoButton(config)];
private readonly DesignColorCombo _colorCombo = new(colors, true);

View file

@ -44,39 +44,6 @@ public static class HeaderDrawer
}
}
public sealed class IncognitoButton(Configuration config) : Button
{
protected override string Description
{
get
{
var hold = config.IncognitoModifier.IsActive();
return (config.Ephemeral.IncognitoMode, hold)
switch
{
(true, true) => "Toggle incognito mode off.",
(false, true) => "Toggle incognito mode on.",
(true, false) => $"Toggle incognito mode off.\n\nHold {config.IncognitoModifier} while clicking to toggle.",
(false, false) => $"Toggle incognito mode on.\n\nHold {config.IncognitoModifier} while clicking to toggle.",
};
}
}
protected override FontAwesomeIcon Icon
=> config.Ephemeral.IncognitoMode
? FontAwesomeIcon.EyeSlash
: FontAwesomeIcon.Eye;
protected override void OnClick()
{
if (!config.IncognitoModifier.IsActive())
return;
config.Ephemeral.IncognitoMode = !config.Ephemeral.IncognitoMode;
config.Ephemeral.Save();
}
}
public static void Draw(string text, uint textColor, uint frameColor, Button[] leftButtons, Button[] rightButtons)
{
var width = Im.Style.FrameHeightWithSpacing;
@ -110,4 +77,4 @@ public static class HeaderDrawer
button.Draw(width);
}
}
}
}

View file

@ -0,0 +1,29 @@
using ImSharp;
using Luna;
namespace Glamourer.Gui.Tabs;
public sealed class IncognitoButton(Configuration config) : BaseIconButton<AwesomeIcon>, IUiService
{
public override AwesomeIcon Icon
=> config.Ephemeral.IncognitoMode
? LunaStyle.IncognitoOn
: LunaStyle.IncognitoOff;
public override bool HasTooltip
=> true;
public override void DrawTooltip()
{
var hold = config.IncognitoModifier.IsActive();
Im.Text(config.Ephemeral.IncognitoMode ? "Toggle incognito mode off."u8 : "Toggle incognito mode on."u8);
if (!hold)
Im.Text($"\nHold {config.IncognitoModifier} while clicking to toggle.");
}
public override void OnClick()
{
if (config.IncognitoModifier.IsActive())
config.Ephemeral.IncognitoMode = !config.Ephemeral.IncognitoMode;
}
}

2
Luna

@ -1 +1 @@
Subproject commit 6235cc8b4d0196c2545bf39834265fb4e0939b08
Subproject commit ab620925015405133533c8811793f64082804d59

@ -1 +1 @@
Subproject commit bdbf134934238c7af53aa8625467bd5ad580d6f6
Subproject commit ab77b934eecc097ca1bf0d6243c8cd0dd4ffa155