From d6c03624045337128ba3a9e903a52fb2fe496c56 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Wed, 27 Jul 2022 10:37:45 +0200 Subject: [PATCH] Let SubMods know their location in a mod. --- Penumbra/Mods/Editor/Mod.Editor.Duplicates.cs | 2 +- Penumbra/Mods/Editor/Mod.Editor.Edit.cs | 33 +++----- Penumbra/Mods/Editor/Mod.Editor.Meta.cs | 2 +- Penumbra/Mods/Editor/Mod.Editor.cs | 9 +-- Penumbra/Mods/Manager/Mod.Manager.Options.cs | 36 ++++++--- Penumbra/Mods/Mod.BasePath.cs | 5 +- Penumbra/Mods/Mod.Creation.cs | 4 +- Penumbra/Mods/Mod.Files.cs | 10 +-- Penumbra/Mods/Mod.Meta.Migration.cs | 12 +-- Penumbra/Mods/Mod.TemporaryMod.cs | 5 +- Penumbra/Mods/Subclasses/IModGroup.cs | 1 + Penumbra/Mods/Subclasses/ISubMod.cs | 3 + .../Subclasses/Mod.Files.MultiModGroup.cs | 30 +++++-- .../Subclasses/Mod.Files.SingleModGroup.cs | 26 +++++- Penumbra/Mods/Subclasses/Mod.Files.SubMod.cs | 35 ++++---- Penumbra/UI/Classes/ModEditWindow.Files.cs | 79 ++++++++++++++++++- Penumbra/UI/Classes/ModEditWindow.cs | 48 +++-------- Penumbra/UI/ConfigWindow.ModPanel.Tabs.cs | 4 +- 18 files changed, 223 insertions(+), 121 deletions(-) diff --git a/Penumbra/Mods/Editor/Mod.Editor.Duplicates.cs b/Penumbra/Mods/Editor/Mod.Editor.Duplicates.cs index 4dbb9bff..b5bd538c 100644 --- a/Penumbra/Mods/Editor/Mod.Editor.Duplicates.cs +++ b/Penumbra/Mods/Editor/Mod.Editor.Duplicates.cs @@ -270,7 +270,7 @@ public partial class Mod { var mod = new Mod( modDirectory ); mod.Reload( out _ ); - var editor = new Editor( mod, 0, 0 ); + var editor = new Editor( mod, mod.Default ); editor.DuplicatesFinished = false; editor.CheckDuplicates( editor.AvailableFiles.OrderByDescending( f => f.FileSize ).ToArray() ); editor.DeleteDuplicates( false ); diff --git a/Penumbra/Mods/Editor/Mod.Editor.Edit.cs b/Penumbra/Mods/Editor/Mod.Editor.Edit.cs index 1e702b67..3b27f573 100644 --- a/Penumbra/Mods/Editor/Mod.Editor.Edit.cs +++ b/Penumbra/Mods/Editor/Mod.Editor.Edit.cs @@ -9,34 +9,16 @@ public partial class Mod { public partial class Editor { - public int GroupIdx { get; private set; } = -1; - public int OptionIdx { get; private set; } - - private IModGroup? _modGroup; - private SubMod _subMod; + private SubMod _subMod; public ISubMod CurrentOption => _subMod; public readonly Dictionary< Utf8GamePath, FullPath > CurrentSwaps = new(); - public void SetSubMod( int groupIdx, int optionIdx ) + public void SetSubMod( ISubMod? subMod ) { - GroupIdx = groupIdx; - OptionIdx = optionIdx; - if( groupIdx >= 0 && groupIdx < _mod.Groups.Count && optionIdx >= 0 && optionIdx < _mod.Groups[ groupIdx ].Count ) - { - _modGroup = _mod.Groups[ groupIdx ]; - _subMod = ( SubMod )_modGroup![ optionIdx ]; - } - else - { - GroupIdx = -1; - OptionIdx = 0; - _modGroup = null; - _subMod = _mod._default; - } - + _subMod = subMod as SubMod ?? _mod._default; UpdateFiles(); RevertSwaps(); RevertManipulations(); @@ -54,11 +36,16 @@ public partial class Mod } } - Penumbra.ModManager.OptionSetFiles( _mod, GroupIdx, OptionIdx, dict ); + Penumbra.ModManager.OptionSetFiles( _mod, _subMod.GroupIdx, _subMod.OptionIdx, dict ); if( num > 0 ) + { RevertFiles(); + } else + { FileChanges = false; + } + return num; } @@ -67,7 +54,7 @@ public partial class Mod public void ApplySwaps() { - Penumbra.ModManager.OptionSetFileSwaps( _mod, GroupIdx, OptionIdx, CurrentSwaps.ToDictionary( kvp => kvp.Key, kvp => kvp.Value ) ); + Penumbra.ModManager.OptionSetFileSwaps( _mod, _subMod.GroupIdx, _subMod.OptionIdx, CurrentSwaps.ToDictionary( kvp => kvp.Key, kvp => kvp.Value ) ); } public void RevertSwaps() diff --git a/Penumbra/Mods/Editor/Mod.Editor.Meta.cs b/Penumbra/Mods/Editor/Mod.Editor.Meta.cs index c0908a9c..0eec38ea 100644 --- a/Penumbra/Mods/Editor/Mod.Editor.Meta.cs +++ b/Penumbra/Mods/Editor/Mod.Editor.Meta.cs @@ -159,7 +159,7 @@ public partial class Mod public void ApplyManipulations() { - Meta.Apply( _mod, GroupIdx, OptionIdx ); + Meta.Apply( _mod, _subMod.GroupIdx, _subMod.OptionIdx ); } } } \ No newline at end of file diff --git a/Penumbra/Mods/Editor/Mod.Editor.cs b/Penumbra/Mods/Editor/Mod.Editor.cs index a1394401..3120bcf3 100644 --- a/Penumbra/Mods/Editor/Mod.Editor.cs +++ b/Penumbra/Mods/Editor/Mod.Editor.cs @@ -10,12 +10,11 @@ public partial class Mod : IMod { private readonly Mod _mod; - public Editor( Mod mod, int groupIdx, int optionIdx ) + public Editor( Mod mod, ISubMod? option ) { - _mod = mod; - SetSubMod( groupIdx, optionIdx ); - GroupIdx = groupIdx; - _subMod = _mod._default; + _mod = mod; + _subMod = null!; + SetSubMod( option ); UpdateFiles(); ScanModels(); } diff --git a/Penumbra/Mods/Manager/Mod.Manager.Options.cs b/Penumbra/Mods/Manager/Mod.Manager.Options.cs index 86d97fd0..6d35b5d1 100644 --- a/Penumbra/Mods/Manager/Mod.Manager.Options.cs +++ b/Penumbra/Mods/Manager/Mod.Manager.Options.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using Dalamud.Logging; +using OtterGui; using OtterGui.Filesystem; using Penumbra.GameData.ByteString; using Penumbra.Meta.Manipulations; @@ -77,6 +78,14 @@ public sealed partial class Mod { if( mod._groups.Move( groupIdxFrom, groupIdxTo ) ) { + foreach( var (group, groupIdx) in mod._groups.WithIndex().Skip( Math.Min( groupIdxFrom, groupIdxTo ) ) ) + { + foreach( var (o, optionIdx) in group.OfType().WithIndex() ) + { + o.SetPosition( groupIdx, optionIdx ); + } + } + ModOptionChanged.Invoke( ModOptionChangeType.GroupMoved, mod, groupIdxFrom, -1, groupIdxTo ); } } @@ -162,17 +171,19 @@ public sealed partial class Mod public void AddOption( Mod mod, int groupIdx, string newName ) { - switch( mod._groups[ groupIdx ] ) + var group = mod._groups[groupIdx]; + switch( group ) { case SingleModGroup s: - s.OptionData.Add( new SubMod { Name = newName } ); + s.OptionData.Add( new SubMod(mod) { Name = newName } ); break; case MultiModGroup m: - m.PrioritizedOptions.Add( ( new SubMod { Name = newName }, 0 ) ); + m.PrioritizedOptions.Add( ( new SubMod(mod) { Name = newName }, 0 ) ); break; } - ModOptionChanged.Invoke( ModOptionChangeType.OptionAdded, mod, groupIdx, mod._groups[ groupIdx ].Count - 1, -1 ); + group.UpdatePositions( group.Count - 1 ); + ModOptionChanged.Invoke( ModOptionChangeType.OptionAdded, mod, groupIdx, group.Count - 1, -1 ); } public void AddOption( Mod mod, int groupIdx, ISubMod option, int priority = 0 ) @@ -182,15 +193,16 @@ public sealed partial class Mod return; } - if( mod._groups[ groupIdx ].Count > 63 ) + var group = mod._groups[ groupIdx ]; + if( group.Count > 63 ) { PluginLog.Error( - $"Could not add option {option.Name} to {mod._groups[ groupIdx ].Name} for mod {mod.Name}, " + $"Could not add option {option.Name} to {group.Name} for mod {mod.Name}, " + "since only up to 64 options are supported in one group." ); return; } - switch( mod._groups[ groupIdx ] ) + switch( group ) { case SingleModGroup s: s.OptionData.Add( o ); @@ -199,23 +211,25 @@ public sealed partial class Mod m.PrioritizedOptions.Add( ( o, priority ) ); break; } - - ModOptionChanged.Invoke( ModOptionChangeType.OptionAdded, mod, groupIdx, mod._groups[ groupIdx ].Count - 1, -1 ); + group.UpdatePositions( group.Count - 1 ); + ModOptionChanged.Invoke( ModOptionChangeType.OptionAdded, mod, groupIdx, group.Count - 1, -1 ); } public void DeleteOption( Mod mod, int groupIdx, int optionIdx ) { + var group = mod._groups[groupIdx]; ModOptionChanged.Invoke( ModOptionChangeType.PrepareChange, mod, groupIdx, optionIdx, -1 ); - switch( mod._groups[ groupIdx ] ) + switch( group ) { case SingleModGroup s: s.OptionData.RemoveAt( optionIdx ); + break; case MultiModGroup m: m.PrioritizedOptions.RemoveAt( optionIdx ); break; } - + group.UpdatePositions( optionIdx ); ModOptionChanged.Invoke( ModOptionChangeType.OptionDeleted, mod, groupIdx, optionIdx, -1 ); } diff --git a/Penumbra/Mods/Mod.BasePath.cs b/Penumbra/Mods/Mod.BasePath.cs index 0add9356..2e01396f 100644 --- a/Penumbra/Mods/Mod.BasePath.cs +++ b/Penumbra/Mods/Mod.BasePath.cs @@ -22,7 +22,10 @@ public partial class Mod => 0; private Mod( DirectoryInfo modPath ) - => ModPath = modPath; + { + ModPath = modPath; + _default = new SubMod( this ); + } private static Mod? LoadMod( DirectoryInfo modPath ) { diff --git a/Penumbra/Mods/Mod.Creation.cs b/Penumbra/Mods/Mod.Creation.cs index b42a3b7d..abe11f25 100644 --- a/Penumbra/Mods/Mod.Creation.cs +++ b/Penumbra/Mods/Mod.Creation.cs @@ -97,7 +97,7 @@ public partial class Mod .Select( f => ( Utf8GamePath.FromFile( f, optionFolder, out var gamePath, true ), gamePath, new FullPath( f ) ) ) .Where( t => t.Item1 ); - var mod = new SubMod + var mod = new SubMod(null!) // Mod is irrelevant here, only used for saving. { Name = option.Name, }; @@ -112,7 +112,7 @@ public partial class Mod // Create an empty sub mod for single groups with None options. internal static ISubMod CreateEmptySubMod( string name ) - => new SubMod() + => new SubMod(null! ) // Mod is irrelevant here, only used for saving. { Name = name, }; diff --git a/Penumbra/Mods/Mod.Files.cs b/Penumbra/Mods/Mod.Files.cs index e22f887f..87c69a82 100644 --- a/Penumbra/Mods/Mod.Files.cs +++ b/Penumbra/Mods/Mod.Files.cs @@ -18,7 +18,7 @@ public partial class Mod public IReadOnlyList< IModGroup > Groups => _groups; - private readonly SubMod _default = new(); + private readonly SubMod _default; private readonly List< IModGroup > _groups = new(); public int TotalFileCount { get; private set; } @@ -70,7 +70,7 @@ public partial class Mod .ToList(); } - private static IModGroup? LoadModGroup( FileInfo file, DirectoryInfo basePath ) + private static IModGroup? LoadModGroup( Mod mod, FileInfo file, int groupIdx ) { if( !File.Exists( file.FullName ) ) { @@ -82,8 +82,8 @@ public partial class Mod var json = JObject.Parse( File.ReadAllText( file.FullName ) ); switch( json[ nameof( Type ) ]?.ToObject< SelectType >() ?? SelectType.Single ) { - case SelectType.Multi: return MultiModGroup.Load( json, basePath ); - case SelectType.Single: return SingleModGroup.Load( json, basePath ); + case SelectType.Multi: return MultiModGroup.Load( mod, json, groupIdx ); + case SelectType.Single: return SingleModGroup.Load( mod, json, groupIdx ); } } catch( Exception e ) @@ -100,7 +100,7 @@ public partial class Mod var changes = false; foreach( var file in GroupFiles ) { - var group = LoadModGroup( file, ModPath ); + var group = LoadModGroup( this, file, _groups.Count ); if( group != null && _groups.All( g => g.Name != group.Name ) ) { changes = changes || group.FileName( ModPath, _groups.Count ) != file.FullName; diff --git a/Penumbra/Mods/Mod.Meta.Migration.cs b/Penumbra/Mods/Mod.Meta.Migration.cs index 3f360ada..1587864f 100644 --- a/Penumbra/Mods/Mod.Meta.Migration.cs +++ b/Penumbra/Mods/Mod.Meta.Migration.cs @@ -141,7 +141,7 @@ public sealed partial class Mod mod._groups.Add( newMultiGroup ); foreach( var option in group.Options ) { - newMultiGroup.PrioritizedOptions.Add( ( SubModFromOption( mod.ModPath, option, seenMetaFiles ), optionPriority++ ) ); + newMultiGroup.PrioritizedOptions.Add( ( SubModFromOption( mod, option, seenMetaFiles ), optionPriority++ ) ); } break; @@ -161,7 +161,7 @@ public sealed partial class Mod mod._groups.Add( newSingleGroup ); foreach( var option in group.Options ) { - newSingleGroup.OptionData.Add( SubModFromOption( mod.ModPath, option, seenMetaFiles ) ); + newSingleGroup.OptionData.Add( SubModFromOption( mod, option, seenMetaFiles ) ); } break; @@ -185,11 +185,11 @@ public sealed partial class Mod } } - private static SubMod SubModFromOption( DirectoryInfo basePath, OptionV0 option, HashSet< FullPath > seenMetaFiles ) + private static SubMod SubModFromOption( Mod mod, OptionV0 option, HashSet< FullPath > seenMetaFiles ) { - var subMod = new SubMod { Name = option.OptionName }; - AddFilesToSubMod( subMod, basePath, option, seenMetaFiles ); - subMod.IncorporateMetaChanges( basePath, false ); + var subMod = new SubMod(mod) { Name = option.OptionName }; + AddFilesToSubMod( subMod, mod.ModPath, option, seenMetaFiles ); + subMod.IncorporateMetaChanges( mod.ModPath, false ); return subMod; } diff --git a/Penumbra/Mods/Mod.TemporaryMod.cs b/Penumbra/Mods/Mod.TemporaryMod.cs index 35a4795a..10d90979 100644 --- a/Penumbra/Mods/Mod.TemporaryMod.cs +++ b/Penumbra/Mods/Mod.TemporaryMod.cs @@ -26,7 +26,10 @@ public sealed partial class Mod public IEnumerable< ISubMod > AllSubMods => new[] { Default }; - private readonly SubMod _default = new(); + private readonly SubMod _default; + + public TemporaryMod() + => _default = new SubMod( this ); public void SetFile( Utf8GamePath gamePath, FullPath fullPath ) => _default.FileData[ gamePath ] = fullPath; diff --git a/Penumbra/Mods/Subclasses/IModGroup.cs b/Penumbra/Mods/Subclasses/IModGroup.cs index 8482af4a..190ca399 100644 --- a/Penumbra/Mods/Subclasses/IModGroup.cs +++ b/Penumbra/Mods/Subclasses/IModGroup.cs @@ -97,4 +97,5 @@ public interface IModGroup : IEnumerable< ISubMod > public IModGroup Convert( SelectType type ); public bool MoveOption( int optionIdxFrom, int optionIdxTo ); + public void UpdatePositions(int from = 0); } \ No newline at end of file diff --git a/Penumbra/Mods/Subclasses/ISubMod.cs b/Penumbra/Mods/Subclasses/ISubMod.cs index f6781566..01bfefc6 100644 --- a/Penumbra/Mods/Subclasses/ISubMod.cs +++ b/Penumbra/Mods/Subclasses/ISubMod.cs @@ -9,11 +9,14 @@ namespace Penumbra.Mods; public interface ISubMod { public string Name { get; } + public string FullName { get; } public IReadOnlyDictionary< Utf8GamePath, FullPath > Files { get; } public IReadOnlyDictionary< Utf8GamePath, FullPath > FileSwaps { get; } public IReadOnlySet< MetaManipulation > Manipulations { get; } + public bool IsDefault { get; } + public static void WriteSubMod( JsonWriter j, JsonSerializer serializer, ISubMod mod, DirectoryInfo basePath, int? priority ) { j.WriteStartObject(); diff --git a/Penumbra/Mods/Subclasses/Mod.Files.MultiModGroup.cs b/Penumbra/Mods/Subclasses/Mod.Files.MultiModGroup.cs index d84ae760..fc735981 100644 --- a/Penumbra/Mods/Subclasses/Mod.Files.MultiModGroup.cs +++ b/Penumbra/Mods/Subclasses/Mod.Files.MultiModGroup.cs @@ -6,6 +6,7 @@ using System.Linq; using Dalamud.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using OtterGui; using OtterGui.Filesystem; namespace Penumbra.Mods; @@ -40,7 +41,7 @@ public partial class Mod IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public static MultiModGroup? Load( JObject json, DirectoryInfo basePath ) + public static MultiModGroup? Load( Mod mod, JObject json, int groupIdx ) { var options = json[ "Options" ]; var ret = new MultiModGroup() @@ -60,11 +61,14 @@ public partial class Mod { if( ret.PrioritizedOptions.Count == IModGroup.MaxMultiOptions ) { - PluginLog.Warning($"Multi Group {ret.Name} has more than {IModGroup.MaxMultiOptions} options, ignoring excessive options." ); + PluginLog.Warning( + $"Multi Group {ret.Name} has more than {IModGroup.MaxMultiOptions} options, ignoring excessive options." ); break; } - var subMod = new SubMod(); - subMod.Load( basePath, child, out var priority ); + + var subMod = new SubMod( mod ); + subMod.SetPosition( groupIdx, ret.PrioritizedOptions.Count ); + subMod.Load( mod.ModPath, child, out var priority ); ret.PrioritizedOptions.Add( ( subMod, priority ) ); } } @@ -91,6 +95,22 @@ public partial class Mod } public bool MoveOption( int optionIdxFrom, int optionIdxTo ) - => PrioritizedOptions.Move( optionIdxFrom, optionIdxTo ); + { + if( !PrioritizedOptions.Move( optionIdxFrom, optionIdxTo ) ) + { + return false; + } + + UpdatePositions( Math.Min( optionIdxFrom, optionIdxTo ) ); + return true; + } + + public void UpdatePositions( int from = 0 ) + { + foreach( var ((o, _), i) in PrioritizedOptions.WithIndex().Skip( from ) ) + { + o.SetPosition( o.GroupIdx, i ); + } + } } } \ No newline at end of file diff --git a/Penumbra/Mods/Subclasses/Mod.Files.SingleModGroup.cs b/Penumbra/Mods/Subclasses/Mod.Files.SingleModGroup.cs index 352bb503..8cfb775c 100644 --- a/Penumbra/Mods/Subclasses/Mod.Files.SingleModGroup.cs +++ b/Penumbra/Mods/Subclasses/Mod.Files.SingleModGroup.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using OtterGui; using OtterGui.Filesystem; namespace Penumbra.Mods; @@ -39,7 +40,7 @@ public partial class Mod IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public static SingleModGroup? Load( JObject json, DirectoryInfo basePath ) + public static SingleModGroup? Load( Mod mod, JObject json, int groupIdx ) { var options = json[ "Options" ]; var ret = new SingleModGroup @@ -57,8 +58,9 @@ public partial class Mod { foreach( var child in options.Children() ) { - var subMod = new SubMod(); - subMod.Load( basePath, child, out _ ); + var subMod = new SubMod( mod ); + subMod.SetPosition( groupIdx, ret.OptionData.Count ); + subMod.Load( mod.ModPath, child, out _ ); ret.OptionData.Add( subMod ); } } @@ -85,6 +87,22 @@ public partial class Mod } public bool MoveOption( int optionIdxFrom, int optionIdxTo ) - => OptionData.Move( optionIdxFrom, optionIdxTo ); + { + if( !OptionData.Move( optionIdxFrom, optionIdxTo ) ) + { + return false; + } + + UpdatePositions( Math.Min( optionIdxFrom, optionIdxTo ) ); + return true; + } + + public void UpdatePositions( int from = 0 ) + { + foreach( var (o, i) in OptionData.WithIndex().Skip( from ) ) + { + o.SetPosition( o.GroupIdx, i ); + } + } } } \ No newline at end of file diff --git a/Penumbra/Mods/Subclasses/Mod.Files.SubMod.cs b/Penumbra/Mods/Subclasses/Mod.Files.SubMod.cs index a1500504..9a13e642 100644 --- a/Penumbra/Mods/Subclasses/Mod.Files.SubMod.cs +++ b/Penumbra/Mods/Subclasses/Mod.Files.SubMod.cs @@ -42,6 +42,7 @@ public partial class Mod private void LoadDefaultOption() { var defaultFile = DefaultFile; + _default.SetPosition( -1, 0 ); try { if( !File.Exists( defaultFile ) ) @@ -72,10 +73,23 @@ public partial class Mod { public string Name { get; set; } = "Default"; + public string FullName + => GroupIdx < 0 ? "Default Option" : $"{ParentMod.Groups[ GroupIdx ].Name}: {Name}"; + + internal IMod ParentMod { get; private init; } + internal int GroupIdx { get; private set; } + internal int OptionIdx { get; private set; } + + public bool IsDefault + => GroupIdx < 0; + public Dictionary< Utf8GamePath, FullPath > FileData = new(); public Dictionary< Utf8GamePath, FullPath > FileSwapData = new(); public HashSet< MetaManipulation > ManipulationData = new(); + public SubMod( IMod parentMod ) + => ParentMod = parentMod; + public IReadOnlyDictionary< Utf8GamePath, FullPath > Files => FileData; @@ -85,25 +99,10 @@ public partial class Mod public IReadOnlySet< MetaManipulation > Manipulations => ManipulationData; - // Insert all changes from the other submod. - // Overwrites already existing changes in this mod. - public void MergeIn( ISubMod other ) + public void SetPosition( int groupIdx, int optionIdx ) { - foreach( var (key, value) in other.Files ) - { - FileData[ key ] = value; - } - - foreach( var (key, value) in other.FileSwaps ) - { - FileSwapData[ key ] = value; - } - - foreach( var manip in other.Manipulations ) - { - ManipulationData.Remove( manip ); - ManipulationData.Add( manip ); - } + GroupIdx = groupIdx; + OptionIdx = optionIdx; } public void Load( DirectoryInfo basePath, JToken json, out int priority ) diff --git a/Penumbra/UI/Classes/ModEditWindow.Files.cs b/Penumbra/UI/Classes/ModEditWindow.Files.cs index 2756489c..400b528b 100644 --- a/Penumbra/UI/Classes/ModEditWindow.Files.cs +++ b/Penumbra/UI/Classes/ModEditWindow.Files.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Numerics; using Dalamud.Interface; @@ -23,6 +24,7 @@ public partial class ModEditWindow private int _fileIdx = -1; private int _pathIdx = -1; private int _folderSkip = 0; + private bool _overviewMode = false; private bool CheckFilter( Mod.Editor.FileRegistry registry ) => _fileFilter.IsEmpty || registry.File.FullName.Contains( _fileFilter.Lower, StringComparison.OrdinalIgnoreCase ); @@ -47,7 +49,73 @@ public partial class ModEditWindow return; } + if( _overviewMode ) + DrawFilesOverviewMode(); + else + DrawFilesNormalMode(); + + } + + private void DrawFilesOverviewMode() + { + using var list = ImRaii.Table( "##table", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit ); + + if( !list ) + { + return; + } + + var idx = 0; + void Draw( Mod.Editor.FileRegistry registry ) + { + if( registry.SubModUsage.Count == 0 ) + { + using var id = ImRaii.PushId( idx++ ); + ImGui.TableNextColumn(); + ImGui.TableSetBgColor( ImGuiTableBgTarget.CellBg, 0x40000080 ); + ImGui.Selectable( registry.RelPath.ToString() ); + ImGui.TableNextColumn(); + ImGui.TableSetBgColor( ImGuiTableBgTarget.CellBg, 0x40000080 ); + ImGui.TextUnformatted( "Unused" ); + ImGui.TableSetBgColor( ImGuiTableBgTarget.CellBg, 0x40000080 ); + ImGui.TableNextColumn(); + } + else + { + foreach( var (mod, path) in registry.SubModUsage ) + { + using var id = ImRaii.PushId( idx++ ); + var color = mod == _editor.CurrentOption && _mod!.HasOptions; + ImGui.TableNextColumn(); + if( color ) + ImGui.TableSetBgColor( ImGuiTableBgTarget.CellBg, 0x40008000 ); + ImGui.Selectable( registry.RelPath.ToString() ); + ImGui.TableNextColumn(); + if( color ) + ImGui.TableSetBgColor( ImGuiTableBgTarget.CellBg, 0x40008000 ); + ImGui.Selectable( path.ToString() ); + ImGui.TableNextColumn(); + if( color ) + ImGui.TableSetBgColor( ImGuiTableBgTarget.CellBg, 0x40008000 ); + ImGui.TextUnformatted( mod.Name ); + } + } + } + + bool Filter( Mod.Editor.FileRegistry registry ) + { + return true; + } + + var skips = ImGuiClip.GetNecessarySkips( ImGui.GetTextLineHeight() ); + var end = ImGuiClip.FilteredClippedDraw( _editor!.AvailableFiles, skips, Filter, Draw, 0 ); + ImGuiClip.DrawEndDummy( end, ImGui.GetTextLineHeight() ); + } + + private void DrawFilesNormalMode() + { using var list = ImRaii.Table( "##table", 1 ); + if( !list ) { return; @@ -68,7 +136,7 @@ public partial class ModEditWindow using var indent = ImRaii.PushIndent( 50f ); for( var j = 0; j < registry.SubModUsage.Count; ++j ) { - var (subMod, gamePath) = registry.SubModUsage[ j ]; + var (subMod, gamePath) = registry.SubModUsage[j]; if( subMod != _editor.CurrentOption ) { continue; @@ -158,6 +226,7 @@ public partial class ModEditWindow { _editor!.SetGamePath( _fileIdx, _pathIdx, path ); } + _fileIdx = -1; _pathIdx = -1; } @@ -180,6 +249,7 @@ public partial class ModEditWindow { _editor!.SetGamePath( _fileIdx, _pathIdx, path ); } + _fileIdx = -1; _pathIdx = -1; } @@ -241,6 +311,13 @@ public partial class ModEditWindow ImGuiUtil.HoverTooltip( "Revert all revertible changes since the last file or option reload or data refresh." ); + ImGui.SameLine(); + ImGui.Checkbox( "Overview Mode", ref _overviewMode ); + if( _overviewMode ) + { + return; + } + ImGui.SetNextItemWidth( 250 * ImGuiHelpers.GlobalScale ); LowerString.InputWithHint( "##filter", "Filter paths...", ref _fileFilter, Utf8GamePath.MaxGamePathLength ); ImGui.SameLine(); diff --git a/Penumbra/UI/Classes/ModEditWindow.cs b/Penumbra/UI/Classes/ModEditWindow.cs index 08709fd9..898a98f2 100644 --- a/Penumbra/UI/Classes/ModEditWindow.cs +++ b/Penumbra/UI/Classes/ModEditWindow.cs @@ -31,7 +31,7 @@ public partial class ModEditWindow : Window, IDisposable } _editor?.Dispose(); - _editor = new Editor( mod, -1, 0 ); + _editor = new Editor( mod, mod.Default ); _mod = mod; SizeConstraints = new WindowSizeConstraints @@ -42,8 +42,8 @@ public partial class ModEditWindow : Window, IDisposable _selectedFiles.Clear(); } - public void ChangeOption( int groupIdx, int optionIdx ) - => _editor?.SetSubMod( groupIdx, optionIdx ); + public void ChangeOption( ISubMod? subMod ) + => _editor?.SetSubMod( subMod ); public override bool DrawConditions() => _editor != null; @@ -437,56 +437,34 @@ public partial class ModEditWindow : Window, IDisposable private void DrawOptionSelectHeader() { - const string defaultOption = "Default Option"; - using var style = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing, Vector2.Zero ).Push( ImGuiStyleVar.FrameRounding, 0 ); - var width = new Vector2( ImGui.GetWindowWidth() / 3, 0 ); - var isDefaultOption = _editor!.GroupIdx == -1 && _editor!.OptionIdx == 0; + const string defaultOption = "Default Option"; + using var style = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing, Vector2.Zero ).Push( ImGuiStyleVar.FrameRounding, 0 ); + var width = new Vector2( ImGui.GetWindowWidth() / 3, 0 ); if( ImGuiUtil.DrawDisabledButton( defaultOption, width, "Switch to the default option for the mod.\nThis resets unsaved changes.", - isDefaultOption ) ) + _editor!.CurrentOption.IsDefault ) ) { - _editor.SetSubMod( -1, 0 ); - isDefaultOption = true; + _editor.SetSubMod( _mod!.Default ); } ImGui.SameLine(); if( ImGuiUtil.DrawDisabledButton( "Refresh Data", width, "Refresh data for the current option.\nThis resets unsaved changes.", false ) ) { - _editor.SetSubMod( _editor.GroupIdx, _editor.OptionIdx ); + _editor.SetSubMod( _editor.CurrentOption ); } ImGui.SameLine(); - string GetLabel() - { - if( isDefaultOption ) - { - return defaultOption; - } - - var group = _mod!.Groups[ _editor!.GroupIdx ]; - return $"{group.Name}: {group[ _editor.OptionIdx ].Name}"; - } - - using var combo = ImRaii.Combo( "##optionSelector", GetLabel(), ImGuiComboFlags.NoArrowButton ); + using var combo = ImRaii.Combo( "##optionSelector", _editor.CurrentOption.FullName, ImGuiComboFlags.NoArrowButton ); if( !combo ) { return; } - if( ImGui.Selectable( $"{defaultOption}###-1_0", isDefaultOption ) ) + foreach( var option in _mod!.AllSubMods ) { - _editor.SetSubMod( -1, 0 ); - } - - foreach( var (group, groupIdx) in _mod!.Groups.WithIndex() ) - { - foreach( var (option, optionIdx) in group.WithIndex() ) + if( ImGui.Selectable( option.FullName, option == _editor.CurrentOption ) ) { - var name = $"{group.Name}: {option.Name}###{groupIdx}_{optionIdx}"; - if( ImGui.Selectable( name, groupIdx == _editor.GroupIdx && optionIdx == _editor.OptionIdx ) ) - { - _editor.SetSubMod( groupIdx, optionIdx ); - } + _editor.SetSubMod( option ); } } } diff --git a/Penumbra/UI/ConfigWindow.ModPanel.Tabs.cs b/Penumbra/UI/ConfigWindow.ModPanel.Tabs.cs index 0d90693a..5d9cc3f4 100644 --- a/Penumbra/UI/ConfigWindow.ModPanel.Tabs.cs +++ b/Penumbra/UI/ConfigWindow.ModPanel.Tabs.cs @@ -61,7 +61,7 @@ public partial class ConfigWindow if( ImGui.TabItemButton( "Advanced Editing", ImGuiTabItemFlags.Trailing | ImGuiTabItemFlags.NoTooltip ) ) { _window.ModEditPopup.ChangeMod( _mod ); - _window.ModEditPopup.ChangeOption( -1, 0 ); + _window.ModEditPopup.ChangeOption( _mod.Default ); _window.ModEditPopup.IsOpen = true; } @@ -142,7 +142,7 @@ public partial class ConfigWindow { var priority = conflict.Mod2.Index < 0 ? conflict.Mod2.Priority - : Penumbra.CollectionManager.Current[conflict.Mod2.Index].Settings!.Priority; + : Penumbra.CollectionManager.Current[ conflict.Mod2.Index ].Settings!.Priority; ImGui.TextUnformatted( $"(Priority {priority})" ); }