Replace all occurences of ImGuiCol.

This commit is contained in:
Ottermandias 2025-10-20 22:41:29 +02:00
parent a4302c9145
commit 74d35870e2
46 changed files with 1586 additions and 1475 deletions

2
Luna

@ -1 +1 @@
Subproject commit 1ffa8c5de118f94b609f7d6352e3b63de463398c
Subproject commit ec5cc05211c5083f276e32816d8c3eb8c09d04b4

@ -1 +1 @@
Subproject commit 3a9406bc634228cc0815ddb6fe5e20419cafb864
Subproject commit 182cca56a49411430233d73d7a8a6bb3d983f8f0

View file

@ -54,8 +54,8 @@ public static class FeatureChecker
const int numButtons = 5;
var innerSpacing = ImGui.GetStyle().ItemInnerSpacing;
var size = new Vector2((width - (numButtons - 1) * innerSpacing.X) / numButtons, 0);
var buttonColor = ImGui.GetColorU32(ImGuiCol.FrameBg);
var textColor = ImGui.GetColorU32(ImGuiCol.TextDisabled);
var buttonColor = Im.Style[ImGuiColor.FrameBackground];
var textColor = Im.Style[ImGuiColor.TextDisabled];
using (var style = ImStyleBorder.Frame.Push(ColorId.FolderLine.Value(), 0)
.Push(ImStyleDouble.ItemSpacing, innerSpacing)
.Push(ImGuiColor.Button, buttonColor)

View file

@ -7,6 +7,103 @@ using FileSystemChangeType = OtterGui.Filesystem.FileSystemChangeType;
namespace Penumbra.Mods.Manager;
//public sealed class ModFileSystem2 : BaseFileSystem
//{
// private readonly Configuration _config;
// private readonly SaveService _saveService;
// public ModFileSystem2(FileSystemChanged @event, DataNodePathChange dataChangeEvent, Configuration config, SaveService saveService, IComparer<ReadOnlySpan<char>>? comparer = null)
// : base(@event, dataChangeEvent, comparer)
// {
// _config = config;
// _saveService = saveService;
// }
//
// public void Dispose()
// {
// _communicator.ModPathChanged.Unsubscribe(OnModPathChange);
// _communicator.ModDiscoveryFinished.Unsubscribe(Reload);
// _communicator.ModDataChanged.Unsubscribe(OnModDataChange);
// }
//
// // Save the filesystem on every filesystem change except full reloading.
// private void OnChange(FileSystemChangeType type, IPath _1, IPath? _2, IPath? _3)
// {
// if (type != FileSystemChangeType.Reload)
// _saveService.DelaySave(this);
// }
//
// // Update sort order when defaulted mod names change.
// private void OnModDataChange(in ModDataChanged.Arguments arguments)
// {
// if (!arguments.Type.HasFlag(ModDataChangeType.Name) || arguments.OldName == null || !TryGetValue(arguments.Mod, out var leaf))
// return;
//
// var old = Extensions.FixName(arguments.OldName);
// if (old == leaf.Name || Extensions.IsDuplicateName(leaf.Name, out var baseName, out _) && baseName == old)
// RenameWithDuplicates(leaf, arguments.Mod.Name);
// }
//
// // Update the filesystem if a mod has been added or removed.
// // Save it, if the mod directory has been moved, since this will change the save format.
// private void OnModPathChange(in ModPathChanged.Arguments arguments)
// {
// switch (arguments.Type)
// {
// case ModPathChangeType.Added:
// var parent = Root;
// if (_config.DefaultImportFolder.Length != 0)
// try
// {
// parent = FindOrCreateAllFolders(_config.DefaultImportFolder);
// }
// catch (Exception e)
// {
// Penumbra.Messager.NotificationMessage(e,
// $"Could not move newly imported mod {arguments.Mod.Name} to default import folder {_config.DefaultImportFolder}.",
// NotificationType.Warning);
// }
//
// CreateDuplicateLeaf(parent, arguments.Mod.Name, arguments.Mod);
// break;
// case ModPathChangeType.Deleted:
// if (arguments.Mod.Node is not null)
// Delete(arguments.Mod.Node);
// break;
// case ModPathChangeType.Moved:
// _saveService.DelaySave(this);
// break;
// case ModPathChangeType.Reloaded:
// // Nothing
// break;
// }
// }
//
// public struct ImportDate : ISortMode
// {
// public ReadOnlySpan<byte> Name
// => "Import Date (Older First)"u8;
//
// public ReadOnlySpan<byte> Description
// => "In each folder, sort all subfolders lexicographically, then sort all leaves using their import date."u8;
//
// public IEnumerable<IFileSystemNode> GetChildren(IFileSystemFolder f)
// => f.GetSubFolders().Cast<IFileSystemNode>().Concat(f.GetLeaves().OfType<IFileSystemData<Mod>>().OrderBy(l => l.Value.ImportDate));
// }
//
// public struct InverseImportDate : ISortMode
// {
// public ReadOnlySpan<byte> Name
// => "Import Date (Newer First)"u8;
//
// public ReadOnlySpan<byte> Description
// => "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse import date."u8;
//
// public IEnumerable<IFileSystemNode> GetChildren(IFileSystemFolder f)
// => f.GetSubFolders().Cast<IFileSystemNode>().Concat(f.GetLeaves().OfType<IFileSystemData<Mod>>().OrderByDescending(l => l.Value.ImportDate));
// }
//
//}
public sealed class ModFileSystem : FileSystem<Mod>, IDisposable, ISavable, IService
{
private readonly ModManager _modManager;

View file

@ -20,7 +20,7 @@ public enum FeatureFlags : ulong
Invalid = 1ul << 62,
}
public sealed class Mod : IMod
public sealed class Mod : IMod, IFileSystemValue<Mod>
{
public static readonly TemporaryMod ForcedFiles = new()
{
@ -69,6 +69,7 @@ public sealed class Mod : IMod
// Local Data
public string FullPath { get; set; } = string.Empty;
public long ImportDate { get; internal set; } = DateTimeOffset.UnixEpoch.ToUnixTimeMilliseconds();
public IReadOnlyList<string> LocalTags { get; internal set; } = [];
public string Note { get; internal set; } = string.Empty;
@ -130,14 +131,15 @@ public sealed class Mod : IMod
}
// Cache
public IFileSystemData<Mod>? Node { get; set; }
public readonly SortedList<string, IIdentifiedObjectData> ChangedItems = new();
public string LowerChangedItemsString { get; internal set; } = string.Empty;
public string AllTagsLower { get; internal set; } = string.Empty;
public int TotalFileCount { get; internal set; }
public int TotalSwapCount { get; internal set; }
public int TotalManipulations { get; internal set; }
public ushort LastChangedItemsUpdate { get; internal set; }
public bool HasOptions { get; internal set; }
public int TotalFileCount { get; internal set; }
public int TotalSwapCount { get; internal set; }
public int TotalManipulations { get; internal set; }
public ushort LastChangedItemsUpdate { get; internal set; }
public bool HasOptions { get; internal set; }
}

View file

@ -1,131 +1,139 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Penumbra.GameData.Structs;
using Penumbra.Mods.Manager;
using Penumbra.Services;
namespace Penumbra.Mods;
public readonly struct ModLocalData(Mod mod) : ISavable
{
public const int FileVersion = 3;
public string ToFilePath(FilenameService fileNames)
=> fileNames.LocalDataFile(mod);
public void Save(StreamWriter writer)
{
var jObject = new JObject
{
{ nameof(FileVersion), JToken.FromObject(FileVersion) },
{ nameof(Mod.ImportDate), JToken.FromObject(mod.ImportDate) },
{ nameof(Mod.LocalTags), JToken.FromObject(mod.LocalTags) },
{ nameof(Mod.Note), JToken.FromObject(mod.Note) },
{ nameof(Mod.Favorite), JToken.FromObject(mod.Favorite) },
{ nameof(Mod.PreferredChangedItems), JToken.FromObject(mod.PreferredChangedItems) },
};
using var jWriter = new JsonTextWriter(writer);
jWriter.Formatting = Formatting.Indented;
jObject.WriteTo(jWriter);
}
public static ModDataChangeType Load(ModDataEditor editor, Mod mod)
{
var dataFile = editor.SaveService.FileNames.LocalDataFile(mod);
var importDate = 0L;
var localTags = Enumerable.Empty<string>();
var favorite = false;
var note = string.Empty;
HashSet<CustomItemId> preferredChangedItems = [];
var save = true;
if (File.Exists(dataFile))
try
{
var text = File.ReadAllText(dataFile);
var json = JObject.Parse(text);
importDate = json[nameof(Mod.ImportDate)]?.Value<long>() ?? importDate;
favorite = json[nameof(Mod.Favorite)]?.Value<bool>() ?? favorite;
note = json[nameof(Mod.Note)]?.Value<string>() ?? note;
localTags = (json[nameof(Mod.LocalTags)] as JArray)?.Values<string>().OfType<string>() ?? localTags;
preferredChangedItems = (json[nameof(Mod.PreferredChangedItems)] as JArray)?.Values<ulong>().Select(i => (CustomItemId) i).ToHashSet() ?? mod.DefaultPreferredItems;
save = false;
}
catch (Exception e)
{
Penumbra.Log.Error($"Could not load local mod data:\n{e}");
}
else
{
preferredChangedItems = mod.DefaultPreferredItems;
}
if (importDate == 0)
importDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
ModDataChangeType changes = 0;
if (mod.ImportDate != importDate)
{
mod.ImportDate = importDate;
changes |= ModDataChangeType.ImportDate;
}
changes |= UpdateTags(mod, null, localTags);
if (mod.Favorite != favorite)
{
mod.Favorite = favorite;
changes |= ModDataChangeType.Favorite;
}
if (mod.Note != note)
{
mod.Note = note;
changes |= ModDataChangeType.Note;
}
if (!preferredChangedItems.SetEquals(mod.PreferredChangedItems))
{
mod.PreferredChangedItems = preferredChangedItems;
changes |= ModDataChangeType.PreferredChangedItems;
}
if (save)
editor.SaveService.QueueSave(new ModLocalData(mod));
return changes;
}
internal static ModDataChangeType UpdateTags(Mod mod, IEnumerable<string>? newModTags, IEnumerable<string>? newLocalTags)
{
if (newModTags == null && newLocalTags == null)
return 0;
ModDataChangeType type = 0;
if (newModTags != null)
{
var modTags = newModTags.Where(t => t.Length > 0).Distinct().ToArray();
if (!modTags.SequenceEqual(mod.ModTags))
{
newLocalTags ??= mod.LocalTags;
mod.ModTags = modTags;
type |= ModDataChangeType.ModTags;
}
}
if (newLocalTags != null)
{
var localTags = newLocalTags!.Where(t => t.Length > 0 && !mod.ModTags.Contains(t)).Distinct().ToArray();
if (!localTags.SequenceEqual(mod.LocalTags))
{
mod.LocalTags = localTags;
type |= ModDataChangeType.LocalTags;
}
}
return type;
}
}
using Luna;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Penumbra.GameData.Structs;
using Penumbra.Mods.Manager;
using Penumbra.Services;
namespace Penumbra.Mods;
public readonly struct ModLocalData(Mod mod) : ISavable
{
public const int FileVersion = 3;
public string ToFilePath(FilenameService fileNames)
=> fileNames.LocalDataFile(mod);
public void Save(StreamWriter writer)
{
var jObject = new JObject
{
{ nameof(FileVersion), JToken.FromObject(FileVersion) },
{ nameof(Mod.ImportDate), JToken.FromObject(mod.ImportDate) },
{ nameof(Mod.LocalTags), JToken.FromObject(mod.LocalTags) },
{ nameof(Mod.Note), JToken.FromObject(mod.Note) },
{ nameof(Mod.Favorite), JToken.FromObject(mod.Favorite) },
{ nameof(Mod.PreferredChangedItems), JToken.FromObject(mod.PreferredChangedItems) },
};
if (mod.FullPath.Length > 0)
{
var baseName = mod.FullPath.GetBaseName(mod.Name, out var folder);
jObject[nameof(Mod.FullPath)] = folder.Length > 0 ? $"{folder}/{baseName}" : baseName.ToString();
}
using var jWriter = new JsonTextWriter(writer);
jWriter.Formatting = Formatting.Indented;
jObject.WriteTo(jWriter);
}
public static ModDataChangeType Load(ModDataEditor editor, Mod mod)
{
var dataFile = editor.SaveService.FileNames.LocalDataFile(mod);
var importDate = 0L;
var localTags = Enumerable.Empty<string>();
var favorite = false;
var note = string.Empty;
HashSet<CustomItemId> preferredChangedItems = [];
var save = true;
if (File.Exists(dataFile))
try
{
var text = File.ReadAllText(dataFile);
var json = JObject.Parse(text);
importDate = json[nameof(Mod.ImportDate)]?.Value<long>() ?? importDate;
favorite = json[nameof(Mod.Favorite)]?.Value<bool>() ?? favorite;
note = json[nameof(Mod.Note)]?.Value<string>() ?? note;
localTags = (json[nameof(Mod.LocalTags)] as JArray)?.Values<string>().OfType<string>() ?? localTags;
preferredChangedItems =
(json[nameof(Mod.PreferredChangedItems)] as JArray)?.Values<ulong>().Select(i => (CustomItemId)i).ToHashSet()
?? mod.DefaultPreferredItems;
save = false;
}
catch (Exception e)
{
Penumbra.Log.Error($"Could not load local mod data:\n{e}");
}
else
preferredChangedItems = mod.DefaultPreferredItems;
if (importDate == 0)
importDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
ModDataChangeType changes = 0;
if (mod.ImportDate != importDate)
{
mod.ImportDate = importDate;
changes |= ModDataChangeType.ImportDate;
}
changes |= UpdateTags(mod, null, localTags);
if (mod.Favorite != favorite)
{
mod.Favorite = favorite;
changes |= ModDataChangeType.Favorite;
}
if (mod.Note != note)
{
mod.Note = note;
changes |= ModDataChangeType.Note;
}
if (!preferredChangedItems.SetEquals(mod.PreferredChangedItems))
{
mod.PreferredChangedItems = preferredChangedItems;
changes |= ModDataChangeType.PreferredChangedItems;
}
if (save)
editor.SaveService.QueueSave(new ModLocalData(mod));
return changes;
}
internal static ModDataChangeType UpdateTags(Mod mod, IEnumerable<string>? newModTags, IEnumerable<string>? newLocalTags)
{
if (newModTags == null && newLocalTags == null)
return 0;
ModDataChangeType type = 0;
if (newModTags != null)
{
var modTags = newModTags.Where(t => t.Length > 0).Distinct().ToArray();
if (!modTags.SequenceEqual(mod.ModTags))
{
newLocalTags ??= mod.LocalTags;
mod.ModTags = modTags;
type |= ModDataChangeType.ModTags;
}
}
if (newLocalTags != null)
{
var localTags = newLocalTags!.Where(t => t.Length > 0 && !mod.ModTags.Contains(t)).Distinct().ToArray();
if (!localTags.SequenceEqual(mod.LocalTags))
{
mod.LocalTags = localTags;
type |= ModDataChangeType.LocalTags;
}
}
return type;
}
}

View file

@ -22,7 +22,7 @@ public static class StaticServiceManager
{
public static ServiceManager CreateProvider(Penumbra penumbra, IDalamudPluginInterface pi, Logger log)
{
var services = new ServiceManager(log)
var services = new ServiceManager(log, Logger.GlobalPluginName)
.AddDalamudServices(pi)
.AddExistingService(log)
.AddExistingService(penumbra);

View file

@ -44,7 +44,7 @@ public partial class MtrlTab
for (var j = 0; j < 8; ++j)
{
var pairIndex = i + j;
using (ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.ButtonActive), pairIndex == _colorTableSelectedPair))
using (ImGuiColor.Button.Push(Im.Style[ImGuiColor.ButtonActive], pairIndex == _colorTableSelectedPair))
{
if (ImUtf8.Button($"#{pairIndex + 1}".PadLeft(3 + spacePadding),
new Vector2(buttonWidth, ImGui.GetFrameHeightWithSpacing() + frameHeight)))
@ -101,6 +101,7 @@ public partial class MtrlTab
{
retA |= DrawRowHeader(rowAIdx, disabled);
}
columns.Next();
using (ImUtf8.PushId("RowHeaderB"u8))
{
@ -232,8 +233,8 @@ public partial class MtrlTab
/// <remarks> Padding styles do not seem to apply to this component. It is recommended to prepend two spaces. </remarks>
private static void DrawHeader(ReadOnlySpan<byte> label)
{
var headerColor = ImGui.GetColorU32(ImGuiCol.Header);
using var _ = ImRaii.PushColor(ImGuiCol.HeaderHovered, headerColor).Push(ImGuiCol.HeaderActive, headerColor);
var headerColor = Im.Style[ImGuiColor.Header];
using var _ = ImGuiColor.HeaderHovered.Push(headerColor).Push(ImGuiColor.HeaderActive, headerColor);
ImUtf8.CollapsingHeader(label, ImGuiTreeNodeFlags.Leaf);
}

View file

@ -308,7 +308,7 @@ public partial class MtrlTab
var style = ImGui.GetStyle();
var frameRounding = style.FrameRounding;
var frameThickness = style.FrameBorderSize;
var borderColor = ImGui.GetColorU32(ImGuiCol.Border);
var borderColor = ImGuiColor.Border.Get();
var drawList = ImGui.GetWindowDrawList();
if (topColor == bottomColor)
{
@ -328,7 +328,7 @@ public partial class MtrlTab
bottomColor, frameRounding, ImDrawFlags.RoundCornersBottomLeft | ImDrawFlags.RoundCornersBottomRight);
}
drawList.AddRect(rcMin, rcMax, borderColor, frameRounding, ImDrawFlags.RoundCornersDefault, frameThickness);
drawList.AddRect(rcMin, rcMax, borderColor.Color, frameRounding, ImDrawFlags.RoundCornersDefault, frameThickness);
}
private static bool CtColorPicker(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, HalfColor current, Action<HalfColor> setter,

View file

@ -24,16 +24,15 @@ public partial class MtrlTab
if (disabled)
return;
if (ImUtf8.Button("Reload live preview"u8))
if (Im.Button("Reload live preview"u8))
BindToMaterialInstances();
if (_materialPreviewers.Count != 0 || _colorTablePreviewers.Count != 0)
return;
Im.Line.Same();
using var c = ImRaii.PushColor(ImGuiCol.Text, Colors.RegexWarningBorder);
ImUtf8.Text(
"The current material has not been found on your character. Please check the Import from Screen tab for more information."u8);
Im.Text("The current material has not been found on your character. Please check the Import from Screen tab for more information."u8,
Colors.RegexWarningBorder);
}
private unsafe void BindToMaterialInstances()
@ -292,7 +291,7 @@ public partial class MtrlTab
{
var level = (MathF.Sin(time * 2.0f * MathF.PI) + 2.0f) / 3.0f / 255.0f;
var baseColor = colorId.Value();
var color = level * new Vector3(baseColor & 0xFF, (baseColor >> 8) & 0xFF, (baseColor >> 16) & 0xFF);
var color = level * new Vector3(baseColor.R, baseColor.G, baseColor.B);
var halfColor = (HalfColor)(color * color);
row.DiffuseColor = halfColor;

View file

@ -1,273 +1,274 @@
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using Newtonsoft.Json.Linq;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.Collections.Cache;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Meta;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class AtrMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<AtrIdentifier, AtrEntry>(editor, metaFiles), Luna.IService
{
public override ReadOnlySpan<byte> Label
=> "Attributes(ATR)###ATR"u8;
private ShapeAttributeString _buffer = ShapeAttributeString.TryRead("atrx_"u8, out var s) ? s : ShapeAttributeString.Empty;
private bool _identifierValid;
public override int NumColumns
=> 7;
public override float ColumnHeight
=> ImUtf8.FrameHeightSpacing;
protected override void Initialize()
{
Identifier = new AtrIdentifier(HumanSlot.Unknown, null, ShapeAttributeString.Empty, GenderRace.Unknown);
Entry = AtrEntry.True;
}
protected override void DrawNew()
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy all current ATR manipulations to clipboard."u8,
new Lazy<JToken?>(() => MetaDictionary.SerializeTo([], Editor.Atr)));
ImGui.TableNextColumn();
var canAdd = !Editor.Contains(Identifier) && _identifierValid;
var tt = canAdd
? "Stage this edit."u8
: _identifierValid
? "This entry does not contain a valid attribute."u8
: "This entry is already edited."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier, AtrEntry.False);
DrawIdentifierInput(ref Identifier);
DrawEntry(ref Entry, true);
}
protected override void DrawEntry(AtrIdentifier identifier, AtrEntry entry)
{
DrawMetaButtons(identifier, entry);
DrawIdentifier(identifier);
if (DrawEntry(ref entry, false))
Editor.Changes |= Editor.Update(identifier, entry);
}
protected override IEnumerable<(AtrIdentifier, AtrEntry)> Enumerate()
=> Editor.Atr
.OrderBy(kvp => kvp.Key.Attribute)
.ThenBy(kvp => kvp.Key.Slot)
.ThenBy(kvp => kvp.Key.Id)
.Select(kvp => (kvp.Key, kvp.Value));
protected override int Count
=> Editor.Atr.Count;
private bool DrawIdentifierInput(ref AtrIdentifier identifier)
{
ImGui.TableNextColumn();
var changes = DrawHumanSlot(ref identifier);
ImGui.TableNextColumn();
changes |= DrawGenderRaceConditionInput(ref identifier);
ImGui.TableNextColumn();
changes |= DrawPrimaryId(ref identifier);
ImGui.TableNextColumn();
changes |= DrawAttributeKeyInput(ref identifier, ref _buffer, ref _identifierValid);
return changes;
}
private static void DrawIdentifier(AtrIdentifier identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed(ShpMetaDrawer.SlotName(identifier.Slot), FrameColor);
ImUtf8.HoverTooltip("Model Slot"u8);
ImGui.TableNextColumn();
if (identifier.GenderRaceCondition is not GenderRace.Unknown)
{
ImUtf8.TextFramed($"{identifier.GenderRaceCondition.ToName()} ({identifier.GenderRaceCondition.ToRaceCode()})", FrameColor);
ImUtf8.HoverTooltip("Gender & Race Code for this attribute to be set.");
}
else
{
ImUtf8.TextFramed("Any Gender & Race"u8, FrameColor);
}
ImGui.TableNextColumn();
if (identifier.Id.HasValue)
ImUtf8.TextFramed($"{identifier.Id.Value.Id}", FrameColor);
else
ImUtf8.TextFramed("All IDs"u8, FrameColor);
ImUtf8.HoverTooltip("Primary ID"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Attribute.AsSpan, FrameColor);
}
private static bool DrawEntry(ref AtrEntry entry, bool disabled)
{
using var dis = ImRaii.Disabled(disabled);
ImGui.TableNextColumn();
var value = entry.Value;
var changes = ImUtf8.Checkbox("##atrEntry"u8, ref value);
if (changes)
entry = new AtrEntry(value);
ImUtf8.HoverTooltip("Whether to enable or disable this attribute for the selected items.");
return changes;
}
public static bool DrawPrimaryId(ref AtrIdentifier identifier, float unscaledWidth = 100)
{
var allSlots = identifier.Slot is HumanSlot.Unknown;
var all = !identifier.Id.HasValue;
var ret = false;
using (ImRaii.Disabled(allSlots))
{
if (ImUtf8.Checkbox("##atrAll"u8, ref all))
{
identifier = identifier with { Id = all ? null : 0 };
ret = true;
}
}
ImUtf8.HoverTooltip(allSlots
? "When using all slots, you also need to use all IDs."u8
: "Enable this attribute for all model IDs."u8);
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
if (all)
{
using var style = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, new Vector2(0.05f, 0.5f));
ImUtf8.TextFramed("All IDs"u8, ImGui.GetColorU32(ImGuiCol.FrameBg, all || allSlots ? ImGui.GetStyle().DisabledAlpha : 1f),
new Vector2(unscaledWidth, 0), ImGui.GetColorU32(ImGuiCol.TextDisabled));
}
else
{
var max = identifier.Slot.ToSpecificEnum() is BodySlot ? byte.MaxValue : ExpandedEqpGmpBase.Count - 1;
if (IdInput("##atrPrimaryId"u8, unscaledWidth, identifier.Id.GetValueOrDefault(0).Id, out var setId, 0, max, false))
{
identifier = identifier with { Id = setId };
ret = true;
}
}
ImUtf8.HoverTooltip("Primary ID - You can usually find this as the 'e####' part of an item path or similar for customizations."u8);
return ret;
}
public bool DrawHumanSlot(ref AtrIdentifier identifier, float unscaledWidth = 150)
{
var ret = false;
ImGui.SetNextItemWidth(unscaledWidth * ImUtf8.GlobalScale);
using (var combo = ImUtf8.Combo("##atrSlot"u8, ShpMetaDrawer.SlotName(identifier.Slot)))
{
if (combo)
foreach (var slot in ShpMetaDrawer.AvailableSlots)
{
if (!ImUtf8.Selectable(ShpMetaDrawer.SlotName(slot), slot == identifier.Slot) || slot == identifier.Slot)
continue;
ret = true;
if (slot is HumanSlot.Unknown)
{
identifier = identifier with
{
Id = null,
Slot = slot,
};
}
else
{
identifier = identifier with
{
Id = identifier.Id.HasValue
? (PrimaryId)Math.Clamp(identifier.Id.Value.Id, 0,
slot.ToSpecificEnum() is BodySlot ? byte.MaxValue : ExpandedEqpGmpBase.Count - 1)
: null,
Slot = slot,
};
ret = true;
}
}
}
ImUtf8.HoverTooltip("Model Slot"u8);
return ret;
}
private static bool DrawGenderRaceConditionInput(ref AtrIdentifier identifier, float unscaledWidth = 250)
{
var ret = false;
ImGui.SetNextItemWidth(unscaledWidth * ImUtf8.GlobalScale);
using (var combo = ImUtf8.Combo("##shpGenderRace"u8,
identifier.GenderRaceCondition is GenderRace.Unknown
? "Any Gender & Race"
: $"{identifier.GenderRaceCondition.ToName()} ({identifier.GenderRaceCondition.ToRaceCode()})"))
{
if (combo)
{
if (ImUtf8.Selectable("Any Gender & Race"u8, identifier.GenderRaceCondition is GenderRace.Unknown)
&& identifier.GenderRaceCondition is not GenderRace.Unknown)
{
identifier = identifier with { GenderRaceCondition = GenderRace.Unknown };
ret = true;
}
foreach (var gr in ShapeAttributeHashSet.GenderRaceValues.Skip(1))
{
if (ImUtf8.Selectable($"{gr.ToName()} ({gr.ToRaceCode()})", identifier.GenderRaceCondition == gr)
&& identifier.GenderRaceCondition != gr)
{
identifier = identifier with { GenderRaceCondition = gr };
ret = true;
}
}
}
}
ImUtf8.HoverTooltip(
"Only activate this attribute for this gender & race code."u8);
return ret;
}
public static unsafe bool DrawAttributeKeyInput(ref AtrIdentifier identifier, ref ShapeAttributeString buffer, ref bool valid,
float unscaledWidth = 150)
{
var ret = false;
var ptr = Unsafe.AsPointer(ref buffer);
var span = new Span<byte>(ptr, ShapeAttributeString.MaxLength + 1);
using (new ImRaii.ColorStyle().Push(ImGuiCol.Border, Colors.RegexWarningBorder, !valid).Push(ImGuiStyleVar.FrameBorderSize, 1f, !valid))
{
ImGui.SetNextItemWidth(unscaledWidth * ImUtf8.GlobalScale);
if (ImUtf8.InputText("##atrAttribute"u8, span, out int newLength, "Attribute..."u8))
{
buffer.ForceLength((byte)newLength);
valid = buffer.ValidateCustomAttributeString();
if (valid)
identifier = identifier with { Attribute = buffer };
ret = true;
}
}
ImUtf8.HoverTooltip("Supported attribute need to have the format `atrx_*` and a maximum length of 30 characters."u8);
return ret;
}
}
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Newtonsoft.Json.Linq;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.Collections.Cache;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Meta;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class AtrMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<AtrIdentifier, AtrEntry>(editor, metaFiles), Luna.IService
{
public override ReadOnlySpan<byte> Label
=> "Attributes(ATR)###ATR"u8;
private ShapeAttributeString _buffer = ShapeAttributeString.TryRead("atrx_"u8, out var s) ? s : ShapeAttributeString.Empty;
private bool _identifierValid;
public override int NumColumns
=> 7;
public override float ColumnHeight
=> ImUtf8.FrameHeightSpacing;
protected override void Initialize()
{
Identifier = new AtrIdentifier(HumanSlot.Unknown, null, ShapeAttributeString.Empty, GenderRace.Unknown);
Entry = AtrEntry.True;
}
protected override void DrawNew()
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy all current ATR manipulations to clipboard."u8,
new Lazy<JToken?>(() => MetaDictionary.SerializeTo([], Editor.Atr)));
ImGui.TableNextColumn();
var canAdd = !Editor.Contains(Identifier) && _identifierValid;
var tt = canAdd
? "Stage this edit."u8
: _identifierValid
? "This entry does not contain a valid attribute."u8
: "This entry is already edited."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier, AtrEntry.False);
DrawIdentifierInput(ref Identifier);
DrawEntry(ref Entry, true);
}
protected override void DrawEntry(AtrIdentifier identifier, AtrEntry entry)
{
DrawMetaButtons(identifier, entry);
DrawIdentifier(identifier);
if (DrawEntry(ref entry, false))
Editor.Changes |= Editor.Update(identifier, entry);
}
protected override IEnumerable<(AtrIdentifier, AtrEntry)> Enumerate()
=> Editor.Atr
.OrderBy(kvp => kvp.Key.Attribute)
.ThenBy(kvp => kvp.Key.Slot)
.ThenBy(kvp => kvp.Key.Id)
.Select(kvp => (kvp.Key, kvp.Value));
protected override int Count
=> Editor.Atr.Count;
private bool DrawIdentifierInput(ref AtrIdentifier identifier)
{
ImGui.TableNextColumn();
var changes = DrawHumanSlot(ref identifier);
ImGui.TableNextColumn();
changes |= DrawGenderRaceConditionInput(ref identifier);
ImGui.TableNextColumn();
changes |= DrawPrimaryId(ref identifier);
ImGui.TableNextColumn();
changes |= DrawAttributeKeyInput(ref identifier, ref _buffer, ref _identifierValid);
return changes;
}
private static void DrawIdentifier(AtrIdentifier identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed(ShpMetaDrawer.SlotName(identifier.Slot), FrameColor);
ImUtf8.HoverTooltip("Model Slot"u8);
ImGui.TableNextColumn();
if (identifier.GenderRaceCondition is not GenderRace.Unknown)
{
ImUtf8.TextFramed($"{identifier.GenderRaceCondition.ToName()} ({identifier.GenderRaceCondition.ToRaceCode()})", FrameColor);
ImUtf8.HoverTooltip("Gender & Race Code for this attribute to be set.");
}
else
{
ImUtf8.TextFramed("Any Gender & Race"u8, FrameColor);
}
ImGui.TableNextColumn();
if (identifier.Id.HasValue)
ImUtf8.TextFramed($"{identifier.Id.Value.Id}", FrameColor);
else
ImUtf8.TextFramed("All IDs"u8, FrameColor);
ImUtf8.HoverTooltip("Primary ID"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Attribute.AsSpan, FrameColor);
}
private static bool DrawEntry(ref AtrEntry entry, bool disabled)
{
using var dis = ImRaii.Disabled(disabled);
ImGui.TableNextColumn();
var value = entry.Value;
var changes = ImUtf8.Checkbox("##atrEntry"u8, ref value);
if (changes)
entry = new AtrEntry(value);
ImUtf8.HoverTooltip("Whether to enable or disable this attribute for the selected items.");
return changes;
}
public static bool DrawPrimaryId(ref AtrIdentifier identifier, float unscaledWidth = 100)
{
var allSlots = identifier.Slot is HumanSlot.Unknown;
var all = !identifier.Id.HasValue;
var ret = false;
using (ImRaii.Disabled(allSlots))
{
if (ImUtf8.Checkbox("##atrAll"u8, ref all))
{
identifier = identifier with { Id = all ? null : 0 };
ret = true;
}
}
ImUtf8.HoverTooltip(allSlots
? "When using all slots, you also need to use all IDs."u8
: "Enable this attribute for all model IDs."u8);
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
if (all)
{
using var style = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, new Vector2(0.05f, 0.5f));
ImUtf8.TextFramed("All IDs"u8, ImGuiColor.FrameBackground.Get(all || allSlots ? ImGui.GetStyle().DisabledAlpha : 1f).Color,
new Vector2(unscaledWidth, 0), ImGuiColor.TextDisabled.Get().Color);
}
else
{
var max = identifier.Slot.ToSpecificEnum() is BodySlot ? byte.MaxValue : ExpandedEqpGmpBase.Count - 1;
if (IdInput("##atrPrimaryId"u8, unscaledWidth, identifier.Id.GetValueOrDefault(0).Id, out var setId, 0, max, false))
{
identifier = identifier with { Id = setId };
ret = true;
}
}
ImUtf8.HoverTooltip("Primary ID - You can usually find this as the 'e####' part of an item path or similar for customizations."u8);
return ret;
}
public bool DrawHumanSlot(ref AtrIdentifier identifier, float unscaledWidth = 150)
{
var ret = false;
ImGui.SetNextItemWidth(unscaledWidth * ImUtf8.GlobalScale);
using (var combo = ImUtf8.Combo("##atrSlot"u8, ShpMetaDrawer.SlotName(identifier.Slot)))
{
if (combo)
foreach (var slot in ShpMetaDrawer.AvailableSlots)
{
if (!ImUtf8.Selectable(ShpMetaDrawer.SlotName(slot), slot == identifier.Slot) || slot == identifier.Slot)
continue;
ret = true;
if (slot is HumanSlot.Unknown)
{
identifier = identifier with
{
Id = null,
Slot = slot,
};
}
else
{
identifier = identifier with
{
Id = identifier.Id.HasValue
? (PrimaryId)Math.Clamp(identifier.Id.Value.Id, 0,
slot.ToSpecificEnum() is BodySlot ? byte.MaxValue : ExpandedEqpGmpBase.Count - 1)
: null,
Slot = slot,
};
ret = true;
}
}
}
ImUtf8.HoverTooltip("Model Slot"u8);
return ret;
}
private static bool DrawGenderRaceConditionInput(ref AtrIdentifier identifier, float unscaledWidth = 250)
{
var ret = false;
ImGui.SetNextItemWidth(unscaledWidth * ImUtf8.GlobalScale);
using (var combo = ImUtf8.Combo("##shpGenderRace"u8,
identifier.GenderRaceCondition is GenderRace.Unknown
? "Any Gender & Race"
: $"{identifier.GenderRaceCondition.ToName()} ({identifier.GenderRaceCondition.ToRaceCode()})"))
{
if (combo)
{
if (ImUtf8.Selectable("Any Gender & Race"u8, identifier.GenderRaceCondition is GenderRace.Unknown)
&& identifier.GenderRaceCondition is not GenderRace.Unknown)
{
identifier = identifier with { GenderRaceCondition = GenderRace.Unknown };
ret = true;
}
foreach (var gr in ShapeAttributeHashSet.GenderRaceValues.Skip(1))
{
if (ImUtf8.Selectable($"{gr.ToName()} ({gr.ToRaceCode()})", identifier.GenderRaceCondition == gr)
&& identifier.GenderRaceCondition != gr)
{
identifier = identifier with { GenderRaceCondition = gr };
ret = true;
}
}
}
}
ImUtf8.HoverTooltip(
"Only activate this attribute for this gender & race code."u8);
return ret;
}
public static unsafe bool DrawAttributeKeyInput(ref AtrIdentifier identifier, ref ShapeAttributeString buffer, ref bool valid,
float unscaledWidth = 150)
{
var ret = false;
var ptr = Unsafe.AsPointer(ref buffer);
var span = new Span<byte>(ptr, ShapeAttributeString.MaxLength + 1);
using (ImStyleBorder.Frame.Push(Colors.RegexWarningBorder, Im.Style.GlobalScale, !valid))
{
ImGui.SetNextItemWidth(unscaledWidth * ImUtf8.GlobalScale);
if (ImUtf8.InputText("##atrAttribute"u8, span, out int newLength, "Attribute..."u8))
{
buffer.ForceLength((byte)newLength);
valid = buffer.ValidateCustomAttributeString();
if (valid)
identifier = identifier with { Attribute = buffer };
ret = true;
}
}
ImUtf8.HoverTooltip("Supported attribute need to have the format `atrx_*` and a maximum length of 30 characters."u8);
return ret;
}
}

View file

@ -1,165 +1,163 @@
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using Newtonsoft.Json.Linq;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.Api.Api;
using Penumbra.Meta;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta;
public interface IMetaDrawer
{
public ReadOnlySpan<byte> Label { get; }
public int NumColumns { get; }
public float ColumnHeight { get; }
public void Draw();
}
public abstract class MetaDrawer<TIdentifier, TEntry>(ModMetaEditor editor, MetaFileManager metaFiles) : IMetaDrawer
where TIdentifier : unmanaged, IMetaIdentifier
where TEntry : unmanaged
{
protected const uint FrameColor = 0;
protected readonly ModMetaEditor Editor = editor;
protected readonly MetaFileManager MetaFiles = metaFiles;
protected TIdentifier Identifier;
protected TEntry Entry;
private bool _initialized;
public void Draw()
{
if (!_initialized)
{
Initialize();
_initialized = true;
}
using var id = ImUtf8.PushId((int)Identifier.Type);
DrawNew();
var height = ColumnHeight;
var skips = ImGuiClip.GetNecessarySkipsAtPos(height, ImGui.GetCursorPosY(), Count);
if (skips < Count)
{
var remainder = ImGuiClip.ClippedTableDraw(Enumerate(), skips, DrawLine, Count);
if (remainder > 0)
ImGuiClip.DrawEndDummy(remainder, height);
}
void DrawLine((TIdentifier Identifier, TEntry Value) pair)
=> DrawEntry(pair.Identifier, pair.Value);
}
public abstract ReadOnlySpan<byte> Label { get; }
public abstract int NumColumns { get; }
public virtual float ColumnHeight
=> ImUtf8.FrameHeightSpacing;
protected abstract void DrawNew();
protected abstract void Initialize();
protected abstract void DrawEntry(TIdentifier identifier, TEntry entry);
protected abstract IEnumerable<(TIdentifier, TEntry)> Enumerate();
protected abstract int Count { get; }
/// <summary>
/// A number input for ids with an optional max id of given width.
/// Returns true if newId changed against currentId.
/// </summary>
protected static bool IdInput(ReadOnlySpan<byte> label, float unscaledWidth, ushort currentId, out ushort newId, int minId, int maxId,
bool border)
{
int tmp = currentId;
ImGui.SetNextItemWidth(unscaledWidth * ImUtf8.GlobalScale);
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, UiHelpers.Scale, border);
using var color = ImRaii.PushColor(ImGuiCol.Border, Colors.RegexWarningBorder, border);
if (ImUtf8.InputScalar(label, ref tmp))
tmp = Math.Clamp(tmp, minId, maxId);
newId = (ushort)tmp;
return newId != currentId;
}
/// <summary>
/// A dragging int input of given width that compares against a default value, shows a tooltip and clamps against min and max.
/// Returns true if newValue changed against currentValue.
/// </summary>
protected static bool DragInput<T>(ReadOnlySpan<byte> label, ReadOnlySpan<byte> tooltip, float width, T currentValue, T defaultValue,
out T newValue, T minValue, T maxValue, float speed, bool addDefault) where T : unmanaged, INumber<T>
{
newValue = currentValue;
using var color = ImRaii.PushColor(ImGuiCol.FrameBg,
defaultValue > currentValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value(),
defaultValue != currentValue);
ImGui.SetNextItemWidth(width);
if (ImUtf8.DragScalar(label, ref newValue, minValue, maxValue, speed))
newValue = newValue <= minValue ? minValue : newValue >= maxValue ? maxValue : newValue;
if (addDefault)
ImUtf8.HoverTooltip($"{tooltip}\nDefault Value: {defaultValue}");
else
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, tooltip);
return newValue != currentValue;
}
/// <summary>
/// A checkmark that compares against a default value and shows a tooltip.
/// Returns true if newValue is changed against currentValue.
/// </summary>
protected static bool Checkmark(ReadOnlySpan<byte> label, ReadOnlySpan<byte> tooltip, bool currentValue, bool defaultValue,
out bool newValue)
{
using var color = ImRaii.PushColor(ImGuiCol.FrameBg,
defaultValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value(),
defaultValue != currentValue);
newValue = currentValue;
ImUtf8.Checkbox(label, ref newValue);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, tooltip);
return newValue != currentValue;
}
/// <summary>
/// A checkmark that compares against a default value and shows a tooltip.
/// Returns true if newValue is changed against currentValue.
/// </summary>
protected static bool Checkmark(ReadOnlySpan<byte> label, ReadOnlySpan<char> tooltip, bool currentValue, bool defaultValue,
out bool newValue)
{
using var color = ImRaii.PushColor(ImGuiCol.FrameBg,
defaultValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value(),
defaultValue != currentValue);
newValue = currentValue;
ImUtf8.Checkbox(label, ref newValue);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, tooltip);
return newValue != currentValue;
}
protected void DrawMetaButtons(TIdentifier identifier, TEntry entry)
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy this manipulation to clipboard."u8, new Lazy<JToken?>(() => new JArray { MetaDictionary.Serialize(identifier, entry)! }));
ImGui.TableNextColumn();
if (ImUtf8.IconButton(FontAwesomeIcon.Trash, "Delete this meta manipulation."u8))
Editor.Changes |= Editor.Remove(identifier);
}
protected void CopyToClipboardButton(ReadOnlySpan<byte> tooltip, Lazy<JToken?> manipulations)
{
if (!ImUtf8.IconButton(FontAwesomeIcon.Clipboard, tooltip))
return;
var text = Functions.ToCompressedBase64(manipulations.Value, 0);
if (text.Length > 0)
ImGui.SetClipboardText(text);
}
}
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Newtonsoft.Json.Linq;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.Api.Api;
using Penumbra.Meta;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta;
public interface IMetaDrawer
{
public ReadOnlySpan<byte> Label { get; }
public int NumColumns { get; }
public float ColumnHeight { get; }
public void Draw();
}
public abstract class MetaDrawer<TIdentifier, TEntry>(ModMetaEditor editor, MetaFileManager metaFiles) : IMetaDrawer
where TIdentifier : unmanaged, IMetaIdentifier
where TEntry : unmanaged
{
protected const uint FrameColor = 0;
protected readonly ModMetaEditor Editor = editor;
protected readonly MetaFileManager MetaFiles = metaFiles;
protected TIdentifier Identifier;
protected TEntry Entry;
private bool _initialized;
public void Draw()
{
if (!_initialized)
{
Initialize();
_initialized = true;
}
using var id = ImUtf8.PushId((int)Identifier.Type);
DrawNew();
var height = ColumnHeight;
var skips = ImGuiClip.GetNecessarySkipsAtPos(height, ImGui.GetCursorPosY(), Count);
if (skips < Count)
{
var remainder = ImGuiClip.ClippedTableDraw(Enumerate(), skips, DrawLine, Count);
if (remainder > 0)
ImGuiClip.DrawEndDummy(remainder, height);
}
void DrawLine((TIdentifier Identifier, TEntry Value) pair)
=> DrawEntry(pair.Identifier, pair.Value);
}
public abstract ReadOnlySpan<byte> Label { get; }
public abstract int NumColumns { get; }
public virtual float ColumnHeight
=> ImUtf8.FrameHeightSpacing;
protected abstract void DrawNew();
protected abstract void Initialize();
protected abstract void DrawEntry(TIdentifier identifier, TEntry entry);
protected abstract IEnumerable<(TIdentifier, TEntry)> Enumerate();
protected abstract int Count { get; }
/// <summary>
/// A number input for ids with an optional max id of given width.
/// Returns true if newId changed against currentId.
/// </summary>
protected static bool IdInput(ReadOnlySpan<byte> label, float unscaledWidth, ushort currentId, out ushort newId, int minId, int maxId,
bool border)
{
int tmp = currentId;
ImGui.SetNextItemWidth(unscaledWidth * ImUtf8.GlobalScale);
using var style = ImStyleBorder.Frame.Push(Colors.RegexWarningBorder, Im.Style.GlobalScale, border);
if (ImUtf8.InputScalar(label, ref tmp))
tmp = Math.Clamp(tmp, minId, maxId);
newId = (ushort)tmp;
return newId != currentId;
}
/// <summary>
/// A dragging int input of given width that compares against a default value, shows a tooltip and clamps against min and max.
/// Returns true if newValue changed against currentValue.
/// </summary>
protected static bool DragInput<T>(ReadOnlySpan<byte> label, ReadOnlySpan<byte> tooltip, float width, T currentValue, T defaultValue,
out T newValue, T minValue, T maxValue, float speed, bool addDefault) where T : unmanaged, INumber<T>
{
newValue = currentValue;
var c = defaultValue > currentValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value();
using var color = ImGuiColor.FrameBackground.Push(c, defaultValue != currentValue);
ImGui.SetNextItemWidth(width);
if (ImUtf8.DragScalar(label, ref newValue, minValue, maxValue, speed))
newValue = newValue <= minValue ? minValue : newValue >= maxValue ? maxValue : newValue;
if (addDefault)
ImUtf8.HoverTooltip($"{tooltip}\nDefault Value: {defaultValue}");
else
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, tooltip);
return newValue != currentValue;
}
/// <summary>
/// A checkmark that compares against a default value and shows a tooltip.
/// Returns true if newValue is changed against currentValue.
/// </summary>
protected static bool Checkmark(ReadOnlySpan<byte> label, ReadOnlySpan<byte> tooltip, bool currentValue, bool defaultValue,
out bool newValue)
{
var c = defaultValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value();
using var color = ImGuiColor.FrameBackground.Push(c, defaultValue != currentValue);
newValue = currentValue;
ImUtf8.Checkbox(label, ref newValue);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, tooltip);
return newValue != currentValue;
}
/// <summary>
/// A checkmark that compares against a default value and shows a tooltip.
/// Returns true if newValue is changed against currentValue.
/// </summary>
protected static bool Checkmark(ReadOnlySpan<byte> label, ReadOnlySpan<char> tooltip, bool currentValue, bool defaultValue,
out bool newValue)
{
var c = defaultValue != currentValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value();
using var color = ImGuiColor.FrameBackground.Push(c, defaultValue != currentValue);
newValue = currentValue;
ImUtf8.Checkbox(label, ref newValue);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, tooltip);
return newValue != currentValue;
}
protected void DrawMetaButtons(TIdentifier identifier, TEntry entry)
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy this manipulation to clipboard."u8,
new Lazy<JToken?>(() => new JArray { MetaDictionary.Serialize(identifier, entry)! }));
ImGui.TableNextColumn();
if (ImUtf8.IconButton(FontAwesomeIcon.Trash, "Delete this meta manipulation."u8))
Editor.Changes |= Editor.Remove(identifier);
}
protected void CopyToClipboardButton(ReadOnlySpan<byte> tooltip, Lazy<JToken?> manipulations)
{
if (!ImUtf8.IconButton(FontAwesomeIcon.Clipboard, tooltip))
return;
var text = Functions.ToCompressedBase64(manipulations.Value, 0);
if (text.Length > 0)
ImGui.SetClipboardText(text);
}
}

View file

@ -1,5 +1,6 @@
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Newtonsoft.Json.Linq;
using OtterGui.Raii;
using OtterGui.Text;
@ -161,8 +162,8 @@ public sealed class ShpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
if (all)
{
using var style = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, new Vector2(0.05f, 0.5f));
ImUtf8.TextFramed("All IDs"u8, ImGui.GetColorU32(ImGuiCol.FrameBg, all || allSlots ? ImGui.GetStyle().DisabledAlpha : 1f),
new Vector2(unscaledWidth, 0), ImGui.GetColorU32(ImGuiCol.TextDisabled));
ImUtf8.TextFramed("All IDs"u8, ImGuiColor.FrameBackground.Get(all || allSlots ? ImGui.GetStyle().DisabledAlpha : 1f).Color,
new Vector2(unscaledWidth, 0), ImGuiColor.TextDisabled.Get().Color);
}
else
{
@ -232,7 +233,7 @@ public sealed class ShpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
var ret = false;
var ptr = Unsafe.AsPointer(ref buffer);
var span = new Span<byte>(ptr, ShapeAttributeString.MaxLength + 1);
using (new ImRaii.ColorStyle().Push(ImGuiCol.Border, Colors.RegexWarningBorder, !valid).Push(ImGuiStyleVar.FrameBorderSize, 1f, !valid))
using (ImStyleBorder.Frame.Push(Colors.RegexWarningBorder, Im.Style.GlobalScale, !valid))
{
ImGui.SetNextItemWidth(unscaledWidth * ImUtf8.GlobalScale);
if (ImUtf8.InputText("##shpShape"u8, span, out int newLength, "Shape Key..."u8))

View file

@ -53,7 +53,7 @@ public partial class ModEditWindow
continue;
using var id = ImUtf8.PushId(index);
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled), deformer.RacialDeformer.IsEmpty);
using var color = ImGuiColor.Text.Push(Im.Style[ImGuiColor.TextDisabled], deformer.RacialDeformer.IsEmpty);
if (ImUtf8.Selectable(name, deformer.GenderRace == _pbdData.SelectedRaceCode))
{
_pbdData.SelectedRaceCode = deformer.GenderRace;
@ -61,7 +61,7 @@ public partial class ModEditWindow
}
Im.Line.Same();
color.Push(ImGuiCol.Text, metaColor);
color.Push(ImGuiColor.Text, metaColor);
ImUtf8.TextRightAligned(raceCode);
}
}
@ -142,7 +142,7 @@ public partial class ModEditWindow
var ret = false;
ImUtf8.TextFrameAligned("Copy the values of the bone "u8);
ImGui.SameLine(0, 0);
using (ImRaii.PushColor(ImGuiCol.Text, ColorId.NewMod.Value()))
using (ImGuiColor.Text.Push(ColorId.NewMod.Value()))
{
ImUtf8.TextFrameAligned(_pbdData.SelectedBone);
}

View file

@ -182,7 +182,7 @@ public partial class ModEditWindow
var selected = _selectedFiles.Contains(registry);
var color = registry.SubModUsage.Count == 0 ? ColorId.ConflictingMod :
registry.CurrentUsage == registry.SubModUsage.Count ? ColorId.NewMod : ColorId.InheritedMod;
using (ImRaii.PushColor(ImGuiCol.Text, color.Value()))
using (ImGuiColor.Text.Push(color.Value()))
{
if (Im.Selectable(registry.RelPath.Path.Span, selected))
{
@ -290,9 +290,9 @@ public partial class ModEditWindow
{
Im.Line.Same();
ImGui.SetCursorPosX(pos);
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGuiUtil.TextColored(0xFF00B0B0, FontAwesomeIcon.ExclamationCircle.ToIconString());
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGuiUtil.TextColored(0xFF00B0B0, FontAwesomeIcon.ExclamationCircle.ToIconString());
}
ImUtf8.HoverTooltip("The game path and the file do not have the same extension."u8);
@ -333,9 +333,9 @@ public partial class ModEditWindow
{
Im.Line.Same();
ImGui.SetCursorPosX(pos);
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGuiUtil.TextColored(0xFF00B0B0, FontAwesomeIcon.ExclamationCircle.ToIconString());
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGuiUtil.TextColored(0xFF00B0B0, FontAwesomeIcon.ExclamationCircle.ToIconString());
}
ImUtf8.HoverTooltip("The game path and the file do not have the same extension."u8);

View file

@ -143,7 +143,7 @@ public partial class ModEditWindow
var text = $"{otherOptionData.TotalCount} Edits in other Options";
var size = ImGui.CalcTextSize(text).X;
ImGui.SetCursorPos(new Vector2(ImGui.GetContentRegionAvail().X - size, oldPos + ImGui.GetStyle().FramePadding.Y));
ImGuiUtil.TextColored(ColorId.RedundantAssignment.Value() | 0xFF000000, text);
Im.Text(text, ColorId.RedundantAssignment.Value().FullAlpha());
if (ImGui.IsItemHovered())
{
using var tt = ImUtf8.Tooltip();

View file

@ -275,9 +275,9 @@ public partial class ModEditWindow
if (tab.GamePaths!.Count == 1)
{
using var style = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, new Vector2(0, 0.5f));
using var color = ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.FrameBg))
.Push(ImGuiCol.ButtonHovered, ImGui.GetColorU32(ImGuiCol.FrameBgHovered))
.Push(ImGuiCol.ButtonActive, ImGui.GetColorU32(ImGuiCol.FrameBgActive));
using var color = ImGuiColor.Button.Push(Im.Style[ImGuiColor.FrameBackground])
.Push(ImGuiColor.ButtonHovered, Im.Style[ImGuiColor.FrameBackgroundHovered])
.Push(ImGuiColor.ButtonActive, Im.Style[ImGuiColor.FrameBackgroundActive]);
using var group = ImRaii.Group();
ImGui.Button(preview, new Vector2(buttonWidth, 0));
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
@ -310,7 +310,7 @@ public partial class ModEditWindow
var width = ImGui.CalcTextSize(text).X + framePadding.X * 2;
// Draw the link button. We set the background colour to transparent to mimic the look of a link.
using var color = ImRaii.PushColor(ImGuiCol.Button, 0x00000000);
using var color = ImGuiColor.Button.Push(Vector4.Zero);
SupportButton.Link(Penumbra.Messager, text, address, width, ""u8);
// Draw an underline for the text.

View file

@ -368,7 +368,7 @@ public partial class ModEditWindow
var ret = ImUtf8.CollapsingHeader(label);
ImGui.GetWindowDrawList()
.AddText(UiBuilder.DefaultFont, UiBuilder.DefaultFont.FontSize, pos, ImGui.GetColorU32(ImGuiCol.Text), "Layout");
.AddText(UiBuilder.DefaultFont, UiBuilder.DefaultFont.FontSize, pos, ImGuiColor.Text.Get().Color, "Layout");
return ret;
}
@ -403,7 +403,7 @@ public partial class ModEditWindow
ImGui.TableSetupColumn("w", ImGuiTableColumnFlags.WidthFixed, 250 * UiHelpers.Scale);
ImGui.TableHeadersRow();
var textColorStart = ImGui.GetColorU32(ImGuiCol.Text);
var textColorStart = ImGuiColor.Text.Get().Color;
var ret = false;
for (var i = 0; i < tab.Matrix.GetLength(0); ++i)
@ -424,7 +424,7 @@ public partial class ModEditWindow
var deletable = !disabled && idx >= 0;
using (ImRaii.PushFont(UiBuilder.MonoFont, tooltip.Length > 0))
{
using (ImRaii.PushColor(ImGuiCol.Text, color))
using (ImGuiColor.Text.Push(color))
{
ImGui.TableNextColumn();
ImUtf8.Selectable(name);

View file

@ -49,7 +49,7 @@ public partial class ModEditWindow
return;
using var id = ImRaii.PushId(label);
ImGuiUtil.DrawTextButton(label, new Vector2(-1, 0), ImGui.GetColorU32(ImGuiCol.FrameBg));
ImEx.TextFramed(label, new Vector2(-1, 0), ImGuiColor.FrameBackground.Get());
ImGui.NewLine();
using (ImRaii.Disabled(!_center.SaveTask.IsCompleted))
@ -206,7 +206,7 @@ public partial class ModEditWindow
case TaskStatus.Faulted:
{
ImGui.TextUnformatted("Could not save file:");
using var color = ImRaii.PushColor(ImGuiCol.Text, 0xFF0000FF);
using var color = ImGuiColor.Text.Push(new Vector4(1, 0, 0, 1));
ImGuiUtil.TextWrapped(_center.SaveTask.Exception?.ToString() ?? "Unknown Error");
break;
}

View file

@ -205,7 +205,7 @@ public partial class ModEditWindow : Window, IDisposable, Luna.IUiService
var offsetX = ImGui.GetContentRegionAvail().X / 2 - radius;
var offsetY = ImGui.GetContentRegionAvail().Y / 2 - radius;
ImGui.SetCursorPos(ImGui.GetCursorPos() + new Vector2(offsetX, offsetY));
ImUtf8.Spinner("##spinner"u8, radius, thickness, ImGui.GetColorU32(ImGuiCol.Text));
ImEx.Spinner("##spinner"u8, radius, thickness, ImGuiColor.Text.Get());
return;
}
@ -501,7 +501,7 @@ public partial class ModEditWindow : Window, IDisposable, Luna.IUiService
{
Im.Line.Same();
ImGuiUtil.DrawTextButton($"There are {otherSwaps} file swaps configured in other options.", Vector2.Zero,
ColorId.RedundantAssignment.Value());
ColorId.RedundantAssignment.Value().Color);
}
using var child = ImRaii.Child("##swaps", -Vector2.One, true);

View file

@ -82,7 +82,7 @@ public class ModMergeTab(ModMerger modMerger) : Luna.IUiService
var color = group != null || modMerger.OptionGroupName.Length == 0 && modMerger.OptionName.Length == 0
? Colors.PressEnterWarningBg
: Colors.DiscordColor;
using var c = ImRaii.PushColor(ImGuiCol.Border, color);
using var c = ImGuiColor.Border.Push(color);
ImGui.SetNextItemWidth(buttonWidth);
ImGui.InputTextWithHint("##optionGroupInput", "Target Option Group", ref modMerger.OptionGroupName, 64);
ImGuiUtil.HoverTooltip(
@ -96,7 +96,7 @@ public class ModMergeTab(ModMerger modMerger) : Luna.IUiService
: group == null || group.Options.Any(o => o.Name == modMerger.OptionName)
? Colors.PressEnterWarningBg
: Colors.DiscordColor;
c.Push(ImGuiCol.Border, color);
c.Push(ImGuiColor.Border, color);
ImGui.SetNextItemWidth(buttonWidth);
ImGui.InputTextWithHint("##optionInput", "Target Option Name", ref modMerger.OptionName, 64);
ImGuiUtil.HoverTooltip(
@ -249,7 +249,7 @@ public class ModMergeTab(ModMerger modMerger) : Luna.IUiService
ImGui.Separator();
ImGui.Dummy(Vector2.One);
using var color = ImRaii.PushColor(ImGuiCol.Text, Colors.TutorialBorder);
using var color = ImGuiColor.Text.Push(Colors.TutorialBorder);
foreach (var warning in modMerger.Warnings.SkipLast(1))
{
ImGuiUtil.TextWrapped(warning);
@ -266,7 +266,7 @@ public class ModMergeTab(ModMerger modMerger) : Luna.IUiService
ImGui.Separator();
ImGui.Dummy(Vector2.One);
using var color = ImRaii.PushColor(ImGuiCol.Text, Colors.RegexWarningBorder);
using var color = ImGuiColor.Text.Push(Colors.RegexWarningBorder);
ImGuiUtil.TextWrapped(modMerger.Error.ToString());
}
}

View file

@ -1,9 +1,10 @@
using Dalamud.Bindings.ImGui;
using OtterGui.Raii;
using ImSharp;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
using MouseWheelType = OtterGui.Widgets.MouseWheelType;
namespace Penumbra.UI.AdvancedWindow;
@ -11,12 +12,12 @@ public sealed class OptionSelectCombo(ModEditor editor, ModEditWindow window)
: FilterComboCache<(string FullName, (int Group, int Data) Index)>(
() => window.Mod!.AllDataContainers.Select(c => (c.GetFullName(), c.GetDataIndices())).ToList(), MouseWheelType.Control, Penumbra.Log)
{
private ImRaii.ColorStyle _border;
private readonly Im.ColorStyleDisposable _border = new();
protected override void DrawCombo(string label, string preview, string tooltip, int currentSelected, float previewWidth, float itemHeight,
ImGuiComboFlags flags)
{
_border = ImRaii.PushFrameBorder(ImUtf8.GlobalScale, ColorId.FolderLine.Value());
_border.PushBorder(ImStyleBorder.Frame, ColorId.FolderLine.Value());
base.DrawCombo(label, preview, tooltip, currentSelected, previewWidth, itemHeight, flags);
_border.Dispose();
}

View file

@ -70,7 +70,7 @@ public class ResourceTreeViewer(
else if (_task.Exception != null)
{
ImGui.NewLine();
using var color = ImRaii.PushColor(ImGuiCol.Text, Colors.RegexWarningBorder);
using var color = ImGuiColor.Text.Push(Colors.RegexWarningBorder);
ImGui.TextUnformatted($"Error during calculation of character list:\n\n{_task.Exception}");
}
else if (_task.IsCompletedSuccessfully)
@ -82,7 +82,7 @@ public class ResourceTreeViewer(
if (!_categoryFilter.HasFlag(category) || !tree.Name.Contains(_nameFilter, StringComparison.OrdinalIgnoreCase))
continue;
using (var c = ImRaii.PushColor(ImGuiCol.Text, CategoryColor(category).Value()))
using (ImGuiColor.Text.Push(CategoryColor(category).Value()))
{
var isOpen = ImGui.CollapsingHeader($"{(incognito.IncognitoMode ? tree.AnonymizedName : tree.Name)}###{index}",
index == 0 ? ImGuiTreeNodeFlags.DefaultOpen : 0);
@ -164,7 +164,7 @@ public class ResourceTreeViewer(
if (!gameData.HasModifiedGameDataFiles)
return;
using var style = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudOrange);
using var style = ImGuiColor.Text.Push(ImGuiColors.DalamudOrange);
ImUtf8.TextWrapped(
"Dalamud is reporting your FFXIV installation has modified game files. Any mods installed through TexTools will produce this message."u8);
@ -189,18 +189,18 @@ public class ResourceTreeViewer(
var checkPadding = 10 * ImGuiHelpers.GlobalScale + ImGui.GetStyle().ItemSpacing.X;
ImGui.SameLine(0, checkPadding);
using (var id = ImRaii.PushId("TreeCategoryFilter"))
{
var categoryFilter = (uint)_categoryFilter;
foreach (var category in Enum.GetValues<TreeCategory>())
{
using var c = ImRaii.PushColor(ImGuiCol.CheckMark, CategoryColor(category).Value());
ImGui.CheckboxFlags($"##{category}", ref categoryFilter, (uint)category);
ImGuiUtil.HoverTooltip(CategoryFilterDescription(category));
ImGui.SameLine(0.0f, checkSpacing);
}
_categoryFilter = (TreeCategory)categoryFilter;
using (ImRaii.PushId("TreeCategoryFilter"))
{
var categoryFilter = (uint)_categoryFilter;
foreach (var category in Enum.GetValues<TreeCategory>())
{
using var c = ImGuiColor.CheckMark.Push(CategoryColor(category).Value());
ImGui.CheckboxFlags($"##{category}", ref categoryFilter, (uint)category);
ImGuiUtil.HoverTooltip(CategoryFilterDescription(category));
ImGui.SameLine(0.0f, checkSpacing);
}
_categoryFilter = (TreeCategory)categoryFilter;
}
ImGui.SameLine(0, checkPadding);
@ -257,7 +257,7 @@ public class ResourceTreeViewer(
if (visibility == NodeVisibility.Hidden)
continue;
using var mutedColor = ImRaii.PushColor(ImGuiCol.Text, ImGuiUtil.HalfTransparentText(), resourceNode.Internal);
using var mutedColor = ImGuiColor.Text.Push(ImGuiUtil.HalfTransparentText(), resourceNode.Internal);
var filterIcon = resourceNode.IconFlag != 0 ? resourceNode.IconFlag : parentFilterIconFlag;
@ -335,7 +335,7 @@ public class ResourceTreeViewer(
var modName = $"[{(hasMod ? mod!.Name : resourceNode.ModName)}]";
var textPos = ImGui.GetCursorPosX() + ImUtf8.CalcTextSize(modName).X + ImGui.GetStyle().ItemInnerSpacing.X;
using var group = ImUtf8.Group();
using (var color = ImRaii.PushColor(ImGuiCol.Text, (hasMod ? ColorId.NewMod : ColorId.DisabledMod).Value()))
using (ImGuiColor.Text.Push((hasMod ? ColorId.NewMod : ColorId.DisabledMod).Value()))
{
ImUtf8.Selectable(modName, false, ImGuiSelectableFlags.AllowItemOverlap,
new Vector2(ImGui.GetContentRegionAvail().X, frameHeight));

View file

@ -144,7 +144,7 @@ public class ChangedItemDrawer : IDisposable, IUiService
Im.Line.Same();
using var color = ImGuiColor.Text.Push(ColorId.ItemId.Value());
Im.Cursor.Y += height - Im.Style.TextHeight / 2;
Im.Cursor.Y += (height - Im.Style.TextHeight) / 2;
ImEx.TextRightAligned(additionalData, Im.Style.ItemInnerSpacing.X);
}
@ -156,7 +156,7 @@ public class ChangedItemDrawer : IDisposable, IUiService
Im.Line.Same();
using var color = ImGuiColor.Text.Push(ColorId.ItemId.Value());
Im.Cursor.Y += height - Im.Style.TextHeight / 2;
Im.Cursor.Y += (height - Im.Style.TextHeight) / 2;
ImEx.TextRightAligned(text, Im.Style.ItemInnerSpacing.X);
}

View file

@ -136,7 +136,7 @@ public class MigrationSectionDrawer(MigrationManager migrationManager, Configura
return;
Im.Line.Same();
ImUtf8.Spinner("Spinner"u8, ImGui.GetTextLineHeight() / 2, 2, ImGui.GetColorU32(ImGuiCol.Text));
ImEx.Spinner("Spinner"u8, ImGui.GetTextLineHeight() / 2, 2, ImGuiColor.Text.Get());
}
private void DrawCancelButton(MigrationManager.TaskType task, ReadOnlySpan<byte> tooltip)

View file

@ -283,7 +283,7 @@ public sealed class CollectionPanel(
if (!context)
return;
using (var color = ImRaii.PushColor(ImGuiCol.Text, Colors.DiscordColor))
using (var color = ImGuiColor.Text.Push(Colors.DiscordColor))
{
if (ImGui.MenuItem("Use no mods."))
_active.SetCollection(ModCollection.Empty, type, _active.Individuals.GetGroup(identifier));
@ -291,7 +291,7 @@ public sealed class CollectionPanel(
if (collection != null && type.CanBeRemoved())
{
using var color = ImRaii.PushColor(ImGuiCol.Text, Colors.RegexWarningBorder);
using var color = ImGuiColor.Text.Push(Colors.RegexWarningBorder);
if (ImGui.MenuItem("Remove this assignment."))
_active.SetCollection(null, type, _active.Individuals.GetGroup(identifier));
}
@ -303,15 +303,15 @@ public sealed class CollectionPanel(
}
}
private bool DrawButton(string text, CollectionType type, Vector2 width, uint borderColor, ActorIdentifier id, char suffix,
private bool DrawButton(string text, CollectionType type, Vector2 width, Rgba32 borderColor, ActorIdentifier id, char suffix,
ModCollection? collection = null)
{
using var group = ImRaii.Group();
var invalid = type == CollectionType.Individual && !id.IsValid;
var redundancy = _active.RedundancyCheck(type, id);
collection ??= _active.ByType(type, id);
using var color = ImRaii.PushColor(ImGuiCol.Button,
collection == null
using var color = ImGuiColor.Button.Push(
collection is null
? ColorId.NoAssignment.Value()
: redundancy.Length > 0
? ColorId.RedundantAssignment.Value()
@ -319,8 +319,8 @@ public sealed class CollectionPanel(
? ColorId.SelectedCollection.Value()
: collection == ModCollection.Empty
? ColorId.NoModsAssignment.Value()
: ImGui.GetColorU32(ImGuiCol.Button), !invalid)
.Push(ImGuiCol.Border, borderColor == 0 ? ImGui.GetColorU32(ImGuiCol.TextDisabled) : borderColor);
: ImGuiColor.Button.Get(), !invalid)
.Push(ImGuiColor.Border, borderColor == 0 ? ImGuiColor.TextDisabled.Get().Color : borderColor);
using var disabled = ImRaii.Disabled(invalid);
var button = ImGui.Button(text, width) || ImGui.IsItemClicked(ImGuiMouseButton.Right);
var hovered = redundancy.Length > 0 && ImGui.IsItemHovered();
@ -332,7 +332,7 @@ public sealed class CollectionPanel(
var name = Name(collection);
var size = ImGui.CalcTextSize(name);
var textPos = ImGui.GetItemRectMax() - size - ImGui.GetStyle().FramePadding;
ImGui.GetWindowDrawList().AddText(textPos, ImGui.GetColorU32(ImGuiCol.Text), name);
ImGui.GetWindowDrawList().AddText(textPos, ImGuiColor.Text.Get().Color, name);
DrawContext(button, collection, type, id, text, suffix);
}
@ -484,7 +484,7 @@ public sealed class CollectionPanel(
private void DrawCollectionName(ModCollection collection)
{
ImGui.Dummy(Vector2.One);
using var color = ImRaii.PushColor(ImGuiCol.Border, Colors.MetaInfoText);
using var color = ImGuiColor.Border.Push(Colors.MetaInfoText);
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2 * UiHelpers.Scale);
using var f = _nameFont.Push();
var name = Name(collection);
@ -574,7 +574,7 @@ public sealed class CollectionPanel(
using var f = _nameFont.Push();
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale);
using var color = ImRaii.PushColor(ImGuiCol.Border, Colors.MetaInfoText);
using var color = ImGuiColor.Border.Push(Colors.MetaInfoText);
ImGuiUtil.DrawTextButton(Name(collection.Inheritance.DirectlyInheritedBy[0]), Vector2.Zero, 0);
var constOffset = (ImGui.GetStyle().FramePadding.X + ImGuiHelpers.GlobalScale) * 2
+ ImGui.GetStyle().ItemSpacing.X

View file

@ -1,4 +1,5 @@
using Dalamud.Bindings.ImGui;
using ImSharp;
using OtterGui;
using OtterGui.Raii;
using Penumbra.Collections;
@ -75,7 +76,7 @@ public sealed class CollectionSelector : ItemSelector<ModCollection>, IDisposabl
protected override bool OnDraw(int idx)
{
using var color = ImRaii.PushColor(ImGuiCol.Header, ColorId.SelectedCollection.Value());
using var color = ImGuiColor.Header.Push(ColorId.SelectedCollection.Value());
var ret = ImGui.Selectable(Name(Items[idx]), idx == CurrentIdx);
using var source = ImRaii.DragDropSource();

View file

@ -24,7 +24,7 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
{
using var id = ImRaii.PushId("##Inheritance");
ImGuiUtil.DrawColoredText(($"The {TutorialService.SelectedCollection} ", 0),
(Name(_active.Current), ColorId.SelectedCollection.Value() | 0xFF000000), (" inherits from:", 0));
(Name(_active.Current), ColorId.SelectedCollection.Value().FullAlpha().Color), (" inherits from:", 0));
ImGui.Dummy(Vector2.One);
DrawCurrentCollectionInheritance();
@ -111,7 +111,7 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
foreach (var inheritance in collection.Inheritance.FlatHierarchy.Skip(1))
{
// Draw the child, already seen collections are colored as conflicts.
using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.HandledConflictMod.Value(),
using var color = ImGuiColor.Text.Push(ColorId.HandledConflictMod.Value(),
_seenInheritedCollections.Contains(inheritance));
_seenInheritedCollections.Add(inheritance);
@ -138,7 +138,7 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
/// <summary> Draw a single primary inherited collection. </summary>
private void DrawInheritance(ModCollection collection)
{
using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.HandledConflictMod.Value(),
using var color = ImGuiColor.Text.Push(ColorId.HandledConflictMod.Value(),
_seenInheritedCollections.Contains(collection));
_seenInheritedCollections.Add(collection);
using var tree = ImRaii.TreeNode($"{Name(collection)}###{collection.Identity.Name}", ImGuiTreeNodeFlags.NoTreePushOnOpen);
@ -172,10 +172,10 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
private void DrawInheritanceTrashButton()
{
var size = UiHelpers.IconButtonSize with { Y = ImGui.GetTextLineHeightWithSpacing() * InheritedCollectionHeight };
var buttonColor = ImGui.GetColorU32(ImGuiCol.Button);
var buttonColor = Im.Style[ImGuiColor.Button];
// Prevent hovering from highlighting the button.
using var color = ImRaii.PushColor(ImGuiCol.ButtonActive, buttonColor)
.Push(ImGuiCol.ButtonHovered, buttonColor);
using var color = ImGuiColor.ButtonActive.Push(buttonColor)
.Push(ImGuiColor.ButtonHovered, buttonColor);
ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), size,
"Drag primary inheritance here to remove it from the list.", false, true);

View file

@ -140,7 +140,7 @@ public sealed class ConfigWindow : Window, Luna.IUiService
private void DrawProblemWindow(string text)
{
using var color = ImRaii.PushColor(ImGuiCol.Text, Colors.RegexWarningBorder);
using var color = ImGuiColor.Text.Push(Colors.RegexWarningBorder);
ImGui.NewLine();
ImGui.NewLine();
ImUtf8.TextWrapped(text);

View file

@ -23,7 +23,7 @@ public readonly struct ImcModGroupEditDrawer(ModGroupEditDrawer editor, ImcModGr
var changes = false;
var width = editor.AvailableWidth.X - 3 * ImUtf8.ItemInnerSpacing.X - ImUtf8.ItemSpacing.X - ImUtf8.CalcTextSize("All Variants"u8).X - ImUtf8.CalcTextSize("Only Attributes"u8).X - 2 * ImUtf8.FrameHeight;
ImUtf8.TextFramed(identifier.ToString(), 0, new Vector2(width, 0), borderColor: ImGui.GetColorU32(ImGuiCol.Border));
ImEx.TextFramed(identifier.ToString(), new Vector2(width, 0), Rgba32.Transparent);
ImUtf8.SameLineInner();
var allVariants = group.AllVariants;
@ -175,7 +175,7 @@ public readonly struct ImcModGroupEditDrawer(ModGroupEditDrawer editor, ImcModGr
protected override void RenderSymbol(bool value, Vector2 position, float size)
{
if (value)
SymbolHelpers.RenderCross(ImGui.GetWindowDrawList(), position, ImGui.GetColorU32(ImGuiCol.CheckMark), size);
SymbolHelpers.RenderCross(ImGui.GetWindowDrawList(), position, ImGuiColor.CheckMark.Get().Color, size);
}
protected override bool NextValue(bool value)

View file

@ -836,7 +836,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
ImGui.SetCursorPos(comboPos);
// Draw combo button
using var color = ImRaii.PushColor(ImGuiCol.Button, Colors.FilterActive, !everything);
using var color = ImGuiColor.Button.Push(Colors.FilterActive, !everything);
var rightClick = DrawFilterCombo(ref everything);
_tutorial.OpenTutorial(BasicTutorialSteps.ModFilters);
if (rightClick)

View file

@ -1,347 +1,356 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using OtterGui;
using OtterGui.Services;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Mods;
using Penumbra.Mods.Manager;
using Penumbra.String;
using Penumbra.UI.Classes;
namespace Penumbra.UI.ModsTab;
public class ModPanelChangedItemsTab(
ModFileSystemSelector selector,
ChangedItemDrawer drawer,
ImGuiCacheService cacheService,
Configuration config,
ModDataEditor dataEditor)
: ITab, Luna.IUiService
{
private readonly ImGuiCacheService.CacheId _cacheId = cacheService.GetNewId();
private class ChangedItemsCache
{
private Mod? _lastSelected;
private ushort _lastUpdate;
private ChangedItemIconFlag _filter = ChangedItemFlagExtensions.DefaultFlags;
private ChangedItemMode _lastMode;
private bool _reset;
public readonly List<Container> Data = [];
public bool AnyExpandable { get; private set; }
public record struct Container
{
public IIdentifiedObjectData Data;
public ByteString Text;
public ByteString ModelData;
public uint Id;
public int Children;
public ChangedItemIconFlag Icon;
public bool Expandable;
public bool Expanded;
public bool Child;
public static Container Single(string text, IIdentifiedObjectData data)
=> new()
{
Child = false,
Text = ByteString.FromStringUnsafe(data.ToName(text), false),
ModelData = ByteString.FromStringUnsafe(data.AdditionalData, false),
Icon = data.GetIcon().ToFlag(),
Expandable = false,
Expanded = false,
Data = data,
Id = 0,
Children = 0,
};
public static Container Parent(string text, IIdentifiedObjectData data, uint id, int children, bool expanded)
=> new()
{
Child = false,
Text = ByteString.FromStringUnsafe(data.ToName(text), false),
ModelData = ByteString.FromStringUnsafe(data.AdditionalData, false),
Icon = data.GetIcon().ToFlag(),
Expandable = true,
Expanded = expanded,
Data = data,
Id = id,
Children = children,
};
public static Container Indent(string text, IIdentifiedObjectData data)
=> new()
{
Child = true,
Text = ByteString.FromStringUnsafe(data.ToName(text), false),
ModelData = ByteString.FromStringUnsafe(data.AdditionalData, false),
Icon = data.GetIcon().ToFlag(),
Expandable = false,
Expanded = false,
Data = data,
Id = 0,
Children = 0,
};
}
public void Reset()
=> _reset = true;
public void Update(Mod? mod, ChangedItemDrawer drawer, ChangedItemIconFlag filter, ChangedItemMode mode)
{
if (mod == _lastSelected
&& _lastSelected!.LastChangedItemsUpdate == _lastUpdate
&& _filter == filter
&& !_reset
&& _lastMode == mode)
return;
_reset = false;
Data.Clear();
AnyExpandable = false;
_lastSelected = mod;
_filter = filter;
_lastMode = mode;
if (_lastSelected == null)
return;
_lastUpdate = _lastSelected.LastChangedItemsUpdate;
if (mode is ChangedItemMode.Alphabetical)
{
foreach (var (s, i) in _lastSelected.ChangedItems)
{
if (drawer.FilterChangedItem(s, i, string.Empty))
Data.Add(Container.Single(s, i));
}
return;
}
var tmp = new Dictionary<(PrimaryId, FullEquipType), List<IdentifiedItem>>();
var defaultExpansion = _lastMode is ChangedItemMode.GroupedExpanded;
foreach (var (s, i) in _lastSelected.ChangedItems)
{
if (i is not IdentifiedItem item)
continue;
if (!drawer.FilterChangedItem(s, item, string.Empty))
continue;
if (tmp.TryGetValue((item.Item.PrimaryId, item.Item.Type), out var p))
p.Add(item);
else
tmp[(item.Item.PrimaryId, item.Item.Type)] = [item];
}
foreach (var list in tmp.Values)
{
list.Sort((i1, i2) =>
{
// reversed
var preferred = _lastSelected.PreferredChangedItems.Contains(i2.Item.Id)
.CompareTo(_lastSelected.PreferredChangedItems.Contains(i1.Item.Id));
if (preferred != 0)
return preferred;
// reversed
var count = i2.Count.CompareTo(i1.Count);
if (count != 0)
return count;
return string.Compare(i1.Item.Name, i2.Item.Name, StringComparison.Ordinal);
});
}
var sortedTmp = tmp.Values.OrderBy(s => s[0].Item.Name).ToArray();
var sortedTmpIdx = 0;
foreach (var (s, i) in _lastSelected.ChangedItems)
{
if (i is IdentifiedItem)
continue;
if (!drawer.FilterChangedItem(s, i, string.Empty))
continue;
while (sortedTmpIdx < sortedTmp.Length
&& string.Compare(sortedTmp[sortedTmpIdx][0].Item.Name, s, StringComparison.Ordinal) <= 0)
AddList(sortedTmp[sortedTmpIdx++]);
Data.Add(Container.Single(s, i));
}
for (; sortedTmpIdx < sortedTmp.Length; ++sortedTmpIdx)
AddList(sortedTmp[sortedTmpIdx]);
return;
void AddList(List<IdentifiedItem> list)
{
var mainItem = list[0];
if (list.Count == 1)
{
Data.Add(Container.Single(mainItem.Item.Name, mainItem));
}
else
{
var id = ImUtf8.GetId($"{mainItem.Item.PrimaryId}{(int)mainItem.Item.Type}");
var expanded = ImGui.GetStateStorage().GetBool(id, defaultExpansion);
Data.Add(Container.Parent(mainItem.Item.Name, mainItem, id, list.Count - 1, expanded));
AnyExpandable = true;
if (!expanded)
return;
foreach (var item in list.Skip(1))
Data.Add(Container.Indent(item.Item.Name, item));
}
}
}
}
public ReadOnlySpan<byte> Label
=> "Changed Items"u8;
public bool IsVisible
=> selector.Selected!.ChangedItems.Count > 0;
private ImGuiStoragePtr _stateStorage;
private Vector2 _buttonSize;
private uint _starColor;
public void DrawContent()
{
if (cacheService.Cache(_cacheId, () => (new ChangedItemsCache(), "ModPanelChangedItemsCache")) is not { } cache)
return;
drawer.DrawTypeFilter();
_stateStorage = ImGui.GetStateStorage();
cache.Update(selector.Selected, drawer, config.Ephemeral.ChangedItemFilter, config.ChangedItemDisplay);
ImGui.Separator();
_buttonSize = new Vector2(ImGui.GetStyle().ItemSpacing.Y + ImGui.GetFrameHeight());
using var style = ImRaii.PushStyle(ImGuiStyleVar.CellPadding, Vector2.Zero)
.Push(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FramePadding, Vector2.Zero)
.Push(ImGuiStyleVar.SelectableTextAlign, new Vector2(0.01f, 0.5f));
using var color = ImRaii.PushColor(ImGuiCol.Button, 0);
using var table = ImUtf8.Table("##changedItems"u8, cache.AnyExpandable ? 2 : 1, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY,
new Vector2(ImGui.GetContentRegionAvail().X, -1));
if (!table)
return;
_starColor = ColorId.ChangedItemPreferenceStar.Value();
if (cache.AnyExpandable)
{
ImUtf8.TableSetupColumn("##exp"u8, ImGuiTableColumnFlags.WidthFixed, _buttonSize.Y);
ImUtf8.TableSetupColumn("##text"u8, ImGuiTableColumnFlags.WidthStretch);
ImGuiClip.ClippedDraw(cache.Data, DrawContainerExpandable, _buttonSize.Y);
}
else
{
ImGuiClip.ClippedDraw(cache.Data, DrawContainer, _buttonSize.Y);
}
}
private void DrawContainerExpandable(ChangedItemsCache.Container obj, int idx)
{
using var id = ImUtf8.PushId(idx);
ImGui.TableNextColumn();
if (obj.Expandable)
{
if (ImUtf8.IconButton(obj.Expanded ? FontAwesomeIcon.CaretDown : FontAwesomeIcon.CaretRight,
obj.Expanded ? "Hide the other items using the same model." :
obj.Children > 1 ? $"Show {obj.Children} other items using the same model." :
"Show one other item using the same model.",
_buttonSize))
{
_stateStorage.SetBool(obj.Id, !obj.Expanded);
if (cacheService.TryGetCache<ChangedItemsCache>(_cacheId, out var cache))
cache.Reset();
}
}
else if (obj is { Child: true, Data: IdentifiedItem item })
{
DrawPreferredButton(item, idx);
}
else
{
ImGui.Dummy(_buttonSize);
}
DrawBaseContainer(obj, idx);
}
private void DrawContainer(ChangedItemsCache.Container obj, int idx)
{
using var id = ImUtf8.PushId(idx);
DrawBaseContainer(obj, idx);
}
private void DrawPreferredButton(IdentifiedItem item, int idx)
{
if (ImUtf8.IconButton(FontAwesomeIcon.Star, "Prefer displaying this item instead of the current primary item.\n\nRight-click for more options."u8, _buttonSize,
false, _starColor))
dataEditor.AddPreferredItem(selector.Selected!, item.Item.Id, false, true);
using var context = ImUtf8.PopupContextItem("StarContext"u8);
if (!context)
return;
if (cacheService.TryGetCache<ChangedItemsCache>(_cacheId, out var cache))
for (--idx; idx >= 0; --idx)
{
if (!cache.Data[idx].Expanded)
continue;
if (cache.Data[idx].Data is IdentifiedItem it)
{
if (selector.Selected!.PreferredChangedItems.Contains(it.Item.Id)
&& ImUtf8.MenuItem("Remove Parent from Local Preferred Items"u8))
dataEditor.RemovePreferredItem(selector.Selected!, it.Item.Id, false);
if (selector.Selected!.DefaultPreferredItems.Contains(it.Item.Id)
&& ImUtf8.MenuItem("Remove Parent from Default Preferred Items"u8))
dataEditor.RemovePreferredItem(selector.Selected!, it.Item.Id, true);
}
break;
}
var enabled = !selector.Selected!.DefaultPreferredItems.Contains(item.Item.Id);
if (enabled)
{
if (ImUtf8.MenuItem("Add to Local and Default Preferred Changed Items"u8))
dataEditor.AddPreferredItem(selector.Selected!, item.Item.Id, true, true);
}
else
{
if (ImUtf8.MenuItem("Remove from Default Preferred Changed Items"u8))
dataEditor.RemovePreferredItem(selector.Selected!, item.Item.Id, true);
}
if (ImUtf8.MenuItem("Reset Local Preferred Items to Default"u8))
dataEditor.ResetPreferredItems(selector.Selected!);
if (ImUtf8.MenuItem("Clear Local and Default Preferred Items not Changed by the Mod"u8))
dataEditor.ClearInvalidPreferredItems(selector.Selected!);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DrawBaseContainer(in ChangedItemsCache.Container obj, int _)
{
ImGui.TableNextColumn();
using var indent = ImRaii.PushIndent(1, obj.Child);
drawer.DrawCategoryIcon(obj.Icon, _buttonSize.Y);
ImGui.SameLine(0, 0);
var clicked = ImUtf8.Selectable(obj.Text.Span, false, ImGuiSelectableFlags.None, _buttonSize with { X = 0 });
drawer.ChangedItemHandling(obj.Data, clicked);
ChangedItemDrawer.DrawModelData(obj.ModelData.Span, _buttonSize.Y);
}
}
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using ImSharp;
using Luna;
using OtterGui;
using OtterGui.Services;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Mods;
using Penumbra.Mods.Manager;
using Penumbra.String;
using Penumbra.UI.Classes;
namespace Penumbra.UI.ModsTab;
public class ModPanelChangedItemsTab(
ModFileSystemSelector selector,
ChangedItemDrawer drawer,
ImGuiCacheService cacheService,
Configuration config,
ModDataEditor dataEditor)
: ITab, Luna.IUiService
{
private readonly ImGuiCacheService.CacheId _cacheId = cacheService.GetNewId();
private class ChangedItemsCache
{
private Mod? _lastSelected;
private ushort _lastUpdate;
private ChangedItemIconFlag _filter = ChangedItemFlagExtensions.DefaultFlags;
private ChangedItemMode _lastMode;
private bool _reset;
public readonly List<Container> Data = [];
public bool AnyExpandable { get; private set; }
public record struct Container
{
public IIdentifiedObjectData Data;
public ByteString Text;
public ByteString ModelData;
public uint Id;
public int Children;
public ChangedItemIconFlag Icon;
public bool Expandable;
public bool Expanded;
public bool Child;
public static Container Single(string text, IIdentifiedObjectData data)
=> new()
{
Child = false,
Text = ByteString.FromStringUnsafe(data.ToName(text), false),
ModelData = ByteString.FromStringUnsafe(data.AdditionalData, false),
Icon = data.GetIcon().ToFlag(),
Expandable = false,
Expanded = false,
Data = data,
Id = 0,
Children = 0,
};
public static Container Parent(string text, IIdentifiedObjectData data, uint id, int children, bool expanded)
=> new()
{
Child = false,
Text = ByteString.FromStringUnsafe(data.ToName(text), false),
ModelData = ByteString.FromStringUnsafe(data.AdditionalData, false),
Icon = data.GetIcon().ToFlag(),
Expandable = true,
Expanded = expanded,
Data = data,
Id = id,
Children = children,
};
public static Container Indent(string text, IIdentifiedObjectData data)
=> new()
{
Child = true,
Text = ByteString.FromStringUnsafe(data.ToName(text), false),
ModelData = ByteString.FromStringUnsafe(data.AdditionalData, false),
Icon = data.GetIcon().ToFlag(),
Expandable = false,
Expanded = false,
Data = data,
Id = 0,
Children = 0,
};
}
public void Reset()
=> _reset = true;
public void Update(Mod? mod, ChangedItemDrawer drawer, ChangedItemIconFlag filter, ChangedItemMode mode)
{
if (mod == _lastSelected
&& _lastSelected!.LastChangedItemsUpdate == _lastUpdate
&& _filter == filter
&& !_reset
&& _lastMode == mode)
return;
_reset = false;
Data.Clear();
AnyExpandable = false;
_lastSelected = mod;
_filter = filter;
_lastMode = mode;
if (_lastSelected == null)
return;
_lastUpdate = _lastSelected.LastChangedItemsUpdate;
if (mode is ChangedItemMode.Alphabetical)
{
foreach (var (s, i) in _lastSelected.ChangedItems)
{
if (drawer.FilterChangedItem(s, i, string.Empty))
Data.Add(Container.Single(s, i));
}
return;
}
var tmp = new Dictionary<(PrimaryId, FullEquipType), List<IdentifiedItem>>();
var defaultExpansion = _lastMode is ChangedItemMode.GroupedExpanded;
foreach (var (s, i) in _lastSelected.ChangedItems)
{
if (i is not IdentifiedItem item)
continue;
if (!drawer.FilterChangedItem(s, item, string.Empty))
continue;
if (tmp.TryGetValue((item.Item.PrimaryId, item.Item.Type), out var p))
p.Add(item);
else
tmp[(item.Item.PrimaryId, item.Item.Type)] = [item];
}
foreach (var list in tmp.Values)
{
list.Sort((i1, i2) =>
{
// reversed
var preferred = _lastSelected.PreferredChangedItems.Contains(i2.Item.Id)
.CompareTo(_lastSelected.PreferredChangedItems.Contains(i1.Item.Id));
if (preferred != 0)
return preferred;
// reversed
var count = i2.Count.CompareTo(i1.Count);
if (count != 0)
return count;
return string.Compare(i1.Item.Name, i2.Item.Name, StringComparison.Ordinal);
});
}
var sortedTmp = tmp.Values.OrderBy(s => s[0].Item.Name).ToArray();
var sortedTmpIdx = 0;
foreach (var (s, i) in _lastSelected.ChangedItems)
{
if (i is IdentifiedItem)
continue;
if (!drawer.FilterChangedItem(s, i, string.Empty))
continue;
while (sortedTmpIdx < sortedTmp.Length
&& string.Compare(sortedTmp[sortedTmpIdx][0].Item.Name, s, StringComparison.Ordinal) <= 0)
AddList(sortedTmp[sortedTmpIdx++]);
Data.Add(Container.Single(s, i));
}
for (; sortedTmpIdx < sortedTmp.Length; ++sortedTmpIdx)
AddList(sortedTmp[sortedTmpIdx]);
return;
void AddList(List<IdentifiedItem> list)
{
var mainItem = list[0];
if (list.Count == 1)
{
Data.Add(Container.Single(mainItem.Item.Name, mainItem));
}
else
{
var id = ImUtf8.GetId($"{mainItem.Item.PrimaryId}{(int)mainItem.Item.Type}");
var expanded = ImGui.GetStateStorage().GetBool(id, defaultExpansion);
Data.Add(Container.Parent(mainItem.Item.Name, mainItem, id, list.Count - 1, expanded));
AnyExpandable = true;
if (!expanded)
return;
foreach (var item in list.Skip(1))
Data.Add(Container.Indent(item.Item.Name, item));
}
}
}
}
public ReadOnlySpan<byte> Label
=> "Changed Items"u8;
public bool IsVisible
=> selector.Selected!.ChangedItems.Count > 0;
private ImGuiStoragePtr _stateStorage;
private Vector2 _buttonSize;
private Rgba32 _starColor;
public void DrawContent()
{
if (cacheService.Cache(_cacheId, () => (new ChangedItemsCache(), "ModPanelChangedItemsCache")) is not { } cache)
return;
drawer.DrawTypeFilter();
_stateStorage = ImGui.GetStateStorage();
cache.Update(selector.Selected, drawer, config.Ephemeral.ChangedItemFilter, config.ChangedItemDisplay);
ImGui.Separator();
_buttonSize = new Vector2(ImGui.GetStyle().ItemSpacing.Y + ImGui.GetFrameHeight());
using var style = ImRaii.PushStyle(ImGuiStyleVar.CellPadding, Vector2.Zero)
.Push(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FramePadding, Vector2.Zero)
.Push(ImGuiStyleVar.SelectableTextAlign, new Vector2(0.01f, 0.5f));
using var color = ImGuiColor.Button.Push(Rgba32.Transparent)
.Push(ImGuiColor.ButtonActive, Rgba32.Transparent)
.Push(ImGuiColor.ButtonHovered, Rgba32.Transparent);
using var table = ImUtf8.Table("##changedItems"u8, cache.AnyExpandable ? 2 : 1, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY,
new Vector2(ImGui.GetContentRegionAvail().X, -1));
if (!table)
return;
_starColor = ColorId.ChangedItemPreferenceStar.Value();
if (cache.AnyExpandable)
{
ImUtf8.TableSetupColumn("##exp"u8, ImGuiTableColumnFlags.WidthFixed, _buttonSize.Y);
ImUtf8.TableSetupColumn("##text"u8, ImGuiTableColumnFlags.WidthStretch);
ImGuiClip.ClippedDraw(cache.Data, DrawContainerExpandable, _buttonSize.Y);
}
else
{
ImGuiClip.ClippedDraw(cache.Data, DrawContainer, _buttonSize.Y);
}
}
private void DrawContainerExpandable(ChangedItemsCache.Container obj, int idx)
{
using var id = ImUtf8.PushId(idx);
ImGui.TableNextColumn();
if (obj.Expandable)
{
if (ImUtf8.IconButton(obj.Expanded ? FontAwesomeIcon.CaretDown : FontAwesomeIcon.CaretRight,
obj.Expanded ? "Hide the other items using the same model." :
obj.Children > 1 ? $"Show {obj.Children} other items using the same model." :
"Show one other item using the same model.",
_buttonSize))
{
_stateStorage.SetBool(obj.Id, !obj.Expanded);
if (cacheService.TryGetCache<ChangedItemsCache>(_cacheId, out var cache))
cache.Reset();
}
}
else if (obj is { Child: true, Data: IdentifiedItem item })
{
DrawPreferredButton(item, idx);
}
else
{
ImGui.Dummy(_buttonSize);
}
DrawBaseContainer(obj, idx);
}
private void DrawContainer(ChangedItemsCache.Container obj, int idx)
{
using var id = ImUtf8.PushId(idx);
DrawBaseContainer(obj, idx);
}
private void DrawPreferredButton(IdentifiedItem item, int idx)
{
var textColor = Im.Mouse.IsHoveringRectangle(Rectangle.FromSize(Im.Cursor.ScreenPosition, _buttonSize))
? LunaStyle.FavoriteColor
: _starColor;
if (ImEx.Icon.Button(LunaStyle.FavoriteIcon,
"Prefer displaying this item instead of the current primary item.\n\nRight-click for more options."u8,
textColor: textColor, size: _buttonSize))
dataEditor.AddPreferredItem(selector.Selected!, item.Item.Id, false, true);
using var context = ImUtf8.PopupContextItem("StarContext"u8);
if (!context)
return;
if (cacheService.TryGetCache<ChangedItemsCache>(_cacheId, out var cache))
for (--idx; idx >= 0; --idx)
{
if (!cache.Data[idx].Expanded)
continue;
if (cache.Data[idx].Data is IdentifiedItem it)
{
if (selector.Selected!.PreferredChangedItems.Contains(it.Item.Id)
&& ImUtf8.MenuItem("Remove Parent from Local Preferred Items"u8))
dataEditor.RemovePreferredItem(selector.Selected!, it.Item.Id, false);
if (selector.Selected!.DefaultPreferredItems.Contains(it.Item.Id)
&& ImUtf8.MenuItem("Remove Parent from Default Preferred Items"u8))
dataEditor.RemovePreferredItem(selector.Selected!, it.Item.Id, true);
}
break;
}
var enabled = !selector.Selected!.DefaultPreferredItems.Contains(item.Item.Id);
if (enabled)
{
if (ImUtf8.MenuItem("Add to Local and Default Preferred Changed Items"u8))
dataEditor.AddPreferredItem(selector.Selected!, item.Item.Id, true, true);
}
else
{
if (ImUtf8.MenuItem("Remove from Default Preferred Changed Items"u8))
dataEditor.RemovePreferredItem(selector.Selected!, item.Item.Id, true);
}
if (ImUtf8.MenuItem("Reset Local Preferred Items to Default"u8))
dataEditor.ResetPreferredItems(selector.Selected!);
if (ImUtf8.MenuItem("Clear Local and Default Preferred Items not Changed by the Mod"u8))
dataEditor.ClearInvalidPreferredItems(selector.Selected!);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DrawBaseContainer(in ChangedItemsCache.Container obj, int _)
{
ImGui.TableNextColumn();
using var indent = ImRaii.PushIndent(1, obj.Child);
drawer.DrawCategoryIcon(obj.Icon, _buttonSize.Y);
ImGui.SameLine(0, 0);
var clicked = ImUtf8.Selectable(obj.Text.Span, false, ImGuiSelectableFlags.None, _buttonSize with { X = 0 });
drawer.ChangedItemHandling(obj.Data, clicked);
ChangedItemDrawer.DrawModelData(obj.ModelData.Span, _buttonSize.Y);
}
}

View file

@ -1,206 +1,205 @@
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Dalamud.Bindings.ImGui;
using ImSharp;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.Collections.Cache;
using Penumbra.Collections.Manager;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods;
using Penumbra.Mods.Editor;
using Penumbra.Mods.Settings;
using Penumbra.String.Classes;
using Penumbra.UI.Classes;
namespace Penumbra.UI.ModsTab;
public class ModPanelConflictsTab(CollectionManager collectionManager, ModFileSystemSelector selector) : ITab, Luna.IUiService
{
private int? _currentPriority;
public ReadOnlySpan<byte> Label
=> "Conflicts"u8;
public bool IsVisible
=> collectionManager.Active.Current.Conflicts(selector.Selected!).Any(c => !GetPriority(c).IsHidden);
private readonly ConditionalWeakTable<IMod, object> _expandedMods = [];
private ModPriority GetPriority(ModConflicts conflicts)
{
if (conflicts.Mod2.Index < 0)
return conflicts.Mod2.Priority;
return collectionManager.Active.Current.GetActualSettings(conflicts.Mod2.Index).Settings?.Priority ?? ModPriority.Default;
}
public void DrawContent()
{
using var table = ImRaii.Table("conflicts", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY, ImGui.GetContentRegionAvail());
if (!table)
return;
var buttonSize = new Vector2(ImGui.GetFrameHeight());
var spacing = ImGui.GetStyle().ItemInnerSpacing with { Y = ImGui.GetStyle().ItemSpacing.Y };
var priorityRowWidth = ImGui.CalcTextSize("Priority").X + 20 * ImGuiHelpers.GlobalScale + 2 * buttonSize.X;
var priorityWidth = priorityRowWidth - 2 * (buttonSize.X + spacing.X);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
ImGui.TableSetupColumn("Conflicting Mod", ImGuiTableColumnFlags.WidthStretch);
ImGui.TableSetupColumn("Priority", ImGuiTableColumnFlags.WidthFixed, priorityRowWidth);
ImGui.TableSetupColumn("Files", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Files").X + spacing.X);
ImGui.TableSetupScrollFreeze(2, 2);
ImGui.TableHeadersRow();
DrawCurrentRow(priorityWidth);
// Can not be null because otherwise the tab bar is never drawn.
var mod = selector.Selected!;
foreach (var (index, conflict) in collectionManager.Active.Current.Conflicts(mod).Where(c => !c.Mod2.Priority.IsHidden)
.OrderByDescending(GetPriority)
.ThenBy(c => c.Mod2.Name, StringComparer.OrdinalIgnoreCase).Index())
{
using var id = ImRaii.PushId(index);
DrawConflictRow(conflict, priorityWidth, buttonSize);
}
}
private void DrawCurrentRow(float priorityWidth)
{
ImGui.TableNextColumn();
using var c = ImRaii.PushColor(ImGuiCol.Text, ColorId.FolderLine.Value());
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(selector.Selected!.Name);
ImGui.TableNextColumn();
var actualSettings = collectionManager.Active.Current.GetActualSettings(selector.Selected!.Index).Settings!;
var priority = actualSettings.Priority.Value;
// TODO
using (ImRaii.Disabled(actualSettings is TemporaryModSettings))
{
ImGui.SetNextItemWidth(priorityWidth);
if (ImGui.InputInt("##priority", ref priority, 0, 0, flags: ImGuiInputTextFlags.EnterReturnsTrue))
_currentPriority = priority;
if (ImGui.IsItemDeactivatedAfterEdit() && _currentPriority.HasValue)
{
if (_currentPriority != actualSettings.Priority.Value)
collectionManager.Editor.SetModPriority(collectionManager.Active.Current, selector.Selected!,
new ModPriority(_currentPriority.Value));
_currentPriority = null;
}
else if (ImGui.IsItemDeactivated())
{
_currentPriority = null;
}
}
ImGui.TableNextColumn();
}
private void DrawConflictSelectable(ModConflicts conflict)
{
ImGui.AlignTextToFramePadding();
if (ImGui.Selectable(conflict.Mod2.Name) && conflict.Mod2 is Mod otherMod)
selector.SelectByValue(otherMod);
var hovered = ImGui.IsItemHovered();
var rightClicked = ImGui.IsItemClicked(ImGuiMouseButton.Right);
if (conflict.Mod2 is Mod otherMod2)
{
if (hovered)
ImGui.SetTooltip("Click to jump to mod, Control + Right-Click to disable mod.");
if (rightClicked && ImGui.GetIO().KeyCtrl)
collectionManager.Editor.SetModState(collectionManager.Active.Current, otherMod2, false);
}
}
private bool DrawExpandedFiles(ModConflicts conflict)
{
if (!_expandedMods.TryGetValue(conflict.Mod2, out _))
return false;
using var indent = ImRaii.PushIndent(30f);
foreach (var data in conflict.Conflicts)
{
_ = data switch
{
Utf8GamePath p => ImUtf8.Selectable(p.Path.Span, false),
IMetaIdentifier m => ImUtf8.Selectable(m.ToString(), false),
_ => false,
};
}
return true;
}
private void DrawConflictRow(ModConflicts conflict, float priorityWidth, Vector2 buttonSize)
{
ImGui.TableNextColumn();
DrawConflictSelectable(conflict);
var expanded = DrawExpandedFiles(conflict);
ImGui.TableNextColumn();
var conflictPriority = DrawPriorityInput(conflict, priorityWidth);
Im.Line.Same();
var selectedPriority = collectionManager.Active.Current.GetActualSettings(selector.Selected!.Index).Settings!.Priority.Value;
DrawPriorityButtons(conflict.Mod2 as Mod, conflictPriority, selectedPriority, buttonSize);
ImGui.TableNextColumn();
DrawExpandButton(conflict.Mod2, expanded, buttonSize);
}
private void DrawExpandButton(IMod mod, bool expanded, Vector2 buttonSize)
{
var (icon, tt) = expanded
? (FontAwesomeIcon.CaretUp.ToIconString(), "Hide the conflicting files for this mod.")
: (FontAwesomeIcon.CaretDown.ToIconString(), "Show the conflicting files for this mod.");
if (ImGuiUtil.DrawDisabledButton(icon, buttonSize, tt, false, true))
{
if (expanded)
_expandedMods.Remove(mod);
else
_expandedMods.Add(mod, new object());
}
}
private int DrawPriorityInput(ModConflicts conflict, float priorityWidth)
{
using var color = ImRaii.PushColor(ImGuiCol.Text,
conflict.HasPriority ? ColorId.HandledConflictMod.Value() : ColorId.ConflictingMod.Value());
using var disabled = ImRaii.Disabled(conflict.Mod2.Index < 0);
var priority = _currentPriority ?? GetPriority(conflict).Value;
ImGui.SetNextItemWidth(priorityWidth);
if (ImGui.InputInt("##priority", ref priority, 0, 0, flags: ImGuiInputTextFlags.EnterReturnsTrue))
_currentPriority = priority;
if (ImGui.IsItemDeactivatedAfterEdit() && _currentPriority.HasValue)
{
if (_currentPriority != GetPriority(conflict).Value)
collectionManager.Editor.SetModPriority(collectionManager.Active.Current, (Mod)conflict.Mod2,
new ModPriority(_currentPriority.Value));
_currentPriority = null;
}
else if (ImGui.IsItemDeactivated())
{
_currentPriority = null;
}
return priority;
}
private void DrawPriorityButtons(Mod? conflict, int conflictPriority, int selectedPriority, Vector2 buttonSize)
{
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.SortNumericUpAlt.ToIconString(), buttonSize,
$"Set the priority of the currently selected mod to this mods priority plus one. ({selectedPriority} -> {conflictPriority + 1})",
selectedPriority > conflictPriority, true))
collectionManager.Editor.SetModPriority(collectionManager.Active.Current, selector.Selected!,
new ModPriority(conflictPriority + 1));
Im.Line.Same();
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.SortNumericDownAlt.ToIconString(), buttonSize,
$"Set the priority of this mod to the currently selected mods priority minus one. ({conflictPriority} -> {selectedPriority - 1})",
selectedPriority > conflictPriority || conflict == null, true))
collectionManager.Editor.SetModPriority(collectionManager.Active.Current, conflict!, new ModPriority(selectedPriority - 1));
}
}
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Dalamud.Bindings.ImGui;
using ImSharp;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.Collections.Cache;
using Penumbra.Collections.Manager;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods;
using Penumbra.Mods.Editor;
using Penumbra.Mods.Settings;
using Penumbra.String.Classes;
using Penumbra.UI.Classes;
namespace Penumbra.UI.ModsTab;
public class ModPanelConflictsTab(CollectionManager collectionManager, ModFileSystemSelector selector) : ITab, Luna.IUiService
{
private int? _currentPriority;
public ReadOnlySpan<byte> Label
=> "Conflicts"u8;
public bool IsVisible
=> collectionManager.Active.Current.Conflicts(selector.Selected!).Any(c => !GetPriority(c).IsHidden);
private readonly ConditionalWeakTable<IMod, object> _expandedMods = [];
private ModPriority GetPriority(ModConflicts conflicts)
{
if (conflicts.Mod2.Index < 0)
return conflicts.Mod2.Priority;
return collectionManager.Active.Current.GetActualSettings(conflicts.Mod2.Index).Settings?.Priority ?? ModPriority.Default;
}
public void DrawContent()
{
using var table = ImRaii.Table("conflicts", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY, ImGui.GetContentRegionAvail());
if (!table)
return;
var buttonSize = new Vector2(ImGui.GetFrameHeight());
var spacing = ImGui.GetStyle().ItemInnerSpacing with { Y = ImGui.GetStyle().ItemSpacing.Y };
var priorityRowWidth = ImGui.CalcTextSize("Priority").X + 20 * ImGuiHelpers.GlobalScale + 2 * buttonSize.X;
var priorityWidth = priorityRowWidth - 2 * (buttonSize.X + spacing.X);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
ImGui.TableSetupColumn("Conflicting Mod", ImGuiTableColumnFlags.WidthStretch);
ImGui.TableSetupColumn("Priority", ImGuiTableColumnFlags.WidthFixed, priorityRowWidth);
ImGui.TableSetupColumn("Files", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Files").X + spacing.X);
ImGui.TableSetupScrollFreeze(2, 2);
ImGui.TableHeadersRow();
DrawCurrentRow(priorityWidth);
// Can not be null because otherwise the tab bar is never drawn.
var mod = selector.Selected!;
foreach (var (index, conflict) in collectionManager.Active.Current.Conflicts(mod).Where(c => !c.Mod2.Priority.IsHidden)
.OrderByDescending(GetPriority)
.ThenBy(c => c.Mod2.Name, StringComparer.OrdinalIgnoreCase).Index())
{
using var id = ImRaii.PushId(index);
DrawConflictRow(conflict, priorityWidth, buttonSize);
}
}
private void DrawCurrentRow(float priorityWidth)
{
ImGui.TableNextColumn();
using var c = ImGuiColor.Text.Push(ColorId.FolderLine.Value());
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(selector.Selected!.Name);
ImGui.TableNextColumn();
var actualSettings = collectionManager.Active.Current.GetActualSettings(selector.Selected!.Index).Settings!;
var priority = actualSettings.Priority.Value;
// TODO
using (ImRaii.Disabled(actualSettings is TemporaryModSettings))
{
ImGui.SetNextItemWidth(priorityWidth);
if (ImGui.InputInt("##priority", ref priority, 0, 0, flags: ImGuiInputTextFlags.EnterReturnsTrue))
_currentPriority = priority;
if (ImGui.IsItemDeactivatedAfterEdit() && _currentPriority.HasValue)
{
if (_currentPriority != actualSettings.Priority.Value)
collectionManager.Editor.SetModPriority(collectionManager.Active.Current, selector.Selected!,
new ModPriority(_currentPriority.Value));
_currentPriority = null;
}
else if (ImGui.IsItemDeactivated())
{
_currentPriority = null;
}
}
ImGui.TableNextColumn();
}
private void DrawConflictSelectable(ModConflicts conflict)
{
ImGui.AlignTextToFramePadding();
if (ImGui.Selectable(conflict.Mod2.Name) && conflict.Mod2 is Mod otherMod)
selector.SelectByValue(otherMod);
var hovered = ImGui.IsItemHovered();
var rightClicked = ImGui.IsItemClicked(ImGuiMouseButton.Right);
if (conflict.Mod2 is Mod otherMod2)
{
if (hovered)
ImGui.SetTooltip("Click to jump to mod, Control + Right-Click to disable mod.");
if (rightClicked && ImGui.GetIO().KeyCtrl)
collectionManager.Editor.SetModState(collectionManager.Active.Current, otherMod2, false);
}
}
private bool DrawExpandedFiles(ModConflicts conflict)
{
if (!_expandedMods.TryGetValue(conflict.Mod2, out _))
return false;
using var indent = ImRaii.PushIndent(30f);
foreach (var data in conflict.Conflicts)
{
_ = data switch
{
Utf8GamePath p => ImUtf8.Selectable(p.Path.Span, false),
IMetaIdentifier m => ImUtf8.Selectable(m.ToString(), false),
_ => false,
};
}
return true;
}
private void DrawConflictRow(ModConflicts conflict, float priorityWidth, Vector2 buttonSize)
{
ImGui.TableNextColumn();
DrawConflictSelectable(conflict);
var expanded = DrawExpandedFiles(conflict);
ImGui.TableNextColumn();
var conflictPriority = DrawPriorityInput(conflict, priorityWidth);
Im.Line.Same();
var selectedPriority = collectionManager.Active.Current.GetActualSettings(selector.Selected!.Index).Settings!.Priority.Value;
DrawPriorityButtons(conflict.Mod2 as Mod, conflictPriority, selectedPriority, buttonSize);
ImGui.TableNextColumn();
DrawExpandButton(conflict.Mod2, expanded, buttonSize);
}
private void DrawExpandButton(IMod mod, bool expanded, Vector2 buttonSize)
{
var (icon, tt) = expanded
? (FontAwesomeIcon.CaretUp.ToIconString(), "Hide the conflicting files for this mod.")
: (FontAwesomeIcon.CaretDown.ToIconString(), "Show the conflicting files for this mod.");
if (ImGuiUtil.DrawDisabledButton(icon, buttonSize, tt, false, true))
{
if (expanded)
_expandedMods.Remove(mod);
else
_expandedMods.Add(mod, new object());
}
}
private int DrawPriorityInput(ModConflicts conflict, float priorityWidth)
{
using var color = ImGuiColor.Text.Push(conflict.HasPriority ? ColorId.HandledConflictMod.Value() : ColorId.ConflictingMod.Value());
using var disabled = ImRaii.Disabled(conflict.Mod2.Index < 0);
var priority = _currentPriority ?? GetPriority(conflict).Value;
ImGui.SetNextItemWidth(priorityWidth);
if (ImGui.InputInt("##priority", ref priority, 0, 0, flags: ImGuiInputTextFlags.EnterReturnsTrue))
_currentPriority = priority;
if (ImGui.IsItemDeactivatedAfterEdit() && _currentPriority.HasValue)
{
if (_currentPriority != GetPriority(conflict).Value)
collectionManager.Editor.SetModPriority(collectionManager.Active.Current, (Mod)conflict.Mod2,
new ModPriority(_currentPriority.Value));
_currentPriority = null;
}
else if (ImGui.IsItemDeactivated())
{
_currentPriority = null;
}
return priority;
}
private void DrawPriorityButtons(Mod? conflict, int conflictPriority, int selectedPriority, Vector2 buttonSize)
{
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.SortNumericUpAlt.ToIconString(), buttonSize,
$"Set the priority of the currently selected mod to this mods priority plus one. ({selectedPriority} -> {conflictPriority + 1})",
selectedPriority > conflictPriority, true))
collectionManager.Editor.SetModPriority(collectionManager.Active.Current, selector.Selected!,
new ModPriority(conflictPriority + 1));
Im.Line.Same();
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.SortNumericDownAlt.ToIconString(), buttonSize,
$"Set the priority of this mod to the currently selected mods priority minus one. ({conflictPriority} -> {selectedPriority - 1})",
selectedPriority > conflictPriority || conflict == null, true))
collectionManager.Editor.SetModPriority(collectionManager.Active.Current, conflict!, new ModPriority(selectedPriority - 1));
}
}

View file

@ -208,8 +208,8 @@ public class ModPanelEditTab(
private void DrawImportDate()
{
ImUtf8.TextFramed($"{DateTimeOffset.FromUnixTimeMilliseconds(_mod.ImportDate).ToLocalTime():yyyy/MM/dd HH:mm}",
ImGui.GetColorU32(ImGuiCol.FrameBg, 0.5f), new Vector2(UiHelpers.InputTextMinusButton3, 0));
ImEx.TextFramed($"{DateTimeOffset.FromUnixTimeMilliseconds(_mod.ImportDate).ToLocalTime():yyyy/MM/dd HH:mm}",
new Vector2(UiHelpers.InputTextMinusButton3, 0), ImGuiColor.FrameBackground.Get(0.5f));
ImGui.SameLine(0, 3 * ImUtf8.GlobalScale);
var canRefresh = config.DeleteModModifier.IsActive();

View file

@ -149,8 +149,7 @@ public class ModPanelHeader : IDisposable
ImGui.SetCursorPosX(offset);
}
using var color = ImRaii.PushColor(ImGuiCol.Border, Colors.MetaInfoText);
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2 * UiHelpers.Scale);
using var style = ImStyleBorder.Frame.Push(Colors.MetaInfoText, 2 * Im.Style.GlobalScale);
using var f = _nameFont.Push();
ImGuiUtil.DrawTextButton(_modName, Vector2.Zero, 0);
_nameWidth = ImGui.GetItemRectSize().X;

View file

@ -92,7 +92,7 @@ public class ModPanelSettingsTab(
if (!_inherited)
return;
using var color = ImRaii.PushColor(ImGuiCol.Button, Colors.PressEnterWarningBg);
using var color = ImGuiColor.Button.Push(Colors.PressEnterWarningBg);
var width = new Vector2(ImGui.GetContentRegionAvail().X, 0);
if (ImUtf8.ButtonEx($"These settings are inherited from {selection.Collection.Identity.Name}.", width, _locked))
{

View file

@ -1,158 +1,158 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Widgets;
using Penumbra.Mods;
using Penumbra.Mods.Manager;
using Penumbra.UI.AdvancedWindow;
namespace Penumbra.UI.ModsTab;
public class ModPanelTabBar : Luna.IUiService
{
private enum ModPanelTabType
{
Description,
Settings,
ChangedItems,
Conflicts,
Collections,
Edit,
};
public readonly ModPanelSettingsTab Settings;
public readonly ModPanelDescriptionTab Description;
public readonly ModPanelCollectionsTab Collections;
public readonly ModPanelConflictsTab Conflicts;
public readonly ModPanelChangedItemsTab ChangedItems;
public readonly ModPanelEditTab Edit;
private readonly ModEditWindow _modEditWindow;
private readonly ModManager _modManager;
private readonly TutorialService _tutorial;
public readonly ITab[] Tabs;
private ModPanelTabType _preferredTab = ModPanelTabType.Settings;
private Mod? _lastMod;
public ModPanelTabBar(ModEditWindow modEditWindow, ModPanelSettingsTab settings, ModPanelDescriptionTab description,
ModPanelConflictsTab conflicts, ModPanelChangedItemsTab changedItems, ModPanelEditTab edit, ModManager modManager,
TutorialService tutorial, ModPanelCollectionsTab collections)
{
_modEditWindow = modEditWindow;
Settings = settings;
Description = description;
Conflicts = conflicts;
ChangedItems = changedItems;
Edit = edit;
_modManager = modManager;
_tutorial = tutorial;
Collections = collections;
Tabs =
[
Settings,
Description,
Conflicts,
ChangedItems,
Collections,
Edit,
];
}
public void Draw(Mod mod)
{
var tabBarHeight = ImGui.GetCursorPosY();
if (_lastMod != mod)
{
_lastMod = mod;
TabBar.Draw(string.Empty, ImGuiTabBarFlags.NoTooltip, ToLabel(_preferredTab), out _, () => DrawAdvancedEditingButton(mod), Tabs);
}
else
{
TabBar.Draw(string.Empty, ImGuiTabBarFlags.NoTooltip, ReadOnlySpan<byte>.Empty, out var label, () => DrawAdvancedEditingButton(mod),
Tabs);
_preferredTab = ToType(label);
}
DrawFavoriteButton(mod, tabBarHeight);
}
private ReadOnlySpan<byte> ToLabel(ModPanelTabType type)
=> type switch
{
ModPanelTabType.Description => Description.Label,
ModPanelTabType.Settings => Settings.Label,
ModPanelTabType.ChangedItems => ChangedItems.Label,
ModPanelTabType.Conflicts => Conflicts.Label,
ModPanelTabType.Collections => Collections.Label,
ModPanelTabType.Edit => Edit.Label,
_ => ReadOnlySpan<byte>.Empty,
};
private ModPanelTabType ToType(ReadOnlySpan<byte> label)
{
if (label == Description.Label)
return ModPanelTabType.Description;
if (label == Settings.Label)
return ModPanelTabType.Settings;
if (label == ChangedItems.Label)
return ModPanelTabType.ChangedItems;
if (label == Conflicts.Label)
return ModPanelTabType.Conflicts;
if (label == Collections.Label)
return ModPanelTabType.Collections;
if (label == Edit.Label)
return ModPanelTabType.Edit;
return 0;
}
private void DrawAdvancedEditingButton(Mod mod)
{
if (ImGui.TabItemButton("Advanced Editing", ImGuiTabItemFlags.Trailing | ImGuiTabItemFlags.NoTooltip))
{
_modEditWindow.ChangeMod(mod);
_modEditWindow.ChangeOption(mod.Default);
_modEditWindow.IsOpen = true;
}
ImGuiUtil.HoverTooltip(
"Clicking this will open a new window in which you can\nedit the following things per option for this mod:\n\n"
+ "\t\t- file redirections\n"
+ "\t\t- file swaps\n"
+ "\t\t- metadata manipulations\n"
+ "\t\t- model materials\n"
+ "\t\t- duplicates\n"
+ "\t\t- textures");
}
private void DrawFavoriteButton(Mod mod, float height)
{
using (var font = ImRaii.PushFont(UiBuilder.IconFont))
{
var size = ImGui.CalcTextSize(FontAwesomeIcon.Star.ToIconString()) + ImGui.GetStyle().FramePadding * 2;
var newPos = new Vector2(ImGui.GetWindowWidth() - size.X - ImGui.GetStyle().ItemSpacing.X, height);
if (ImGui.GetScrollMaxX() > 0)
newPos.X += ImGui.GetScrollX();
var rectUpper = ImGui.GetWindowPos() + newPos;
var color = ImGui.IsMouseHoveringRect(rectUpper, rectUpper + size) ? ImGui.GetColorU32(ImGuiCol.Text) :
mod.Favorite ? 0xFF00FFFF : ImGui.GetColorU32(ImGuiCol.TextDisabled);
using var c = ImRaii.PushColor(ImGuiCol.Text, color)
.Push(ImGuiCol.Button, 0)
.Push(ImGuiCol.ButtonHovered, 0)
.Push(ImGuiCol.ButtonActive, 0);
ImGui.SetCursorPos(newPos);
if (ImGui.Button(FontAwesomeIcon.Star.ToIconString()))
_modManager.DataEditor.ChangeModFavorite(mod, !mod.Favorite);
}
var hovered = ImGui.IsItemHovered();
_tutorial.OpenTutorial(BasicTutorialSteps.Favorites);
if (hovered)
ImGui.SetTooltip("Favorite");
}
}
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using ImSharp;
using Luna;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Widgets;
using Penumbra.Mods;
using Penumbra.Mods.Manager;
using Penumbra.UI.AdvancedWindow;
using ImGuiColor = ImSharp.ImGuiColor;
namespace Penumbra.UI.ModsTab;
public class ModPanelTabBar : IUiService
{
private enum ModPanelTabType
{
Description,
Settings,
ChangedItems,
Conflicts,
Collections,
Edit,
};
public readonly ModPanelSettingsTab Settings;
public readonly ModPanelDescriptionTab Description;
public readonly ModPanelCollectionsTab Collections;
public readonly ModPanelConflictsTab Conflicts;
public readonly ModPanelChangedItemsTab ChangedItems;
public readonly ModPanelEditTab Edit;
private readonly ModEditWindow _modEditWindow;
private readonly ModManager _modManager;
private readonly TutorialService _tutorial;
public readonly ITab[] Tabs;
private ModPanelTabType _preferredTab = ModPanelTabType.Settings;
private Mod? _lastMod;
public ModPanelTabBar(ModEditWindow modEditWindow, ModPanelSettingsTab settings, ModPanelDescriptionTab description,
ModPanelConflictsTab conflicts, ModPanelChangedItemsTab changedItems, ModPanelEditTab edit, ModManager modManager,
TutorialService tutorial, ModPanelCollectionsTab collections)
{
_modEditWindow = modEditWindow;
Settings = settings;
Description = description;
Conflicts = conflicts;
ChangedItems = changedItems;
Edit = edit;
_modManager = modManager;
_tutorial = tutorial;
Collections = collections;
Tabs =
[
Settings,
Description,
Conflicts,
ChangedItems,
Collections,
Edit,
];
}
public void Draw(Mod mod)
{
var tabBarHeight = ImGui.GetCursorPosY();
if (_lastMod != mod)
{
_lastMod = mod;
TabBar.Draw(string.Empty, ImGuiTabBarFlags.NoTooltip, ToLabel(_preferredTab), out _, () => DrawAdvancedEditingButton(mod), Tabs);
}
else
{
TabBar.Draw(string.Empty, ImGuiTabBarFlags.NoTooltip, ReadOnlySpan<byte>.Empty, out var label, () => DrawAdvancedEditingButton(mod),
Tabs);
_preferredTab = ToType(label);
}
DrawFavoriteButton(mod, tabBarHeight);
}
private ReadOnlySpan<byte> ToLabel(ModPanelTabType type)
=> type switch
{
ModPanelTabType.Description => Description.Label,
ModPanelTabType.Settings => Settings.Label,
ModPanelTabType.ChangedItems => ChangedItems.Label,
ModPanelTabType.Conflicts => Conflicts.Label,
ModPanelTabType.Collections => Collections.Label,
ModPanelTabType.Edit => Edit.Label,
_ => ReadOnlySpan<byte>.Empty,
};
private ModPanelTabType ToType(ReadOnlySpan<byte> label)
{
if (label == Description.Label)
return ModPanelTabType.Description;
if (label == Settings.Label)
return ModPanelTabType.Settings;
if (label == ChangedItems.Label)
return ModPanelTabType.ChangedItems;
if (label == Conflicts.Label)
return ModPanelTabType.Conflicts;
if (label == Collections.Label)
return ModPanelTabType.Collections;
if (label == Edit.Label)
return ModPanelTabType.Edit;
return 0;
}
private void DrawAdvancedEditingButton(Mod mod)
{
if (ImGui.TabItemButton("Advanced Editing", ImGuiTabItemFlags.Trailing | ImGuiTabItemFlags.NoTooltip))
{
_modEditWindow.ChangeMod(mod);
_modEditWindow.ChangeOption(mod.Default);
_modEditWindow.IsOpen = true;
}
ImGuiUtil.HoverTooltip(
"Clicking this will open a new window in which you can\nedit the following things per option for this mod:\n\n"
+ "\t\t- file redirections\n"
+ "\t\t- file swaps\n"
+ "\t\t- metadata manipulations\n"
+ "\t\t- model materials\n"
+ "\t\t- duplicates\n"
+ "\t\t- textures");
}
private void DrawFavoriteButton(Mod mod, float height)
{
var size = ImEx.Icon.CalculateSize(LunaStyle.FavoriteIcon) + ImGui.GetStyle().FramePadding * 2;
var newPos = new Vector2(ImGui.GetWindowWidth() - size.X - ImGui.GetStyle().ItemSpacing.X, height);
if (ImGui.GetScrollMaxX() > 0)
newPos.X += ImGui.GetScrollX();
var rectUpper = ImGui.GetWindowPos() + newPos;
var color = ImGui.IsMouseHoveringRect(rectUpper, rectUpper + size) ? Im.Style[ImGuiColor.Text] :
mod.Favorite ? LunaStyle.FavoriteColor : Im.Style[ImGuiColor.TextDisabled];
using var c = ImGuiColor.Text.Push(color)
.Push(ImGuiColor.Button, Vector4.Zero)
.Push(ImGuiColor.ButtonHovered, Vector4.Zero)
.Push(ImGuiColor.ButtonActive, Vector4.Zero);
ImGui.SetCursorPos(newPos);
if (ImEx.Icon.Button(LunaStyle.FavoriteIcon))
_modManager.DataEditor.ChangeModFavorite(mod, !mod.Favorite);
var hovered = ImGui.IsItemHovered();
_tutorial.OpenTutorial(BasicTutorialSteps.Favorites);
if (hovered)
ImGui.SetTooltip("Favorite");
}
}

View file

@ -21,8 +21,8 @@ public sealed class PredefinedTagManager : ISavable, IReadOnlyList<string>, ISer
private readonly SaveService _saveService;
private bool _isListOpen;
private uint _enabledColor;
private uint _disabledColor;
private Rgba32 _enabledColor;
private Rgba32 _disabledColor;
private readonly SortedList<string, TagData> _predefinedTags = [];

View file

@ -1,5 +1,6 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using ImSharp;
using Luna;
using OtterGui;
using OtterGui.Raii;
@ -336,17 +337,13 @@ internal sealed class ResourceWatcherTable : Table<Record>
$"Successfully loaded ({(byte)item.LoadState})."),
LoadState.FailedSubResource => (FontAwesomeIcon.ExclamationCircle, ColorId.DecreasedMetaValue.Value(),
$"Dependencies failed to load ({(byte)item.LoadState})."),
<= LoadState.Constructed => (FontAwesomeIcon.QuestionCircle, ColorId.UndefinedMod.Value(), $"Not yet loaded ({(byte)item.LoadState})."),
<= LoadState.Constructed => (FontAwesomeIcon.QuestionCircle, ColorId.UndefinedMod.Value(),
$"Not yet loaded ({(byte)item.LoadState})."),
< LoadState.Success => (FontAwesomeIcon.Clock, ColorId.FolderLine.Value(), $"Loading asynchronously ({(byte)item.LoadState})."),
> LoadState.Success => (FontAwesomeIcon.Times, ColorId.DecreasedMetaValue.Value(),
$"Failed to load ({(byte)item.LoadState})."),
};
using (var font = ImRaii.PushFont(UiBuilder.IconFont))
{
using var c = ImRaii.PushColor(ImGuiCol.Text, color);
ImGui.TextUnformatted(icon.ToIconString());
}
ImEx.Icon.Draw(icon.Icon(), color);
ImGuiUtil.HoverTooltip(tt);
}

View file

@ -86,29 +86,30 @@ public sealed class CollectionsTab : IDisposable, ITab, Luna.IUiService
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 0).Push(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
var buttonSize = new Vector2((ImGui.GetContentRegionAvail().X - withSpacing) / 4f, ImGui.GetFrameHeight());
using var _ = ImRaii.Group();
using var color = ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.TabActive), Mode is PanelMode.SimpleAssignment);
using var _ = ImRaii.Group();
var tabSelectedColor = Im.Style[ImGuiColor.TabSelected];
using var color = ImGuiColor.Button.Push(tabSelectedColor, Mode is PanelMode.SimpleAssignment);
if (ImGui.Button("Simple Assignments", buttonSize))
Mode = PanelMode.SimpleAssignment;
color.Pop();
_tutorial.OpenTutorial(BasicTutorialSteps.SimpleAssignments);
Im.Line.Same();
color.Push(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.TabActive), Mode is PanelMode.IndividualAssignment);
color.Push(ImGuiColor.Button, tabSelectedColor, Mode is PanelMode.IndividualAssignment);
if (ImGui.Button("Individual Assignments", buttonSize))
Mode = PanelMode.IndividualAssignment;
color.Pop();
_tutorial.OpenTutorial(BasicTutorialSteps.IndividualAssignments);
Im.Line.Same();
color.Push(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.TabActive), Mode is PanelMode.GroupAssignment);
color.Push(ImGuiColor.Button, tabSelectedColor, Mode is PanelMode.GroupAssignment);
if (ImGui.Button("Group Assignments", buttonSize))
Mode = PanelMode.GroupAssignment;
color.Pop();
_tutorial.OpenTutorial(BasicTutorialSteps.GroupAssignments);
Im.Line.Same();
color.Push(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.TabActive), Mode is PanelMode.Details);
color.Push(ImGuiColor.Button, tabSelectedColor, Mode is PanelMode.Details);
if (ImGui.Button("Collection Details", buttonSize))
Mode = PanelMode.Details;
color.Pop();

View file

@ -31,7 +31,6 @@ using Penumbra.Mods.Manager;
using Penumbra.Services;
using Penumbra.String;
using Penumbra.UI.Classes;
using static OtterGui.Raii.ImRaii;
using CharacterBase = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.CharacterBase;
using ImGuiClip = OtterGui.ImGuiClip;
using Penumbra.Api.IpcTester;
@ -68,7 +67,7 @@ public class Diagnostics(ServiceManager provider) : IUiService
}
}
public class DebugTab : Window, ITab, IUiService
public class DebugTab : Window, ITab
{
private readonly Configuration _config;
private readonly CollectionManager _collectionManager;
@ -180,7 +179,7 @@ public class DebugTab : Window, ITab, IUiService
public void DrawContent()
{
using var child = Child("##DebugTab", -Vector2.One);
using var child = Im.Child.Begin("##DebugTab"u8, -Vector2.One);
if (!child)
return;
@ -218,9 +217,9 @@ public class DebugTab : Window, ITab, IUiService
{
if (collection.HasCache)
{
using var color = PushColor(ImGuiCol.Text, ColorId.FolderExpanded.Value());
using var color = ImGuiColor.Text.Push(ColorId.FolderExpanded.Value());
using var node =
TreeNode($"{collection.Identity.Name} (Change Counter {collection.Counters.Change})###{collection.Identity.Name}");
Im.Tree.Node($"{collection.Identity.Name} (Change Counter {collection.Counters.Change})###{collection.Identity.Name}");
if (!node)
continue;
@ -272,24 +271,24 @@ public class DebugTab : Window, ITab, IUiService
if (modNode)
foreach (var (mod, paths, manips) in collection._cache!.ModData.Data.OrderBy(t => t.Item1.Name))
{
using var id = mod is TemporaryMod t ? PushId(t.Priority.Value) : PushId(((Mod)mod).ModPath.Name);
using var node2 = TreeNode(mod.Name);
using var id = mod is TemporaryMod t ? Im.Id.Push(t.Priority.Value) : Im.Id.Push(((Mod)mod).ModPath.Name);
using var node2 = Im.Tree.Node(mod.Name);
if (!node2)
continue;
foreach (var path in paths)
TreeNode(path.ToString(), ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.Leaf).Dispose();
Im.Tree.Node(path.Path.Span, TreeNodeFlags.Bullet | TreeNodeFlags.Leaf).Dispose();
foreach (var manip in manips)
TreeNode(manip.ToString(), ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.Leaf).Dispose();
Im.Tree.Node($"{manip}", TreeNodeFlags.Bullet | TreeNodeFlags.Leaf).Dispose();
}
}
else
{
using var color = PushColor(ImGuiCol.Text, ColorId.UndefinedMod.Value());
TreeNode($"{collection.Identity.Name} (Change Counter {collection.Counters.Change})",
ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.Leaf).Dispose();
using var color = ImGuiColor.Text.Push(ColorId.UndefinedMod.Value());
Im.Tree.Node($"{collection.Identity.Name} (Change Counter {collection.Counters.Change})",
TreeNodeFlags.Bullet | TreeNodeFlags.Leaf).Dispose();
}
}
}
@ -308,7 +307,7 @@ public class DebugTab : Window, ITab, IUiService
_config.Ephemeral.Save();
}
using (var table = Table("##DebugGeneralTable", 2, ImGuiTableFlags.SizingFixedFit))
using (var table = Im.Table.Begin("##DebugGeneralTable"u8, 2, TableFlags.SizingFixedFit))
{
if (table)
{
@ -329,11 +328,11 @@ public class DebugTab : Window, ITab, IUiService
var issues = _modManager.Index().Count(p => p.Index != p.Item.Index);
using (var tree = TreeNode($"Mods ({issues} Issues)###Mods"))
using (var tree = Im.Tree.Node($"Mods ({issues} Issues)###Mods"))
{
if (tree)
{
using var table = Table("##DebugModsTable", 3, ImGuiTableFlags.SizingFixedFit);
using var table = Im.Table.Begin("##DebugModsTable"u8, 3, TableFlags.SizingFixedFit);
if (table)
{
var lastIndex = -1;
@ -350,11 +349,11 @@ public class DebugTab : Window, ITab, IUiService
}
}
using (var tree = TreeNode("Mod Import"))
using (var tree = Im.Tree.Node("Mod Import"u8))
{
if (tree)
{
using var table = Table("##DebugModImport", 2, ImGuiTableFlags.SizingFixedFit);
using var table = Im.Table.Begin("##DebugModImport"u8, 2, TableFlags.SizingFixedFit);
if (table)
{
var importing = _modImporter.IsImporting(out var importer);
@ -384,11 +383,11 @@ public class DebugTab : Window, ITab, IUiService
}
}
using (var tree = TreeNode("Framework"))
using (var tree = Im.Tree.Node("Framework"u8))
{
if (tree)
{
using var table = Table("##DebugFramework", 2, ImGuiTableFlags.SizingFixedFit);
using var table = Im.Table.Begin("##DebugFramework"u8, 2, TableFlags.SizingFixedFit);
if (table)
{
foreach (var important in _framework.Important)
@ -406,11 +405,11 @@ public class DebugTab : Window, ITab, IUiService
}
}
using (var tree = TreeNode($"Texture Manager {_textureManager.Tasks.Count}###Texture Manager"))
using (var tree = Im.Tree.Node($"Texture Manager {_textureManager.Tasks.Count}###Texture Manager"))
{
if (tree)
{
using var table = Table("##Tasks", 2, ImGuiTableFlags.RowBg);
using var table = Im.Table.Begin("##Tasks"u8, 2, TableFlags.RowBackground);
if (table)
foreach (var task in _textureManager.Tasks)
{
@ -420,11 +419,11 @@ public class DebugTab : Window, ITab, IUiService
}
}
using (var tree = TreeNode("Redraw Service"))
using (var tree = Im.Tree.Node("Redraw Service"u8))
{
if (tree)
{
using var table = Table("##redraws", 3, ImGuiTableFlags.RowBg);
using var table = Im.Table.Begin("##redraws"u8, 3, TableFlags.RowBackground);
if (table)
{
ImGuiUtil.DrawTableColumn("In GPose");
@ -494,7 +493,7 @@ public class DebugTab : Window, ITab, IUiService
if (!ImGui.CollapsingHeader("Performance"))
return;
using (var start = TreeNode("Startup Performance", ImGuiTreeNodeFlags.DefaultOpen))
using (var start = Im.Tree.Node("Startup Performance"u8, TreeNodeFlags.DefaultOpen))
{
if (start)
ImGui.NewLine();
@ -512,7 +511,7 @@ public class DebugTab : Window, ITab, IUiService
{
_objects.DrawDebug();
using var table = Table("##actors", 8, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit,
using var table = Im.Table.Begin("##actors"u8, 8, TableFlags.RowBackground | TableFlags.SizingFixedFit,
-Vector2.UnitX);
if (!table)
return;
@ -581,11 +580,11 @@ public class DebugTab : Window, ITab, IUiService
ImGui.TextUnformatted(
$"Last Game Object: 0x{_collectionResolver.IdentifyLastGameObjectCollection(true).AssociatedGameObject:X} ({_collectionResolver.IdentifyLastGameObjectCollection(true).ModCollection.Identity.Name})");
using (var drawTree = TreeNode("Draw Object to Object"))
using (var drawTree = Im.Tree.Node("Draw Object to Object"u8))
{
if (drawTree)
{
using var table = Table("###DrawObjectResolverTable", 8, ImGuiTableFlags.SizingFixedFit);
using var table = Im.Table.Begin("###DrawObjectResolverTable"u8, 8, TableFlags.SizingFixedFit);
if (table)
foreach (var (drawObject, (gameObjectPtr, idx, child)) in _drawObjectState
.OrderBy(kvp => kvp.Value.Item2.Index)
@ -595,7 +594,7 @@ public class DebugTab : Window, ITab, IUiService
ImGui.TableNextColumn();
ImUtf8.CopyOnClickSelectable($"{drawObject}");
ImUtf8.DrawTableColumn($"{gameObjectPtr.Index}");
using (ImRaii.PushColor(ImGuiCol.Text, 0xFF0000FF, gameObjectPtr.Index != idx))
using (ImGuiColor.Text.Push(new Vector4(1, 0, 0, 1), gameObjectPtr.Index != idx))
{
ImUtf8.DrawTableColumn($"{idx}");
}
@ -603,7 +602,7 @@ public class DebugTab : Window, ITab, IUiService
ImUtf8.DrawTableColumn(child ? "Child"u8 : "Main"u8);
ImGui.TableNextColumn();
ImUtf8.CopyOnClickSelectable($"{gameObjectPtr}");
using (ImRaii.PushColor(ImGuiCol.Text, 0xFF0000FF, _objects[idx] != gameObjectPtr))
using (ImGuiColor.Text.Push(new Vector4(1, 0, 0, 1), _objects[idx] != gameObjectPtr))
{
ImUtf8.DrawTableColumn($"{_objects[idx]}");
}
@ -615,11 +614,11 @@ public class DebugTab : Window, ITab, IUiService
}
}
using (var pathTree = TreeNode("Path Collections"))
using (var pathTree = Im.Tree.Node("Path Collections"u8))
{
if (pathTree)
{
using var table = Table("###PathCollectionResolverTable", 2, ImGuiTableFlags.SizingFixedFit);
using var table = Im.Table.Begin("###PathCollectionResolverTable"u8, 2, TableFlags.SizingFixedFit);
if (table)
foreach (var data in _pathState.CurrentData)
{
@ -631,11 +630,11 @@ public class DebugTab : Window, ITab, IUiService
}
}
using (var resourceTree = TreeNode("Subfile Collections"))
using (var resourceTree = Im.Tree.Node("Subfile Collections"u8))
{
if (resourceTree)
{
using var table = Table("###ResourceCollectionResolverTable", 4, ImGuiTableFlags.SizingFixedFit);
using var table = Im.Table.Begin("###ResourceCollectionResolverTable"u8, 4, TableFlags.SizingFixedFit);
if (table)
{
ImGuiUtil.DrawTableColumn("Current Mtrl Data");
@ -664,11 +663,11 @@ public class DebugTab : Window, ITab, IUiService
}
}
using (var identifiedTree = TreeNode("Identified Collections"))
using (var identifiedTree = Im.Tree.Node("Identified Collections"u8))
{
if (identifiedTree)
{
using var table = Table("##PathCollectionsIdentifiedTable", 4, ImGuiTableFlags.SizingFixedFit);
using var table = Im.Table.Begin("##PathCollectionsIdentifiedTable"u8, 4, TableFlags.SizingFixedFit);
if (table)
foreach (var (address, identifier, collection) in _identifiedCollectionCache
.OrderBy(kvp => ((GameObject*)kvp.Address)->ObjectIndex))
@ -681,11 +680,11 @@ public class DebugTab : Window, ITab, IUiService
}
}
using (var cutsceneTree = TreeNode("Cutscene Actors"))
using (var cutsceneTree = Im.Tree.Node("Cutscene Actors"u8))
{
if (cutsceneTree)
{
using var table = Table("###PCutsceneResolverTable", 2, ImGuiTableFlags.SizingFixedFit);
using var table = Im.Table.Begin("###PCutsceneResolverTable"u8, 2, TableFlags.SizingFixedFit);
if (table)
foreach (var (idx, actor) in _cutsceneService.Actors)
{
@ -695,11 +694,11 @@ public class DebugTab : Window, ITab, IUiService
}
}
using (var groupTree = TreeNode("Group"))
using (var groupTree = Im.Tree.Node("Group"u8))
{
if (groupTree)
{
using var table = Table("###PGroupTable", 2, ImGuiTableFlags.SizingFixedFit);
using var table = Im.Table.Begin("###PGroupTable"u8, 2, TableFlags.SizingFixedFit);
if (table)
{
ImGuiUtil.DrawTableColumn("Group Members");
@ -714,7 +713,7 @@ public class DebugTab : Window, ITab, IUiService
}
}
using (var bannerTree = TreeNode("Party Banner"))
using (var bannerTree = Im.Tree.Node("Party Banner"u8))
{
if (bannerTree)
{
@ -727,7 +726,7 @@ public class DebugTab : Window, ITab, IUiService
Penumbra.Dynamis.DrawPointer((nint)agent);
if (agent->Data != null)
{
using var table = Table("###PBannerTable", 2, ImGuiTableFlags.SizingFixedFit);
using var table = Im.Table.Begin("###PBannerTable"u8, 2, TableFlags.SizingFixedFit);
if (table)
for (var i = 0; i < 8; ++i)
{
@ -744,11 +743,11 @@ public class DebugTab : Window, ITab, IUiService
}
}
using (var tmbCache = TreeNode("TMB Cache"))
using (var tmbCache = Im.Tree.Node("TMB Cache"u8))
{
if (tmbCache)
{
using var table = Table("###TmbTable", 2, ImGuiTableFlags.SizingFixedFit);
using var table = Im.Table.Begin("###TmbTable"u8, 2, TableFlags.SizingFixedFit);
if (table)
foreach (var (id, name) in _schedulerService.ListedTmbs.OrderBy(kvp => kvp.Key))
{
@ -777,7 +776,7 @@ public class DebugTab : Window, ITab, IUiService
private void DrawFileTest()
{
using var node = TreeNode("Game File Test");
using var node = Im.Tree.Node("Game File Test"u8);
if (!node)
return;
@ -803,7 +802,7 @@ public class DebugTab : Window, ITab, IUiService
private void DrawChangedItemTest()
{
using var node = TreeNode("Changed Item Test");
using var node = Im.Tree.Node("Changed Item Test"u8);
if (!node)
return;
@ -855,13 +854,13 @@ public class DebugTab : Window, ITab, IUiService
private void DrawEmotes()
{
using var mainTree = TreeNode("Emotes");
using var mainTree = Im.Tree.Node("Emotes"u8);
if (!mainTree)
return;
ImGui.InputText("File Name", ref _emoteSearchFile, 256);
ImGui.InputText("Emote Name", ref _emoteSearchName, 256);
using var table = Table("##table", 2, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY | ImGuiTableFlags.SizingFixedFit,
using var table = Im.Table.Begin("##table"u8, 2, TableFlags.RowBackground | TableFlags.ScrollY | TableFlags.SizingFixedFit,
new Vector2(-1, 12 * ImGui.GetTextLineHeightWithSpacing()));
if (!table)
return;
@ -886,13 +885,13 @@ public class DebugTab : Window, ITab, IUiService
private void DrawActionTmbs()
{
using var mainTree = TreeNode("Action TMBs");
using var mainTree = Im.Tree.Node("Action TMBs"u8);
if (!mainTree)
return;
if (ImGui.InputText("Key", ref _tmbKeyFilter, 256))
_tmbKeyFilterU8 = CiByteString.FromString(_tmbKeyFilter, out var r, MetaDataComputation.All) ? r : CiByteString.Empty;
using var table = Table("##table", 2, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY | ImGuiTableFlags.SizingFixedFit,
using var table = Im.Table.Begin("##table"u8, 2, TableFlags.RowBackground | TableFlags.ScrollY | TableFlags.SizingFixedFit,
new Vector2(-1, 12 * ImGui.GetTextLineHeightWithSpacing()));
if (!table)
return;
@ -910,17 +909,17 @@ public class DebugTab : Window, ITab, IUiService
private void DrawStainTemplates()
{
using var mainTree = TreeNode("Staining Templates");
using var mainTree = Im.Tree.Node("Staining Templates"u8);
if (!mainTree)
return;
using (var legacyTree = TreeNode("stainingtemplate.stm"))
using (var legacyTree = Im.Tree.Node("stainingtemplate.stm"u8))
{
if (legacyTree)
DrawStainTemplatesFile(_stains.LegacyStmFile);
}
using (var gudTree = TreeNode("stainingtemplate_gud.stm"))
using (var gudTree = Im.Tree.Node("stainingtemplate_gud.stm"u8))
{
if (gudTree)
DrawStainTemplatesFile(_stains.GudStmFile);
@ -931,12 +930,12 @@ public class DebugTab : Window, ITab, IUiService
{
foreach (var (key, data) in stmFile.Entries)
{
using var tree = TreeNode($"Template {key}");
using var tree = Im.Tree.Node($"Template {key}");
if (!tree)
continue;
using var table = Table("##table", data.Colors.Length + data.Scalars.Length,
ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg);
using var table = Im.Table.Begin("##table"u8, data.Colors.Length + data.Scalars.Length,
TableFlags.SizingFixedFit | TableFlags.RowBackground);
if (!table)
continue;
@ -974,7 +973,7 @@ public class DebugTab : Window, ITab, IUiService
if (!enableShaderReplacementFixer)
return;
using var table = Table("##ShaderReplacementFixer", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit,
using var table = Im.Table.Begin("##ShaderReplacementFixer"u8, 3, TableFlags.RowBackground | TableFlags.SizingFixedFit,
-Vector2.UnitX);
if (!table)
return;
@ -1066,7 +1065,7 @@ public class DebugTab : Window, ITab, IUiService
DrawCopyableAddress("CharacterBase"u8, model);
using (var t1 = Table("##table", 2, ImGuiTableFlags.SizingFixedFit))
using (var t1 = Im.Table.Begin("##table"u8, 2, TableFlags.SizingFixedFit))
{
if (t1)
{
@ -1079,7 +1078,7 @@ public class DebugTab : Window, ITab, IUiService
}
}
using var table = Table($"##{name}DrawTable", 5, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit);
using var table = Im.Table.Begin($"##{name}DrawTable", 5, TableFlags.RowBackground | TableFlags.SizingFixedFit);
if (!table)
return;
@ -1120,7 +1119,7 @@ public class DebugTab : Window, ITab, IUiService
private string _crcInput = string.Empty;
private FullPath _crcPath = FullPath.Empty;
private unsafe void DrawCrcCache()
private void DrawCrcCache()
{
var header = ImUtf8.CollapsingHeader("CRC Cache"u8);
if (!header)
@ -1188,7 +1187,7 @@ public class DebugTab : Window, ITab, IUiService
if (!header)
return;
using var table = Table("##ProblemsTable", 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit);
using var table = Im.Table.Begin("##ProblemsTable"u8, 6, TableFlags.RowBackground | TableFlags.SizingFixedFit);
if (!table)
return;
@ -1240,10 +1239,7 @@ public class DebugTab : Window, ITab, IUiService
ImUtf8.Text($"Is Cloud Synced? {_cloudTesterReturn}");
if (_cloudTesterError is not null)
{
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed);
ImUtf8.Text($"{_cloudTesterError}");
}
Im.Text($"{_cloudTesterError}", ImGuiColors.DalamudRed);
}

View file

@ -84,7 +84,7 @@ public class RenderTargetDrawer(RenderTargetHdrEnabler renderTargetHdrEnabler, D
+ record.Offset);
if (texture != null)
{
using var color = Dalamud.Interface.Utility.Raii.ImRaii.PushColor(ImGuiCol.Text, ImGuiUtil.HalfBlendText(0xFF),
using var color = ImGuiColor.Text.Push(ImGuiUtil.HalfBlendText(0xFF),
texture->TextureFormat != record.OriginalTextureFormat);
ImUtf8.Text($"{texture->TextureFormat}");
}

View file

@ -1,6 +1,7 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using ImSharp;
using OtterGui.Text;
using Penumbra.Collections.Cache;
using Penumbra.GameData.Enums;
@ -90,10 +91,10 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
private static void DrawValues(in ShapeAttributeString shapeAttribute, ShapeAttributeHashSet set)
{
ImGui.TableNextColumn();
var disabledColor = Im.Style[ImGuiColor.TextDisabled];
if (set.All is { } value)
{
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled), !value);
using var color = ImGuiColor.Text.Push(disabledColor, !value);
ImUtf8.Text("All, "u8);
ImGui.SameLine(0, 0);
}
@ -103,7 +104,7 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
if (set[slot] is not { } value2)
continue;
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled), !value2);
using var color = ImGuiColor.Text.Push(disabledColor, !value2);
ImUtf8.Text($"All {slot.ToName()}, ");
ImGui.SameLine(0, 0);
}
@ -112,7 +113,7 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
{
if (set[gr] is { } value3)
{
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled), !value3);
using var color = ImGuiColor.Text.Push(disabledColor, !value3);
ImUtf8.Text($"All {gr.ToName()}, ");
ImGui.SameLine(0, 0);
}
@ -123,7 +124,7 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
if (set[slot, gr] is not { } value4)
continue;
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled), !value4);
using var color = ImGuiColor.Text.Push(disabledColor, !value4);
ImUtf8.Text($"All {gr.ToName()} {slot.ToName()}, ");
ImGui.SameLine(0, 0);
}
@ -138,7 +139,7 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
if (set[slot, GenderRace.Unknown] != enabled)
{
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled), !enabled);
using var color = ImGuiColor.Text.Push(disabledColor, !enabled);
ImUtf8.Text($"{slot.ToName()} {id.Id:D4}, ");
ImGui.SameLine(0, 0);
}
@ -153,7 +154,7 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
var gr = ShapeAttributeHashSet.GenderRaceValues[currentIndex];
if (set[slot, gr] != enabled)
{
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled), !enabled);
using var color = ImGuiColor.Text.Push(disabledColor, !enabled);
ImUtf8.Text($"{gr.ToName()} {slot.ToName()} #{id.Id:D4}, ");
ImGui.SameLine(0, 0);
}
@ -186,7 +187,7 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
ImGui.TableHeadersRow();
var disabledColor = ImGui.GetColorU32(ImGuiCol.TextDisabled);
var disabledColor = Im.Style[ImGuiColor.TextDisabled];
for (var i = 0; i < human.AsHuman->SlotCount; ++i)
{
ImUtf8.DrawTableColumn($"{(uint)i:D2}");
@ -205,7 +206,7 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
foreach (var (idx, (shape, flag)) in model->ModelResourceHandle->Shapes.Index())
{
var disabled = (mask & (1u << flag)) is 0;
using var color = ImRaii.PushColor(ImGuiCol.Text, disabledColor, disabled);
using var color = ImGuiColor.Text.Push(disabledColor, disabled);
ImUtf8.Text(shape.AsSpan());
ImGui.SameLine(0, 0);
ImUtf8.Text(", "u8);
@ -243,7 +244,7 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
ImGui.TableHeadersRow();
var disabledColor = ImGui.GetColorU32(ImGuiCol.TextDisabled);
var disabledColor = Im.Style[ImGuiColor.TextDisabled];
for (var i = 0; i < human.AsHuman->SlotCount; ++i)
{
ImUtf8.DrawTableColumn($"{(uint)i:D2}");
@ -262,7 +263,7 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
foreach (var (idx, (attribute, flag)) in model->ModelResourceHandle->Attributes.Index())
{
var disabled = (mask & (1u << flag)) is 0;
using var color = ImRaii.PushColor(ImGuiCol.Text, disabledColor, disabled);
using var color = ImGuiColor.Text.Push(disabledColor, disabled);
ImUtf8.Text(attribute.AsSpan());
ImGui.SameLine(0, 0);
ImUtf8.Text(", "u8);

View file

@ -1,6 +1,7 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.DragDrop;
using Dalamud.Interface.Utility.Raii;
using ImSharp;
using Lumina.Data.Files;
using OtterGui.Text;
using Penumbra.UI.Classes;
@ -47,7 +48,7 @@ public class TexHeaderDrawer(IDragDropManager dragDrop) : Luna.IUiService
if (_exception != null)
{
using var color = ImRaii.PushColor(ImGuiCol.Text, Colors.RegexWarningBorder);
using var color = ImGuiColor.Text.Push(Colors.RegexWarningBorder);
ImUtf8.TextWrapped($"Failure to load file:\n{_exception}");
}
else if (_tex != null)

View file

@ -95,7 +95,7 @@ public class ModsTab(
}
var frameHeight = new Vector2(0, ImGui.GetFrameHeight());
var frameColor = ImGui.GetColorU32(ImGuiCol.FrameBg);
var frameColor = ImGuiColor.FrameBackground.Get().Color;
using (var _ = ImRaii.Group())
{
using (ImRaii.PushFont(UiBuilder.IconFont))

View file

@ -242,7 +242,7 @@ public class SettingsTab : ITab, IUiService
/// </summary>
private bool DrawPressEnterWarning(string newName, string old, float width, bool saved, bool selected)
{
using var color = ImRaii.PushColor(ImGuiCol.Button, Colors.PressEnterWarningBg);
using var color = ImGuiColor.Button.Push(Colors.PressEnterWarningBg);
var w = new Vector2(width, 0);
var (text, valid) = CheckRootDirectoryPath(newName, old, selected);
@ -339,10 +339,9 @@ public class SettingsTab : ITab, IUiService
using (ImRaii.Group())
{
ImGui.SetNextItemWidth(UiHelpers.InputTextMinusButton3);
using (ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale, !_modManager.Valid))
using (var color = ImStyleBorder.Frame.Push(Colors.RegexWarningBorder, Im.Style.GlobalScale, !_modManager.Valid))
{
using var color = ImRaii.PushColor(ImGuiCol.Border, Colors.RegexWarningBorder)
.Push(ImGuiCol.TextDisabled, Colors.RegexWarningBorder, !_modManager.Valid);
color.Push(ImGuiColor.TextDisabled, Colors.RegexWarningBorder, !_modManager.Valid);
save = ImGui.InputTextWithHint("##rootDirectory", "Enter Root Directory here (MANDATORY)...", ref _newModDirectory,
RootDirectoryMaxLength, ImGuiInputTextFlags.EnterReturnsTrue);
}