diff --git a/Luna b/Luna index c764db88..2870892f 160000 --- a/Luna +++ b/Luna @@ -1 +1 @@ -Subproject commit c764db88097c88cd49f2bed4f60268d617f97fdb +Subproject commit 2870892f30f7b92bd74110c7da366779eae85ce7 diff --git a/Penumbra.GameData b/Penumbra.GameData index cf3d868e..3a5a03ff 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit cf3d868eeeb4ea3ea728ae15a8d09ec127ce80e9 +Subproject commit 3a5a03ff24e24151f80584cd4a8d45b36d94db36 diff --git a/Penumbra/ChangedItemMode.cs b/Penumbra/ChangedItemMode.cs index 4a1d2b98..471b3a77 100644 --- a/Penumbra/ChangedItemMode.cs +++ b/Penumbra/ChangedItemMode.cs @@ -1,57 +1,52 @@ -using Dalamud.Bindings.ImGui; -using OtterGui.Text; - -namespace Penumbra; +using ImSharp; +using Luna.Generators; -public enum ChangedItemMode -{ - GroupedCollapsed, - GroupedExpanded, - Alphabetical, -} - -public static class ChangedItemModeExtensions -{ - public static ReadOnlySpan ToName(this ChangedItemMode mode) - => mode switch - { - ChangedItemMode.GroupedCollapsed => "Grouped (Collapsed)"u8, - ChangedItemMode.GroupedExpanded => "Grouped (Expanded)"u8, - ChangedItemMode.Alphabetical => "Alphabetical"u8, - _ => "Error"u8, - }; - - public static ReadOnlySpan ToTooltip(this ChangedItemMode mode) - => mode switch - { - ChangedItemMode.GroupedCollapsed => - "Display items as groups by their model and slot. Collapse those groups to a single item by default. Prefers items with more changes affecting them or configured items as the main item."u8, - ChangedItemMode.GroupedExpanded => - "Display items as groups by their model and slot. Expand those groups showing all items by default. Prefers items with more changes affecting them or configured items as the main item."u8, - ChangedItemMode.Alphabetical => "Display all changed items in a single list sorted alphabetically."u8, - _ => ""u8, - }; - - public static bool DrawCombo(ReadOnlySpan label, ChangedItemMode value, float width, Action setter) - { - ImGui.SetNextItemWidth(width); - using var combo = ImUtf8.Combo(label, value.ToName()); - if (!combo) - return false; - - var ret = false; - foreach (var newValue in Enum.GetValues()) - { - var selected = ImUtf8.Selectable(newValue.ToName(), newValue == value); - if (selected) - { - ret = true; - setter(newValue); - } - - ImUtf8.HoverTooltip(newValue.ToTooltip()); - } - - return ret; - } -} +namespace Penumbra; + +[NamedEnum(Unknown: "Error")] +[TooltipEnum] +public enum ChangedItemMode +{ + [Name("Grouped (Collapsed)")] + [Tooltip( + "Display items as groups by their model and slot. Collapse those groups to a single item by default. Prefers items with more changes affecting them or configured items as the main item.")] + GroupedCollapsed, + + [Name("Grouped (Expanded)")] + [Tooltip( + "Display items as groups by their model and slot. Expand those groups showing all items by default. Prefers items with more changes affecting them or configured items as the main item.")] + GroupedExpanded, + + [Name("Alphabetical")] + [Tooltip("Display all changed items in a single list sorted alphabetically.")] + Alphabetical, +} + +public static partial class ChangedItemModeExtensions +{ + private static readonly ChangedItemModeCombo Combo = new(); + + private sealed class ChangedItemModeCombo() : SimpleFilterCombo(SimpleFilterType.Text) + { + public override StringU8 DisplayString(in ChangedItemMode value) + => new(value.ToNameU8()); + + public override string FilterString(in ChangedItemMode value) + => value.ToName(); + + public override StringU8 Tooltip(in ChangedItemMode value) + => new(value.Tooltip()); + + public override IEnumerable GetBaseItems() + => Enum.GetValues(); + } + + public static bool DrawCombo(ReadOnlySpan label, ChangedItemMode value, float width, Action setter) + { + if (!Combo.Draw(label, ref value, StringU8.Empty, width)) + return false; + + setter(value); + return true; + } +} diff --git a/Penumbra/Import/Models/ModelManager.cs b/Penumbra/Import/Models/ModelManager.cs index 3f684307..63b496f1 100644 --- a/Penumbra/Import/Models/ModelManager.cs +++ b/Penumbra/Import/Models/ModelManager.cs @@ -1,7 +1,6 @@ using Dalamud.Plugin.Services; using Lumina.Data.Parsing; using Luna; -using OtterGui.Tasks; using Penumbra.Collections.Manager; using Penumbra.GameData; using Penumbra.GameData.Data; diff --git a/Penumbra/Import/TexToolsImporter.Gui.cs b/Penumbra/Import/TexToolsImporter.Gui.cs index 0fbe3f3c..88068db6 100644 --- a/Penumbra/Import/TexToolsImporter.Gui.cs +++ b/Penumbra/Import/TexToolsImporter.Gui.cs @@ -1,7 +1,4 @@ -using Dalamud.Bindings.ImGui; using ImSharp; -using OtterGui; -using OtterGui.Raii; using Penumbra.Import.Structs; using Penumbra.UI.Classes; @@ -23,9 +20,9 @@ public partial class TexToolsImporter public bool DrawProgressInfo(Vector2 size) { - if (_modPackCount == 0) + if (_modPackCount is 0) { - ImGuiUtil.Center("Nothing to extract."); + ImEx.TextCentered("Nothing to extract."u8); return true; } @@ -35,40 +32,40 @@ public partial class TexToolsImporter return true; } - ImGui.NewLine(); + Im.Line.New(); var percentage = (float)_currentModPackIdx / _modPackCount; - ImGui.ProgressBar(percentage, size, $"Mod {_currentModPackIdx + 1} / {_modPackCount}"); - ImGui.NewLine(); - ImGui.TextUnformatted(State == ImporterState.DeduplicatingFiles + Im.ProgressBar(percentage, size, $"Mod {_currentModPackIdx + 1} / {_modPackCount}"); + Im.Line.New(); + Im.Text(State is ImporterState.DeduplicatingFiles ? $"Deduplicating {_currentModName}..." : $"Extracting {_currentModName}..."); if (_currentNumOptions > 1) { - ImGui.NewLine(); - ImGui.NewLine(); + Im.Line.New(); + Im.Line.New(); if (_currentOptionIdx >= _currentNumOptions) - ImGui.ProgressBar(1f, size, $"Extracted {_currentNumOptions} Options"); + Im.ProgressBar(1f, size, $"Extracted {_currentNumOptions} Options"); else - ImGui.ProgressBar(_currentOptionIdx / (float)_currentNumOptions, size, + Im.ProgressBar(_currentOptionIdx / (float)_currentNumOptions, size, $"Extracting Option {_currentOptionIdx + 1} / {_currentNumOptions}..."); - ImGui.NewLine(); - if (State != ImporterState.DeduplicatingFiles) - ImGui.TextUnformatted( + Im.Line.New(); + if (State is not ImporterState.DeduplicatingFiles) + Im.Text( $"Extracting Option {(_currentGroupName.Length == 0 ? string.Empty : $"{_currentGroupName} - ")}{_currentOptionName}..."); } - ImGui.NewLine(); - ImGui.NewLine(); + Im.Line.New(); + Im.Line.New(); if (_currentFileIdx >= _currentNumFiles) - ImGui.ProgressBar(1f, size, $"Extracted {_currentNumFiles} Files"); + Im.ProgressBar(1f, size, $"Extracted {_currentNumFiles} Files"); else - ImGui.ProgressBar(_currentFileIdx / (float)_currentNumFiles, size, $"Extracting File {_currentFileIdx + 1} / {_currentNumFiles}..."); + Im.ProgressBar(_currentFileIdx / (float)_currentNumFiles, size, $"Extracting File {_currentFileIdx + 1} / {_currentNumFiles}..."); - ImGui.NewLine(); - if (State != ImporterState.DeduplicatingFiles) - ImGui.TextUnformatted($"Extracting File {_currentFileName}..."); + Im.Line.New(); + if (State is not ImporterState.DeduplicatingFiles) + Im.Text($"Extracting File {_currentFileName}..."); return false; } @@ -77,31 +74,33 @@ public partial class TexToolsImporter { var success = ExtractedMods.Count(t => t.Error == null); - ImGui.TextUnformatted($"Successfully extracted {success} / {ExtractedMods.Count} files."); - ImGui.NewLine(); - using var table = ImRaii.Table("##files", 2); + Im.Text($"Successfully extracted {success} / {ExtractedMods.Count} files."); + Im.Line.New(); + using var table = Im.Table.Begin("##files"u8, 2); if (!table) return; foreach (var (file, dir, ex) in ExtractedMods) { - ImGui.TableNextColumn(); - ImGui.TextUnformatted(file.Name); - ImGui.TableNextColumn(); - if (ex == null) + table.DrawColumn(file.Name); + table.NextColumn(); + if (ex is null) { using var color = ImGuiColor.Text.Push(ColorId.FolderExpanded.Value()); - ImGui.TextUnformatted(dir?.FullName[(_baseDirectory.FullName.Length + 1)..] ?? "Unknown Directory"); + if (dir is null) + Im.Text("Unknown Directory"u8); + else + Im.Text(dir.FullName.AsSpan(_baseDirectory.FullName.Length + 1)); } else { using var color = ImGuiColor.Text.Push(ColorId.ConflictingMod.Value()); - ImGui.TextUnformatted(ex.Message); - ImGuiUtil.HoverTooltip(ex.ToString()); + Im.Text(ex.Message); + Im.Tooltip.OnHover($"{ex}"); } } } public bool DrawCancelButton(Vector2 size) - => ImGuiUtil.DrawDisabledButton("Cancel", size, string.Empty, _token.IsCancellationRequested); + => ImEx.Button("Cancel"u8, size, StringU8.Empty, _token.IsCancellationRequested); } diff --git a/Penumbra/Import/Textures/CombinedTexture.Manipulation.cs b/Penumbra/Import/Textures/CombinedTexture.Manipulation.cs index 46eef5f5..99da1fab 100644 --- a/Penumbra/Import/Textures/CombinedTexture.Manipulation.cs +++ b/Penumbra/Import/Textures/CombinedTexture.Manipulation.cs @@ -1,5 +1,4 @@ using ImSharp; -using OtterGui.Text; using Rgba32 = SixLabors.ImageSharp.PixelFormats.Rgba32; namespace Penumbra.Import.Textures; diff --git a/Penumbra/Import/Textures/TextureManager.cs b/Penumbra/Import/Textures/TextureManager.cs index 102645c7..72509d97 100644 --- a/Penumbra/Import/Textures/TextureManager.cs +++ b/Penumbra/Import/Textures/TextureManager.cs @@ -3,8 +3,7 @@ using Dalamud.Interface.Textures; using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Plugin.Services; using Lumina.Data.Files; -using OtterGui.Log; -using OtterGui.Tasks; +using Luna; using OtterTex; using SharpDX.Direct3D11; using SixLabors.ImageSharp; @@ -17,7 +16,7 @@ using Image = SixLabors.ImageSharp.Image; namespace Penumbra.Import.Textures; public sealed class TextureManager(IDataManager gameData, Logger logger, ITextureProvider textureProvider, IUiBuilder uiBuilder) - : SingleTaskQueue, IDisposable, Luna.IService + : SingleTaskQueue, IDisposable, IService { private readonly Logger _logger = logger; diff --git a/Penumbra/Interop/Hooks/Animation/ApricotListenerSoundPlay.cs b/Penumbra/Interop/Hooks/Animation/ApricotListenerSoundPlay.cs index 8838971c..de76b6d9 100644 --- a/Penumbra/Interop/Hooks/Animation/ApricotListenerSoundPlay.cs +++ b/Penumbra/Interop/Hooks/Animation/ApricotListenerSoundPlay.cs @@ -1,6 +1,6 @@ using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using OtterGui.Services; +using Luna; using Penumbra.Collections; using Penumbra.CrashHandler.Buffers; using Penumbra.GameData; diff --git a/Penumbra/Interop/Hooks/Animation/CharacterBaseLoadAnimation.cs b/Penumbra/Interop/Hooks/Animation/CharacterBaseLoadAnimation.cs index 22609afc..c50ad9d9 100644 --- a/Penumbra/Interop/Hooks/Animation/CharacterBaseLoadAnimation.cs +++ b/Penumbra/Interop/Hooks/Animation/CharacterBaseLoadAnimation.cs @@ -1,6 +1,6 @@ using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using OtterGui.Services; +using Luna; using Penumbra.CrashHandler.Buffers; using Penumbra.GameData; using Penumbra.Interop.PathResolving; diff --git a/Penumbra/Interop/Hooks/Animation/Dismount.cs b/Penumbra/Interop/Hooks/Animation/Dismount.cs index 17151083..cc87a258 100644 --- a/Penumbra/Interop/Hooks/Animation/Dismount.cs +++ b/Penumbra/Interop/Hooks/Animation/Dismount.cs @@ -1,6 +1,6 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Object; -using OtterGui.Services; +using Luna; using Penumbra.GameData; using Penumbra.Interop.PathResolving; diff --git a/Penumbra/Interop/Hooks/Animation/GetCachedScheduleResource.cs b/Penumbra/Interop/Hooks/Animation/GetCachedScheduleResource.cs index 6ce1f899..133cb5c5 100644 --- a/Penumbra/Interop/Hooks/Animation/GetCachedScheduleResource.cs +++ b/Penumbra/Interop/Hooks/Animation/GetCachedScheduleResource.cs @@ -1,6 +1,6 @@ using FFXIVClientStructs.FFXIV.Client.System.Scheduler.Resource; using JetBrains.Annotations; -using OtterGui.Services; +using Luna; using Penumbra.GameData; using Penumbra.Interop.Structs; using Penumbra.String; diff --git a/Penumbra/Interop/Hooks/Animation/LoadActionTmb.cs b/Penumbra/Interop/Hooks/Animation/LoadActionTmb.cs index 457465d2..671beca5 100644 --- a/Penumbra/Interop/Hooks/Animation/LoadActionTmb.cs +++ b/Penumbra/Interop/Hooks/Animation/LoadActionTmb.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.System.Scheduler.Resource; -using OtterGui.Services; +using Luna; using Penumbra.GameData; using Penumbra.Interop.Services; using Penumbra.String; diff --git a/Penumbra/Interop/Hooks/Animation/LoadAreaVfx.cs b/Penumbra/Interop/Hooks/Animation/LoadAreaVfx.cs index 29afd4ea..60fd0878 100644 --- a/Penumbra/Interop/Hooks/Animation/LoadAreaVfx.cs +++ b/Penumbra/Interop/Hooks/Animation/LoadAreaVfx.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Game.Object; -using OtterGui.Services; +using Luna; using Penumbra.Collections; using Penumbra.CrashHandler.Buffers; using Penumbra.GameData; diff --git a/Penumbra/Interop/Hooks/Animation/LoadCharacterSound.cs b/Penumbra/Interop/Hooks/Animation/LoadCharacterSound.cs index 91b70ede..7f4d44f3 100644 --- a/Penumbra/Interop/Hooks/Animation/LoadCharacterSound.cs +++ b/Penumbra/Interop/Hooks/Animation/LoadCharacterSound.cs @@ -1,6 +1,6 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Object; -using OtterGui.Services; +using Luna; using Penumbra.CrashHandler.Buffers; using Penumbra.Interop.PathResolving; using Penumbra.Services; diff --git a/Penumbra/Interop/Hooks/Animation/LoadTimelineResources.cs b/Penumbra/Interop/Hooks/Animation/LoadTimelineResources.cs index cdd82b95..134c8f16 100644 --- a/Penumbra/Interop/Hooks/Animation/LoadTimelineResources.cs +++ b/Penumbra/Interop/Hooks/Animation/LoadTimelineResources.cs @@ -1,8 +1,7 @@ using Dalamud.Game.ClientState.Conditions; using Dalamud.Plugin.Services; -using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.System.Scheduler.Base; -using OtterGui.Services; +using Luna; using Penumbra.Collections; using Penumbra.GameData; using Penumbra.GameData.Interop; diff --git a/Penumbra/Interop/Hooks/Animation/ScheduleClipUpdate.cs b/Penumbra/Interop/Hooks/Animation/ScheduleClipUpdate.cs index dfbc615a..81c76711 100644 --- a/Penumbra/Interop/Hooks/Animation/ScheduleClipUpdate.cs +++ b/Penumbra/Interop/Hooks/Animation/ScheduleClipUpdate.cs @@ -1,4 +1,4 @@ -using OtterGui.Services; +using Luna; using Penumbra.CrashHandler.Buffers; using Penumbra.GameData; using Penumbra.GameData.Interop; diff --git a/Penumbra/Interop/Hooks/Animation/SomeActionLoad.cs b/Penumbra/Interop/Hooks/Animation/SomeActionLoad.cs index e1751261..aafd2a05 100644 --- a/Penumbra/Interop/Hooks/Animation/SomeActionLoad.cs +++ b/Penumbra/Interop/Hooks/Animation/SomeActionLoad.cs @@ -1,6 +1,6 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Object; -using OtterGui.Services; +using Luna; using Penumbra.CrashHandler.Buffers; using Penumbra.GameData; using Penumbra.Interop.PathResolving; diff --git a/Penumbra/Interop/Hooks/Animation/SomeMountAnimation.cs b/Penumbra/Interop/Hooks/Animation/SomeMountAnimation.cs index 75f1240a..871a64c2 100644 --- a/Penumbra/Interop/Hooks/Animation/SomeMountAnimation.cs +++ b/Penumbra/Interop/Hooks/Animation/SomeMountAnimation.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using OtterGui.Services; +using Luna; using Penumbra.GameData; using Penumbra.Interop.PathResolving; diff --git a/Penumbra/Interop/Hooks/Animation/SomePapLoad.cs b/Penumbra/Interop/Hooks/Animation/SomePapLoad.cs index f19e4ce2..1278883e 100644 --- a/Penumbra/Interop/Hooks/Animation/SomePapLoad.cs +++ b/Penumbra/Interop/Hooks/Animation/SomePapLoad.cs @@ -1,4 +1,4 @@ -using OtterGui.Services; +using Luna; using Penumbra.CrashHandler.Buffers; using Penumbra.GameData; using Penumbra.GameData.Interop; diff --git a/Penumbra/Interop/Hooks/Animation/SomeParasolAnimation.cs b/Penumbra/Interop/Hooks/Animation/SomeParasolAnimation.cs index 9df8d4eb..2d3b17b3 100644 --- a/Penumbra/Interop/Hooks/Animation/SomeParasolAnimation.cs +++ b/Penumbra/Interop/Hooks/Animation/SomeParasolAnimation.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using OtterGui.Services; +using Luna; using Penumbra.GameData; using Penumbra.Interop.PathResolving; diff --git a/Penumbra/Interop/Hooks/Meta/AtchCallerHook1.cs b/Penumbra/Interop/Hooks/Meta/AtchCallerHook1.cs index 2a3d7468..c72c9dfe 100644 --- a/Penumbra/Interop/Hooks/Meta/AtchCallerHook1.cs +++ b/Penumbra/Interop/Hooks/Meta/AtchCallerHook1.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character; -using OtterGui.Services; +using Luna; using Penumbra.GameData; using Penumbra.GameData.Interop; using Penumbra.Interop.PathResolving; @@ -20,7 +20,7 @@ public unsafe class AtchCallerHook1 : FastHook, IDispo Task = hooks.CreateHook("AtchCaller1", Sigs.AtchCaller1, Detour, metaState.Config.EnableMods && !HookOverrides.Instance.Meta.AtchCaller1); if (!HookOverrides.Instance.Meta.AtchCaller1) - _metaState.Config.ModsEnabled += Toggle; + _metaState.Config.ModsEnabled += Set; } private void Detour(DrawObjectData* data, uint slot, nint unk, Model playerModel) @@ -34,5 +34,5 @@ public unsafe class AtchCallerHook1 : FastHook, IDispo } public void Dispose() - => _metaState.Config.ModsEnabled -= Toggle; + => _metaState.Config.ModsEnabled -= Set; } diff --git a/Penumbra/Interop/Hooks/Meta/AtchCallerHook2.cs b/Penumbra/Interop/Hooks/Meta/AtchCallerHook2.cs index af38ce50..18d6902e 100644 --- a/Penumbra/Interop/Hooks/Meta/AtchCallerHook2.cs +++ b/Penumbra/Interop/Hooks/Meta/AtchCallerHook2.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character; -using OtterGui.Services; +using Luna; using Penumbra.GameData; using Penumbra.GameData.Interop; using Penumbra.Interop.PathResolving; @@ -20,7 +20,7 @@ public unsafe class AtchCallerHook2 : FastHook, IDispo Task = hooks.CreateHook("AtchCaller2", Sigs.AtchCaller2, Detour, metaState.Config.EnableMods && !HookOverrides.Instance.Meta.AtchCaller2); if (!HookOverrides.Instance.Meta.AtchCaller2) - _metaState.Config.ModsEnabled += Toggle; + _metaState.Config.ModsEnabled += Set; } private void Detour(DrawObjectData* data, uint slot, nint unk, Model playerModel, uint unk2) @@ -34,5 +34,5 @@ public unsafe class AtchCallerHook2 : FastHook, IDispo } public void Dispose() - => _metaState.Config.ModsEnabled -= Toggle; + => _metaState.Config.ModsEnabled -= Set; } diff --git a/Penumbra/Interop/Hooks/Meta/CalculateHeight.cs b/Penumbra/Interop/Hooks/Meta/CalculateHeight.cs index 3dac17bd..5fe0f39b 100644 --- a/Penumbra/Interop/Hooks/Meta/CalculateHeight.cs +++ b/Penumbra/Interop/Hooks/Meta/CalculateHeight.cs @@ -1,6 +1,6 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Object; -using OtterGui.Services; +using Luna; using Penumbra.Interop.PathResolving; namespace Penumbra.Interop.Hooks.Meta; diff --git a/Penumbra/Interop/Hooks/Meta/ChangeCustomize.cs b/Penumbra/Interop/Hooks/Meta/ChangeCustomize.cs index 523ae610..e84376a8 100644 --- a/Penumbra/Interop/Hooks/Meta/ChangeCustomize.cs +++ b/Penumbra/Interop/Hooks/Meta/ChangeCustomize.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using OtterGui.Services; +using Luna; using Penumbra.Collections; using Penumbra.GameData; using Penumbra.GameData.Structs; diff --git a/Penumbra/Interop/Hooks/Meta/EqdpAccessoryHook.cs b/Penumbra/Interop/Hooks/Meta/EqdpAccessoryHook.cs index 43328600..216fa7fb 100644 --- a/Penumbra/Interop/Hooks/Meta/EqdpAccessoryHook.cs +++ b/Penumbra/Interop/Hooks/Meta/EqdpAccessoryHook.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using OtterGui.Services; +using Luna; using Penumbra.GameData; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; @@ -19,7 +19,7 @@ public unsafe class EqdpAccessoryHook : FastHook, ID Task = hooks.CreateHook("GetEqdpAccessoryEntry", Sigs.GetEqdpAccessoryEntry, Detour, metaState.Config.EnableMods && !HookOverrides.Instance.Meta.EqdpAccessoryHook); if (!HookOverrides.Instance.Meta.EqdpAccessoryHook) - _metaState.Config.ModsEnabled += Toggle; + _metaState.Config.ModsEnabled += Set; } private void Detour(CharacterUtility* utility, EqdpEntry* entry, uint setId, uint raceCode) @@ -33,5 +33,5 @@ public unsafe class EqdpAccessoryHook : FastHook, ID } public void Dispose() - => _metaState.Config.ModsEnabled -= Toggle; + => _metaState.Config.ModsEnabled -= Set; } diff --git a/Penumbra/Interop/Hooks/Meta/EqdpEquipHook.cs b/Penumbra/Interop/Hooks/Meta/EqdpEquipHook.cs index fa0d5a29..1dd28bbd 100644 --- a/Penumbra/Interop/Hooks/Meta/EqdpEquipHook.cs +++ b/Penumbra/Interop/Hooks/Meta/EqdpEquipHook.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using OtterGui.Services; +using Luna; using Penumbra.GameData; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; @@ -18,7 +18,7 @@ public unsafe class EqdpEquipHook : FastHook, IDisposabl _metaState = metaState; Task = hooks.CreateHook("GetEqdpEquipEntry", Sigs.GetEqdpEquipEntry, Detour, metaState.Config.EnableMods && !HookOverrides.Instance.Meta.EqdpEquipHook); if (!HookOverrides.Instance.Meta.EqdpEquipHook) - _metaState.Config.ModsEnabled += Toggle; + _metaState.Config.ModsEnabled += Set; } private void Detour(CharacterUtility* utility, EqdpEntry* entry, uint setId, uint raceCode) @@ -32,5 +32,5 @@ public unsafe class EqdpEquipHook : FastHook, IDisposabl } public void Dispose() - => _metaState.Config.ModsEnabled -= Toggle; + => _metaState.Config.ModsEnabled -= Set; } diff --git a/Penumbra/Interop/Hooks/Meta/EqpHook.cs b/Penumbra/Interop/Hooks/Meta/EqpHook.cs index f35b922b..bc628e99 100644 --- a/Penumbra/Interop/Hooks/Meta/EqpHook.cs +++ b/Penumbra/Interop/Hooks/Meta/EqpHook.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using OtterGui.Services; +using Luna; using Penumbra.GameData; using Penumbra.GameData.Structs; using Penumbra.Interop.PathResolving; @@ -18,7 +18,7 @@ public unsafe class EqpHook : FastHook, IDisposable Task = hooks.CreateHook("GetEqpFlags", Sigs.GetEqpEntry, Detour, metaState.Config.EnableMods && !HookOverrides.Instance.Meta.EqpHook); if (!HookOverrides.Instance.Meta.EqpHook) - _metaState.Config.ModsEnabled += Toggle; + _metaState.Config.ModsEnabled += Set; } private void Detour(CharacterUtility* utility, EqpEntry* flags, CharacterArmor* armor) @@ -37,5 +37,5 @@ public unsafe class EqpHook : FastHook, IDisposable } public void Dispose() - => _metaState.Config.ModsEnabled -= Toggle; + => _metaState.Config.ModsEnabled -= Set; } diff --git a/Penumbra/Interop/Hooks/Meta/EstHook.cs b/Penumbra/Interop/Hooks/Meta/EstHook.cs index 8284eb69..fc9de4f8 100644 --- a/Penumbra/Interop/Hooks/Meta/EstHook.cs +++ b/Penumbra/Interop/Hooks/Meta/EstHook.cs @@ -1,4 +1,4 @@ -using OtterGui.Services; +using Luna; using Penumbra.GameData; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; @@ -23,7 +23,7 @@ public unsafe class EstHook : FastHook, IDisposable Task = hooks.CreateHook("FindEstEntry", Sigs.FindEstEntry, Detour, metaState.Config.EnableMods && !HookOverrides.Instance.Meta.EstHook); if (!HookOverrides.Instance.Meta.EstHook) - _metaState.Config.ModsEnabled += Toggle; + _metaState.Config.ModsEnabled += Set; } private EstEntry Detour(ResourceHandle* estResource, uint genderRace, uint id) @@ -59,5 +59,5 @@ public unsafe class EstHook : FastHook, IDisposable } public void Dispose() - => _metaState.Config.ModsEnabled -= Toggle; + => _metaState.Config.ModsEnabled -= Set; } diff --git a/Penumbra/Interop/Hooks/Meta/GmpHook.cs b/Penumbra/Interop/Hooks/Meta/GmpHook.cs index d656ebdb..6bb181f1 100644 --- a/Penumbra/Interop/Hooks/Meta/GmpHook.cs +++ b/Penumbra/Interop/Hooks/Meta/GmpHook.cs @@ -1,9 +1,7 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using Lumina.Data.Parsing.Uld; -using OtterGui.Services; +using Luna; using Penumbra.GameData; using Penumbra.Interop.PathResolving; -using Penumbra.Meta.Files; using Penumbra.Meta.Manipulations; namespace Penumbra.Interop.Hooks.Meta; @@ -20,7 +18,7 @@ public unsafe class GmpHook : FastHook, IDisposable Task = hooks.CreateHook("GetGmpEntry", Sigs.GetGmpEntry, Detour, metaState.Config.EnableMods && !HookOverrides.Instance.Meta.GmpHook); if (!HookOverrides.Instance.Meta.GmpHook) - _metaState.Config.ModsEnabled += Toggle; + _metaState.Config.ModsEnabled += Set; } private ulong Detour(CharacterUtility* characterUtility, ulong* outputEntry, ushort setId) @@ -39,5 +37,5 @@ public unsafe class GmpHook : FastHook, IDisposable } public void Dispose() - => _metaState.Config.ModsEnabled -= Toggle; + => _metaState.Config.ModsEnabled -= Set; } diff --git a/Penumbra/Interop/Hooks/Meta/ModelLoadComplete.cs b/Penumbra/Interop/Hooks/Meta/ModelLoadComplete.cs index 4b9b05b1..d18e06bb 100644 --- a/Penumbra/Interop/Hooks/Meta/ModelLoadComplete.cs +++ b/Penumbra/Interop/Hooks/Meta/ModelLoadComplete.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using OtterGui.Services; +using Luna; using Penumbra.Interop.PathResolving; namespace Penumbra.Interop.Hooks.Meta; diff --git a/Penumbra/Interop/Hooks/Meta/RspBustHook.cs b/Penumbra/Interop/Hooks/Meta/RspBustHook.cs index c49556bf..1a95307c 100644 --- a/Penumbra/Interop/Hooks/Meta/RspBustHook.cs +++ b/Penumbra/Interop/Hooks/Meta/RspBustHook.cs @@ -1,4 +1,4 @@ -using OtterGui.Services; +using Luna; using Penumbra.GameData; using Penumbra.GameData.Enums; using Penumbra.Interop.PathResolving; @@ -22,7 +22,7 @@ public unsafe class RspBustHook : FastHook, IDisposable Task = hooks.CreateHook("GetRspBust", Sigs.GetRspBust, Detour, metaState.Config.EnableMods && !HookOverrides.Instance.Meta.RspBustHook); if (!HookOverrides.Instance.Meta.RspBustHook) - _metaState.Config.ModsEnabled += Toggle; + _metaState.Config.ModsEnabled += Set; } private float* Detour(nint cmpResource, float* storage, SubRace clan, byte gender, byte bodyType, byte bustSize) @@ -69,5 +69,5 @@ public unsafe class RspBustHook : FastHook, IDisposable } public void Dispose() - => _metaState.Config.ModsEnabled -= Toggle; + => _metaState.Config.ModsEnabled -= Set; } diff --git a/Penumbra/Interop/Hooks/Meta/RspHeightHook.cs b/Penumbra/Interop/Hooks/Meta/RspHeightHook.cs index 49180d6e..fd0de23b 100644 --- a/Penumbra/Interop/Hooks/Meta/RspHeightHook.cs +++ b/Penumbra/Interop/Hooks/Meta/RspHeightHook.cs @@ -1,4 +1,4 @@ -using OtterGui.Services; +using Luna; using Penumbra.GameData; using Penumbra.GameData.Enums; using Penumbra.Interop.PathResolving; @@ -22,7 +22,7 @@ public class RspHeightHook : FastHook, IDisposable Task = hooks.CreateHook("GetRspHeight", Sigs.GetRspHeight, Detour, metaState.Config.EnableMods && !HookOverrides.Instance.Meta.RspHeightHook); if (!HookOverrides.Instance.Meta.RspHeightHook) - _metaState.Config.ModsEnabled += Toggle; + _metaState.Config.ModsEnabled += Set; } private unsafe float Detour(nint cmpResource, SubRace clan, byte gender, byte bodyType, byte height) @@ -79,5 +79,5 @@ public class RspHeightHook : FastHook, IDisposable } public void Dispose() - => _metaState.Config.ModsEnabled -= Toggle; + => _metaState.Config.ModsEnabled -= Set; } diff --git a/Penumbra/Interop/Hooks/Meta/RspSetupCharacter.cs b/Penumbra/Interop/Hooks/Meta/RspSetupCharacter.cs index 952a2e29..df0de37e 100644 --- a/Penumbra/Interop/Hooks/Meta/RspSetupCharacter.cs +++ b/Penumbra/Interop/Hooks/Meta/RspSetupCharacter.cs @@ -1,6 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using OtterGui.Services; -using Penumbra.Collections; +using Luna; using Penumbra.GameData; using Penumbra.Interop.PathResolving; diff --git a/Penumbra/Interop/Hooks/Meta/RspTailHook.cs b/Penumbra/Interop/Hooks/Meta/RspTailHook.cs index b434efa6..783c0f80 100644 --- a/Penumbra/Interop/Hooks/Meta/RspTailHook.cs +++ b/Penumbra/Interop/Hooks/Meta/RspTailHook.cs @@ -1,4 +1,4 @@ -using OtterGui.Services; +using Luna; using Penumbra.GameData; using Penumbra.GameData.Enums; using Penumbra.Interop.PathResolving; @@ -22,7 +22,7 @@ public class RspTailHook : FastHook, IDisposable Task = hooks.CreateHook("GetRspTail", Sigs.GetRspTail, Detour, metaState.Config.EnableMods && !HookOverrides.Instance.Meta.RspTailHook); if (!HookOverrides.Instance.Meta.RspTailHook) - _metaState.Config.ModsEnabled += Toggle; + _metaState.Config.ModsEnabled += Set; } private unsafe float Detour(nint cmpResource, Race race, byte gender, byte isSecondSubRace, byte bodyType, byte tailLength) @@ -73,5 +73,5 @@ public class RspTailHook : FastHook, IDisposable } public void Dispose() - => _metaState.Config.ModsEnabled -= Toggle; + => _metaState.Config.ModsEnabled -= Set; } diff --git a/Penumbra/Interop/Hooks/Meta/SetupVisor.cs b/Penumbra/Interop/Hooks/Meta/SetupVisor.cs index 063a9462..77f82b50 100644 --- a/Penumbra/Interop/Hooks/Meta/SetupVisor.cs +++ b/Penumbra/Interop/Hooks/Meta/SetupVisor.cs @@ -1,6 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using OtterGui.Services; -using Penumbra.Collections; +using Luna; using Penumbra.GameData; using Penumbra.Interop.PathResolving; diff --git a/Penumbra/Interop/Hooks/Meta/UpdateModel.cs b/Penumbra/Interop/Hooks/Meta/UpdateModel.cs index 9189ce3b..3401b806 100644 --- a/Penumbra/Interop/Hooks/Meta/UpdateModel.cs +++ b/Penumbra/Interop/Hooks/Meta/UpdateModel.cs @@ -1,6 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using OtterGui.Services; -using Penumbra.Collections; +using Luna; using Penumbra.GameData; using Penumbra.Interop.PathResolving; diff --git a/Penumbra/Interop/Hooks/Meta/UpdateRender.cs b/Penumbra/Interop/Hooks/Meta/UpdateRender.cs index ef0068b6..156a5cac 100644 --- a/Penumbra/Interop/Hooks/Meta/UpdateRender.cs +++ b/Penumbra/Interop/Hooks/Meta/UpdateRender.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using OtterGui.Services; +using Luna; using Penumbra.Interop.PathResolving; namespace Penumbra.Interop.Hooks.Meta; diff --git a/Penumbra/Interop/Hooks/Objects/EnableDraw.cs b/Penumbra/Interop/Hooks/Objects/EnableDraw.cs index 40c7a9fe..f9e068f3 100644 --- a/Penumbra/Interop/Hooks/Objects/EnableDraw.cs +++ b/Penumbra/Interop/Hooks/Objects/EnableDraw.cs @@ -1,6 +1,6 @@ using Dalamud.Hooking; using FFXIVClientStructs.FFXIV.Client.Game.Object; -using OtterGui.Services; +using Luna; using Penumbra.GameData; namespace Penumbra.Interop.Hooks.Objects; @@ -9,7 +9,7 @@ namespace Penumbra.Interop.Hooks.Objects; /// EnableDraw is what creates DrawObjects for gameObjects, /// so we always keep track of the current GameObject to be able to link it to the DrawObject. /// -public sealed unsafe class EnableDraw : Luna.IHookService +public sealed unsafe class EnableDraw : IHookService { private readonly Task> _task; private readonly GameState _state; diff --git a/Penumbra/Interop/Hooks/Objects/SetupPlayerNpc.cs b/Penumbra/Interop/Hooks/Objects/SetupPlayerNpc.cs index 8f1226c3..dd725f54 100644 --- a/Penumbra/Interop/Hooks/Objects/SetupPlayerNpc.cs +++ b/Penumbra/Interop/Hooks/Objects/SetupPlayerNpc.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character; -using OtterGui.Services; +using Luna; using Penumbra.GameData; namespace Penumbra.Interop.Hooks.Objects; diff --git a/Penumbra/Interop/Hooks/PostProcessing/HumanSetupScalingHook.cs b/Penumbra/Interop/Hooks/PostProcessing/HumanSetupScalingHook.cs index 870229d6..383809ca 100644 --- a/Penumbra/Interop/Hooks/PostProcessing/HumanSetupScalingHook.cs +++ b/Penumbra/Interop/Hooks/PostProcessing/HumanSetupScalingHook.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using OtterGui.Services; +using Luna; namespace Penumbra.Interop.Hooks.PostProcessing; diff --git a/Penumbra/Interop/Hooks/Resources/ApricotResourceLoad.cs b/Penumbra/Interop/Hooks/Resources/ApricotResourceLoad.cs index 40860b0b..2876df21 100644 --- a/Penumbra/Interop/Hooks/Resources/ApricotResourceLoad.cs +++ b/Penumbra/Interop/Hooks/Resources/ApricotResourceLoad.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; -using OtterGui.Services; +using Luna; using Penumbra.GameData; namespace Penumbra.Interop.Hooks.Resources; diff --git a/Penumbra/Interop/Hooks/Resources/LoadMtrl.cs b/Penumbra/Interop/Hooks/Resources/LoadMtrl.cs index bf7584f0..81f038e8 100644 --- a/Penumbra/Interop/Hooks/Resources/LoadMtrl.cs +++ b/Penumbra/Interop/Hooks/Resources/LoadMtrl.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; -using OtterGui.Services; +using Luna; using Penumbra.Communication; using Penumbra.GameData; using Penumbra.Services; diff --git a/Penumbra/Interop/Hooks/Resources/LoadMtrlTex.cs b/Penumbra/Interop/Hooks/Resources/LoadMtrlTex.cs index 1866e859..c1349a69 100644 --- a/Penumbra/Interop/Hooks/Resources/LoadMtrlTex.cs +++ b/Penumbra/Interop/Hooks/Resources/LoadMtrlTex.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; -using OtterGui.Services; +using Luna; using Penumbra.GameData; namespace Penumbra.Interop.Hooks.Resources; diff --git a/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs b/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs index c204f141..ce8db759 100644 --- a/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs +++ b/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs @@ -1,6 +1,6 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; -using OtterGui.Text.HelperObjects; +using Luna; using Penumbra.GameData.Data; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; diff --git a/Penumbra/Interop/ResourceTree/ResolveContext.cs b/Penumbra/Interop/ResourceTree/ResolveContext.cs index d48a2794..eb5b9324 100644 --- a/Penumbra/Interop/ResourceTree/ResolveContext.cs +++ b/Penumbra/Interop/ResourceTree/ResolveContext.cs @@ -3,7 +3,6 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Render; using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; using FFXIVClientStructs.Interop; using Luna; -using OtterGui.Text.HelperObjects; using Penumbra.Api.Enums; using Penumbra.Collections; using Penumbra.GameData.Data; @@ -17,6 +16,7 @@ using Penumbra.String.Classes; using Penumbra.UI; using static Penumbra.Interop.Structs.StructExtensions; using CharaBase = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.CharacterBase; +using SpanTextWriter = Luna.SpanTextWriter; namespace Penumbra.Interop.ResourceTree; diff --git a/Penumbra/Mods/Editor/ModBackup.cs b/Penumbra/Mods/Editor/ModBackup.cs index 994ca0b5..51ec8a25 100644 --- a/Penumbra/Mods/Editor/ModBackup.cs +++ b/Penumbra/Mods/Editor/ModBackup.cs @@ -1,4 +1,3 @@ -using OtterGui.Tasks; using Penumbra.Mods.Manager; namespace Penumbra.Mods.Editor; @@ -77,7 +76,7 @@ public class ModBackup return; CreatingBackup = true; - await AsyncTask.Run(Create); + await Task.Run(Create); CreatingBackup = false; } diff --git a/Penumbra/Mods/Editor/ModMerger.cs b/Penumbra/Mods/Editor/ModMerger.cs index 4b63de56..a8b5f294 100644 --- a/Penumbra/Mods/Editor/ModMerger.cs +++ b/Penumbra/Mods/Editor/ModMerger.cs @@ -59,9 +59,6 @@ public class ModMerger : IDisposable, IService _communicator.ModPathChanged.Unsubscribe(OnModPathChange); } - public IEnumerable ModsWithoutCurrent - => _mods.Where(m => m != MergeFromMod); - public bool CanMerge => MergeToMod != null && MergeToMod != MergeFromMod; diff --git a/Penumbra/Mods/Editor/ModNormalizer.cs b/Penumbra/Mods/Editor/ModNormalizer.cs index 0c5cdd89..60b6a839 100644 --- a/Penumbra/Mods/Editor/ModNormalizer.cs +++ b/Penumbra/Mods/Editor/ModNormalizer.cs @@ -1,6 +1,5 @@ using Dalamud.Interface.ImGuiNotification; using Luna; -using OtterGui.Tasks; using Penumbra.Mods.Groups; using Penumbra.Mods.Manager; using Penumbra.Mods.SubMods; @@ -9,7 +8,7 @@ using Penumbra.String.Classes; namespace Penumbra.Mods.Editor; -public class ModNormalizer(ModManager modManager, Configuration config, SaveService saveService) : Luna.IService +public class ModNormalizer(ModManager modManager, Configuration config, SaveService saveService) : IService { private readonly List>> _redirections = []; @@ -36,7 +35,7 @@ public class ModNormalizer(ModManager modManager, Configuration config, SaveServ Step = 0; TotalSteps = mod.TotalFileCount + 5; - Worker = TrackedTask.Run(NormalizeSync); + Worker = Task.Run(NormalizeSync); } public void NormalizeUi(DirectoryInfo modDirectory) diff --git a/Penumbra/Mods/FeatureChecker.cs b/Penumbra/Mods/FeatureChecker.cs index c97a860c..7f742be6 100644 --- a/Penumbra/Mods/FeatureChecker.cs +++ b/Penumbra/Mods/FeatureChecker.cs @@ -1,95 +1,90 @@ -using System.Collections.Frozen; -using Dalamud.Bindings.ImGui; -using Dalamud.Interface.ImGuiNotification; -using Dalamud.Interface.Utility.Raii; -using ImSharp; -using OtterGui.Text; -using Penumbra.Mods.Manager; -using Penumbra.UI.Classes; -using Notification = Luna.Notification; - -namespace Penumbra.Mods; - -public static class FeatureChecker -{ - /// Manually setup supported features to exclude None and Invalid and not make something supported too early. - private static readonly FrozenDictionary SupportedFlags = new[] - { - FeatureFlags.Atch, - FeatureFlags.Shp, - FeatureFlags.Atr, - }.ToFrozenDictionary(f => f.ToString(), f => f); - - public static IReadOnlyCollection SupportedFeatures - => SupportedFlags.Keys; - - public static FeatureFlags ParseFlags(string modDirectory, string modName, IEnumerable features) - { - var featureFlags = FeatureFlags.None; - HashSet missingFeatures = []; - foreach (var feature in features) - { - if (SupportedFlags.TryGetValue(feature, out var featureFlag)) - featureFlags |= featureFlag; - else - missingFeatures.Add(feature); - } - - if (missingFeatures.Count > 0) - { - Penumbra.Messager.AddMessage(new Notification( - $"Please update Penumbra to use the mod {modName}{(modDirectory != modName ? $" at {modDirectory}" : string.Empty)}!\n\nLoading failed because it requires the unsupported feature{(missingFeatures.Count > 1 ? $"s\n\n\t[{string.Join("], [", missingFeatures)}]." : $" [{missingFeatures.First()}].")}", - NotificationType.Warning)); - return FeatureFlags.Invalid; - } - - return featureFlags; - } - - public static bool Supported(string features) - => SupportedFlags.ContainsKey(features); - - public static void DrawFeatureFlagInput(ModDataEditor editor, Mod mod, float width) - { - const int numButtons = 5; - var innerSpacing = ImGui.GetStyle().ItemInnerSpacing; - var size = new Vector2((width - (numButtons - 1) * innerSpacing.X) / numButtons, 0); - var buttonColor = Im.Style[ImGuiColor.FrameBackground]; - var textColor = Im.Style[ImGuiColor.TextDisabled]; +using System.Collections.Frozen; +using ImSharp; +using Penumbra.Mods.Manager; +using Penumbra.UI.Classes; +using Notification = Luna.Notification; + +namespace Penumbra.Mods; + +public static class FeatureChecker +{ + /// Manually setup supported features to exclude None and Invalid and not make something supported too early. + private static readonly FrozenDictionary SupportedFlags = new[] + { + FeatureFlags.Atch, + FeatureFlags.Shp, + FeatureFlags.Atr, + }.ToFrozenDictionary(f => f.ToString(), f => f); + + public static IReadOnlyCollection SupportedFeatures + => SupportedFlags.Keys; + + public static FeatureFlags ParseFlags(string modDirectory, string modName, IEnumerable features) + { + var featureFlags = FeatureFlags.None; + HashSet missingFeatures = []; + foreach (var feature in features) + { + if (SupportedFlags.TryGetValue(feature, out var featureFlag)) + featureFlags |= featureFlag; + else + missingFeatures.Add(feature); + } + + if (missingFeatures.Count > 0) + { + Penumbra.Messager.AddMessage(new Notification($"Please update Penumbra to use the mod {modName}{(modDirectory != modName ? $" at {modDirectory}" : string.Empty)}!\n\n" + + $"Loading failed because it requires the unsupported feature{(missingFeatures.Count > 1 ? $"s\n\n\t[{string.Join("], [", missingFeatures)}]." : $" [{missingFeatures.First()}].")}")); + return FeatureFlags.Invalid; + } + + return featureFlags; + } + + public static bool Supported(string features) + => SupportedFlags.ContainsKey(features); + + public static void DrawFeatureFlagInput(ModDataEditor editor, Mod mod, float width) + { + const int numButtons = 5; + var innerSpacing = Im.Style.ItemInnerSpacing; + var size = new Vector2((width - (numButtons - 1) * innerSpacing.X) / numButtons, 0); + 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) - .Push(ImGuiColor.Text, textColor)) - { - foreach (var flag in SupportedFlags.Values) - { - if (mod.RequiredFeatures.HasFlag(flag)) + .Push(ImStyleDouble.ItemSpacing, innerSpacing) + .Push(ImGuiColor.Button, buttonColor) + .Push(ImGuiColor.Text, textColor)) + { + foreach (var flag in SupportedFlags.Values) + { + if (mod.RequiredFeatures.HasFlag(flag)) { - style.Push(ImStyleSingle.FrameBorderThickness, ImUtf8.GlobalScale); - style.PopColor(2); - if (Im.Button($"{flag}", size)) - editor.ChangeRequiredFeatures(mod, mod.RequiredFeatures & ~flag); - style.Push(ImGuiColor.Button, buttonColor) - .Push(ImGuiColor.Text, textColor); - style.PopStyle(); - } - else if (Im.Button($"{flag}", size)) - { - editor.ChangeRequiredFeatures(mod, mod.RequiredFeatures | flag); - } - - Im.Line.Same(); - } - } - - if (ImUtf8.ButtonEx("Compute"u8, "Compute the required features automatically from the used features."u8, size)) - editor.ChangeRequiredFeatures(mod, mod.ComputeRequiredFeatures()); - - Im.Line.Same(); - if (ImUtf8.ButtonEx("Clear"u8, "Clear all required features."u8, size)) - editor.ChangeRequiredFeatures(mod, FeatureFlags.None); - - Im.Line.Same(); - ImUtf8.Text("Required Features"u8); - } -} + style.Push(ImStyleSingle.FrameBorderThickness, Im.Style.GlobalScale); + style.PopColor(2); + if (Im.Button($"{flag}", size)) + editor.ChangeRequiredFeatures(mod, mod.RequiredFeatures & ~flag); + style.Push(ImGuiColor.Button, buttonColor) + .Push(ImGuiColor.Text, textColor); + style.PopStyle(); + } + else if (Im.Button($"{flag}", size)) + { + editor.ChangeRequiredFeatures(mod, mod.RequiredFeatures | flag); + } + + Im.Line.Same(); + } + } + + if (ImEx.Button("Compute"u8, size, "Compute the required features automatically from the used features."u8)) + editor.ChangeRequiredFeatures(mod, mod.ComputeRequiredFeatures()); + + Im.Line.Same(); + if (ImEx.Button("Clear"u8, size, "Clear all required features."u8)) + editor.ChangeRequiredFeatures(mod, FeatureFlags.None); + + Im.Line.Same(); + Im.Text("Required Features"u8); + } +} diff --git a/Penumbra/Mods/Manager/ModCombo.cs b/Penumbra/Mods/Manager/ModCombo.cs new file mode 100644 index 00000000..4cc57aaf --- /dev/null +++ b/Penumbra/Mods/Manager/ModCombo.cs @@ -0,0 +1,25 @@ +using ImSharp; +using Luna; +using Penumbra.Mods.Editor; + +namespace Penumbra.Mods.Manager; + +public class ModCombo(ModStorage modStorage) : SimpleFilterCombo(SimpleFilterType.Regex), IUiService +{ + protected readonly ModStorage ModStorage = modStorage; + + public override StringU8 DisplayString(in Mod value) + => new(value.Name); + + public override string FilterString(in Mod value) + => value.Name; + + public override IEnumerable GetBaseItems() + => ModStorage; +} + +public sealed class ModComboWithoutCurrent(ModStorage modStorage, ModMerger modMerger) : ModCombo(modStorage) +{ + public override IEnumerable GetBaseItems() + => ModStorage.Where(m => m != modMerger.MergeFromMod); +} diff --git a/Penumbra/Mods/Manager/ModManager.cs b/Penumbra/Mods/Manager/ModManager.cs index fd3a84bf..58e9fe12 100644 --- a/Penumbra/Mods/Manager/ModManager.cs +++ b/Penumbra/Mods/Manager/ModManager.cs @@ -1,3 +1,4 @@ +using Luna; using Penumbra.Communication; using Penumbra.Interop; using Penumbra.Mods.Editor; @@ -228,7 +229,7 @@ public sealed class ModManager : ModStorage, IDisposable, Luna.IService if (oldName == newName) return NewDirectoryState.Identical; - var fixedNewName = ModCreator.ReplaceBadXivSymbols(newName, _config.ReplaceNonAsciiOnImport); + var fixedNewName = newName.ReplaceBadXivSymbols(_config.ReplaceNonAsciiOnImport); if (fixedNewName != newName) return NewDirectoryState.ContainsInvalidSymbols; diff --git a/Penumbra/Mods/Manager/ModStorage.cs b/Penumbra/Mods/Manager/ModStorage.cs index 15d86866..7989f206 100644 --- a/Penumbra/Mods/Manager/ModStorage.cs +++ b/Penumbra/Mods/Manager/ModStorage.cs @@ -1,17 +1,5 @@ -using OtterGui.Classes; -using OtterGui.Widgets; - namespace Penumbra.Mods.Manager; -public class ModCombo(Func> generator) : FilterComboCache(generator, MouseWheelType.None, Penumbra.Log) -{ - protected override bool IsVisible(int globalIndex, LowerString filter) - => Items[globalIndex].Name.Contains(filter); - - protected override string ToString(Mod obj) - => obj.Name; -} - public class ModStorage : IReadOnlyList { /// The actual list of mods. diff --git a/Penumbra/Mods/Manager/OptionEditor/ImcModGroupEditor.cs b/Penumbra/Mods/Manager/OptionEditor/ImcModGroupEditor.cs index 803bb4a4..2b69f04d 100644 --- a/Penumbra/Mods/Manager/OptionEditor/ImcModGroupEditor.cs +++ b/Penumbra/Mods/Manager/OptionEditor/ImcModGroupEditor.cs @@ -1,5 +1,4 @@ using Luna; -using OtterGui.Filesystem; using Penumbra.Communication; using Penumbra.GameData.Structs; using Penumbra.Meta.Manipulations; @@ -137,7 +136,7 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ protected override bool MoveOption(ImcModGroup group, int optionIdxFrom, int optionIdxTo) { - if (!Extensions.Move(group.OptionData, ref optionIdxFrom, ref optionIdxTo)) + if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo)) return false; group.DefaultSettings = group.DefaultSettings.MoveBit(optionIdxFrom, optionIdxTo); diff --git a/Penumbra/Mods/Manager/OptionEditor/ModGroupEditor.cs b/Penumbra/Mods/Manager/OptionEditor/ModGroupEditor.cs index fa3154cd..ebed1178 100644 --- a/Penumbra/Mods/Manager/OptionEditor/ModGroupEditor.cs +++ b/Penumbra/Mods/Manager/OptionEditor/ModGroupEditor.cs @@ -1,6 +1,5 @@ using Dalamud.Interface.ImGuiNotification; using Luna; -using OtterGui.Filesystem; using Penumbra.Api.Enums; using Penumbra.Communication; using Penumbra.Meta.Manipulations; @@ -40,7 +39,7 @@ public class ModGroupEditor( CombiningModGroupEditor combiningEditor, CommunicatorService communicator, SaveService saveService, - Configuration config) : Luna.IService + Configuration config) : IService { public SingleModGroupEditor SingleEditor => singleEditor; diff --git a/Penumbra/Mods/Manager/OptionEditor/MultiModGroupEditor.cs b/Penumbra/Mods/Manager/OptionEditor/MultiModGroupEditor.cs index 488d4025..0f5cc7c9 100644 --- a/Penumbra/Mods/Manager/OptionEditor/MultiModGroupEditor.cs +++ b/Penumbra/Mods/Manager/OptionEditor/MultiModGroupEditor.cs @@ -1,5 +1,4 @@ using Luna; -using OtterGui.Filesystem; using Penumbra.Communication; using Penumbra.Mods.Groups; using Penumbra.Mods.Settings; @@ -9,7 +8,7 @@ using Penumbra.Services; namespace Penumbra.Mods.Manager.OptionEditor; public sealed class MultiModGroupEditor(CommunicatorService communicator, SaveService saveService, Configuration config) - : ModOptionEditor(communicator, saveService, config), Luna.IService + : ModOptionEditor(communicator, saveService, config), IService { public void ChangeToSingle(MultiModGroup group) { @@ -75,7 +74,7 @@ public sealed class MultiModGroupEditor(CommunicatorService communicator, SaveSe protected override bool MoveOption(MultiModGroup group, int optionIdxFrom, int optionIdxTo) { - if (!Extensions.Move(group.OptionData, ref optionIdxFrom, ref optionIdxTo)) + if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo)) return false; group.DefaultSettings = group.DefaultSettings.MoveBit(optionIdxFrom, optionIdxTo); diff --git a/Penumbra/Mods/Manager/OptionEditor/SingleModGroupEditor.cs b/Penumbra/Mods/Manager/OptionEditor/SingleModGroupEditor.cs index baea361b..7467b28f 100644 --- a/Penumbra/Mods/Manager/OptionEditor/SingleModGroupEditor.cs +++ b/Penumbra/Mods/Manager/OptionEditor/SingleModGroupEditor.cs @@ -1,5 +1,4 @@ using Luna; -using OtterGui.Filesystem; using Penumbra.Communication; using Penumbra.Mods.Groups; using Penumbra.Mods.Settings; @@ -48,7 +47,7 @@ public sealed class SingleModGroupEditor(CommunicatorService communicator, SaveS protected override bool MoveOption(SingleModGroup group, int optionIdxFrom, int optionIdxTo) { - if (!Extensions.Move(group.OptionData, ref optionIdxFrom, ref optionIdxTo)) + if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo)) return false; group.DefaultSettings = group.DefaultSettings.MoveSingle(optionIdxFrom, optionIdxTo); diff --git a/Penumbra/Mods/ModCreator.cs b/Penumbra/Mods/ModCreator.cs index 447a059b..665eb900 100644 --- a/Penumbra/Mods/ModCreator.cs +++ b/Penumbra/Mods/ModCreator.cs @@ -2,7 +2,6 @@ using Dalamud.Interface.ImGuiNotification; using Luna; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using OtterGui.Filesystem; using Penumbra.Api.Enums; using Penumbra.GameData.Data; using Penumbra.Import; @@ -29,7 +28,8 @@ public partial class ModCreator( public readonly Configuration Config = config; /// Creates directory and files necessary for a new mod without adding it to the manager. - public DirectoryInfo? CreateEmptyMod(DirectoryInfo basePath, string newName, string description = "", string? author = null, params string[] tags) + public DirectoryInfo? CreateEmptyMod(DirectoryInfo basePath, string newName, string description = "", string? author = null, + params string[] tags) { try { @@ -81,7 +81,7 @@ public partial class ModCreator( if (incorporateMetaChanges) IncorporateAllMetaChanges(mod, true, deleteDefaultMetaChanges); else if (deleteDefaultMetaChanges) - ModMetaEditor.DeleteDefaultValues(mod, metaFileManager, saveService, false); + ModMetaEditor.DeleteDefaultValues(mod, metaFileManager, saveService); return true; } @@ -98,7 +98,7 @@ public partial class ModCreator( { changes = changes || saveService.FileNames.OptionGroupFile(mod.ModPath.FullName, mod.Groups.Count, group.Name, true) - != Path.Combine(file.DirectoryName!, ReplaceBadXivSymbols(file.Name, true)); + != Path.Combine(file.DirectoryName!, file.Name.ReplaceBadXivSymbols(true)); mod.Groups.Add(group); } else @@ -167,7 +167,7 @@ public partial class ModCreator( DeleteDeleteList(deleteList, delete); if (removeDefaultValues && !Config.KeepDefaultMetaChanges) - changes |= ModMetaEditor.DeleteDefaultValues(mod, metaFileManager, null, false); + changes |= ModMetaEditor.DeleteDefaultValues(mod, metaFileManager, null); if (!changes) return; @@ -306,33 +306,10 @@ public partial class ModCreator( /// Return the name of a new valid directory based on the base directory and the given name. public static DirectoryInfo NewOptionDirectory(DirectoryInfo baseDir, string optionName, bool onlyAscii) { - var option = ReplaceBadXivSymbols(optionName, onlyAscii); + var option = optionName.ReplaceBadXivSymbols(onlyAscii); return new DirectoryInfo(Path.Combine(baseDir.FullName, option.Length > 0 ? option : "_")); } - /// Normalize for nicer names, and remove invalid symbols or invalid paths. - public static string ReplaceBadXivSymbols(string s, bool onlyAscii, string replacement = "_") - { - switch (s) - { - case ".": return replacement; - case "..": return replacement + replacement; - } - - StringBuilder sb = new(s.Length); - foreach (var c in s.Normalize(NormalizationForm.FormKC)) - { - if (c.IsInvalidInPath()) - sb.Append(replacement); - else if (onlyAscii && c.IsInvalidAscii()) - sb.Append(replacement); - else - sb.Append(c); - } - - return sb.ToString().Trim(); - } - public void SplitMultiGroups(DirectoryInfo baseDir) { var mod = new Mod(baseDir); diff --git a/Penumbra/Mods/Settings/ModSettings.cs b/Penumbra/Mods/Settings/ModSettings.cs index 7701a717..3fa26dcd 100644 --- a/Penumbra/Mods/Settings/ModSettings.cs +++ b/Penumbra/Mods/Settings/ModSettings.cs @@ -1,4 +1,4 @@ -using OtterGui.Filesystem; +using Luna; using Penumbra.Api.Enums; using Penumbra.Mods.Editor; using Penumbra.Mods.Groups; diff --git a/Penumbra/Mods/Settings/Setting.cs b/Penumbra/Mods/Settings/Setting.cs index e8ad103c..25bfa178 100644 --- a/Penumbra/Mods/Settings/Setting.cs +++ b/Penumbra/Mods/Settings/Setting.cs @@ -1,5 +1,5 @@ +using Luna; using Newtonsoft.Json; -using OtterGui; namespace Penumbra.Mods.Settings; @@ -27,10 +27,10 @@ public readonly record struct Setting(ulong Value) => idx >= 0 && (Value & (1ul << idx)) != 0; public Setting MoveBit(int idx1, int idx2) - => new(Functions.MoveBit(Value, idx1, idx2)); + => new(BitFunctions.MoveBit(Value, idx1, idx2)); public Setting RemoveBit(int idx) - => new(Functions.RemoveBit(Value, idx)); + => new(BitFunctions.RemoveBit(Value, idx)); public Setting SetBit(int idx, bool value) => new(value ? Value | (1ul << idx) : Value & ~(1ul << idx)); diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs index 24066e6e..b169a9a4 100644 --- a/Penumbra/Penumbra.cs +++ b/Penumbra/Penumbra.cs @@ -17,13 +17,11 @@ using Penumbra.Interop.Hooks; using Penumbra.Interop.Hooks.PostProcessing; using Penumbra.Interop.PathResolving; using Penumbra.Interop.Services; -using Penumbra.Mods; using Penumbra.Mods.Manager; using Penumbra.Services; using Penumbra.UI; using Penumbra.UI.AdvancedWindow; using Penumbra.UI.Tabs; -using static System.Collections.Specialized.BitVector32; using ChangedItemClick = Penumbra.Communication.ChangedItemClick; using ChangedItemHover = Penumbra.Communication.ChangedItemHover; using DynamisIpc = OtterGui.Services.DynamisIpc; diff --git a/Penumbra/Services/FileWatcher.cs b/Penumbra/Services/FileWatcher.cs index 92f5bebe..c9fec3f8 100644 --- a/Penumbra/Services/FileWatcher.cs +++ b/Penumbra/Services/FileWatcher.cs @@ -1,9 +1,10 @@ -using Luna; +using ImSharp; +using Luna; using Penumbra.Mods.Manager; namespace Penumbra.Services; -public class FileWatcher : IDisposable, IService +public sealed class FileWatcher : IDisposable, IService { private readonly ConcurrentSet _pending = new(StringComparer.OrdinalIgnoreCase); private readonly ModImportManager _modImportManager; @@ -47,9 +48,6 @@ public class FileWatcher : IDisposable, IService } } - internal void PauseConsumer(bool pause) - => _pausedConsumer = pause; - private void EndFileWatcher() { if (_fsw is null) @@ -206,4 +204,47 @@ public class FileWatcher : IDisposable, IService EndConsumerTask(); EndFileWatcher(); } + + public sealed class FileWatcherDrawer(Configuration config, FileWatcher fileWatcher) : IUiService + { + public void Draw() + { + using var tree = Im.Tree.Node("File Watcher"u8); + if (!tree) + return; + + using var table = Im.Table.Begin("table"u8, 2); + if (!table) + return; + + table.DrawColumn("Enabled"u8); + table.DrawColumn($"{config.EnableDirectoryWatch}"); + + table.DrawColumn("Automatic Import"u8); + table.DrawColumn($"{config.EnableAutomaticModImport}"); + + table.DrawColumn("Watched Directory"u8); + table.DrawColumn(config.WatchDirectory); + + table.DrawColumn("File Watcher Path"u8); + table.DrawColumn(fileWatcher._fsw?.Path ?? ""); + + table.DrawColumn("Raising Events"u8); + table.DrawColumn($"{fileWatcher._fsw?.EnableRaisingEvents ?? false}"); + + table.DrawColumn("File Filters"u8); + table.DrawColumn(StringU8.Join(", ", fileWatcher._fsw?.Filters ?? [])); + + table.DrawColumn("Consumer Task State"u8); + table.DrawColumn($"{fileWatcher._consumer?.Status.ToString() ?? ""}"); + + table.DrawColumn("Debug Pause Consumer"u8); + table.NextColumn(); + if (Im.SmallButton(fileWatcher._pausedConsumer ? "Unpause"u8 : "Pause"u8)) + fileWatcher._pausedConsumer = !fileWatcher._pausedConsumer; + + table.DrawColumn("Pending Files"u8); + table.DrawColumn(StringU8.Join('\n', fileWatcher._pending)); + } + } } diff --git a/Penumbra/Services/FilenameService.cs b/Penumbra/Services/FilenameService.cs index b135ea10..3021bb97 100644 --- a/Penumbra/Services/FilenameService.cs +++ b/Penumbra/Services/FilenameService.cs @@ -64,15 +64,15 @@ public sealed class FilenameService(IDalamudPluginInterface pi) : BaseFilePathPr public string ModMetaPath(string modDirectory) => Path.Combine(modDirectory, "meta.json"); - /// Obtain the path of the file describing a given option group by its index and the mod. If the index is < 0, return the path for the default mod file. + /// Obtain the path of the file describing a given option group by its index and the mod. If the index is less than 0, return the path for the default mod file. public string OptionGroupFile(Mod mod, int index, bool onlyAscii) => OptionGroupFile(mod.ModPath.FullName, index, index >= 0 ? mod.Groups[index].Name : string.Empty, onlyAscii); - /// Obtain the path of the file describing a given option group by its index, name and basepath. If the index is < 0, return the path for the default mod file. + /// Obtain the path of the file describing a given option group by its index, name and basepath. If the index is less than 0, return the path for the default mod file. public string OptionGroupFile(string basePath, int index, string name, bool onlyAscii) { var fileName = index >= 0 - ? $"group_{index + 1:D3}_{ModCreator.ReplaceBadXivSymbols(name.ToLowerInvariant(), onlyAscii)}.json" + ? $"group_{index + 1:D3}_{name.ToLowerInvariant().ReplaceBadXivSymbols(onlyAscii)}.json" : "default_mod.json"; return Path.Combine(basePath, fileName); } diff --git a/Penumbra/Services/ServiceWrapper.cs b/Penumbra/Services/ServiceWrapper.cs deleted file mode 100644 index e321b35c..00000000 --- a/Penumbra/Services/ServiceWrapper.cs +++ /dev/null @@ -1,91 +0,0 @@ -using OtterGui.Tasks; - -namespace Penumbra.Services; - -public abstract class SyncServiceWrapper : IDisposable -{ - public string Name { get; } - public T Service { get; } - private bool _isDisposed; - - public bool Valid - => !_isDisposed; - - protected SyncServiceWrapper(string name, Func factory) - { - Name = name; - Service = factory(); - Penumbra.Log.Verbose($"[{Name}] Created."); - } - - public void Dispose() - { - if (_isDisposed) - return; - - _isDisposed = true; - if (Service is IDisposable d) - d.Dispose(); - Penumbra.Log.Verbose($"[{Name}] Disposed."); - } -} - -public abstract class AsyncServiceWrapper : IDisposable -{ - public string Name { get; } - public T? Service { get; private set; } - - public T AwaitedService - { - get - { - _task?.Wait(); - return Service!; - } - } - - public bool Valid - => Service != null && !_isDisposed; - - public event Action? FinishedCreation; - private Task? _task; - - private bool _isDisposed; - - protected AsyncServiceWrapper(string name, Func factory) - { - Name = name; - _task = TrackedTask.Run(() => - { - var service = factory(); - if (_isDisposed) - { - if (service is IDisposable d) - d.Dispose(); - } - else - { - Service = service; - Penumbra.Log.Verbose($"[{Name}] Created."); - _task = null; - } - }); - _task.ContinueWith((t, x) => - { - if (!_isDisposed) - FinishedCreation?.Invoke(); - }, TaskScheduler.Default); - } - - public void Dispose() - { - if (_isDisposed) - return; - - _isDisposed = true; - _task = null; - if (Service is IDisposable d) - d.Dispose(); - Penumbra.Log.Verbose($"[{Name}] Disposed."); - } -} diff --git a/Penumbra/Services/StainService.cs b/Penumbra/Services/StainService.cs index f959ad4e..174e0d0f 100644 --- a/Penumbra/Services/StainService.cs +++ b/Penumbra/Services/StainService.cs @@ -1,6 +1,4 @@ using Dalamud.Bindings.ImGui; -using Dalamud.Interface; -using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin.Services; using ImSharp; using OtterGui.Widgets; @@ -14,6 +12,55 @@ using MouseWheelType = OtterGui.Widgets.MouseWheelType; namespace Penumbra.Services; +// TODO +//public sealed class StainTemplateCombo(FilterComboColors[] stainCombos, StmFile stmFile) : SimpleFilterCombo(SimpleFilterType.Text) +// where TDyePack : unmanaged, IDyePack +//{ +// public override StringU8 DisplayString(in StmKeyType value) +// => new($"{value,4}"); +// +// public override string FilterString(in StmKeyType value) +// => $"{value,4}"; +// +// public override IEnumerable GetBaseItems() +// => throw new NotImplementedException(); +// +// protected override bool DrawFilter(float width, FilterComboBaseCache> cache) +// { +// using var font = Im.Font.PushDefault(); +// return base.DrawFilter(width, cache); +// } +// +// public bool Draw(Utf8StringHandler label, Utf8StringHandler preview, Utf8StringHandler tooltip, ref int currentSelection, float previewWidth, float itemHeight, +// ComboFlags flags = ComboFlags.None) +// { +// using var font = Im.Font.PushMono(); +// using var style = ImStyleDouble.ButtonTextAlign.Push(new Vector2(1, 0.5f)) +// .PushX(ImStyleDouble.ItemSpacing, Im.Style.ItemInnerSpacing.X); +// var spaceSize = Im.Font.Mono.GetCharacterAdvance(' '); +// var spaces = (int)(previewWidth / spaceSize) - 1; +// return base.Draw(label, preview.PadLeft(spaces), tooltip, ref currentSelection, previewWidth, itemHeight, flags); +// } +// +// protected override bool DrawSelectable(int globalIdx, bool selected) +// { +// var ret = base.DrawSelectable(globalIdx, selected); +// var selection = stainCombos[CurrentDyeChannel].CurrentSelection.Key; +// if (selection == 0 || !stmFile.TryGetValue(Items[globalIdx], selection, out var colors)) +// return ret; +// +// Im.Line.Same(); +// +// var frame = new Vector2(Im.Style.TextHeight); +// Im.Color.Button("D"u8, new Vector4(MtrlTab.PseudoSqrtRgb((Vector3)colors.DiffuseColor), 1), 0, frame); +// Im.Line.Same(); +// Im.Color.Button("S"u8, new Vector4(MtrlTab.PseudoSqrtRgb((Vector3)colors.SpecularColor), 1), 0, frame); +// Im.Line.Same(); +// Im.Color.Button("E"u8, new Vector4(MtrlTab.PseudoSqrtRgb((Vector3)colors.EmissiveColor), 1), 0, frame); +// return ret; +// } +//} + public class StainService : Luna.IService { public sealed class StainTemplateCombo(FilterComboColors[] stainCombos, StmFile stmFile) @@ -25,11 +72,11 @@ public class StainService : Luna.IService protected override float GetFilterWidth() { - var baseSize = ImGui.CalcTextSize("0000").X + ImGui.GetStyle().ScrollbarSize + ImGui.GetStyle().ItemInnerSpacing.X; + var baseSize = Im.Font.CalculateSize("0000"u8).X + Im.Style.ScrollbarSize + Im.Style.ItemInnerSpacing.X; if (stainCombos[CurrentDyeChannel].CurrentSelection.Key == 0) return baseSize; - return baseSize + ImGui.GetTextLineHeight() * 3 + ImGui.GetStyle().ItemInnerSpacing.X * 3; + return baseSize + Im.Style.TextHeight * 3 + Im.Style.ItemInnerSpacing.X * 3; } protected override string ToString(StmKeyType obj) @@ -37,17 +84,17 @@ public class StainService : Luna.IService protected override void DrawFilter(int currentSelected, float width) { - using var font = ImRaii.PushFont(UiBuilder.DefaultFont); + using var font = Im.Font.PushDefault(); base.DrawFilter(currentSelected, width); } public override bool Draw(string label, string preview, string tooltip, ref int currentSelection, float previewWidth, float itemHeight, ImGuiComboFlags flags = ImGuiComboFlags.None) { - using var font = ImRaii.PushFont(UiBuilder.MonoFont); - using var style = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, new Vector2(1, 0.5f)) - .Push(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemInnerSpacing.X }); - var spaceSize = ImGui.CalcTextSize(" ").X; + using var font = Im.Font.PushMono(); + using var style = ImStyleDouble.ButtonTextAlign.Push(new Vector2(1, 0.5f)) + .PushX(ImStyleDouble.ItemSpacing, Im.Style.ItemInnerSpacing.X); + var spaceSize = Im.Font.Mono.GetCharacterAdvance(' '); var spaces = (int)(previewWidth / spaceSize) - 1; return base.Draw(label, preview.PadLeft(spaces), tooltip, ref currentSelection, previewWidth, itemHeight, flags); } @@ -60,12 +107,13 @@ public class StainService : Luna.IService return ret; Im.Line.Same(); - var frame = new Vector2(ImGui.GetTextLineHeight()); - ImGui.ColorButton("D", new Vector4(MtrlTab.PseudoSqrtRgb((Vector3)colors.DiffuseColor), 1), 0, frame); + + var frame = new Vector2(Im.Style.TextHeight); + Im.Color.Button("D"u8, new Vector4(MtrlTab.PseudoSqrtRgb((Vector3)colors.DiffuseColor), 1), 0, frame); Im.Line.Same(); - ImGui.ColorButton("S", new Vector4(MtrlTab.PseudoSqrtRgb((Vector3)colors.SpecularColor), 1), 0, frame); + Im.Color.Button("S"u8, new Vector4(MtrlTab.PseudoSqrtRgb((Vector3)colors.SpecularColor), 1), 0, frame); Im.Line.Same(); - ImGui.ColorButton("E", new Vector4(MtrlTab.PseudoSqrtRgb((Vector3)colors.EmissiveColor), 1), 0, frame); + Im.Color.Button("E"u8, new Vector4(MtrlTab.PseudoSqrtRgb((Vector3)colors.EmissiveColor), 1), 0, frame); return ret; } } diff --git a/Penumbra/UI/AdvancedWindow/ModMergeTab.cs b/Penumbra/UI/AdvancedWindow/ModMergeTab.cs index 5d959c51..44fc5d4c 100644 --- a/Penumbra/UI/AdvancedWindow/ModMergeTab.cs +++ b/Penumbra/UI/AdvancedWindow/ModMergeTab.cs @@ -1,272 +1,271 @@ -using Dalamud.Interface.Utility; -using Dalamud.Bindings.ImGui; -using ImSharp; -using OtterGui; -using OtterGui.Raii; -using OtterGui.Text; -using Penumbra.Mods.Editor; -using Penumbra.Mods.Manager; -using Penumbra.Mods.SubMods; -using Penumbra.UI.Classes; - -namespace Penumbra.UI.AdvancedWindow; - -public class ModMergeTab(ModMerger modMerger) : Luna.IUiService -{ - private readonly ModCombo _modCombo = new(() => modMerger.ModsWithoutCurrent.ToList()); - private string _newModName = string.Empty; - - public void Draw() - { - if (modMerger.MergeFromMod == null) - return; - - using var tab = ImRaii.TabItem("Merge Mods"); - if (!tab) - return; - - ImGui.Dummy(Vector2.One); - var size = 550 * ImGuiHelpers.GlobalScale; - DrawMergeInto(size); - Im.Line.Same(); - DrawMergeIntoDesc(); - - ImGui.Dummy(Vector2.One); - ImGui.Separator(); - ImGui.Dummy(Vector2.One); - - DrawSplitOff(size); - Im.Line.Same(); - DrawSplitOffDesc(); - - - DrawError(); - DrawWarnings(); - } - - private void DrawMergeInto(float size) - { - using var bigGroup = ImRaii.Group(); - var minComboSize = 300 * ImGuiHelpers.GlobalScale; - var textSize = ImUtf8.CalcTextSize($"Merge {modMerger.MergeFromMod!.Name} into ").X; - - ImGui.AlignTextToFramePadding(); - - using (ImRaii.Group()) - { - ImUtf8.Text("Merge "u8); - ImGui.SameLine(0, 0); - if (size - textSize < minComboSize) - { - Im.Text("selected mod"u8, ColorId.FolderLine.Value()); - ImUtf8.HoverTooltip(modMerger.MergeFromMod!.Name); - } - else - { - Im.Text(modMerger.MergeFromMod!.Name, ColorId.FolderLine.Value()); - } - - ImGui.SameLine(0, 0); - ImUtf8.Text(" into"u8); - } - - Im.Line.Same(); - DrawCombo(size - ImGui.GetItemRectSize().X - ImGui.GetStyle().ItemSpacing.X); - - using (ImRaii.Group()) - { - using var disabled = ImRaii.Disabled(modMerger.MergeFromMod.HasOptions); - var buttonWidth = (size - ImGui.GetStyle().ItemSpacing.X) / 2; - using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 1); - var group = modMerger.MergeToMod?.Groups.FirstOrDefault(g => g.Name == modMerger.OptionGroupName); - var color = group != null || modMerger.OptionGroupName.Length == 0 && modMerger.OptionName.Length == 0 - ? Colors.PressEnterWarningBg - : Colors.DiscordColor; - using var c = ImGuiColor.Border.Push(color); - ImGui.SetNextItemWidth(buttonWidth); - ImGui.InputTextWithHint("##optionGroupInput", "Target Option Group", ref modMerger.OptionGroupName, 64); - ImGuiUtil.HoverTooltip( - "The name of the new or existing option group to find or create the option in. Leave both group and option name blank for the default option.\n" - + "A red border indicates an existing option group, a blue border indicates a new one."); - Im.Line.Same(); - - - color = color == Colors.DiscordColor - ? Colors.DiscordColor - : group == null || group.Options.Any(o => o.Name == modMerger.OptionName) - ? Colors.PressEnterWarningBg - : Colors.DiscordColor; - c.Push(ImGuiColor.Border, color); - ImGui.SetNextItemWidth(buttonWidth); - ImGui.InputTextWithHint("##optionInput", "Target Option Name", ref modMerger.OptionName, 64); - ImGuiUtil.HoverTooltip( - "The name of the new or existing option to merge this mod into. Leave both group and option name blank for the default option.\n" - + "A red border indicates an existing option, a blue border indicates a new one."); - } - - if (modMerger.MergeFromMod.HasOptions) - ImGuiUtil.HoverTooltip("You can only specify a target option if the source mod has no true options itself.", - ImGuiHoveredFlags.AllowWhenDisabled); - - if (ImGuiUtil.DrawDisabledButton("Merge", new Vector2(size, 0), - modMerger.CanMerge ? string.Empty : "Please select a target mod different from the current mod.", !modMerger.CanMerge)) - modMerger.Merge(); - } - - private void DrawMergeIntoDesc() - { - ImGuiUtil.TextWrapped(modMerger.MergeFromMod!.HasOptions - ? "The currently selected mod has options.\n\nThis means, that all of those options will be merged into the target. If merging an option is not possible due to the redirections already existing in an existing option, it will revert all changes and break." - : "The currently selected mod has no true options.\n\nThis means that you can select an existing or new option to merge all its changes into in the target mod. On failure to merge into an existing option, all changes will be reverted."); - } - - private void DrawCombo(float width) - { - _modCombo.Draw("##ModSelection", _modCombo.CurrentSelection?.Name ?? "Select the target Mod...", string.Empty, width, - ImGui.GetTextLineHeight()); - modMerger.MergeToMod = _modCombo.CurrentSelection; - } - - private void DrawSplitOff(float size) - { - using var group = ImRaii.Group(); - ImGui.SetNextItemWidth(size); - ImGui.InputTextWithHint("##newModInput", "New Mod Name...", ref _newModName, 64); - ImGuiUtil.HoverTooltip("Choose a name for the newly created mod. This does not need to be unique."); - var tt = _newModName.Length == 0 - ? "Please enter a name for the newly created mod first." - : modMerger.SelectedOptions.Count == 0 - ? "Please select at least one option to split off." - : string.Empty; - var buttonText = - $"Split Off {modMerger.SelectedOptions.Count} Option{(modMerger.SelectedOptions.Count > 1 ? "s" : string.Empty)}###SplitOff"; - if (ImGuiUtil.DrawDisabledButton(buttonText, new Vector2(size, 0), tt, tt.Length > 0)) - modMerger.SplitIntoMod(_newModName); - - ImGui.Dummy(Vector2.One); - var buttonSize = new Vector2((size - 2 * ImGui.GetStyle().ItemSpacing.X) / 3, 0); - if (ImGui.Button("Select All", buttonSize)) - modMerger.SelectedOptions.UnionWith(modMerger.MergeFromMod!.AllDataContainers); - Im.Line.Same(); - if (ImGui.Button("Unselect All", buttonSize)) - modMerger.SelectedOptions.Clear(); - Im.Line.Same(); - if (ImGui.Button("Invert Selection", buttonSize)) - modMerger.SelectedOptions.SymmetricExceptWith(modMerger.MergeFromMod!.AllDataContainers); - DrawOptionTable(size); - } - - private void DrawSplitOffDesc() - { - ImGuiUtil.TextWrapped("Here you can create a copy or a partial copy of the currently selected mod.\n\n" - + "Select as many of the options you want to copy over, enter a new mod name and click Split Off.\n\n" - + "You can right-click option groups to select or unselect all options from that specific group, and use the three buttons above the table for quick manipulation of your selection.\n\n" - + "Only required files will be copied over to the new mod. The names of options and groups will be retained. If the Default option is not selected, the new mods default option will be empty."); - } - - private void DrawOptionTable(float size) - { - var options = modMerger.MergeFromMod!.AllDataContainers.ToList(); - var height = modMerger.Warnings.Count == 0 && modMerger.Error == null - ? ImGui.GetContentRegionAvail().Y - 3 * ImGui.GetFrameHeightWithSpacing() - : 8 * ImGui.GetFrameHeightWithSpacing(); - height = Math.Min(height, (options.Count + 1) * ImGui.GetFrameHeightWithSpacing()); - var tableSize = new Vector2(size, height); - using var table = ImRaii.Table("##options", 6, - ImGuiTableFlags.RowBg - | ImGuiTableFlags.SizingFixedFit - | ImGuiTableFlags.ScrollY - | ImGuiTableFlags.BordersOuterV - | ImGuiTableFlags.BordersOuterH, - tableSize); - if (!table) - return; - - ImGui.TableSetupColumn("##Selected", ImGuiTableColumnFlags.WidthFixed, ImGui.GetFrameHeight()); - ImGui.TableSetupColumn("Option", ImGuiTableColumnFlags.WidthStretch); - ImGui.TableSetupColumn("Option Group", ImGuiTableColumnFlags.WidthFixed, 120 * ImGuiHelpers.GlobalScale); - ImGui.TableSetupColumn("#Files", ImGuiTableColumnFlags.WidthFixed, 50 * ImGuiHelpers.GlobalScale); - ImGui.TableSetupColumn("#Swaps", ImGuiTableColumnFlags.WidthFixed, 50 * ImGuiHelpers.GlobalScale); - ImGui.TableSetupColumn("#Manips", ImGuiTableColumnFlags.WidthFixed, 50 * ImGuiHelpers.GlobalScale); - ImGui.TableHeadersRow(); - foreach (var (idx, option) in options.Index()) - { - using var id = ImRaii.PushId(idx); - var selected = modMerger.SelectedOptions.Contains(option); - - ImGui.TableNextColumn(); - if (ImGui.Checkbox("##check", ref selected)) - Handle(option, selected); - - if (option.Group is not { } group) - { - ImGuiUtil.DrawTableColumn(option.GetFullName()); - ImGui.TableNextColumn(); - } - else - { - ImGuiUtil.DrawTableColumn(option.GetName()); - - ImGui.TableNextColumn(); - ImGui.Selectable(group.Name, false); - if (ImGui.BeginPopupContextItem("##groupContext")) - { - if (ImGui.MenuItem("Select All")) - // ReSharper disable once PossibleMultipleEnumeration - foreach (var opt in group.DataContainers) - Handle(opt, true); - - if (ImGui.MenuItem("Unselect All")) - // ReSharper disable once PossibleMultipleEnumeration - foreach (var opt in group.DataContainers) - Handle(opt, false); - ImGui.EndPopup(); - } - } - - ImGui.TableNextColumn(); - ImGuiUtil.RightAlign(option.Files.Count.ToString(), 3 * ImGuiHelpers.GlobalScale); - ImGui.TableNextColumn(); - ImGuiUtil.RightAlign(option.FileSwaps.Count.ToString(), 3 * ImGuiHelpers.GlobalScale); - ImGui.TableNextColumn(); - ImGuiUtil.RightAlign(option.Manipulations.Count.ToString(), 3 * ImGuiHelpers.GlobalScale); - continue; - - void Handle(IModDataContainer option2, bool selected2) - { - if (selected2) - modMerger.SelectedOptions.Add(option2); - else - modMerger.SelectedOptions.Remove(option2); - } - } - } - - private void DrawWarnings() - { - if (modMerger.Warnings.Count == 0) - return; - - ImGui.Separator(); - ImGui.Dummy(Vector2.One); - using var color = ImGuiColor.Text.Push(Colors.TutorialBorder); - foreach (var warning in modMerger.Warnings.SkipLast(1)) - { - ImGuiUtil.TextWrapped(warning); - ImGui.Separator(); - } - - ImGuiUtil.TextWrapped(modMerger.Warnings[^1]); - } - - private void DrawError() - { - if (modMerger.Error == null) - return; - - ImGui.Separator(); - ImGui.Dummy(Vector2.One); - using var color = ImGuiColor.Text.Push(Colors.RegexWarningBorder); - ImGuiUtil.TextWrapped(modMerger.Error.ToString()); - } -} +using Dalamud.Interface.Utility; +using Dalamud.Bindings.ImGui; +using ImSharp; +using OtterGui; +using OtterGui.Raii; +using OtterGui.Text; +using Penumbra.Mods.Editor; +using Penumbra.Mods.Manager; +using Penumbra.Mods.SubMods; +using Penumbra.UI.Classes; + +namespace Penumbra.UI.AdvancedWindow; + +public class ModMergeTab(ModMerger modMerger, ModComboWithoutCurrent combo) : Luna.IUiService +{ + private string _newModName = string.Empty; + + public void Draw() + { + if (modMerger.MergeFromMod == null) + return; + + using var tab = ImRaii.TabItem("Merge Mods"); + if (!tab) + return; + + ImGui.Dummy(Vector2.One); + var size = 550 * ImGuiHelpers.GlobalScale; + DrawMergeInto(size); + Im.Line.Same(); + DrawMergeIntoDesc(); + + ImGui.Dummy(Vector2.One); + ImGui.Separator(); + ImGui.Dummy(Vector2.One); + + DrawSplitOff(size); + Im.Line.Same(); + DrawSplitOffDesc(); + + + DrawError(); + DrawWarnings(); + } + + private void DrawMergeInto(float size) + { + using var bigGroup = ImRaii.Group(); + var minComboSize = 300 * ImGuiHelpers.GlobalScale; + var textSize = ImUtf8.CalcTextSize($"Merge {modMerger.MergeFromMod!.Name} into ").X; + + ImGui.AlignTextToFramePadding(); + + using (ImRaii.Group()) + { + ImUtf8.Text("Merge "u8); + ImGui.SameLine(0, 0); + if (size - textSize < minComboSize) + { + Im.Text("selected mod"u8, ColorId.FolderLine.Value()); + ImUtf8.HoverTooltip(modMerger.MergeFromMod!.Name); + } + else + { + Im.Text(modMerger.MergeFromMod!.Name, ColorId.FolderLine.Value()); + } + + ImGui.SameLine(0, 0); + ImUtf8.Text(" into"u8); + } + + Im.Line.Same(); + DrawCombo(size - ImGui.GetItemRectSize().X - ImGui.GetStyle().ItemSpacing.X); + + using (ImRaii.Group()) + { + using var disabled = ImRaii.Disabled(modMerger.MergeFromMod.HasOptions); + var buttonWidth = (size - ImGui.GetStyle().ItemSpacing.X) / 2; + using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 1); + var group = modMerger.MergeToMod?.Groups.FirstOrDefault(g => g.Name == modMerger.OptionGroupName); + var color = group != null || modMerger.OptionGroupName.Length == 0 && modMerger.OptionName.Length == 0 + ? Colors.PressEnterWarningBg + : Colors.DiscordColor; + using var c = ImGuiColor.Border.Push(color); + ImGui.SetNextItemWidth(buttonWidth); + ImGui.InputTextWithHint("##optionGroupInput", "Target Option Group", ref modMerger.OptionGroupName, 64); + ImGuiUtil.HoverTooltip( + "The name of the new or existing option group to find or create the option in. Leave both group and option name blank for the default option.\n" + + "A red border indicates an existing option group, a blue border indicates a new one."); + Im.Line.Same(); + + + color = color == Colors.DiscordColor + ? Colors.DiscordColor + : group == null || group.Options.Any(o => o.Name == modMerger.OptionName) + ? Colors.PressEnterWarningBg + : Colors.DiscordColor; + c.Push(ImGuiColor.Border, color); + ImGui.SetNextItemWidth(buttonWidth); + ImGui.InputTextWithHint("##optionInput", "Target Option Name", ref modMerger.OptionName, 64); + ImGuiUtil.HoverTooltip( + "The name of the new or existing option to merge this mod into. Leave both group and option name blank for the default option.\n" + + "A red border indicates an existing option, a blue border indicates a new one."); + } + + if (modMerger.MergeFromMod.HasOptions) + ImGuiUtil.HoverTooltip("You can only specify a target option if the source mod has no true options itself.", + ImGuiHoveredFlags.AllowWhenDisabled); + + if (ImGuiUtil.DrawDisabledButton("Merge", new Vector2(size, 0), + modMerger.CanMerge ? string.Empty : "Please select a target mod different from the current mod.", !modMerger.CanMerge)) + modMerger.Merge(); + } + + private void DrawMergeIntoDesc() + { + ImGuiUtil.TextWrapped(modMerger.MergeFromMod!.HasOptions + ? "The currently selected mod has options.\n\nThis means, that all of those options will be merged into the target. If merging an option is not possible due to the redirections already existing in an existing option, it will revert all changes and break." + : "The currently selected mod has no true options.\n\nThis means that you can select an existing or new option to merge all its changes into in the target mod. On failure to merge into an existing option, all changes will be reverted."); + } + + private void DrawCombo(float width) + { + if (combo.Draw("##ModSelection"u8, modMerger.MergeToMod?.Name ?? "Select the target Mod...", StringU8.Empty, width, + out var cacheMod)) + modMerger.MergeToMod = cacheMod.Item; + } + + private void DrawSplitOff(float size) + { + using var group = ImRaii.Group(); + ImGui.SetNextItemWidth(size); + ImGui.InputTextWithHint("##newModInput", "New Mod Name...", ref _newModName, 64); + ImGuiUtil.HoverTooltip("Choose a name for the newly created mod. This does not need to be unique."); + var tt = _newModName.Length == 0 + ? "Please enter a name for the newly created mod first." + : modMerger.SelectedOptions.Count == 0 + ? "Please select at least one option to split off." + : string.Empty; + var buttonText = + $"Split Off {modMerger.SelectedOptions.Count} Option{(modMerger.SelectedOptions.Count > 1 ? "s" : string.Empty)}###SplitOff"; + if (ImGuiUtil.DrawDisabledButton(buttonText, new Vector2(size, 0), tt, tt.Length > 0)) + modMerger.SplitIntoMod(_newModName); + + ImGui.Dummy(Vector2.One); + var buttonSize = new Vector2((size - 2 * ImGui.GetStyle().ItemSpacing.X) / 3, 0); + if (ImGui.Button("Select All", buttonSize)) + modMerger.SelectedOptions.UnionWith(modMerger.MergeFromMod!.AllDataContainers); + Im.Line.Same(); + if (ImGui.Button("Unselect All", buttonSize)) + modMerger.SelectedOptions.Clear(); + Im.Line.Same(); + if (ImGui.Button("Invert Selection", buttonSize)) + modMerger.SelectedOptions.SymmetricExceptWith(modMerger.MergeFromMod!.AllDataContainers); + DrawOptionTable(size); + } + + private void DrawSplitOffDesc() + { + ImGuiUtil.TextWrapped("Here you can create a copy or a partial copy of the currently selected mod.\n\n" + + "Select as many of the options you want to copy over, enter a new mod name and click Split Off.\n\n" + + "You can right-click option groups to select or unselect all options from that specific group, and use the three buttons above the table for quick manipulation of your selection.\n\n" + + "Only required files will be copied over to the new mod. The names of options and groups will be retained. If the Default option is not selected, the new mods default option will be empty."); + } + + private void DrawOptionTable(float size) + { + var options = modMerger.MergeFromMod!.AllDataContainers.ToList(); + var height = modMerger.Warnings.Count == 0 && modMerger.Error == null + ? ImGui.GetContentRegionAvail().Y - 3 * ImGui.GetFrameHeightWithSpacing() + : 8 * ImGui.GetFrameHeightWithSpacing(); + height = Math.Min(height, (options.Count + 1) * ImGui.GetFrameHeightWithSpacing()); + var tableSize = new Vector2(size, height); + using var table = ImRaii.Table("##options", 6, + ImGuiTableFlags.RowBg + | ImGuiTableFlags.SizingFixedFit + | ImGuiTableFlags.ScrollY + | ImGuiTableFlags.BordersOuterV + | ImGuiTableFlags.BordersOuterH, + tableSize); + if (!table) + return; + + ImGui.TableSetupColumn("##Selected", ImGuiTableColumnFlags.WidthFixed, ImGui.GetFrameHeight()); + ImGui.TableSetupColumn("Option", ImGuiTableColumnFlags.WidthStretch); + ImGui.TableSetupColumn("Option Group", ImGuiTableColumnFlags.WidthFixed, 120 * ImGuiHelpers.GlobalScale); + ImGui.TableSetupColumn("#Files", ImGuiTableColumnFlags.WidthFixed, 50 * ImGuiHelpers.GlobalScale); + ImGui.TableSetupColumn("#Swaps", ImGuiTableColumnFlags.WidthFixed, 50 * ImGuiHelpers.GlobalScale); + ImGui.TableSetupColumn("#Manips", ImGuiTableColumnFlags.WidthFixed, 50 * ImGuiHelpers.GlobalScale); + ImGui.TableHeadersRow(); + foreach (var (idx, option) in options.Index()) + { + using var id = ImRaii.PushId(idx); + var selected = modMerger.SelectedOptions.Contains(option); + + ImGui.TableNextColumn(); + if (ImGui.Checkbox("##check", ref selected)) + Handle(option, selected); + + if (option.Group is not { } group) + { + ImGuiUtil.DrawTableColumn(option.GetFullName()); + ImGui.TableNextColumn(); + } + else + { + ImGuiUtil.DrawTableColumn(option.GetName()); + + ImGui.TableNextColumn(); + ImGui.Selectable(group.Name, false); + if (ImGui.BeginPopupContextItem("##groupContext")) + { + if (ImGui.MenuItem("Select All")) + // ReSharper disable once PossibleMultipleEnumeration + foreach (var opt in group.DataContainers) + Handle(opt, true); + + if (ImGui.MenuItem("Unselect All")) + // ReSharper disable once PossibleMultipleEnumeration + foreach (var opt in group.DataContainers) + Handle(opt, false); + ImGui.EndPopup(); + } + } + + ImGui.TableNextColumn(); + ImGuiUtil.RightAlign(option.Files.Count.ToString(), 3 * ImGuiHelpers.GlobalScale); + ImGui.TableNextColumn(); + ImGuiUtil.RightAlign(option.FileSwaps.Count.ToString(), 3 * ImGuiHelpers.GlobalScale); + ImGui.TableNextColumn(); + ImGuiUtil.RightAlign(option.Manipulations.Count.ToString(), 3 * ImGuiHelpers.GlobalScale); + continue; + + void Handle(IModDataContainer option2, bool selected2) + { + if (selected2) + modMerger.SelectedOptions.Add(option2); + else + modMerger.SelectedOptions.Remove(option2); + } + } + } + + private void DrawWarnings() + { + if (modMerger.Warnings.Count == 0) + return; + + ImGui.Separator(); + ImGui.Dummy(Vector2.One); + using var color = ImGuiColor.Text.Push(Colors.TutorialBorder); + foreach (var warning in modMerger.Warnings.SkipLast(1)) + { + ImGuiUtil.TextWrapped(warning); + ImGui.Separator(); + } + + ImGuiUtil.TextWrapped(modMerger.Warnings[^1]); + } + + private void DrawError() + { + if (modMerger.Error == null) + return; + + ImGui.Separator(); + ImGui.Dummy(Vector2.One); + using var color = ImGuiColor.Text.Push(Colors.RegexWarningBorder); + ImGuiUtil.TextWrapped(modMerger.Error.ToString()); + } +} diff --git a/Penumbra/UI/Tabs/Debug/DebugTab.cs b/Penumbra/UI/Tabs/Debug/DebugTab.cs index 9fdb3a9b..2a22465a 100644 --- a/Penumbra/UI/Tabs/Debug/DebugTab.cs +++ b/Penumbra/UI/Tabs/Debug/DebugTab.cs @@ -106,6 +106,7 @@ public class DebugTab : Window, ITab private readonly RenderTargetDrawer _renderTargetDrawer; private readonly ModMigratorDebug _modMigratorDebug; private readonly ShapeInspector _shapeInspector; + private readonly FileWatcher.FileWatcherDrawer _fileWatcherDrawer; public DebugTab(Configuration config, CollectionManager collectionManager, ObjectManager objects, IClientState clientState, IDataManager dataManager, @@ -117,7 +118,7 @@ public class DebugTab : Window, ITab Diagnostics diagnostics, IpcTester ipcTester, CrashHandlerPanel crashHandlerPanel, TexHeaderDrawer texHeaderDrawer, HookOverrideDrawer hookOverrides, RsfService rsfService, GlobalVariablesDrawer globalVariablesDrawer, SchedulerResourceManagementService schedulerService, ObjectIdentification objectIdentification, RenderTargetDrawer renderTargetDrawer, - ModMigratorDebug modMigratorDebug, ShapeInspector shapeInspector) + ModMigratorDebug modMigratorDebug, ShapeInspector shapeInspector, FileWatcher.FileWatcherDrawer fileWatcherDrawer) : base("Penumbra Debug Window", WindowFlags.NoCollapse) { IsOpen = true; @@ -160,6 +161,7 @@ public class DebugTab : Window, ITab _renderTargetDrawer = renderTargetDrawer; _modMigratorDebug = modMigratorDebug; _shapeInspector = shapeInspector; + _fileWatcherDrawer = fileWatcherDrawer; _objects = objects; _clientState = clientState; _dataManager = dataManager; @@ -486,6 +488,8 @@ public class DebugTab : Window, ITab } } } + + _fileWatcherDrawer.Draw(); } private void DrawPerformanceTab()