Merge remote-tracking branch 'Exter-N/luna-multi-advedit' into luna

# Conflicts:
#	Penumbra/UI/AdvancedWindow/Meta/AtrMetaDrawer.cs
#	Penumbra/UI/ModsTab/ModPanelTabBar.cs
This commit is contained in:
Ottermandias 2025-10-20 22:42:54 +02:00
commit 97c9f1c8c4
32 changed files with 295 additions and 68 deletions

View file

@ -29,19 +29,22 @@ public partial class TexToolsImporter : IDisposable
public readonly List<(FileInfo File, DirectoryInfo? Mod, Exception? Error)> ExtractedMods; public readonly List<(FileInfo File, DirectoryInfo? Mod, Exception? Error)> ExtractedMods;
private readonly Configuration _config; private readonly Configuration _config;
private readonly ModEditor _editor; private readonly DuplicateManager _duplicates;
private readonly ModNormalizer _modNormalizer;
private readonly ModManager _modManager; private readonly ModManager _modManager;
private readonly FileCompactor _compactor; private readonly FileCompactor _compactor;
private readonly MigrationManager _migrationManager; private readonly MigrationManager _migrationManager;
public TexToolsImporter(int count, IEnumerable<FileInfo> modPackFiles, Action<FileInfo, DirectoryInfo?, Exception?> handler, public TexToolsImporter(int count, IEnumerable<FileInfo> modPackFiles, Action<FileInfo, DirectoryInfo?, Exception?> 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; _baseDirectory = modManager.BasePath;
_tmpFile = Path.Combine(_baseDirectory.FullName, TempFileName); _tmpFile = Path.Combine(_baseDirectory.FullName, TempFileName);
_modPackFiles = modPackFiles; _modPackFiles = modPackFiles;
_config = config; _config = config;
_editor = editor; _duplicates = duplicates;
_modNormalizer = modNormalizer;
_modManager = modManager; _modManager = modManager;
_compactor = compactor; _compactor = compactor;
_migrationManager = migrationManager; _migrationManager = migrationManager;
@ -97,7 +100,7 @@ public partial class TexToolsImporter : IDisposable
if (_config.AutoDeduplicateOnImport) if (_config.AutoDeduplicateOnImport)
{ {
State = ImporterState.DeduplicatingFiles; State = ImporterState.DeduplicatingFiles;
_editor.Duplicates.DeduplicateMod(directory); _duplicates.DeduplicateMod(directory);
} }
} }
catch (Exception e) catch (Exception e)

View file

@ -131,7 +131,7 @@ public partial class TexToolsImporter
_currentModDirectory.Refresh(); _currentModDirectory.Refresh();
_modManager.Creator.SplitMultiGroups(_currentModDirectory); _modManager.Creator.SplitMultiGroups(_currentModDirectory);
_editor.ModNormalizer.NormalizeUi(_currentModDirectory); _modNormalizer.NormalizeUi(_currentModDirectory);
return _currentModDirectory; return _currentModDirectory;
} }

View file

@ -4,7 +4,7 @@ using Penumbra.GameData.Files;
namespace Penumbra.Mods.Editor; 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)] [GeneratedRegex(@"/mt_c(?'RaceCode'\d{4})b0001_(?'Suffix'.*?)\.mtrl", RegexOptions.ExplicitCapture | RegexOptions.NonBacktracking)]
private static partial Regex MaterialRegex(); private static partial Regex MaterialRegex();

View file

@ -13,7 +13,7 @@ public class ModEditor(
ModSwapEditor swapEditor, ModSwapEditor swapEditor,
MdlMaterialEditor mdlMaterialEditor, MdlMaterialEditor mdlMaterialEditor,
FileCompactor compactor) FileCompactor compactor)
: IDisposable, Luna.IService : IDisposable
{ {
public readonly ModNormalizer ModNormalizer = modNormalizer; public readonly ModNormalizer ModNormalizer = modNormalizer;
public readonly ModMetaEditor MetaEditor = metaEditor; public readonly ModMetaEditor MetaEditor = metaEditor;

View file

@ -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);
}
}

View file

@ -4,7 +4,7 @@ using Penumbra.String.Classes;
namespace Penumbra.Mods.Editor; namespace Penumbra.Mods.Editor;
public class ModFileCollection : IDisposable, Luna.IService public class ModFileCollection : IDisposable
{ {
private readonly List<FileRegistry> _available = []; private readonly List<FileRegistry> _available = [];
private readonly List<FileRegistry> _mtrl = []; private readonly List<FileRegistry> _mtrl = [];

View file

@ -6,7 +6,7 @@ using Penumbra.String.Classes;
namespace Penumbra.Mods.Editor; 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; } public bool Changes { get; private set; }

View file

@ -13,7 +13,7 @@ namespace Penumbra.Mods.Editor;
public class ModMetaEditor( public class ModMetaEditor(
ModGroupEditor groupEditor, ModGroupEditor groupEditor,
MetaFileManager metaFileManager) : MetaDictionary, IService MetaFileManager metaFileManager) : MetaDictionary
{ {
public sealed class OtherOptionData : HashSet<string> public sealed class OtherOptionData : HashSet<string>
{ {

View file

@ -3,7 +3,7 @@ using Penumbra.Mods.SubMods;
using Penumbra.String.Classes; using Penumbra.String.Classes;
using Penumbra.Util; using Penumbra.Util;
public class ModSwapEditor(ModManager modManager) : Luna.IService public class ModSwapEditor(ModManager modManager)
{ {
private readonly Dictionary<Utf8GamePath, FullPath> _swaps = []; private readonly Dictionary<Utf8GamePath, FullPath> _swaps = [];

View file

@ -6,7 +6,13 @@ using Penumbra.Services;
namespace Penumbra.Mods.Manager; 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<string[]> _modsToUnpack = new(); private readonly ConcurrentQueue<string[]> _modsToUnpack = new();
@ -42,7 +48,8 @@ public class ModImportManager(ModManager modManager, Configuration config, ModEd
if (files.Length == 0) if (files.Length == 0)
return; 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 public bool Importing

View file

@ -7,6 +7,7 @@
<FileVersion>9.0.0.1</FileVersion> <FileVersion>9.0.0.1</FileVersion>
<AssemblyVersion>9.0.0.1</AssemblyVersion> <AssemblyVersion>9.0.0.1</AssemblyVersion>
<OutputPath>bin\$(Configuration)\</OutputPath> <OutputPath>bin\$(Configuration)\</OutputPath>
<LangVersion>preview</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View file

@ -29,7 +29,7 @@ using MouseWheelType = OtterGui.Widgets.MouseWheelType;
namespace Penumbra.UI.AdvancedWindow; namespace Penumbra.UI.AdvancedWindow;
public class ItemSwapTab : IDisposable, ITab, IUiService public class ItemSwapTab : IDisposable, ITab
{ {
private readonly Configuration _config; private readonly Configuration _config;
private readonly CommunicatorService _communicator; private readonly CommunicatorService _communicator;

View file

@ -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);
}

View file

@ -39,7 +39,7 @@ public sealed partial class MtrlTab : IWritable, IDisposable
private bool _updateOnNextFrame; 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, StainService stainService, ResourceTreeFactory resourceTreeFactory, FileDialogService fileDialog,
MaterialTemplatePickers materialTemplatePickers, MaterialTemplatePickers materialTemplatePickers,
Configuration config, ModEditWindow edit, MtrlFile file, string filePath, bool writable) Configuration config, ModEditWindow edit, MtrlFile file, string filePath, bool writable)

View file

@ -18,7 +18,7 @@ using Notification = Luna.Notification;
namespace Penumbra.UI.AdvancedWindow.Meta; namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class AtchMetaDrawer : MetaDrawer<AtchIdentifier, AtchEntry>, Luna.IService public sealed class AtchMetaDrawer : MetaDrawer<AtchIdentifier, AtchEntry>
{ {
public override ReadOnlySpan<byte> Label public override ReadOnlySpan<byte> Label
=> "Attachment Points (ATCH)###ATCH"u8; => "Attachment Points (ATCH)###ATCH"u8;

View file

@ -16,7 +16,7 @@ using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta; namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class AtrMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) public sealed class AtrMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<AtrIdentifier, AtrEntry>(editor, metaFiles), Luna.IService : MetaDrawer<AtrIdentifier, AtrEntry>(editor, metaFiles)
{ {
public override ReadOnlySpan<byte> Label public override ReadOnlySpan<byte> Label
=> "Attributes(ATR)###ATR"u8; => "Attributes(ATR)###ATR"u8;

View file

@ -15,7 +15,7 @@ using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta; namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class EqdpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) public sealed class EqdpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<EqdpIdentifier, EqdpEntryInternal>(editor, metaFiles), Luna.IService : MetaDrawer<EqdpIdentifier, EqdpEntryInternal>(editor, metaFiles)
{ {
public override ReadOnlySpan<byte> Label public override ReadOnlySpan<byte> Label
=> "Racial Model Edits (EQDP)###EQDP"u8; => "Racial Model Edits (EQDP)###EQDP"u8;

View file

@ -14,7 +14,7 @@ using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta; namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class EqpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) public sealed class EqpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<EqpIdentifier, EqpEntryInternal>(editor, metaFiles), Luna.IService : MetaDrawer<EqpIdentifier, EqpEntryInternal>(editor, metaFiles)
{ {
public override ReadOnlySpan<byte> Label public override ReadOnlySpan<byte> Label
=> "Equipment Parameter Edits (EQP)###EQP"u8; => "Equipment Parameter Edits (EQP)###EQP"u8;

View file

@ -13,7 +13,7 @@ using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta; namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class EstMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) public sealed class EstMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<EstIdentifier, EstEntry>(editor, metaFiles), Luna.IService : MetaDrawer<EstIdentifier, EstEntry>(editor, metaFiles)
{ {
public override ReadOnlySpan<byte> Label public override ReadOnlySpan<byte> Label
=> "Extra Skeleton Parameters (EST)###EST"u8; => "Extra Skeleton Parameters (EST)###EST"u8;

View file

@ -10,7 +10,7 @@ using Penumbra.Mods.Editor;
namespace Penumbra.UI.AdvancedWindow.Meta; namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class GlobalEqpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) public sealed class GlobalEqpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<GlobalEqpManipulation, byte>(editor, metaFiles), Luna.IService : MetaDrawer<GlobalEqpManipulation, byte>(editor, metaFiles)
{ {
public override ReadOnlySpan<byte> Label public override ReadOnlySpan<byte> Label
=> "Global Equipment Parameter Edits (Global EQP)###GEQP"u8; => "Global Equipment Parameter Edits (Global EQP)###GEQP"u8;

View file

@ -12,7 +12,7 @@ using Newtonsoft.Json.Linq;
namespace Penumbra.UI.AdvancedWindow.Meta; namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class GmpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) public sealed class GmpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<GmpIdentifier, GmpEntry>(editor, metaFiles), Luna.IService : MetaDrawer<GmpIdentifier, GmpEntry>(editor, metaFiles)
{ {
public override ReadOnlySpan<byte> Label public override ReadOnlySpan<byte> Label
=> "Visor/Gimmick Edits (GMP)###GMP"u8; => "Visor/Gimmick Edits (GMP)###GMP"u8;
@ -59,7 +59,7 @@ public sealed class GmpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
protected override IEnumerable<(GmpIdentifier, GmpEntry)> Enumerate() protected override IEnumerable<(GmpIdentifier, GmpEntry)> Enumerate()
=> Editor.Gmp => Editor.Gmp
.OrderBy(kvp => kvp.Key.SetId.Id) .OrderBy(kvp => kvp.Key.SetId.Id)
.Select(kvp => (kvp.Key, kvp.Value)); .Select(kvp => (kvp.Key, kvp.Value));
protected override int Count protected override int Count
=> Editor.Gmp.Count; => Editor.Gmp.Count;

View file

@ -13,7 +13,7 @@ using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta; namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class ImcMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) public sealed class ImcMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<ImcIdentifier, ImcEntry>(editor, metaFiles), Luna.IService : MetaDrawer<ImcIdentifier, ImcEntry>(editor, metaFiles)
{ {
public override ReadOnlySpan<byte> Label public override ReadOnlySpan<byte> Label
=> "Variant Edits (IMC)###IMC"u8; => "Variant Edits (IMC)###IMC"u8;

View file

@ -12,7 +12,7 @@ public class MetaDrawers(
RspMetaDrawer rsp, RspMetaDrawer rsp,
AtchMetaDrawer atch, AtchMetaDrawer atch,
ShpMetaDrawer shp, ShpMetaDrawer shp,
AtrMetaDrawer atr) : Luna.IService AtrMetaDrawer atr)
{ {
public readonly EqdpMetaDrawer Eqdp = eqdp; public readonly EqdpMetaDrawer Eqdp = eqdp;
public readonly EqpMetaDrawer Eqp = eqp; public readonly EqpMetaDrawer Eqp = eqp;

View file

@ -13,7 +13,7 @@ using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta; namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class RspMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) public sealed class RspMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<RspIdentifier, RspEntry>(editor, metaFiles), Luna.IService : MetaDrawer<RspIdentifier, RspEntry>(editor, metaFiles)
{ {
public override ReadOnlySpan<byte> Label public override ReadOnlySpan<byte> Label
=> "Racial Scaling Edits (RSP)###RSP"u8; => "Racial Scaling Edits (RSP)###RSP"u8;

View file

@ -16,7 +16,7 @@ using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta; namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class ShpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles) public sealed class ShpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<ShpIdentifier, ShpEntry>(editor, metaFiles), Luna.IService : MetaDrawer<ShpIdentifier, ShpEntry>(editor, metaFiles)
{ {
public override ReadOnlySpan<byte> Label public override ReadOnlySpan<byte> Label
=> "Shape Keys (SHP)###SHP"u8; => "Shape Keys (SHP)###SHP"u8;

View file

@ -31,7 +31,7 @@ using MdlMaterialEditor = Penumbra.Mods.Editor.MdlMaterialEditor;
namespace Penumbra.UI.AdvancedWindow; namespace Penumbra.UI.AdvancedWindow;
public partial class ModEditWindow : Window, IDisposable, Luna.IUiService public partial class ModEditWindow : IndexedWindow, IDisposable
{ {
private const string WindowBaseLabel = "###SubModEdit"; private const string WindowBaseLabel = "###SubModEdit";
@ -84,7 +84,7 @@ public partial class ModEditWindow : Window, IDisposable, Luna.IUiService
if (mod == Mod) if (mod == Mod)
return; return;
WindowName = $"{mod.Name} (LOADING){WindowBaseLabel}"; WindowName = $"{mod.Name} (LOADING){WindowBaseLabel}{Index}";
AppendTask(() => AppendTask(() =>
{ {
_editor.LoadMod(mod, -1, 0).Wait(); _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; _allowReduplicate = redirections != _editor.Files.Available.Count || _editor.Files.Missing.Count > 0 || unused > 0;
sb.Append(WindowBaseLabel); sb.Append(WindowBaseLabel);
sb.Append(Index);
WindowName = sb.ToString(); WindowName = sb.ToString();
} }
public override void OnClose() public override void OnClose()
{ {
base.OnClose();
_config.Ephemeral.AdvancedEditingOpen = false; _config.Ephemeral.AdvancedEditingOpen = false;
_config.Ephemeral.Save(); _config.Ephemeral.Save();
AppendTask(() => AppendTask(() =>
@ -620,9 +622,9 @@ public partial class ModEditWindow : Window, IDisposable, Luna.IUiService
ActiveCollections activeCollections, ModMergeTab modMergeTab, ActiveCollections activeCollections, ModMergeTab modMergeTab,
CommunicatorService communicator, TextureManager textures, ModelManager models, IDragDropManager dragDropManager, CommunicatorService communicator, TextureManager textures, ModelManager models, IDragDropManager dragDropManager,
ResourceTreeViewerFactory resourceTreeViewerFactory, IFramework framework, ResourceTreeViewerFactory resourceTreeViewerFactory, IFramework framework,
MetaDrawers metaDrawers, MigrationManager migrationManager, MetaDrawers metaDrawers,
MtrlTabFactory mtrlTabFactory, ModSelection selection) MtrlTabFactory mtrlTabFactory, WindowSystem windowSystem, int index)
: base(WindowBaseLabel) : base($"{WindowBaseLabel}{index}", windowSystem, index)
{ {
_itemSwapTab = itemSwapTab; _itemSwapTab = itemSwapTab;
_gameData = gameData; _gameData = gameData;
@ -658,9 +660,6 @@ public partial class ModEditWindow : Window, IDisposable, Luna.IUiService
_resourceTreeFactory = resourceTreeFactory; _resourceTreeFactory = resourceTreeFactory;
_quickImportViewer = resourceTreeViewerFactory.Create(1, OnQuickImportRefresh, DrawQuickImportActions); _quickImportViewer = resourceTreeViewerFactory.Create(1, OnQuickImportRefresh, DrawQuickImportActions);
_communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.ModEditWindow); _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() public void Dispose()
@ -677,10 +676,19 @@ public partial class ModEditWindow : Window, IDisposable, Luna.IUiService
private void OnModPathChange(in ModPathChanged.Arguments arguments) 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; return;
Mod = null; switch (arguments.Type)
ChangeMod(arguments.Mod); {
case ModPathChangeType.Reloaded or ModPathChangeType.Moved:
Mod = null;
ChangeMod(arguments.Mod);
break;
case ModPathChangeType.Deleted:
IsOpen = false;
Dispose();
break;
}
} }
} }

View file

@ -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<ModEditWindow>, 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);
}
}

View file

@ -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);
}
}

View file

@ -0,0 +1,70 @@
using Dalamud.Interface.Windowing;
namespace Penumbra.UI.Classes;
public abstract class WindowFactory<T> where T : IndexedWindow
{
protected WindowSystem? WindowSystem;
private readonly HashSet<T> _openWindows = [];
private readonly HashSet<int> _reusableIndices = [];
private int _nextIndex = 0;
protected IEnumerable<T> 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);
}
}

View file

@ -11,16 +11,14 @@ public class ModPanel : IDisposable, Luna.IUiService
{ {
private readonly MultiModPanel _multiModPanel; private readonly MultiModPanel _multiModPanel;
private readonly ModSelection _selection; private readonly ModSelection _selection;
private readonly ModEditWindow _editWindow;
private readonly ModPanelHeader _header; private readonly ModPanelHeader _header;
private readonly ModPanelTabBar _tabs; private readonly ModPanelTabBar _tabs;
private bool _resetCursor; 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) MultiModPanel multiModPanel, CommunicatorService communicator)
{ {
_selection = selection; _selection = selection;
_editWindow = editWindow;
_tabs = tabs; _tabs = tabs;
_multiModPanel = multiModPanel; _multiModPanel = multiModPanel;
_header = new ModPanelHeader(pi, communicator); _header = new ModPanelHeader(pi, communicator);
@ -64,13 +62,10 @@ public class ModPanel : IDisposable, Luna.IUiService
_resetCursor = true; _resetCursor = true;
if (arguments.NewSelection is null || _selection.Mod is null) if (arguments.NewSelection is null || _selection.Mod is null)
{ {
_editWindow.IsOpen = false; _valid = false;
_valid = false;
} }
else else
{ {
if (_editWindow.IsOpen)
_editWindow.ChangeMod(arguments.NewSelection);
_valid = true; _valid = true;
_mod = arguments.NewSelection; _mod = arguments.NewSelection;
_header.ChangeMod(_mod); _header.ChangeMod(_mod);

View file

@ -30,7 +30,7 @@ public class ModPanelTabBar : IUiService
public readonly ModPanelConflictsTab Conflicts; public readonly ModPanelConflictsTab Conflicts;
public readonly ModPanelChangedItemsTab ChangedItems; public readonly ModPanelChangedItemsTab ChangedItems;
public readonly ModPanelEditTab Edit; public readonly ModPanelEditTab Edit;
private readonly ModEditWindow _modEditWindow; private readonly ModEditWindowFactory _modEditWindowFactory;
private readonly ModManager _modManager; private readonly ModManager _modManager;
private readonly TutorialService _tutorial; private readonly TutorialService _tutorial;
@ -38,19 +38,19 @@ public class ModPanelTabBar : IUiService
private ModPanelTabType _preferredTab = ModPanelTabType.Settings; private ModPanelTabType _preferredTab = ModPanelTabType.Settings;
private Mod? _lastMod; 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, ModPanelConflictsTab conflicts, ModPanelChangedItemsTab changedItems, ModPanelEditTab edit, ModManager modManager,
TutorialService tutorial, ModPanelCollectionsTab collections) TutorialService tutorial, ModPanelCollectionsTab collections)
{ {
_modEditWindow = modEditWindow; _modEditWindowFactory = modEditWindowFactory;
Settings = settings; Settings = settings;
Description = description; Description = description;
Conflicts = conflicts; Conflicts = conflicts;
ChangedItems = changedItems; ChangedItems = changedItems;
Edit = edit; Edit = edit;
_modManager = modManager; _modManager = modManager;
_tutorial = tutorial; _tutorial = tutorial;
Collections = collections; Collections = collections;
Tabs = Tabs =
[ [
@ -115,9 +115,7 @@ public class ModPanelTabBar : IUiService
{ {
if (ImGui.TabItemButton("Advanced Editing", ImGuiTabItemFlags.Trailing | ImGuiTabItemFlags.NoTooltip)) if (ImGui.TabItemButton("Advanced Editing", ImGuiTabItemFlags.Trailing | ImGuiTabItemFlags.NoTooltip))
{ {
_modEditWindow.ChangeMod(mod); _modEditWindowFactory.OpenForMod(mod);
_modEditWindow.ChangeOption(mod.Default);
_modEditWindow.IsOpen = true;
} }
ImGuiUtil.HoverTooltip( ImGuiUtil.HoverTooltip(

View file

@ -10,16 +10,16 @@ namespace Penumbra.UI;
public class PenumbraWindowSystem : IDisposable, Luna.IUiService public class PenumbraWindowSystem : IDisposable, Luna.IUiService
{ {
private readonly IUiBuilder _uiBuilder; private readonly IUiBuilder _uiBuilder;
private readonly WindowSystem _windowSystem; internal readonly WindowSystem _windowSystem;
private readonly FileDialogService _fileDialog; private readonly FileDialogService _fileDialog;
private readonly TextureArraySlicer _textureArraySlicer; private readonly TextureArraySlicer _textureArraySlicer;
public readonly ConfigWindow Window; public readonly ConfigWindow Window;
public readonly PenumbraChangelog Changelog; public readonly PenumbraChangelog Changelog;
public readonly KnowledgeWindow KnowledgeWindow; public readonly KnowledgeWindow KnowledgeWindow;
public PenumbraWindowSystem(IDalamudPluginInterface pi, Configuration config, PenumbraChangelog changelog, ConfigWindow window, 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) KnowledgeWindow knowledgeWindow, TextureArraySlicer textureArraySlicer)
{ {
_uiBuilder = pi.UiBuilder; _uiBuilder = pi.UiBuilder;
@ -31,10 +31,10 @@ public class PenumbraWindowSystem : IDisposable, Luna.IUiService
_windowSystem = new WindowSystem("Penumbra"); _windowSystem = new WindowSystem("Penumbra");
_windowSystem.AddWindow(changelog.Changelog); _windowSystem.AddWindow(changelog.Changelog);
_windowSystem.AddWindow(window); _windowSystem.AddWindow(window);
_windowSystem.AddWindow(editWindow);
_windowSystem.AddWindow(importPopup); _windowSystem.AddWindow(importPopup);
_windowSystem.AddWindow(debugTab); _windowSystem.AddWindow(debugTab);
_windowSystem.AddWindow(KnowledgeWindow); _windowSystem.AddWindow(KnowledgeWindow);
editWindowFactory.SetWindowSystem(_windowSystem);
_uiBuilder.OpenMainUi += Window.Toggle; _uiBuilder.OpenMainUi += Window.Toggle;
_uiBuilder.OpenConfigUi += Window.OpenSettings; _uiBuilder.OpenConfigUi += Window.OpenSettings;
_uiBuilder.Draw += _windowSystem.Draw; _uiBuilder.Draw += _windowSystem.Draw;