From c24796a9d816afb05e43cffb943d683026b62f5e Mon Sep 17 00:00:00 2001 From: Exter-N Date: Sun, 19 Oct 2025 17:20:49 +0200 Subject: [PATCH 1/3] Multiple advanced editors --- Penumbra/Import/TexToolsImport.cs | 11 +-- Penumbra/Import/TexToolsImporter.Archives.cs | 2 +- Penumbra/Mods/Editor/MdlMaterialEditor.cs | 2 +- Penumbra/Mods/Editor/ModEditor.cs | 2 +- Penumbra/Mods/Editor/ModEditorFactory.cs | 28 ++++++++ Penumbra/Mods/Editor/ModFileCollection.cs | 2 +- Penumbra/Mods/Editor/ModFileEditor.cs | 2 +- Penumbra/Mods/Editor/ModMetaEditor.cs | 2 +- Penumbra/Mods/Editor/ModSwapEditor.cs | 2 +- Penumbra/Mods/Manager/ModImportManager.cs | 11 ++- Penumbra/UI/AdvancedWindow/ItemSwapTab.cs | 2 +- .../UI/AdvancedWindow/ItemSwapTabFactory.cs | 17 +++++ .../UI/AdvancedWindow/Materials/MtrlTab.cs | 2 +- .../UI/AdvancedWindow/Meta/AtchMetaDrawer.cs | 2 +- .../UI/AdvancedWindow/Meta/AtrMetaDrawer.cs | 2 +- .../UI/AdvancedWindow/Meta/EqdpMetaDrawer.cs | 2 +- .../UI/AdvancedWindow/Meta/EqpMetaDrawer.cs | 2 +- .../UI/AdvancedWindow/Meta/EstMetaDrawer.cs | 2 +- .../Meta/GlobalEqpMetaDrawer.cs | 2 +- .../UI/AdvancedWindow/Meta/GmpMetaDrawer.cs | 4 +- .../UI/AdvancedWindow/Meta/ImcMetaDrawer.cs | 2 +- .../UI/AdvancedWindow/Meta/MetaDrawers.cs | 2 +- .../UI/AdvancedWindow/Meta/RspMetaDrawer.cs | 2 +- .../UI/AdvancedWindow/Meta/ShpMetaDrawer.cs | 2 +- Penumbra/UI/AdvancedWindow/ModEditWindow.cs | 29 +++++--- .../UI/AdvancedWindow/ModEditWindowFactory.cs | 69 ++++++++++++++++++ Penumbra/UI/Classes/IndexedWindow.cs | 31 ++++++++ Penumbra/UI/Classes/WindowFactory.cs | 70 +++++++++++++++++++ Penumbra/UI/ModsTab/ModPanel.cs | 9 +-- Penumbra/UI/ModsTab/ModPanelTabBar.cs | 26 ++++--- Penumbra/UI/WindowSystem.cs | 18 ++--- 31 files changed, 293 insertions(+), 68 deletions(-) create mode 100644 Penumbra/Mods/Editor/ModEditorFactory.cs create mode 100644 Penumbra/UI/AdvancedWindow/ItemSwapTabFactory.cs create mode 100644 Penumbra/UI/AdvancedWindow/ModEditWindowFactory.cs create mode 100644 Penumbra/UI/Classes/IndexedWindow.cs create mode 100644 Penumbra/UI/Classes/WindowFactory.cs diff --git a/Penumbra/Import/TexToolsImport.cs b/Penumbra/Import/TexToolsImport.cs index a5fd2531..29c3d7c5 100644 --- a/Penumbra/Import/TexToolsImport.cs +++ b/Penumbra/Import/TexToolsImport.cs @@ -29,19 +29,22 @@ public partial class TexToolsImporter : IDisposable public readonly List<(FileInfo File, DirectoryInfo? Mod, Exception? Error)> ExtractedMods; private readonly Configuration _config; - private readonly ModEditor _editor; + private readonly DuplicateManager _duplicates; + private readonly ModNormalizer _modNormalizer; private readonly ModManager _modManager; private readonly FileCompactor _compactor; private readonly MigrationManager _migrationManager; public TexToolsImporter(int count, IEnumerable modPackFiles, Action handler, - Configuration config, ModEditor editor, ModManager modManager, FileCompactor compactor, MigrationManager migrationManager) + Configuration config, DuplicateManager duplicates, ModNormalizer modNormalizer, ModManager modManager, FileCompactor compactor, + MigrationManager migrationManager) { _baseDirectory = modManager.BasePath; _tmpFile = Path.Combine(_baseDirectory.FullName, TempFileName); _modPackFiles = modPackFiles; _config = config; - _editor = editor; + _duplicates = duplicates; + _modNormalizer = modNormalizer; _modManager = modManager; _compactor = compactor; _migrationManager = migrationManager; @@ -97,7 +100,7 @@ public partial class TexToolsImporter : IDisposable if (_config.AutoDeduplicateOnImport) { State = ImporterState.DeduplicatingFiles; - _editor.Duplicates.DeduplicateMod(directory); + _duplicates.DeduplicateMod(directory); } } catch (Exception e) diff --git a/Penumbra/Import/TexToolsImporter.Archives.cs b/Penumbra/Import/TexToolsImporter.Archives.cs index a80730bf..0040a784 100644 --- a/Penumbra/Import/TexToolsImporter.Archives.cs +++ b/Penumbra/Import/TexToolsImporter.Archives.cs @@ -131,7 +131,7 @@ public partial class TexToolsImporter _currentModDirectory.Refresh(); _modManager.Creator.SplitMultiGroups(_currentModDirectory); - _editor.ModNormalizer.NormalizeUi(_currentModDirectory); + _modNormalizer.NormalizeUi(_currentModDirectory); return _currentModDirectory; } diff --git a/Penumbra/Mods/Editor/MdlMaterialEditor.cs b/Penumbra/Mods/Editor/MdlMaterialEditor.cs index 7d91714a..f659c22a 100644 --- a/Penumbra/Mods/Editor/MdlMaterialEditor.cs +++ b/Penumbra/Mods/Editor/MdlMaterialEditor.cs @@ -4,7 +4,7 @@ using Penumbra.GameData.Files; namespace Penumbra.Mods.Editor; -public partial class MdlMaterialEditor(ModFileCollection files) : Luna.IService +public partial class MdlMaterialEditor(ModFileCollection files) { [GeneratedRegex(@"/mt_c(?'RaceCode'\d{4})b0001_(?'Suffix'.*?)\.mtrl", RegexOptions.ExplicitCapture | RegexOptions.NonBacktracking)] private static partial Regex MaterialRegex(); diff --git a/Penumbra/Mods/Editor/ModEditor.cs b/Penumbra/Mods/Editor/ModEditor.cs index 5c32ee23..c77f2d5e 100644 --- a/Penumbra/Mods/Editor/ModEditor.cs +++ b/Penumbra/Mods/Editor/ModEditor.cs @@ -13,7 +13,7 @@ public class ModEditor( ModSwapEditor swapEditor, MdlMaterialEditor mdlMaterialEditor, FileCompactor compactor) - : IDisposable, Luna.IService + : IDisposable { public readonly ModNormalizer ModNormalizer = modNormalizer; public readonly ModMetaEditor MetaEditor = metaEditor; diff --git a/Penumbra/Mods/Editor/ModEditorFactory.cs b/Penumbra/Mods/Editor/ModEditorFactory.cs new file mode 100644 index 00000000..fca7d773 --- /dev/null +++ b/Penumbra/Mods/Editor/ModEditorFactory.cs @@ -0,0 +1,28 @@ +using Luna; +using Penumbra.Meta; +using Penumbra.Mods.Manager; +using Penumbra.Mods.Manager.OptionEditor; +using Penumbra.Services; + +namespace Penumbra.Mods.Editor; + +public class ModEditorFactory( + ModNormalizer modNormalizer, + ModGroupEditor groupEditor, + MetaFileManager metaFileManager, + ModManager modManager, + CommunicatorService communicator, + DuplicateManager duplicates, + FileCompactor compactor) : IService +{ + public ModEditor Create() + { + var metaEditor = new ModMetaEditor(groupEditor, metaFileManager); + var files = new ModFileCollection(); + var fileEditor = new ModFileEditor(files, modManager, communicator); + var swapEditor = new ModSwapEditor(modManager); + var mdlMaterialEditor = new MdlMaterialEditor(files); + + return new(modNormalizer, metaEditor, files, fileEditor, duplicates, swapEditor, mdlMaterialEditor, compactor); + } +} diff --git a/Penumbra/Mods/Editor/ModFileCollection.cs b/Penumbra/Mods/Editor/ModFileCollection.cs index 507dfd72..cec4ecbe 100644 --- a/Penumbra/Mods/Editor/ModFileCollection.cs +++ b/Penumbra/Mods/Editor/ModFileCollection.cs @@ -4,7 +4,7 @@ using Penumbra.String.Classes; namespace Penumbra.Mods.Editor; -public class ModFileCollection : IDisposable, Luna.IService +public class ModFileCollection : IDisposable { private readonly List _available = []; private readonly List _mtrl = []; diff --git a/Penumbra/Mods/Editor/ModFileEditor.cs b/Penumbra/Mods/Editor/ModFileEditor.cs index 722aad36..832b5ee5 100644 --- a/Penumbra/Mods/Editor/ModFileEditor.cs +++ b/Penumbra/Mods/Editor/ModFileEditor.cs @@ -6,7 +6,7 @@ using Penumbra.String.Classes; namespace Penumbra.Mods.Editor; -public class ModFileEditor(ModFileCollection files, ModManager modManager, CommunicatorService communicator) : Luna.IService +public class ModFileEditor(ModFileCollection files, ModManager modManager, CommunicatorService communicator) { public bool Changes { get; private set; } diff --git a/Penumbra/Mods/Editor/ModMetaEditor.cs b/Penumbra/Mods/Editor/ModMetaEditor.cs index 96915376..784d8deb 100644 --- a/Penumbra/Mods/Editor/ModMetaEditor.cs +++ b/Penumbra/Mods/Editor/ModMetaEditor.cs @@ -13,7 +13,7 @@ namespace Penumbra.Mods.Editor; public class ModMetaEditor( ModGroupEditor groupEditor, - MetaFileManager metaFileManager) : MetaDictionary, IService + MetaFileManager metaFileManager) : MetaDictionary { public sealed class OtherOptionData : HashSet { diff --git a/Penumbra/Mods/Editor/ModSwapEditor.cs b/Penumbra/Mods/Editor/ModSwapEditor.cs index 91476c0b..bc26a1a0 100644 --- a/Penumbra/Mods/Editor/ModSwapEditor.cs +++ b/Penumbra/Mods/Editor/ModSwapEditor.cs @@ -3,7 +3,7 @@ using Penumbra.Mods.SubMods; using Penumbra.String.Classes; using Penumbra.Util; -public class ModSwapEditor(ModManager modManager) : Luna.IService +public class ModSwapEditor(ModManager modManager) { private readonly Dictionary _swaps = []; diff --git a/Penumbra/Mods/Manager/ModImportManager.cs b/Penumbra/Mods/Manager/ModImportManager.cs index 88e195de..bef6254c 100644 --- a/Penumbra/Mods/Manager/ModImportManager.cs +++ b/Penumbra/Mods/Manager/ModImportManager.cs @@ -6,7 +6,13 @@ using Penumbra.Services; namespace Penumbra.Mods.Manager; -public class ModImportManager(ModManager modManager, Configuration config, ModEditor modEditor, MigrationManager migrationManager) : IDisposable, Luna.IService +public class ModImportManager( + ModManager modManager, + Configuration config, + DuplicateManager duplicates, + ModNormalizer modNormalizer, + MigrationManager migrationManager, + FileCompactor compactor) : IDisposable, Luna.IService { private readonly ConcurrentQueue _modsToUnpack = new(); @@ -42,7 +48,8 @@ public class ModImportManager(ModManager modManager, Configuration config, ModEd if (files.Length == 0) return; - _import = new TexToolsImporter(files.Length, files, AddNewMod, config, modEditor, modManager, modEditor.Compactor, migrationManager); + _import = new TexToolsImporter(files.Length, files, AddNewMod, config, duplicates, modNormalizer, modManager, compactor, + migrationManager); } public bool Importing diff --git a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs index 57be786f..34fb6d80 100644 --- a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs +++ b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs @@ -29,7 +29,7 @@ using MouseWheelType = OtterGui.Widgets.MouseWheelType; namespace Penumbra.UI.AdvancedWindow; -public class ItemSwapTab : IDisposable, ITab, IUiService +public class ItemSwapTab : IDisposable, ITab { private readonly Configuration _config; private readonly CommunicatorService _communicator; diff --git a/Penumbra/UI/AdvancedWindow/ItemSwapTabFactory.cs b/Penumbra/UI/AdvancedWindow/ItemSwapTabFactory.cs new file mode 100644 index 00000000..ba06807d --- /dev/null +++ b/Penumbra/UI/AdvancedWindow/ItemSwapTabFactory.cs @@ -0,0 +1,17 @@ +using Luna; +using Penumbra.Collections.Manager; +using Penumbra.GameData.Data; +using Penumbra.Meta; +using Penumbra.Mods.Manager; +using Penumbra.Services; +using Penumbra.UI.ModsTab; + +namespace Penumbra.UI.AdvancedWindow; + +public class ItemSwapTabFactory(CommunicatorService communicator, ItemData itemService, CollectionManager collectionManager, + ModManager modManager, ModFileSystemSelector selector, ObjectIdentification identifier, MetaFileManager metaFileManager, + Configuration config) : IUiService +{ + public ItemSwapTab Create() + => new(communicator, itemService, collectionManager, modManager, selector, identifier, metaFileManager, config); +} diff --git a/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.cs b/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.cs index 2c7c889e..877246a3 100644 --- a/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.cs +++ b/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.cs @@ -39,7 +39,7 @@ public sealed partial class MtrlTab : IWritable, IDisposable private bool _updateOnNextFrame; - public unsafe MtrlTab(IDataManager gameData, IFramework framework, ObjectManager objects, CharacterBaseDestructor characterBaseDestructor, + public MtrlTab(IDataManager gameData, IFramework framework, ObjectManager objects, CharacterBaseDestructor characterBaseDestructor, StainService stainService, ResourceTreeFactory resourceTreeFactory, FileDialogService fileDialog, MaterialTemplatePickers materialTemplatePickers, Configuration config, ModEditWindow edit, MtrlFile file, string filePath, bool writable) diff --git a/Penumbra/UI/AdvancedWindow/Meta/AtchMetaDrawer.cs b/Penumbra/UI/AdvancedWindow/Meta/AtchMetaDrawer.cs index 64a58020..b69a9b56 100644 --- a/Penumbra/UI/AdvancedWindow/Meta/AtchMetaDrawer.cs +++ b/Penumbra/UI/AdvancedWindow/Meta/AtchMetaDrawer.cs @@ -18,7 +18,7 @@ using Notification = Luna.Notification; namespace Penumbra.UI.AdvancedWindow.Meta; -public sealed class AtchMetaDrawer : MetaDrawer, Luna.IService +public sealed class AtchMetaDrawer : MetaDrawer { public override ReadOnlySpan Label => "Attachment Points (ATCH)###ATCH"u8; diff --git a/Penumbra/UI/AdvancedWindow/Meta/AtrMetaDrawer.cs b/Penumbra/UI/AdvancedWindow/Meta/AtrMetaDrawer.cs index 9dc2a4d4..c72f4a64 100644 --- a/Penumbra/UI/AdvancedWindow/Meta/AtrMetaDrawer.cs +++ b/Penumbra/UI/AdvancedWindow/Meta/AtrMetaDrawer.cs @@ -15,7 +15,7 @@ using Penumbra.UI.Classes; namespace Penumbra.UI.AdvancedWindow.Meta; public sealed class AtrMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) - : MetaDrawer(editor, metaFiles), Luna.IService + : MetaDrawer(editor, metaFiles) { public override ReadOnlySpan Label => "Attributes(ATR)###ATR"u8; diff --git a/Penumbra/UI/AdvancedWindow/Meta/EqdpMetaDrawer.cs b/Penumbra/UI/AdvancedWindow/Meta/EqdpMetaDrawer.cs index 04b44883..50c8b2d5 100644 --- a/Penumbra/UI/AdvancedWindow/Meta/EqdpMetaDrawer.cs +++ b/Penumbra/UI/AdvancedWindow/Meta/EqdpMetaDrawer.cs @@ -15,7 +15,7 @@ using Penumbra.UI.Classes; namespace Penumbra.UI.AdvancedWindow.Meta; public sealed class EqdpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) - : MetaDrawer(editor, metaFiles), Luna.IService + : MetaDrawer(editor, metaFiles) { public override ReadOnlySpan Label => "Racial Model Edits (EQDP)###EQDP"u8; diff --git a/Penumbra/UI/AdvancedWindow/Meta/EqpMetaDrawer.cs b/Penumbra/UI/AdvancedWindow/Meta/EqpMetaDrawer.cs index 89376be3..3ecc90d9 100644 --- a/Penumbra/UI/AdvancedWindow/Meta/EqpMetaDrawer.cs +++ b/Penumbra/UI/AdvancedWindow/Meta/EqpMetaDrawer.cs @@ -14,7 +14,7 @@ using Penumbra.UI.Classes; namespace Penumbra.UI.AdvancedWindow.Meta; public sealed class EqpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) - : MetaDrawer(editor, metaFiles), Luna.IService + : MetaDrawer(editor, metaFiles) { public override ReadOnlySpan Label => "Equipment Parameter Edits (EQP)###EQP"u8; diff --git a/Penumbra/UI/AdvancedWindow/Meta/EstMetaDrawer.cs b/Penumbra/UI/AdvancedWindow/Meta/EstMetaDrawer.cs index e2b1a3c3..e7183a4d 100644 --- a/Penumbra/UI/AdvancedWindow/Meta/EstMetaDrawer.cs +++ b/Penumbra/UI/AdvancedWindow/Meta/EstMetaDrawer.cs @@ -13,7 +13,7 @@ using Penumbra.UI.Classes; namespace Penumbra.UI.AdvancedWindow.Meta; public sealed class EstMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) - : MetaDrawer(editor, metaFiles), Luna.IService + : MetaDrawer(editor, metaFiles) { public override ReadOnlySpan Label => "Extra Skeleton Parameters (EST)###EST"u8; diff --git a/Penumbra/UI/AdvancedWindow/Meta/GlobalEqpMetaDrawer.cs b/Penumbra/UI/AdvancedWindow/Meta/GlobalEqpMetaDrawer.cs index f618dc60..505450a5 100644 --- a/Penumbra/UI/AdvancedWindow/Meta/GlobalEqpMetaDrawer.cs +++ b/Penumbra/UI/AdvancedWindow/Meta/GlobalEqpMetaDrawer.cs @@ -10,7 +10,7 @@ using Penumbra.Mods.Editor; namespace Penumbra.UI.AdvancedWindow.Meta; public sealed class GlobalEqpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) - : MetaDrawer(editor, metaFiles), Luna.IService + : MetaDrawer(editor, metaFiles) { public override ReadOnlySpan Label => "Global Equipment Parameter Edits (Global EQP)###GEQP"u8; diff --git a/Penumbra/UI/AdvancedWindow/Meta/GmpMetaDrawer.cs b/Penumbra/UI/AdvancedWindow/Meta/GmpMetaDrawer.cs index 6f531cb7..81976324 100644 --- a/Penumbra/UI/AdvancedWindow/Meta/GmpMetaDrawer.cs +++ b/Penumbra/UI/AdvancedWindow/Meta/GmpMetaDrawer.cs @@ -12,7 +12,7 @@ using Newtonsoft.Json.Linq; namespace Penumbra.UI.AdvancedWindow.Meta; public sealed class GmpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) - : MetaDrawer(editor, metaFiles), Luna.IService + : MetaDrawer(editor, metaFiles) { public override ReadOnlySpan Label => "Visor/Gimmick Edits (GMP)###GMP"u8; @@ -59,7 +59,7 @@ public sealed class GmpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile protected override IEnumerable<(GmpIdentifier, GmpEntry)> Enumerate() => Editor.Gmp .OrderBy(kvp => kvp.Key.SetId.Id) - .Select(kvp => (kvp.Key, kvp.Value)); + .Select(kvp => (kvp.Key, kvp.Value)); protected override int Count => Editor.Gmp.Count; diff --git a/Penumbra/UI/AdvancedWindow/Meta/ImcMetaDrawer.cs b/Penumbra/UI/AdvancedWindow/Meta/ImcMetaDrawer.cs index ccd676f1..8b84a11a 100644 --- a/Penumbra/UI/AdvancedWindow/Meta/ImcMetaDrawer.cs +++ b/Penumbra/UI/AdvancedWindow/Meta/ImcMetaDrawer.cs @@ -13,7 +13,7 @@ using Penumbra.UI.Classes; namespace Penumbra.UI.AdvancedWindow.Meta; public sealed class ImcMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) - : MetaDrawer(editor, metaFiles), Luna.IService + : MetaDrawer(editor, metaFiles) { public override ReadOnlySpan Label => "Variant Edits (IMC)###IMC"u8; diff --git a/Penumbra/UI/AdvancedWindow/Meta/MetaDrawers.cs b/Penumbra/UI/AdvancedWindow/Meta/MetaDrawers.cs index 73ee0126..f7bba294 100644 --- a/Penumbra/UI/AdvancedWindow/Meta/MetaDrawers.cs +++ b/Penumbra/UI/AdvancedWindow/Meta/MetaDrawers.cs @@ -12,7 +12,7 @@ public class MetaDrawers( RspMetaDrawer rsp, AtchMetaDrawer atch, ShpMetaDrawer shp, - AtrMetaDrawer atr) : Luna.IService + AtrMetaDrawer atr) { public readonly EqdpMetaDrawer Eqdp = eqdp; public readonly EqpMetaDrawer Eqp = eqp; diff --git a/Penumbra/UI/AdvancedWindow/Meta/RspMetaDrawer.cs b/Penumbra/UI/AdvancedWindow/Meta/RspMetaDrawer.cs index dee83a63..44b6d5ed 100644 --- a/Penumbra/UI/AdvancedWindow/Meta/RspMetaDrawer.cs +++ b/Penumbra/UI/AdvancedWindow/Meta/RspMetaDrawer.cs @@ -13,7 +13,7 @@ using Penumbra.UI.Classes; namespace Penumbra.UI.AdvancedWindow.Meta; public sealed class RspMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) - : MetaDrawer(editor, metaFiles), Luna.IService + : MetaDrawer(editor, metaFiles) { public override ReadOnlySpan Label => "Racial Scaling Edits (RSP)###RSP"u8; diff --git a/Penumbra/UI/AdvancedWindow/Meta/ShpMetaDrawer.cs b/Penumbra/UI/AdvancedWindow/Meta/ShpMetaDrawer.cs index 2cf43e71..aff63364 100644 --- a/Penumbra/UI/AdvancedWindow/Meta/ShpMetaDrawer.cs +++ b/Penumbra/UI/AdvancedWindow/Meta/ShpMetaDrawer.cs @@ -15,7 +15,7 @@ using Penumbra.UI.Classes; namespace Penumbra.UI.AdvancedWindow.Meta; public sealed class ShpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) - : MetaDrawer(editor, metaFiles), Luna.IService + : MetaDrawer(editor, metaFiles) { public override ReadOnlySpan Label => "Shape Keys (SHP)###SHP"u8; diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs index c14443da..e45e6368 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs @@ -31,7 +31,7 @@ using MdlMaterialEditor = Penumbra.Mods.Editor.MdlMaterialEditor; namespace Penumbra.UI.AdvancedWindow; -public partial class ModEditWindow : Window, IDisposable, Luna.IUiService +public partial class ModEditWindow : IndexedWindow, IDisposable { private const string WindowBaseLabel = "###SubModEdit"; @@ -84,7 +84,7 @@ public partial class ModEditWindow : Window, IDisposable, Luna.IUiService if (mod == Mod) return; - WindowName = $"{mod.Name} (LOADING){WindowBaseLabel}"; + WindowName = $"{mod.Name} (LOADING){WindowBaseLabel}{Index}"; AppendTask(() => { _editor.LoadMod(mod, -1, 0).Wait(); @@ -173,11 +173,13 @@ public partial class ModEditWindow : Window, IDisposable, Luna.IUiService _allowReduplicate = redirections != _editor.Files.Available.Count || _editor.Files.Missing.Count > 0 || unused > 0; sb.Append(WindowBaseLabel); + sb.Append(Index); WindowName = sb.ToString(); } public override void OnClose() { + base.OnClose(); _config.Ephemeral.AdvancedEditingOpen = false; _config.Ephemeral.Save(); AppendTask(() => @@ -620,9 +622,9 @@ public partial class ModEditWindow : Window, IDisposable, Luna.IUiService ActiveCollections activeCollections, ModMergeTab modMergeTab, CommunicatorService communicator, TextureManager textures, ModelManager models, IDragDropManager dragDropManager, ResourceTreeViewerFactory resourceTreeViewerFactory, IFramework framework, - MetaDrawers metaDrawers, MigrationManager migrationManager, - MtrlTabFactory mtrlTabFactory, ModSelection selection) - : base(WindowBaseLabel) + MetaDrawers metaDrawers, + MtrlTabFactory mtrlTabFactory, WindowSystem windowSystem, int index) + : base($"{WindowBaseLabel}{index}", windowSystem, index) { _itemSwapTab = itemSwapTab; _gameData = gameData; @@ -658,9 +660,6 @@ public partial class ModEditWindow : Window, IDisposable, Luna.IUiService _resourceTreeFactory = resourceTreeFactory; _quickImportViewer = resourceTreeViewerFactory.Create(1, OnQuickImportRefresh, DrawQuickImportActions); _communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.ModEditWindow); - IsOpen = _config is { OpenWindowAtStart: true, Ephemeral.AdvancedEditingOpen: true }; - if (IsOpen && selection.Mod != null) - ChangeMod(selection.Mod); } public void Dispose() @@ -677,10 +676,18 @@ public partial class ModEditWindow : Window, IDisposable, Luna.IUiService private void OnModPathChange(in ModPathChanged.Arguments arguments) { - if (arguments.Type is not (ModPathChangeType.Reloaded or ModPathChangeType.Moved) || arguments.Mod != Mod) + if (arguments.Mod != Mod) return; - Mod = null; - ChangeMod(arguments.Mod); + switch (arguments.Type) + { + case ModPathChangeType.Reloaded or ModPathChangeType.Moved: + Mod = null; + ChangeMod(arguments.Mod); + break; + case ModPathChangeType.Deleted: + Dispose(); + break; + } } } diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindowFactory.cs b/Penumbra/UI/AdvancedWindow/ModEditWindowFactory.cs new file mode 100644 index 00000000..ade0f0e4 --- /dev/null +++ b/Penumbra/UI/AdvancedWindow/ModEditWindowFactory.cs @@ -0,0 +1,69 @@ +using Dalamud.Interface.DragDrop; +using Dalamud.Plugin.Services; +using Penumbra.Collections.Manager; +using Penumbra.Import.Models; +using Penumbra.Import.Textures; +using Penumbra.Interop.ResourceTree; +using Penumbra.Meta; +using Penumbra.Mods; +using Penumbra.Mods.Editor; +using Penumbra.Services; +using Penumbra.UI.AdvancedWindow.Materials; +using Penumbra.UI.AdvancedWindow.Meta; +using Penumbra.UI.Classes; + +namespace Penumbra.UI.AdvancedWindow; + +public class ModEditWindowFactory(FileDialogService fileDialog, ItemSwapTabFactory itemSwapTabFactory, IDataManager gameData, + Configuration config, ModEditorFactory editorFactory, ResourceTreeFactory resourceTreeFactory, MetaFileManager metaFileManager, + ActiveCollections activeCollections, ModMergeTab modMergeTab, + CommunicatorService communicator, TextureManager textures, ModelManager models, IDragDropManager dragDropManager, + ResourceTreeViewerFactory resourceTreeViewerFactory, IFramework framework, + MtrlTabFactory mtrlTabFactory, ModSelection selection) : WindowFactory, Luna.IUiService +{ + protected override void OnWindowSystemSet() + { + if (config is { OpenWindowAtStart: true, Ephemeral.AdvancedEditingOpen: true } && selection.Mod is not null) + OpenForMod(selection.Mod); + } + + protected override ModEditWindow? DoCreateWindow() + { + var editor = editorFactory.Create(); + + return new(fileDialog, itemSwapTabFactory.Create(), gameData, config, editor, resourceTreeFactory, metaFileManager, + activeCollections, modMergeTab, communicator, textures, models, dragDropManager, resourceTreeViewerFactory, framework, + CreateMetaDrawers(editor.MetaEditor), mtrlTabFactory, WindowSystem ?? throw new InvalidOperationException("WindowSystem not set"), + GetFreeIndex()); + } + + private MetaDrawers CreateMetaDrawers(ModMetaEditor metaEditor) + { + var eqdp = new EqdpMetaDrawer(metaEditor, metaFileManager); + var eqp = new EqpMetaDrawer(metaEditor, metaFileManager); + var est = new EstMetaDrawer(metaEditor, metaFileManager); + var globalEqp = new GlobalEqpMetaDrawer(metaEditor, metaFileManager); + var gmp = new GmpMetaDrawer(metaEditor, metaFileManager); + var imc = new ImcMetaDrawer(metaEditor, metaFileManager); + var rsp = new RspMetaDrawer(metaEditor, metaFileManager); + var atch = new AtchMetaDrawer(metaEditor, metaFileManager); + var shp = new ShpMetaDrawer(metaEditor, metaFileManager); + var atr = new AtrMetaDrawer(metaEditor, metaFileManager); + + return new(eqdp, eqp, est, globalEqp, gmp, imc, rsp, atch, shp, atr); + } + + public void OpenForMod(Mod mod) + { + var window = OpenWindows.FirstOrDefault(window => window.Mod == mod); + if (window is not null) + { + window.BringToFront(); + return; + } + + window = CreateWindow()!; + window.ChangeMod(mod); + window.ChangeOption(mod.Default); + } +} diff --git a/Penumbra/UI/Classes/IndexedWindow.cs b/Penumbra/UI/Classes/IndexedWindow.cs new file mode 100644 index 00000000..58d2386b --- /dev/null +++ b/Penumbra/UI/Classes/IndexedWindow.cs @@ -0,0 +1,31 @@ +using Dalamud.Interface.Windowing; +using ImSharp; +using Window = Luna.Window; + +namespace Penumbra.UI.Classes; + +public abstract class IndexedWindow : Window +{ + private readonly WindowSystem _windowSystem; + private readonly int _index; + + public int Index + => _index; + + public event EventHandler? Close; + + protected IndexedWindow(string name, WindowSystem windowSystem, int index, + WindowFlags flags = WindowFlags.None, bool forceMainWindow = false) : base( + name, flags, forceMainWindow + ) + { + _windowSystem = windowSystem; + _index = index; + } + + public override void OnClose() + { + _windowSystem.RemoveWindow(this); + Close?.Invoke(this, EventArgs.Empty); + } +} diff --git a/Penumbra/UI/Classes/WindowFactory.cs b/Penumbra/UI/Classes/WindowFactory.cs new file mode 100644 index 00000000..7cb37f61 --- /dev/null +++ b/Penumbra/UI/Classes/WindowFactory.cs @@ -0,0 +1,70 @@ +using Dalamud.Interface.Windowing; + +namespace Penumbra.UI.Classes; + +public abstract class WindowFactory where T : IndexedWindow +{ + protected WindowSystem? WindowSystem; + + private readonly HashSet _openWindows = []; + private readonly HashSet _reusableIndices = []; + private int _nextIndex = 0; + + protected IEnumerable OpenWindows + => _openWindows; + + internal void SetWindowSystem(WindowSystem windowSystem) + { + if (WindowSystem is not null) + throw new InvalidOperationException("WindowSystem is already set"); + + WindowSystem = windowSystem; + + OnWindowSystemSet(); + } + + protected virtual void OnWindowSystemSet() + { + } + + protected int GetFreeIndex() + { + foreach (var index in _reusableIndices) { + _reusableIndices.Remove(index); + return index; + } + + return _nextIndex++; + } + + protected abstract T? DoCreateWindow(); + + protected T? CreateWindow() + { + var window = DoCreateWindow(); + if (window is not null) { + SetupWindow(window); + } + + return window; + } + + protected void SetupWindow(T window) + { + window.Close += WindowClose; + WindowSystem?.AddWindow(window); + window.IsOpen = true; + _openWindows.Add(window); + window.BringToFront(); + } + + private void WindowClose(object? sender, EventArgs e) + { + if (sender is not T window) { + return; + } + + _openWindows.Remove(window); + _reusableIndices.Add(window.Index); + } +} diff --git a/Penumbra/UI/ModsTab/ModPanel.cs b/Penumbra/UI/ModsTab/ModPanel.cs index fe594d4b..6bbe447f 100644 --- a/Penumbra/UI/ModsTab/ModPanel.cs +++ b/Penumbra/UI/ModsTab/ModPanel.cs @@ -11,16 +11,14 @@ public class ModPanel : IDisposable, Luna.IUiService { private readonly MultiModPanel _multiModPanel; private readonly ModSelection _selection; - private readonly ModEditWindow _editWindow; private readonly ModPanelHeader _header; private readonly ModPanelTabBar _tabs; private bool _resetCursor; - public ModPanel(IDalamudPluginInterface pi, ModSelection selection, ModEditWindow editWindow, ModPanelTabBar tabs, + public ModPanel(IDalamudPluginInterface pi, ModSelection selection, ModPanelTabBar tabs, MultiModPanel multiModPanel, CommunicatorService communicator) { _selection = selection; - _editWindow = editWindow; _tabs = tabs; _multiModPanel = multiModPanel; _header = new ModPanelHeader(pi, communicator); @@ -64,13 +62,10 @@ public class ModPanel : IDisposable, Luna.IUiService _resetCursor = true; if (arguments.NewSelection is null || _selection.Mod is null) { - _editWindow.IsOpen = false; - _valid = false; + _valid = false; } else { - if (_editWindow.IsOpen) - _editWindow.ChangeMod(arguments.NewSelection); _valid = true; _mod = arguments.NewSelection; _header.ChangeMod(_mod); diff --git a/Penumbra/UI/ModsTab/ModPanelTabBar.cs b/Penumbra/UI/ModsTab/ModPanelTabBar.cs index 81c0ff89..a8cd75e5 100644 --- a/Penumbra/UI/ModsTab/ModPanelTabBar.cs +++ b/Penumbra/UI/ModsTab/ModPanelTabBar.cs @@ -27,7 +27,7 @@ public class ModPanelTabBar : Luna.IUiService public readonly ModPanelConflictsTab Conflicts; public readonly ModPanelChangedItemsTab ChangedItems; public readonly ModPanelEditTab Edit; - private readonly ModEditWindow _modEditWindow; + private readonly ModEditWindowFactory _modEditWindowFactory; private readonly ModManager _modManager; private readonly TutorialService _tutorial; @@ -35,19 +35,19 @@ public class ModPanelTabBar : Luna.IUiService private ModPanelTabType _preferredTab = ModPanelTabType.Settings; private Mod? _lastMod; - public ModPanelTabBar(ModEditWindow modEditWindow, ModPanelSettingsTab settings, ModPanelDescriptionTab description, + public ModPanelTabBar(ModEditWindowFactory modEditWindowFactory, 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; + _modEditWindowFactory = modEditWindowFactory; + Settings = settings; + Description = description; + Conflicts = conflicts; + ChangedItems = changedItems; + Edit = edit; + _modManager = modManager; + _tutorial = tutorial; + Collections = collections; Tabs = [ @@ -112,9 +112,7 @@ public class ModPanelTabBar : Luna.IUiService { if (ImGui.TabItemButton("Advanced Editing", ImGuiTabItemFlags.Trailing | ImGuiTabItemFlags.NoTooltip)) { - _modEditWindow.ChangeMod(mod); - _modEditWindow.ChangeOption(mod.Default); - _modEditWindow.IsOpen = true; + _modEditWindowFactory.OpenForMod(mod); } ImGuiUtil.HoverTooltip( diff --git a/Penumbra/UI/WindowSystem.cs b/Penumbra/UI/WindowSystem.cs index 9c43ac22..be6b11cd 100644 --- a/Penumbra/UI/WindowSystem.cs +++ b/Penumbra/UI/WindowSystem.cs @@ -10,16 +10,16 @@ namespace Penumbra.UI; public class PenumbraWindowSystem : IDisposable, Luna.IUiService { - private readonly IUiBuilder _uiBuilder; - private readonly WindowSystem _windowSystem; - private readonly FileDialogService _fileDialog; - private readonly TextureArraySlicer _textureArraySlicer; - public readonly ConfigWindow Window; - public readonly PenumbraChangelog Changelog; - public readonly KnowledgeWindow KnowledgeWindow; + private readonly IUiBuilder _uiBuilder; + internal readonly WindowSystem _windowSystem; + private readonly FileDialogService _fileDialog; + private readonly TextureArraySlicer _textureArraySlicer; + public readonly ConfigWindow Window; + public readonly PenumbraChangelog Changelog; + public readonly KnowledgeWindow KnowledgeWindow; public PenumbraWindowSystem(IDalamudPluginInterface pi, Configuration config, PenumbraChangelog changelog, ConfigWindow window, - LaunchButton _, ModEditWindow editWindow, FileDialogService fileDialog, ImportPopup importPopup, DebugTab debugTab, + LaunchButton _, ModEditWindowFactory editWindowFactory, FileDialogService fileDialog, ImportPopup importPopup, DebugTab debugTab, KnowledgeWindow knowledgeWindow, TextureArraySlicer textureArraySlicer) { _uiBuilder = pi.UiBuilder; @@ -31,10 +31,10 @@ public class PenumbraWindowSystem : IDisposable, Luna.IUiService _windowSystem = new WindowSystem("Penumbra"); _windowSystem.AddWindow(changelog.Changelog); _windowSystem.AddWindow(window); - _windowSystem.AddWindow(editWindow); _windowSystem.AddWindow(importPopup); _windowSystem.AddWindow(debugTab); _windowSystem.AddWindow(KnowledgeWindow); + editWindowFactory.SetWindowSystem(_windowSystem); _uiBuilder.OpenMainUi += Window.Toggle; _uiBuilder.OpenConfigUi += Window.OpenSettings; _uiBuilder.Draw += _windowSystem.Draw; From 3e57f8dd1ea5072ab70b5f8794dc9206de8c859d Mon Sep 17 00:00:00 2001 From: Exter-N Date: Sun, 19 Oct 2025 17:26:42 +0200 Subject: [PATCH 2/3] Actually close AE window on mod deletion --- Penumbra/UI/AdvancedWindow/ModEditWindow.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs index e45e6368..8e598895 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs @@ -686,6 +686,7 @@ public partial class ModEditWindow : IndexedWindow, IDisposable ChangeMod(arguments.Mod); break; case ModPathChangeType.Deleted: + IsOpen = false; Dispose(); break; } From dc7d91cf3f9b9770c72eb65d3ab799eb9585c1bc Mon Sep 17 00:00:00 2001 From: Exter-N Date: Sun, 19 Oct 2025 17:43:35 +0200 Subject: [PATCH 3/3] Switch to C# 14 --- Penumbra/Penumbra.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Penumbra/Penumbra.csproj b/Penumbra/Penumbra.csproj index fa45ffbf..d28c575e 100644 --- a/Penumbra/Penumbra.csproj +++ b/Penumbra/Penumbra.csproj @@ -7,6 +7,7 @@ 9.0.0.1 9.0.0.1 bin\$(Configuration)\ + preview