diff --git a/Penumbra/Api/ModsController.cs b/Penumbra/Api/ModsController.cs index bde6d2db..77f1b496 100644 --- a/Penumbra/Api/ModsController.cs +++ b/Penumbra/Api/ModsController.cs @@ -16,17 +16,15 @@ public class ModsController : WebApiController [Route( HttpVerbs.Get, "/mods" )] public object? GetMods() { - // TODO - return null; - //return Penumbra.ModManager.Mods.Zip( Penumbra.CollectionManager.Current.ActualSettings ).Select( x => new - //{ - // x.Second?.Enabled, - // x.Second?.Priority, - // x.First.BasePath.Name, - // x.First.Name, - // BasePath = x.First.BasePath.FullName, - // Files = x.First.Resources.ModFiles.Select( fi => fi.FullName ), - //} ); + return Penumbra.ModManager.Zip( Penumbra.CollectionManager.Current.ActualSettings ).Select( x => new + { + x.Second?.Enabled, + x.Second?.Priority, + FolderName = x.First.BasePath.Name, + x.First.Name, + BasePath = x.First.BasePath.FullName, + Files = x.First.AllFiles, + } ); } [Route( HttpVerbs.Post, "/mods" )] diff --git a/Penumbra/Collections/ModCollection.Cache.cs b/Penumbra/Collections/ModCollection.Cache.cs index 5283ec4f..fe008820 100644 --- a/Penumbra/Collections/ModCollection.Cache.cs +++ b/Penumbra/Collections/ModCollection.Cache.cs @@ -251,7 +251,7 @@ public partial class ModCollection return; } - var mod = Penumbra.ModManager.Mods[ modIdx ]; + var mod = Penumbra.ModManager[ modIdx ]; AddSubMod( mod.Default, new FileRegister( modIdx, settings.Priority, 0, 0 ), withManipulations ); for( var idx = 0; idx < mod.Groups.Count; ++idx ) { diff --git a/Penumbra/Collections/ModCollection.Changes.cs b/Penumbra/Collections/ModCollection.Changes.cs index cbf0b09d..9ff07910 100644 --- a/Penumbra/Collections/ModCollection.Changes.cs +++ b/Penumbra/Collections/ModCollection.Changes.cs @@ -98,7 +98,7 @@ public partial class ModCollection if( oldValue != newValue ) { var inheritance = FixInheritance( idx, false ); - _settings[ idx ]!.SetValue( Penumbra.ModManager.Mods[ idx ], groupIdx, newValue ); + _settings[ idx ]!.SetValue( Penumbra.ModManager[ idx ], groupIdx, newValue ); ModSettingChanged.Invoke( ModSettingChange.Setting, idx, inheritance ? -1 : ( int )oldValue, groupIdx, false ); } } @@ -137,7 +137,7 @@ public partial class ModCollection return false; } - _settings[ idx ] = inherit ? null : this[ idx ].Settings?.DeepCopy() ?? ModSettings.DefaultSettings( Penumbra.ModManager.Mods[ idx ] ); + _settings[ idx ] = inherit ? null : this[ idx ].Settings?.DeepCopy() ?? ModSettings.DefaultSettings( Penumbra.ModManager[ idx ] ); return true; } diff --git a/Penumbra/Collections/ModCollection.cs b/Penumbra/Collections/ModCollection.cs index a540272f..54561930 100644 --- a/Penumbra/Collections/ModCollection.cs +++ b/Penumbra/Collections/ModCollection.cs @@ -32,6 +32,10 @@ public partial class ModCollection public IReadOnlyList< ModSettings? > Settings => _settings; + // Returns whether there are settings not in use by any current mod. + public bool HasUnusedSettings + => _unusedSettings.Count > 0; + // Evaluates the settings along the whole inheritance tree. public IEnumerable< ModSettings? > ActualSettings => Enumerable.Range( 0, _settings.Count ).Select( i => this[ i ].Settings ); diff --git a/Penumbra/Configuration.Constants.cs b/Penumbra/Configuration.Constants.cs deleted file mode 100644 index 0cdcd9ec..00000000 --- a/Penumbra/Configuration.Constants.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Penumbra; - -public partial class Configuration -{ - // Contains some default values or boundaries for config values. - public static class Constants - { - public const int CurrentVersion = 3; - public const float MaxAbsoluteSize = 600; - public const int DefaultAbsoluteSize = 250; - public const float MinAbsoluteSize = 50; - public const int MaxScaledSize = 80; - public const int DefaultScaledSize = 20; - public const int MinScaledSize = 5; - } -} \ No newline at end of file diff --git a/Penumbra/MigrateConfiguration.cs b/Penumbra/Configuration.Migration.cs similarity index 100% rename from Penumbra/MigrateConfiguration.cs rename to Penumbra/Configuration.Migration.cs diff --git a/Penumbra/Configuration.cs b/Penumbra/Configuration.cs index 05d9cc81..565d7975 100644 --- a/Penumbra/Configuration.cs +++ b/Penumbra/Configuration.cs @@ -86,4 +86,16 @@ public partial class Configuration : IPluginConfiguration Save(); } } + + // Contains some default values or boundaries for config values. + public static class Constants + { + public const int CurrentVersion = 3; + public const float MaxAbsoluteSize = 600; + public const int DefaultAbsoluteSize = 250; + public const float MinAbsoluteSize = 50; + public const int MaxScaledSize = 80; + public const int DefaultScaledSize = 20; + public const int MinScaledSize = 5; + } } \ No newline at end of file diff --git a/Penumbra/Mods/Manager/Mod.Manager.BasePath.cs b/Penumbra/Mods/Manager/Mod.Manager.BasePath.cs index ce095609..8e44edfd 100644 --- a/Penumbra/Mods/Manager/Mod.Manager.BasePath.cs +++ b/Penumbra/Mods/Manager/Mod.Manager.BasePath.cs @@ -12,14 +12,19 @@ public partial class Mod public delegate void ModPathChangeDelegate( ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory, DirectoryInfo? newDirectory ); - public event ModPathChangeDelegate? ModPathChanged; + public event ModPathChangeDelegate ModPathChanged; + // Rename/Move a mod directory. + // Updates all collection settings and sort order settings. public void MoveModDirectory( Index idx, DirectoryInfo newDirectory ) { var mod = this[ idx ]; // TODO } + // Delete a mod by its index. + // Deletes from filesystem as well as from internal data. + // Updates indices of later mods. public void DeleteMod( int idx ) { var mod = this[ idx ]; @@ -41,9 +46,10 @@ public partial class Mod --remainingMod.Index; } - ModPathChanged?.Invoke( ModPathChangeType.Deleted, mod, mod.BasePath, null ); + ModPathChanged.Invoke( ModPathChangeType.Deleted, mod, mod.BasePath, null ); } + // Load a new mod and add it to the manager if successful. public void AddMod( DirectoryInfo modFolder ) { if( _mods.Any( m => m.BasePath.Name == modFolder.Name ) ) @@ -59,7 +65,22 @@ public partial class Mod mod.Index = _mods.Count; _mods.Add( mod ); - ModPathChanged?.Invoke( ModPathChangeType.Added, mod, null, mod.BasePath ); + ModPathChanged.Invoke( ModPathChangeType.Added, mod, null, mod.BasePath ); + } + + // Add new mods to NewMods and remove deleted mods from NewMods. + private void OnModPathChange( ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory, + DirectoryInfo? newDirectory ) + { + switch( type ) + { + case ModPathChangeType.Added: + NewMods.Add( mod ); + break; + case ModPathChangeType.Deleted: + NewMods.Remove( mod ); + break; + } } } } \ No newline at end of file diff --git a/Penumbra/Mods/Manager/Mod.Manager.Options.cs b/Penumbra/Mods/Manager/Mod.Manager.Options.cs index 78f3d58c..cdfa142b 100644 --- a/Penumbra/Mods/Manager/Mod.Manager.Options.cs +++ b/Penumbra/Mods/Manager/Mod.Manager.Options.cs @@ -197,6 +197,14 @@ public sealed partial class Mod return; } + if( mod._groups[ groupIdx ].Count > 63 ) + { + PluginLog.Error( + $"Could not add option {option.Name} to {mod._groups[ groupIdx ].Name} for mod {mod.Name}, " + + "since only up to 64 options are supported in one group." ); + return; + } + switch( mod._groups[ groupIdx ] ) { case SingleModGroup s: diff --git a/Penumbra/Mods/Manager/Mod.Manager.Root.cs b/Penumbra/Mods/Manager/Mod.Manager.Root.cs index 6967f0bc..a27d5c02 100644 --- a/Penumbra/Mods/Manager/Mod.Manager.Root.cs +++ b/Penumbra/Mods/Manager/Mod.Manager.Root.cs @@ -11,16 +11,19 @@ public sealed partial class Mod public DirectoryInfo BasePath { get; private set; } = null!; public bool Valid { get; private set; } - public event Action? ModDiscoveryStarted; public event Action? ModDiscoveryFinished; + // Change the mod base directory and discover available mods. public void DiscoverMods( string newDir ) { SetBaseDirectory( newDir, false ); DiscoverMods(); } + // Set the mod base directory. + // If its not the first time, check if it is the same directory as before. + // Also checks if the directory is available and tries to create it if it is not. private void SetBaseDirectory( string newPath, bool firstTime ) { if( !firstTime && string.Equals( newPath, Penumbra.Config.ModDirectory, StringComparison.InvariantCultureIgnoreCase ) ) @@ -59,8 +62,10 @@ public sealed partial class Mod } } + // Discover new mods. public void DiscoverMods() { + NewMods.Clear(); ModDiscoveryStarted?.Invoke(); _mods.Clear(); BasePath.Refresh(); diff --git a/Penumbra/Mods/Manager/Mod.Manager.cs b/Penumbra/Mods/Manager/Mod.Manager.cs index e467c733..7a21a3a8 100644 --- a/Penumbra/Mods/Manager/Mod.Manager.cs +++ b/Penumbra/Mods/Manager/Mod.Manager.cs @@ -6,16 +6,22 @@ namespace Penumbra.Mods; public sealed partial class Mod { - public sealed partial class Manager : IEnumerable< Mod > + public sealed partial class Manager : IReadOnlyList< Mod > { + // An easily accessible set of new mods. + // Mods are added when they are created or imported. + // Mods are removed when they are deleted or when they are toggled in any collection. + // Also gets cleared on mod rediscovery. + public readonly HashSet< Mod > NewMods = new(); + private readonly List< Mod > _mods = new(); + public Mod this[ int idx ] + => _mods[ idx ]; + public Mod this[ Index idx ] => _mods[ idx ]; - public IReadOnlyList< Mod > Mods - => _mods; - public int Count => _mods.Count; @@ -29,6 +35,7 @@ public sealed partial class Mod { SetBaseDirectory( modDirectory, true ); ModOptionChanged += OnModOptionChange; + ModPathChanged += OnModPathChange; } } } \ No newline at end of file diff --git a/Penumbra/Mods/Mod.BasePath.cs b/Penumbra/Mods/Mod.BasePath.cs index c925a4f0..24cf84f1 100644 --- a/Penumbra/Mods/Mod.BasePath.cs +++ b/Penumbra/Mods/Mod.BasePath.cs @@ -18,7 +18,7 @@ public partial class Mod private Mod( DirectoryInfo basePath ) => BasePath = basePath; - public static Mod? LoadMod( DirectoryInfo basePath ) + private static Mod? LoadMod( DirectoryInfo basePath ) { basePath.Refresh(); if( !basePath.Exists ) diff --git a/Penumbra/Mods/Mod.ChangedItems.cs b/Penumbra/Mods/Mod.ChangedItems.cs index 9e68225c..d2996b71 100644 --- a/Penumbra/Mods/Mod.ChangedItems.cs +++ b/Penumbra/Mods/Mod.ChangedItems.cs @@ -8,7 +8,7 @@ public sealed partial class Mod public SortedList< string, object? > ChangedItems { get; } = new(); public string LowerChangedItemsString { get; private set; } = string.Empty; - public void ComputeChangedItems() + private void ComputeChangedItems() { var identifier = GameData.GameData.GetIdentifier(); ChangedItems.Clear(); diff --git a/Penumbra/Mods/Mod.Files.cs b/Penumbra/Mods/Mod.Files.cs index 4ff1bbd9..0eee20b0 100644 --- a/Penumbra/Mods/Mod.Files.cs +++ b/Penumbra/Mods/Mod.Files.cs @@ -78,7 +78,7 @@ public partial class Mod public List< FullPath > FindMissingFiles() => AllFiles.Where( f => !f.Exists ).ToList(); - public static IModGroup? LoadModGroup( FileInfo file, DirectoryInfo basePath ) + private static IModGroup? LoadModGroup( FileInfo file, DirectoryInfo basePath ) { if( !File.Exists( file.FullName ) ) { diff --git a/Penumbra/Mods/Mod.Meta.cs b/Penumbra/Mods/Mod.Meta.cs index 33760dfe..6e5291a6 100644 --- a/Penumbra/Mods/Mod.Meta.cs +++ b/Penumbra/Mods/Mod.Meta.cs @@ -25,13 +25,13 @@ public sealed partial class Mod { public const uint CurrentFileVersion = 1; public uint FileVersion { get; private set; } = CurrentFileVersion; - public LowerString Name { get; private set; } = "Mod"; + public LowerString Name { get; private set; } = "New Mod"; public LowerString Author { get; private set; } = LowerString.Empty; public string Description { get; private set; } = string.Empty; public string Version { get; private set; } = string.Empty; public string Website { get; private set; } = string.Empty; - private FileInfo MetaFile + internal FileInfo MetaFile => new(Path.Combine( BasePath.FullName, "meta.json" )); private MetaChangeType LoadMeta() diff --git a/Penumbra/Mods/ModCleanup.cs b/Penumbra/Mods/ModCleanup.cs index 9497c9c6..996c6437 100644 --- a/Penumbra/Mods/ModCleanup.cs +++ b/Penumbra/Mods/ModCleanup.cs @@ -12,6 +12,7 @@ using Penumbra.Util; namespace Penumbra.Mods; +// TODO Everything //ublic class ModCleanup // // private const string Duplicates = "Duplicates"; @@ -521,7 +522,6 @@ namespace Penumbra.Mods; // } // } // -// // TODO // var idx = Penumbra.ModManager.Mods.IndexOf( m => m.Meta == meta ); // foreach( var collection in Penumbra.CollectionManager ) // { diff --git a/Penumbra/Mods/ModFileSystem.cs b/Penumbra/Mods/ModFileSystem.cs index d310250b..eb22b314 100644 --- a/Penumbra/Mods/ModFileSystem.cs +++ b/Penumbra/Mods/ModFileSystem.cs @@ -42,7 +42,7 @@ public sealed class ModFileSystem : FileSystem< Mod >, IDisposable // Used on construction and on mod rediscoveries. private void Reload() { - if( Load( new FileInfo( ModFileSystemFile ), Penumbra.ModManager.Mods, ModToIdentifier, ModToName ) ) + if( Load( new FileInfo( ModFileSystemFile ), Penumbra.ModManager, ModToIdentifier, ModToName ) ) { Save(); } diff --git a/Penumbra/Mods/Subclasses/IModGroup.cs b/Penumbra/Mods/Subclasses/IModGroup.cs index 924e294e..c89b2da9 100644 --- a/Penumbra/Mods/Subclasses/IModGroup.cs +++ b/Penumbra/Mods/Subclasses/IModGroup.cs @@ -4,10 +4,15 @@ using System.IO; using Dalamud.Logging; using Newtonsoft.Json; using OtterGui.Filesystem; -using Penumbra.Util; namespace Penumbra.Mods; +public enum SelectType +{ + Single, + Multi, +} + public interface IModGroup : IEnumerable< ISubMod > { public string Name { get; } diff --git a/Penumbra/Mods/Subclasses/Mod.Files.MultiModGroup.cs b/Penumbra/Mods/Subclasses/Mod.Files.MultiModGroup.cs index 88350364..26d3abe3 100644 --- a/Penumbra/Mods/Subclasses/Mod.Files.MultiModGroup.cs +++ b/Penumbra/Mods/Subclasses/Mod.Files.MultiModGroup.cs @@ -11,6 +11,7 @@ namespace Penumbra.Mods; public partial class Mod { + // Groups that allow all available options to be selected at once. private sealed class MultiModGroup : IModGroup { public SelectType Type diff --git a/Penumbra/Mods/Subclasses/Mod.Files.SingleModGroup.cs b/Penumbra/Mods/Subclasses/Mod.Files.SingleModGroup.cs index 8c0b4103..352bb503 100644 --- a/Penumbra/Mods/Subclasses/Mod.Files.SingleModGroup.cs +++ b/Penumbra/Mods/Subclasses/Mod.Files.SingleModGroup.cs @@ -11,6 +11,7 @@ namespace Penumbra.Mods; public partial class Mod { + // Groups that allow only one of their available options to be selected. private sealed class SingleModGroup : IModGroup { public SelectType Type diff --git a/Penumbra/Mods/Subclasses/Mod.Files.SubMod.cs b/Penumbra/Mods/Subclasses/Mod.Files.SubMod.cs index e9be472d..786ac6de 100644 --- a/Penumbra/Mods/Subclasses/Mod.Files.SubMod.cs +++ b/Penumbra/Mods/Subclasses/Mod.Files.SubMod.cs @@ -13,9 +13,11 @@ namespace Penumbra.Mods; public partial class Mod { - private string DefaultFile + internal string DefaultFile => Path.Combine( BasePath.FullName, "default_mod.json" ); + // The default mod contains setting-independent sets of file replacements, file swaps and meta changes. + // Every mod has an default mod, though it may be empty. private void SaveDefaultMod() { var defaultFile = DefaultFile; @@ -55,6 +57,14 @@ public partial class Mod } + // A sub mod is a collection of + // - file replacements + // - file swaps + // - meta manipulations + // that can be used either as an option or as the default data for a mod. + // It can be loaded and reloaded from Json. + // Nothing is checked for existence or validity when loading. + // Objects are also not checked for uniqueness, the first appearance of a game path or meta path decides. private sealed class SubMod : ISubMod { public string Name { get; set; } = "Default"; @@ -78,6 +88,7 @@ public partial class Mod FileSwapData.Clear(); ManipulationData.Clear(); + // Every option has a name, but priorities are only relevant for multi group options. Name = json[ nameof( ISubMod.Name ) ]?.ToObject< string >() ?? string.Empty; priority = json[ nameof( IModGroup.Priority ) ]?.ToObject< int >() ?? 0; @@ -115,6 +126,8 @@ public partial class Mod } } + // If .meta or .rgsp files are encountered, parse them and incorporate their meta changes into the mod. + // If delete is true, the files are deleted afterwards. public void IncorporateMetaChanges( DirectoryInfo basePath, bool delete ) { foreach( var (key, file) in Files.ToList() ) diff --git a/Penumbra/Mods/Subclasses/ModSettings.cs b/Penumbra/Mods/Subclasses/ModSettings.cs index 982776f8..3f6b7b53 100644 --- a/Penumbra/Mods/Subclasses/ModSettings.cs +++ b/Penumbra/Mods/Subclasses/ModSettings.cs @@ -14,6 +14,7 @@ public class ModSettings public int Priority { get; set; } public bool Enabled { get; set; } + // Create an independent copy of the current settings. public ModSettings DeepCopy() => new() { @@ -22,6 +23,7 @@ public class ModSettings Settings = Settings.ToList(), }; + // Create default settings for a given mod. public static ModSettings DefaultSettings( Mod mod ) => new() { @@ -30,24 +32,30 @@ public class ModSettings Settings = Enumerable.Repeat( 0u, mod.Groups.Count ).ToList(), }; + // Automatically react to changes in a mods available options. public bool HandleChanges( ModOptionChangeType type, Mod mod, int groupIdx, int optionIdx, int movedToIdx ) { switch( type ) { case ModOptionChangeType.GroupRenamed: return true; case ModOptionChangeType.GroupAdded: + // Add new empty setting for new mod. Settings.Insert( groupIdx, 0 ); return true; case ModOptionChangeType.GroupDeleted: + // Remove setting for deleted mod. Settings.RemoveAt( groupIdx ); return true; case ModOptionChangeType.GroupTypeChanged: { + // Fix settings for a changed group type. + // Single -> Multi: set single as enabled, rest as disabled + // Multi -> Single: set the first enabled option or 0. var group = mod.Groups[ groupIdx ]; var config = Settings[ groupIdx ]; Settings[ groupIdx ] = group.Type switch { - SelectType.Single => ( uint )Math.Min( group.Count - 1, BitOperations.TrailingZeroCount( config ) ), + SelectType.Single => ( uint )Math.Max( Math.Min( group.Count - 1, BitOperations.TrailingZeroCount( config ) ), 0 ), SelectType.Multi => 1u << ( int )config, _ => config, }; @@ -55,6 +63,8 @@ public class ModSettings } case ModOptionChangeType.OptionDeleted: { + // Single -> select the previous option if any. + // Multi -> excise the corresponding bit. var group = mod.Groups[ groupIdx ]; var config = Settings[ groupIdx ]; Settings[ groupIdx ] = group.Type switch @@ -65,9 +75,13 @@ public class ModSettings }; return config != Settings[ groupIdx ]; } - case ModOptionChangeType.GroupMoved: return Settings.Move( groupIdx, movedToIdx ); + case ModOptionChangeType.GroupMoved: + // Move the group the same way. + return Settings.Move( groupIdx, movedToIdx ); case ModOptionChangeType.OptionMoved: { + // Single -> select the moved option if it was currently selected + // Multi -> move the corresponding bit var group = mod.Groups[ groupIdx ]; var config = Settings[ groupIdx ]; Settings[ groupIdx ] = group.Type switch @@ -82,6 +96,7 @@ public class ModSettings } } + // Ensure that a value is valid for a group. private static uint FixSetting( IModGroup group, uint value ) => group.Type switch { @@ -90,6 +105,7 @@ public class ModSettings _ => value, }; + // Set a setting. Ensures that there are enough settings and fixes the setting beforehand. public void SetValue( Mod mod, int groupIdx, uint newValue ) { AddMissingSettings( groupIdx + 1 ); @@ -97,6 +113,7 @@ public class ModSettings Settings[ groupIdx ] = FixSetting( group, newValue ); } + // Remove a single bit, moving all further bits one down. private static uint RemoveBit( uint config, int bit ) { var lowMask = ( 1u << bit ) - 1u; @@ -106,6 +123,7 @@ public class ModSettings return low | high; } + // Move a bit in an uint from its position to another, shifting other bits accordingly. private static uint MoveBit( uint config, int bit1, int bit2 ) { var enabled = ( config & ( 1 << bit1 ) ) != 0 ? 1u << bit2 : 0u; @@ -116,7 +134,8 @@ public class ModSettings return low | enabled | high; } - internal bool AddMissingSettings( int totalCount ) + // Add defaulted settings up to the required count. + private bool AddMissingSettings( int totalCount ) { if( totalCount <= Settings.Count ) { @@ -127,6 +146,7 @@ public class ModSettings return true; } + // A simple struct conversion to easily save settings by name instead of value. public struct SavedSettings { public Dictionary< string, uint > Settings; @@ -154,6 +174,7 @@ public class ModSettings } } + // Convert and fix. public bool ToSettings( Mod mod, out ModSettings settings ) { var list = new List< uint >( mod.Groups.Count ); diff --git a/Penumbra/Mods/Subclasses/SelectType.cs b/Penumbra/Mods/Subclasses/SelectType.cs deleted file mode 100644 index 0843729c..00000000 --- a/Penumbra/Mods/Subclasses/SelectType.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Penumbra.Mods; - -public enum SelectType -{ - Single, - Multi, -} \ No newline at end of file diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs index 50ce0d07..8d11133d 100644 --- a/Penumbra/Penumbra.cs +++ b/Penumbra/Penumbra.cs @@ -30,9 +30,9 @@ public class Penumbra : IDalamudPlugin private const string CommandName = "/penumbra"; - public static string Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? string.Empty; + public static readonly string Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? string.Empty; - public static string CommitHash = + public static readonly string CommitHash = Assembly.GetExecutingAssembly().GetCustomAttribute< AssemblyInformationalVersionAttribute >()?.InformationalVersion ?? "Unknown"; public static Configuration Config { get; private set; } = null!; @@ -295,7 +295,7 @@ public class Penumbra : IDalamudPlugin case "reload": { ModManager.DiscoverMods(); - Dalamud.Chat.Print( $"Reloaded Penumbra mods. You have {ModManager.Mods.Count} mods." + Dalamud.Chat.Print( $"Reloaded Penumbra mods. You have {ModManager.Count} mods." ); break; } diff --git a/Penumbra/UI/Classes/ModFileSystemSelector.Filters.cs b/Penumbra/UI/Classes/ModFileSystemSelector.Filters.cs index de9cf7b7..2b342208 100644 --- a/Penumbra/UI/Classes/ModFileSystemSelector.Filters.cs +++ b/Penumbra/UI/Classes/ModFileSystemSelector.Filters.cs @@ -21,11 +21,10 @@ public partial class ModFileSystemSelector public uint Color; } - private const StringComparison IgnoreCase = StringComparison.InvariantCultureIgnoreCase; - private readonly IReadOnlySet< Mod > _newMods = new HashSet< Mod >(); - private LowerString _modFilter = LowerString.Empty; - private int _filterType = -1; - private ModFilter _stateFilter = ModFilterExtensions.UnfilteredStateMods; + private const StringComparison IgnoreCase = StringComparison.InvariantCultureIgnoreCase; + private LowerString _modFilter = LowerString.Empty; + private int _filterType = -1; + private ModFilter _stateFilter = ModFilterExtensions.UnfilteredStateMods; private void SetFilterTooltip() { @@ -105,7 +104,7 @@ public partial class ModFileSystemSelector // Only get the text color for a mod if no filters are set. private uint GetTextColor( Mod mod, ModSettings? settings, ModCollection collection ) { - if( _newMods.Contains( mod ) ) + if( Penumbra.ModManager.NewMods.Contains( mod ) ) { return ColorId.NewMod.Value(); } @@ -133,7 +132,7 @@ public partial class ModFileSystemSelector private bool CheckStateFilters( Mod mod, ModSettings? settings, ModCollection collection, ref ModState state ) { - var isNew = _newMods.Contains( mod ); + var isNew = Penumbra.ModManager.NewMods.Contains( mod ); // Handle mod details. if( CheckFlags( mod.TotalFileCount, ModFilter.HasNoFiles, ModFilter.HasFiles ) || CheckFlags( mod.TotalSwapCount, ModFilter.HasNoFileSwaps, ModFilter.HasFileSwaps ) diff --git a/Penumbra/UI/Classes/ModFileSystemSelector.cs b/Penumbra/UI/Classes/ModFileSystemSelector.cs index 4d7a236e..be36ab68 100644 --- a/Penumbra/UI/Classes/ModFileSystemSelector.cs +++ b/Penumbra/UI/Classes/ModFileSystemSelector.cs @@ -24,10 +24,9 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod public ModSettings SelectedSettings { get; private set; } = ModSettings.Empty; public ModCollection SelectedSettingCollection { get; private set; } = ModCollection.Empty; - public ModFileSystemSelector( ModFileSystem fileSystem, IReadOnlySet< Mod > newMods ) + public ModFileSystemSelector( ModFileSystem fileSystem ) : base( fileSystem ) { - _newMods = newMods; SubscribeRightClickFolder( EnableDescendants, 10 ); SubscribeRightClickFolder( DisableDescendants, 10 ); SubscribeRightClickFolder( InheritDescendants, 15 ); @@ -122,7 +121,8 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod private void AddNewModButton( Vector2 size ) { - if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Plus.ToIconString(), size, "Create a new, empty mod of a given name.", !Penumbra.ModManager.Valid, true ) ) + if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Plus.ToIconString(), size, "Create a new, empty mod of a given name.", + !Penumbra.ModManager.Valid, true ) ) { ImGui.OpenPopup( "Create New Mod" ); } @@ -167,17 +167,18 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod private void DrawInfoPopup() { var display = ImGui.GetIO().DisplaySize; - ImGui.SetNextWindowSize( display / 4 ); + ImGui.SetNextWindowSize( display / 4 ); ImGui.SetNextWindowPos( 3 * display / 8 ); using var popup = ImRaii.Popup( "Import Status", ImGuiWindowFlags.Modal ); if( _import != null && popup.Success ) { - _import.DrawProgressInfo( ImGuiHelpers.ScaledVector2( -1, ImGui.GetFrameHeight() ) ); + _import.DrawProgressInfo( new Vector2( -1, ImGui.GetFrameHeight() ) ); if( _import.State == ImporterState.Done ) { ImGui.SetCursorPosY( ImGui.GetWindowHeight() - ImGui.GetFrameHeight() * 2 ); if( ImGui.Button( "Close", -Vector2.UnitX ) ) { + AddNewMods( _import.ExtractedMods ); _import = null; ImGui.CloseCurrentPopup(); } @@ -185,6 +186,37 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod } } + // Clean up invalid directories if necessary. + // Add all successfully extracted mods. + private static void AddNewMods( IEnumerable< (FileInfo File, DirectoryInfo? Mod, Exception? Error) > list ) + { + foreach( var (file, dir, error) in list ) + { + if( error != null ) + { + if( dir != null && Directory.Exists( dir.FullName ) ) + { + try + { + Directory.Delete( dir.FullName ); + } + catch( Exception e ) + { + PluginLog.Error($"Error cleaning up failed mod extraction of {file.FullName} to {dir.FullName}:\n{e}" ); + } + } + + PluginLog.Error( $"Error extracting {file.FullName}, mod skipped:\n{error}" ); + continue; + } + + if( dir != null ) + { + Penumbra.ModManager.AddMod( dir ); + } + } + } + private void DeleteModButton( Vector2 size ) { var keys = ImGui.GetIO().KeyCtrl && ImGui.GetIO().KeyShift; diff --git a/Penumbra/UI/ConfigWindow.ChangedItemsTab.cs b/Penumbra/UI/ConfigWindow.ChangedItemsTab.cs index c99cb3d4..aa7b6c1e 100644 --- a/Penumbra/UI/ConfigWindow.ChangedItemsTab.cs +++ b/Penumbra/UI/ConfigWindow.ChangedItemsTab.cs @@ -31,6 +31,7 @@ public partial class ConfigWindow return; } + // Draw filters. ImGui.SetNextItemWidth( -1 ); LowerString.InputWithHint( "##changedItemsFilter", "Filter...", ref _changedItemFilter, 64 ); @@ -40,6 +41,7 @@ public partial class ConfigWindow return; } + // Draw table of changed items. var height = ImGui.GetTextLineHeightWithSpacing() + 2 * ImGui.GetStyle().CellPadding.Y; var skips = ImGuiClip.GetNecessarySkips( height ); using var list = ImRaii.Table( "##changedItems", 1, ImGuiTableFlags.RowBg, -Vector2.One ); diff --git a/Penumbra/UI/ConfigWindow.CollectionsTab.Inheritance.cs b/Penumbra/UI/ConfigWindow.CollectionsTab.Inheritance.cs index ac803463..6d4ed0ec 100644 --- a/Penumbra/UI/ConfigWindow.CollectionsTab.Inheritance.cs +++ b/Penumbra/UI/ConfigWindow.CollectionsTab.Inheritance.cs @@ -112,7 +112,7 @@ public partial class ConfigWindow private void DrawCurrentCollectionInheritance() { using var list = ImRaii.ListBox( "##inheritanceList", - new Vector2( _window._inputTextWidth.X - ImGui.GetFrameHeight() - ImGui.GetStyle().ItemSpacing.X, + new Vector2( _window._inputTextWidth.X - _window._iconButtonSize.X - ImGui.GetStyle().ItemSpacing.X, ImGui.GetTextLineHeightWithSpacing() * InheritedCollectionHeight ) ); if( !list ) { @@ -131,7 +131,7 @@ public partial class ConfigWindow private void DrawInheritanceTrashButton() { ImGui.SameLine(); - var size = new Vector2( ImGui.GetFrameHeight(), ImGui.GetTextLineHeightWithSpacing() * InheritedCollectionHeight ); + var size = new Vector2( _window._iconButtonSize.X, ImGui.GetTextLineHeightWithSpacing() * InheritedCollectionHeight ); var buttonColor = ImGui.GetColorU32( ImGuiCol.Button ); // Prevent hovering from highlighting the button. using var color = ImRaii.PushColor( ImGuiCol.ButtonActive, buttonColor ) @@ -142,7 +142,7 @@ public partial class ConfigWindow using var target = ImRaii.DragDropTarget(); if( target.Success && ImGuiUtil.IsDropping( InheritanceDragDropLabel ) ) { - _inheritanceAction = ( Penumbra.CollectionManager.Current.Inheritance.IndexOf( _movedInheritance ), -1 ); + _inheritanceAction = ( Penumbra.CollectionManager.Current.Inheritance.IndexOf( _movedInheritance! ), -1 ); } } @@ -192,7 +192,7 @@ public partial class ConfigWindow ModCollection.ValidInheritance.Circle => "Inheriting from selected collection would lead to cyclic inheritance.", _ => string.Empty, }; - if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Plus.ToIconString(), ImGui.GetFrameHeight() * Vector2.One, tt, + if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Plus.ToIconString(), _window._iconButtonSize, tt, inheritance != ModCollection.ValidInheritance.Valid, true ) && Penumbra.CollectionManager.Current.AddInheritance( _newInheritance! ) ) { @@ -211,7 +211,7 @@ public partial class ConfigWindow // Only valid inheritances are drawn in the preview, or nothing if no inheritance is available. private void DrawNewInheritanceCombo() { - ImGui.SetNextItemWidth( _window._inputTextWidth.X - ImGui.GetFrameHeight() - ImGui.GetStyle().ItemSpacing.X ); + ImGui.SetNextItemWidth( _window._inputTextWidth.X - _window._iconButtonSize.X - ImGui.GetStyle().ItemSpacing.X ); _newInheritance ??= Penumbra.CollectionManager.FirstOrDefault( c => c != Penumbra.CollectionManager.Current && !Penumbra.CollectionManager.Current.Inheritance.Contains( c ) ) ?? ModCollection.Empty; diff --git a/Penumbra/UI/ConfigWindow.CollectionsTab.cs b/Penumbra/UI/ConfigWindow.CollectionsTab.cs index 6d38cb69..76024c0e 100644 --- a/Penumbra/UI/ConfigWindow.CollectionsTab.cs +++ b/Penumbra/UI/ConfigWindow.CollectionsTab.cs @@ -47,14 +47,19 @@ public partial class ConfigWindow } } + // Only gets drawn when actually relevant. private static void DrawCleanCollectionButton() { - if( ImGui.Button( "Clean Settings" ) ) + if( Penumbra.Config.ShowAdvanced && Penumbra.CollectionManager.Current.HasUnusedSettings ) { - Penumbra.CollectionManager.Current.CleanUnavailableSettings(); + ImGui.SameLine(); + if( ImGuiUtil.DrawDisabledButton( "Clean Settings", Vector2.Zero + , "Remove all stored settings for mods not currently available and fix invalid settings.\nUse at own risk." + , false ) ) + { + Penumbra.CollectionManager.Current.CleanUnavailableSettings(); + } } - - ImGuiUtil.HoverTooltip( "Remove all stored settings for mods not currently available and fix invalid settings.\nUse at own risk." ); } // Draw the new collection input as well as its buttons. @@ -94,11 +99,7 @@ public partial class ConfigWindow Penumbra.CollectionManager.RemoveCollection( Penumbra.CollectionManager.Current ); } - if( Penumbra.Config.ShowAdvanced ) - { - ImGui.SameLine(); - DrawCleanCollectionButton(); - } + DrawCleanCollectionButton(); } private void DrawCurrentCollectionSelector() @@ -152,7 +153,7 @@ public partial class ConfigWindow using var id = ImRaii.PushId( name ); DrawCollectionSelector( string.Empty, _window._inputTextWidth.X, ModCollection.Type.Character, true, name ); ImGui.SameLine(); - if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Trash.ToIconString(), Vector2.One * ImGui.GetFrameHeight(), string.Empty, + if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Trash.ToIconString(), _window._iconButtonSize, string.Empty, false, true ) ) { diff --git a/Penumbra/UI/ConfigWindow.EffectiveTab.cs b/Penumbra/UI/ConfigWindow.EffectiveTab.cs index 3e843231..241a22bb 100644 --- a/Penumbra/UI/ConfigWindow.EffectiveTab.cs +++ b/Penumbra/UI/ConfigWindow.EffectiveTab.cs @@ -124,7 +124,7 @@ public partial class ConfigWindow // Filters mean we can not use the known counts. if( hasFilters ) { - var it2 = it.Select( p => ( p.Item1.ToString() ?? string.Empty, Penumbra.ModManager.Mods[ p.Item2 ].Name ) ); + var it2 = it.Select( p => ( p.Item1.ToString() ?? string.Empty, Penumbra.ModManager[ p.Item2 ].Name ) ); if( stop >= 0 ) { ImGuiClip.DrawEndDummy( stop + it2.Count( CheckFilters ), height ); @@ -190,7 +190,7 @@ public partial class ConfigWindow ImGui.TableNextColumn(); ImGuiUtil.PrintIcon( FontAwesomeIcon.LongArrowAltLeft ); ImGui.TableNextColumn(); - ImGuiUtil.CopyOnClickSelectable( Penumbra.ModManager.Mods[ modIdx ].Name ); + ImGuiUtil.CopyOnClickSelectable( Penumbra.ModManager[ modIdx ].Name ); } // Check filters for file replacements. diff --git a/Penumbra/UI/ConfigWindow.Misc.cs b/Penumbra/UI/ConfigWindow.Misc.cs index b4cf7835..850d4e85 100644 --- a/Penumbra/UI/ConfigWindow.Misc.cs +++ b/Penumbra/UI/ConfigWindow.Misc.cs @@ -21,16 +21,16 @@ public partial class ConfigWindow => ImGuiNative.igTextUnformatted( s.Path, s.Path + s.Length ); // Draw text given by a byte pointer. - internal static unsafe void Text( byte* s, int length ) + private static unsafe void Text( byte* s, int length ) => ImGuiNative.igTextUnformatted( s, s + length ); // Draw the name of a resource file. - internal static unsafe void Text( ResourceHandle* resource ) + private static unsafe void Text( ResourceHandle* resource ) => Text( resource->FileName(), resource->FileNameLength ); // Draw a changed item, invoking the Api-Events for clicks and tooltips. // Also draw the item Id in grey - internal void DrawChangedItem( string name, object? data, float itemIdOffset = 0 ) + private void DrawChangedItem( string name, object? data, float itemIdOffset = 0 ) { var ret = ImGui.Selectable( name ) ? MouseButton.Left : MouseButton.None; ret = ImGui.IsItemClicked( ImGuiMouseButton.Right ) ? MouseButton.Right : ret; @@ -64,7 +64,7 @@ public partial class ConfigWindow // A selectable that copies its text to clipboard on selection and provides a on-hover tooltip about that, // using an Utf8String. - internal static unsafe void CopyOnClickSelectable( Utf8String text ) + private static unsafe void CopyOnClickSelectable( Utf8String text ) { if( ImGuiNative.igSelectable_Bool( text.Path, 0, ImGuiSelectableFlags.None, Vector2.Zero ) != 0 ) { diff --git a/Penumbra/UI/ConfigWindow.ModPanel.Edit.cs b/Penumbra/UI/ConfigWindow.ModPanel.Edit.cs index 0ca5dcd4..560dae22 100644 --- a/Penumbra/UI/ConfigWindow.ModPanel.Edit.cs +++ b/Penumbra/UI/ConfigWindow.ModPanel.Edit.cs @@ -1,6 +1,7 @@ using System; -using System.Collections; using System.Collections.Generic; +using System.Diagnostics; +using System.IO; using System.Numerics; using Dalamud.Interface; using ImGuiNET; @@ -14,7 +15,7 @@ public partial class ConfigWindow { private partial class ModPanel { - public readonly Queue< Action > _delayedActions = new(); + private readonly Queue< Action > _delayedActions = new(); private void DrawAddOptionGroupInput() { @@ -24,7 +25,7 @@ public partial class ConfigWindow var nameValid = Mod.Manager.VerifyFileName( _mod, null, _newGroupName, false ); var tt = nameValid ? "Add new option group to the mod." : "Can not add a group of this name."; - if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Plus.ToIconString(), ImGui.GetFrameHeight() * Vector2.One, + if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Plus.ToIconString(), _window._iconButtonSize, tt, !nameValid, true ) ) { Penumbra.ModManager.AddModGroup( _mod, SelectType.Single, _newGroupName ); @@ -73,6 +74,33 @@ public partial class ConfigWindow EditDescriptionPopup(); } + private void EditButtons() + { + var folderExists = Directory.Exists( _mod.BasePath.FullName ); + var tt = folderExists + ? $"Open {_mod.BasePath.FullName} in the file explorer of your choice." + : $"Mod directory {_mod.BasePath.FullName} does not exist."; + if( ImGuiUtil.DrawDisabledButton( "Open Mod Directory", Vector2.Zero, tt, !folderExists ) ) + { + Process.Start( new ProcessStartInfo( _mod.BasePath.FullName ) { UseShellExecute = true } ); + } + + ImGui.SameLine(); + ImGuiUtil.DrawDisabledButton( "Rename Mod Directory", Vector2.Zero, "Not implemented yet", true ); + ImGui.SameLine(); + ImGuiUtil.DrawDisabledButton( "Reload Mod", Vector2.Zero, "Not implemented yet", true ); + + ImGuiUtil.DrawDisabledButton( "Deduplicate", Vector2.Zero, "Not implemented yet", true ); + ImGui.SameLine(); + ImGuiUtil.DrawDisabledButton( "Normalize", Vector2.Zero, "Not implemented yet", true ); + ImGui.SameLine(); + ImGuiUtil.DrawDisabledButton( "Auto-Create Groups", Vector2.Zero, "Not implemented yet", true ); + + ImGuiUtil.DrawDisabledButton( "Change Material Suffix", Vector2.Zero, "Not implemented yet", true ); + + ImGui.Dummy( _window._defaultSpace ); + } + // Special field indices to reuse the same string buffer. private const int NoFieldIdx = -1; @@ -105,15 +133,37 @@ public partial class ConfigWindow Penumbra.ModManager.ChangeModWebsite( _mod.Index, newWebsite ); } - if( ImGui.Button( "Edit Description", _window._inputTextWidth ) ) + var reducedSize = new Vector2( _window._inputTextWidth.X - _window._iconButtonSize.X - ImGui.GetStyle().ItemSpacing.X, 0 ); + + if( ImGui.Button( "Edit Description", reducedSize ) ) { _delayedActions.Enqueue( () => OpenEditDescriptionPopup( DescriptionFieldIdx ) ); } - if( ImGui.Button( "Edit Default Mod", _window._inputTextWidth ) ) + ImGui.SameLine(); + var fileExists = File.Exists( _mod.MetaFile.FullName ); + var tt = fileExists + ? "Open the metadata json file in the text editor of your choice." + : "The metadata json file does not exist."; + if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.FileExport.ToIconString(), _window._iconButtonSize, tt, !fileExists, true ) ) + { + Process.Start( new ProcessStartInfo( _mod.MetaFile.FullName ) { UseShellExecute = true } ); + } + + if( ImGui.Button( "Edit Default Mod", reducedSize ) ) { _window.SubModPopup.Activate( _mod, -1, 0 ); } + + ImGui.SameLine(); + fileExists = File.Exists( _mod.DefaultFile ); + tt = fileExists + ? "Open the default option json file in the text editor of your choice." + : "The default option json file does not exist."; + if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.FileExport.ToIconString(), _window._iconButtonSize, tt, !fileExists, true ) ) + { + Process.Start( new ProcessStartInfo( _mod.DefaultFile ) { UseShellExecute = true } ); + } } @@ -144,7 +194,7 @@ public partial class ConfigWindow ImGuiUtil.HoverTooltip( "Group Name" ); ImGui.SameLine(); - if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Trash.ToIconString(), ImGui.GetFrameHeight() * Vector2.One, + if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Trash.ToIconString(), _window._iconButtonSize, "Delete this option group.\nHold Control while clicking to delete.", !ImGui.GetIO().KeyCtrl, true ) ) { _delayedActions.Enqueue( () => Penumbra.ModManager.DeleteModGroup( _mod, groupIdx ) ); @@ -152,7 +202,7 @@ public partial class ConfigWindow ImGui.SameLine(); - if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Edit.ToIconString(), ImGui.GetFrameHeight() * Vector2.One, + if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Edit.ToIconString(), _window._iconButtonSize, "Edit group description.", false, true ) ) { _delayedActions.Enqueue( () => OpenEditDescriptionPopup( groupIdx ) ); @@ -167,7 +217,7 @@ public partial class ConfigWindow ImGuiUtil.HoverTooltip( "Group Priority" ); - ImGui.SetNextItemWidth( _window._inputTextWidth.X - 2 * ImGui.GetFrameHeight() - 8 * ImGuiHelpers.GlobalScale ); + ImGui.SetNextItemWidth( _window._inputTextWidth.X - 3 * _window._iconButtonSize.X - 12 * ImGuiHelpers.GlobalScale ); using( var combo = ImRaii.Combo( "##GroupType", GroupTypeName( group.Type ) ) ) { if( combo ) @@ -185,7 +235,7 @@ public partial class ConfigWindow ImGui.SameLine(); var tt = groupIdx == 0 ? "Can not move this group further upwards." : $"Move this group up to group {groupIdx}."; - if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.ArrowUp.ToIconString(), ImGui.GetFrameHeight() * Vector2.One, + if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.ArrowUp.ToIconString(), _window._iconButtonSize, tt, groupIdx == 0, true ) ) { _delayedActions.Enqueue( () => Penumbra.ModManager.MoveModGroup( _mod, groupIdx, groupIdx - 1 ) ); @@ -195,19 +245,30 @@ public partial class ConfigWindow tt = groupIdx == _mod.Groups.Count - 1 ? "Can not move this group further downwards." : $"Move this group down to group {groupIdx + 2}."; - if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.ArrowDown.ToIconString(), ImGui.GetFrameHeight() * Vector2.One, + if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.ArrowDown.ToIconString(), _window._iconButtonSize, tt, groupIdx == _mod.Groups.Count - 1, true ) ) { _delayedActions.Enqueue( () => Penumbra.ModManager.MoveModGroup( _mod, groupIdx, groupIdx + 1 ) ); } + ImGui.SameLine(); + var fileName = group.FileName( _mod.BasePath ); + var fileExists = File.Exists( fileName ); + tt = fileExists + ? $"Open the {group.Name} json file in the text editor of your choice." + : $"The {group.Name} json file does not exist."; + if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.FileExport.ToIconString(), _window._iconButtonSize, tt, !fileExists, true ) ) + { + Process.Start( new ProcessStartInfo( fileName ) { UseShellExecute = true } ); + } + ImGui.Dummy( _window._defaultSpace ); using var table = ImRaii.Table( string.Empty, 5, ImGuiTableFlags.SizingFixedFit ); ImGui.TableSetupColumn( "idx", ImGuiTableColumnFlags.WidthFixed, 60 * ImGuiHelpers.GlobalScale ); ImGui.TableSetupColumn( "name", ImGuiTableColumnFlags.WidthFixed, _window._inputTextWidth.X - 62 * ImGuiHelpers.GlobalScale ); - ImGui.TableSetupColumn( "delete", ImGuiTableColumnFlags.WidthFixed, ImGui.GetFrameHeight() ); - ImGui.TableSetupColumn( "edit", ImGuiTableColumnFlags.WidthFixed, ImGui.GetFrameHeight() ); + ImGui.TableSetupColumn( "delete", ImGuiTableColumnFlags.WidthFixed, _window._iconButtonSize.X ); + ImGui.TableSetupColumn( "edit", ImGuiTableColumnFlags.WidthFixed, _window._iconButtonSize.X ); ImGui.TableSetupColumn( "priority", ImGuiTableColumnFlags.WidthFixed, 50 * ImGuiHelpers.GlobalScale ); if( table ) { @@ -221,7 +282,7 @@ public partial class ConfigWindow ImGui.SetNextItemWidth( -1 ); ImGui.InputTextWithHint( "##newOption", "Add new option...", ref _newOptionName, 256 ); ImGui.TableNextColumn(); - if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Plus.ToIconString(), ImGui.GetFrameHeight() * Vector2.One, + if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Plus.ToIconString(), _window._iconButtonSize, "Add a new option to this group.", _newOptionName.Length == 0, true ) ) { Penumbra.ModManager.AddOption( _mod, groupIdx, _newOptionName ); @@ -258,6 +319,7 @@ public partial class ConfigWindow } } + // TODO drag options to other groups without options. using( var target = ImRaii.DragDropTarget() ) { if( target.Success && ImGuiUtil.IsDropping( label ) ) @@ -266,14 +328,21 @@ public partial class ConfigWindow { if( _dragDropGroupIdx == groupIdx ) { - // TODO - Dalamud.Chat.Print( - $"Dropped {_mod.Groups[ _dragDropGroupIdx ][ _dragDropOptionIdx ].Name} onto {_mod.Groups[ groupIdx ][ optionIdx ].Name}" ); + var sourceOption = _dragDropOptionIdx; + _delayedActions.Enqueue( () => Penumbra.ModManager.MoveOption( _mod, groupIdx, sourceOption, optionIdx ) ); } else { - Dalamud.Chat.Print( - $"Dropped {_mod.Groups[ _dragDropGroupIdx ][ _dragDropOptionIdx ].Name} onto {_mod.Groups[ groupIdx ][ optionIdx ].Name}" ); + // Move from one group to another by deleting, then adding the option. + var sourceGroup = _dragDropGroupIdx; + var sourceOption = _dragDropOptionIdx; + var option = group[ _dragDropOptionIdx ]; + var priority = group.OptionPriority( _dragDropGroupIdx ); + _delayedActions.Enqueue( () => + { + Penumbra.ModManager.DeleteOption( _mod, sourceGroup, sourceOption ); + Penumbra.ModManager.AddOption( _mod, groupIdx, option, priority ); + } ); } } @@ -299,14 +368,14 @@ public partial class ConfigWindow } ImGui.TableNextColumn(); - if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Trash.ToIconString(), ImGui.GetFrameHeight() * Vector2.One, + if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Trash.ToIconString(), _window._iconButtonSize, "Delete this option.\nHold Control while clicking to delete.", !ImGui.GetIO().KeyCtrl, true ) ) { _delayedActions.Enqueue( () => Penumbra.ModManager.DeleteOption( _mod, groupIdx, optionIdx ) ); } ImGui.TableNextColumn(); - if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Edit.ToIconString(), ImGui.GetFrameHeight() * Vector2.One, + if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Edit.ToIconString(), _window._iconButtonSize, "Edit this option.", false, true ) ) { _window.SubModPopup.Activate( _mod, groupIdx, optionIdx ); diff --git a/Penumbra/UI/ConfigWindow.ModPanel.Settings.cs b/Penumbra/UI/ConfigWindow.ModPanel.Settings.cs index 7ab3b496..f4168fb3 100644 --- a/Penumbra/UI/ConfigWindow.ModPanel.Settings.cs +++ b/Penumbra/UI/ConfigWindow.ModPanel.Settings.cs @@ -1,4 +1,3 @@ -using System.Linq; using System.Numerics; using Dalamud.Interface; using ImGuiNET; @@ -16,7 +15,7 @@ public partial class ConfigWindow { private partial class ModPanel { - private ModSettings _settings = null!; + private ModSettings _settings = null!; private ModCollection _collection = null!; private bool _emptySetting; private bool _inherited; @@ -92,6 +91,7 @@ public partial class ConfigWindow var enabled = _settings.Enabled; if( ImGui.Checkbox( "Enabled", ref enabled ) ) { + Penumbra.ModManager.NewMods.Remove( _mod ); Penumbra.CollectionManager.Current.SetModState( _mod.Index, enabled ); } } @@ -132,7 +132,7 @@ public partial class ConfigWindow } var scroll = ImGui.GetScrollMaxY() > 0 ? ImGui.GetStyle().ScrollbarSize : 0; - ImGui.SameLine( ImGui.GetWindowWidth() - ImGui.CalcTextSize( text ).X - ImGui.GetStyle().FramePadding.X * 2 - scroll); + ImGui.SameLine( ImGui.GetWindowWidth() - ImGui.CalcTextSize( text ).X - ImGui.GetStyle().FramePadding.X * 2 - scroll ); if( ImGui.Button( text ) ) { Penumbra.CollectionManager.Current.SetModInheritance( _mod.Index, true ); diff --git a/Penumbra/UI/ConfigWindow.ModPanel.Tabs.cs b/Penumbra/UI/ConfigWindow.ModPanel.Tabs.cs index 556be8dc..f8982852 100644 --- a/Penumbra/UI/ConfigWindow.ModPanel.Tabs.cs +++ b/Penumbra/UI/ConfigWindow.ModPanel.Tabs.cs @@ -1,6 +1,4 @@ using System; -using System.ComponentModel.Design; -using System.Linq; using System.Numerics; using ImGuiNET; using OtterGui; @@ -112,6 +110,7 @@ public partial class ConfigWindow { return; } + var conflicts = Penumbra.CollectionManager.Current.ModConflicts( _mod.Index ); Mod? oldBadMod = null; using var indent = ImRaii.PushIndent( 0f ); @@ -124,19 +123,20 @@ public partial class ConfigWindow { indent.Pop( 30f ); } - + if( ImGui.Selectable( badMod.Name ) ) { _window._selector.SelectByValue( badMod ); } - + ImGui.SameLine(); - using var color = ImRaii.PushColor( ImGuiCol.Text, conflict.Mod1Priority ? ColorId.HandledConflictMod.Value() : ColorId.ConflictingMod.Value() ); + using var color = ImRaii.PushColor( ImGuiCol.Text, + conflict.Mod1Priority ? ColorId.HandledConflictMod.Value() : ColorId.ConflictingMod.Value() ); ImGui.Text( $"(Priority {Penumbra.CollectionManager.Current[ conflict.Mod2 ].Settings!.Priority})" ); - + indent.Push( 30f ); } - + if( conflict.Data is Utf8GamePath p ) { unsafe @@ -148,7 +148,7 @@ public partial class ConfigWindow { ImGui.Selectable( m.Manipulation?.ToString() ?? string.Empty ); } - + oldBadMod = badMod; } } diff --git a/Penumbra/UI/ConfigWindow.ModsTab.Details.cs b/Penumbra/UI/ConfigWindow.ModsTab.Details.cs deleted file mode 100644 index 6dfb603f..00000000 --- a/Penumbra/UI/ConfigWindow.ModsTab.Details.cs +++ /dev/null @@ -1,714 +0,0 @@ -//using System.IO; -//using System.Linq; -//using System.Numerics; -//using Dalamud.Interface; -//using FFXIVClientStructs.FFXIV.Client.UI.Misc; -//using ImGuiNET; -//using Lumina.Data.Parsing; -//using Lumina.Excel.GeneratedSheets; -//using Penumbra.GameData.ByteString; -//using Penumbra.GameData.Enums; -//using Penumbra.GameData.Util; -//using Penumbra.Meta; -//using Penumbra.Meta.Manipulations; -//using Penumbra.Mods; -//using Penumbra.UI.Custom; -//using Penumbra.Util; -//using ImGui = ImGuiNET.ImGui; -// -//namespace Penumbra.UI; -// -//public partial class SettingsInterface -//{ -// private partial class PluginDetails -// { -// private const string LabelPluginDetails = "PenumbraPluginDetails"; -// private const string LabelAboutTab = "About"; -// private const string LabelChangedItemsTab = "Changed Items"; -// private const string LabelChangedItemsHeader = "##changedItems"; -// private const string LabelConflictsTab = "Mod Conflicts"; -// private const string LabelConflictsHeader = "##conflicts"; -// private const string LabelFileSwapTab = "File Swaps"; -// private const string LabelFileSwapHeader = "##fileSwaps"; -// private const string LabelFileListTab = "Files"; -// private const string LabelFileListHeader = "##fileList"; -// private const string LabelGroupSelect = "##groupSelect"; -// private const string LabelOptionSelect = "##optionSelect"; -// private const string LabelConfigurationTab = "Configuration"; -// -// private const string TooltipFilesTab = -// "Green files replace their standard game path counterpart (not in any option) or are in all options of a Single-Select option.\n" -// + "Yellow files are restricted to some options."; -// -// private const float OptionSelectionWidth = 140f; -// private const float CheckMarkSize = 50f; -// private const uint ColorDarkGreen = 0xFF00A000; -// private const uint ColorGreen = 0xFF00C800; -// private const uint ColorYellow = 0xFF00C8C8; -// private const uint ColorDarkRed = 0xFF0000A0; -// private const uint ColorRed = 0xFF0000C8; -// -// -// private bool _editMode; -// private int _selectedGroupIndex; -// private OptionGroup? _selectedGroup; -// private int _selectedOptionIndex; -// private ConfigModule.Option? _selectedOption; -// private string _currentGamePaths = ""; -// -// private (FullPath name, bool selected, uint color, Utf8RelPath relName)[]? _fullFilenameList; -// -// private readonly Selector _selector; -// private readonly SettingsInterface _base; -// -// private void SelectGroup( int idx ) -// { -// // Not using the properties here because we need it to be not null forgiving in this case. -// var numGroups = _selector.Mod?.Data.Meta.Groups.Count ?? 0; -// _selectedGroupIndex = idx; -// if( _selectedGroupIndex >= numGroups ) -// { -// _selectedGroupIndex = 0; -// } -// -// if( numGroups > 0 ) -// { -// _selectedGroup = Meta.Groups.ElementAt( _selectedGroupIndex ).Value; -// } -// else -// { -// _selectedGroup = null; -// } -// } -// -// private void SelectGroup() -// => SelectGroup( _selectedGroupIndex ); -// -// private void SelectOption( int idx ) -// { -// _selectedOptionIndex = idx; -// if( _selectedOptionIndex >= _selectedGroup?.Options.Count ) -// { -// _selectedOptionIndex = 0; -// } -// -// if( _selectedGroup?.Options.Count > 0 ) -// { -// _selectedOption = ( ( OptionGroup )_selectedGroup ).Options[ _selectedOptionIndex ]; -// } -// else -// { -// _selectedOption = null; -// } -// } -// -// private void SelectOption() -// => SelectOption( _selectedOptionIndex ); -// -// public void ResetState() -// { -// _fullFilenameList = null; -// SelectGroup(); -// SelectOption(); -// } -// -// public PluginDetails( SettingsInterface ui, Selector s ) -// { -// _base = ui; -// _selector = s; -// ResetState(); -// } -// -// // This is only drawn when we have a mod selected, so we can forgive nulls. -// private FullMod Mod -// => _selector.Mod!; -// -// private ModMeta Meta -// => Mod.Data.Meta; -// -// private void DrawAboutTab() -// { -// if( !_editMode && Meta.Description.Length == 0 ) -// { -// return; -// } -// -// if( !ImGui.BeginTabItem( LabelAboutTab ) ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTabItem ); -// -// var desc = Meta.Description; -// var flags = _editMode -// ? ImGuiInputTextFlags.EnterReturnsTrue | ImGuiInputTextFlags.CtrlEnterForNewLine -// : ImGuiInputTextFlags.ReadOnly; -// -// if( _editMode ) -// { -// if( ImGui.InputTextMultiline( LabelDescEdit, ref desc, 1 << 16, -// AutoFillSize, flags ) ) -// { -// Meta.Description = desc; -// _selector.SaveCurrentMod(); -// } -// -// ImGuiCustom.HoverTooltip( TooltipAboutEdit ); -// } -// else -// { -// ImGui.TextWrapped( desc ); -// } -// } -// -// private void DrawChangedItemsTab() -// { -// if( Mod.Data.ChangedItems.Count == 0 || !ImGui.BeginTabItem( LabelChangedItemsTab ) ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTabItem ); -// -// if( !ImGui.BeginListBox( LabelChangedItemsHeader, AutoFillSize ) ) -// { -// return; -// } -// -// raii.Push( ImGui.EndListBox ); -// foreach( var (name, data) in Mod.Data.ChangedItems ) -// { -// _base.DrawChangedItem( name, data ); -// } -// } -// -// private void DrawConflictTab() -// { -// var conflicts = Penumbra.CollectionManager.Current.ModConflicts( Mod.Data.Index ).ToList(); -// if( conflicts.Count == 0 || !ImGui.BeginTabItem( LabelConflictsTab ) ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTabItem ); -// -// ImGui.SetNextItemWidth( -1 ); -// if( !ImGui.BeginListBox( LabelConflictsHeader, AutoFillSize ) ) -// { -// return; -// } -// -// raii.Push( ImGui.EndListBox ); -// using var indent = ImGuiRaii.PushIndent( 0 ); -// Mods.Mod? oldBadMod = null; -// foreach( var conflict in conflicts ) -// { -// var badMod = Penumbra.ModManager[ conflict.Mod2 ]; -// if( badMod != oldBadMod ) -// { -// if( oldBadMod != null ) -// { -// indent.Pop( 30f ); -// } -// -// if( ImGui.Selectable( badMod.Meta.Name ) ) -// { -// _selector.SelectModByDir( badMod.BasePath.Name ); -// } -// -// ImGui.SameLine(); -// using var color = ImGuiRaii.PushColor( ImGuiCol.Text, conflict.Mod1Priority ? ColorGreen : ColorRed ); -// ImGui.Text( $"(Priority {Penumbra.CollectionManager.Current[ conflict.Mod2 ].Settings!.Priority})" ); -// -// indent.Push( 30f ); -// } -// -// if( conflict.Data is Utf8GamePath p ) -// { -// unsafe -// { -// ImGuiNative.igSelectable_Bool( p.Path.Path, 0, ImGuiSelectableFlags.None, Vector2.Zero ); -// } -// } -// else if( conflict.Data is MetaManipulation m ) -// { -// ImGui.Selectable( m.Manipulation?.ToString() ?? string.Empty ); -// } -// -// oldBadMod = badMod; -// } -// } -// -// private void DrawFileSwapTab() -// { -// if( _editMode ) -// { -// DrawFileSwapTabEdit(); -// return; -// } -// -// if( !Meta.FileSwaps.Any() || !ImGui.BeginTabItem( LabelFileSwapTab ) ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTabItem ); -// -// const ImGuiTableFlags flags = ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollX; -// -// ImGui.SetNextItemWidth( -1 ); -// if( !ImGui.BeginTable( LabelFileSwapHeader, 3, flags, AutoFillSize ) ) -// { -// return; -// } -// -// raii.Push( ImGui.EndTable ); -// -// foreach( var (source, target) in Meta.FileSwaps ) -// { -// ImGui.TableNextColumn(); -// ImGuiCustom.CopyOnClickSelectable( source.Path ); -// -// ImGui.TableNextColumn(); -// ImGuiCustom.PrintIcon( FontAwesomeIcon.LongArrowAltRight ); -// -// ImGui.TableNextColumn(); -// ImGuiCustom.CopyOnClickSelectable( target.InternalName ); -// -// ImGui.TableNextRow(); -// } -// } -// -// private void UpdateFilenameList() -// { -// if( _fullFilenameList != null ) -// { -// return; -// } -// -// _fullFilenameList = Mod.Data.Resources.ModFiles -// .Select( f => ( f, false, ColorGreen, Utf8RelPath.FromFile( f, Mod.Data.BasePath, out var p ) ? p : Utf8RelPath.Empty ) ) -// .ToArray(); -// -// if( Meta.Groups.Count == 0 ) -// { -// return; -// } -// -// for( var i = 0; i < Mod.Data.Resources.ModFiles.Count; ++i ) -// { -// foreach( var group in Meta.Groups.Values ) -// { -// var inAll = true; -// foreach( var option in group.Options ) -// { -// if( option.OptionFiles.ContainsKey( _fullFilenameList[ i ].relName ) ) -// { -// _fullFilenameList[ i ].color = ColorYellow; -// } -// else -// { -// inAll = false; -// } -// } -// -// if( inAll && group.SelectionType == SelectType.Single ) -// { -// _fullFilenameList[ i ].color = ColorGreen; -// } -// } -// } -// } -// -// private void DrawFileListTab() -// { -// if( !ImGui.BeginTabItem( LabelFileListTab ) ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTabItem ); -// ImGuiCustom.HoverTooltip( TooltipFilesTab ); -// -// ImGui.SetNextItemWidth( -1 ); -// if( ImGui.BeginListBox( LabelFileListHeader, AutoFillSize ) ) -// { -// raii.Push( ImGui.EndListBox ); -// UpdateFilenameList(); -// using var colorRaii = new ImGuiRaii.Color(); -// foreach( var (name, _, color, _) in _fullFilenameList! ) -// { -// colorRaii.Push( ImGuiCol.Text, color ); -// ImGui.Selectable( name.FullName ); -// colorRaii.Pop(); -// } -// } -// else -// { -// _fullFilenameList = null; -// } -// } -// -// private static int HandleDefaultString( Utf8GamePath[] gamePaths, out int removeFolders ) -// { -// removeFolders = 0; -// var defaultIndex = gamePaths.IndexOf( p => p.Path.StartsWith( DefaultUtf8GamePath ) ); -// if( defaultIndex < 0 ) -// { -// return defaultIndex; -// } -// -// var path = gamePaths[ defaultIndex ].Path; -// if( path.Length == TextDefaultGamePath.Length ) -// { -// return defaultIndex; -// } -// -// if( path[ TextDefaultGamePath.Length ] != ( byte )'-' -// || !int.TryParse( path.Substring( TextDefaultGamePath.Length + 1 ).ToString(), out removeFolders ) ) -// { -// return -1; -// } -// -// return defaultIndex; -// } -// -// private void HandleSelectedFilesButton( bool remove ) -// { -// if( _selectedOption == null ) -// { -// return; -// } -// -// var option = ( ConfigModule.Option )_selectedOption; -// -// var gamePaths = _currentGamePaths.Split( ';' ) -// .Select( p => Utf8GamePath.FromString( p, out var path, false ) ? path : Utf8GamePath.Empty ).Where( p => !p.IsEmpty ).ToArray(); -// if( gamePaths.Length == 0 ) -// { -// return; -// } -// -// var defaultIndex = HandleDefaultString( gamePaths, out var removeFolders ); -// var changed = false; -// for( var i = 0; i < Mod.Data.Resources.ModFiles.Count; ++i ) -// { -// if( !_fullFilenameList![ i ].selected ) -// { -// continue; -// } -// -// _fullFilenameList![ i ].selected = false; -// var relName = _fullFilenameList[ i ].relName; -// if( defaultIndex >= 0 ) -// { -// gamePaths[ defaultIndex ] = relName.ToGamePath( removeFolders ); -// } -// -// if( remove && option.OptionFiles.TryGetValue( relName, out var setPaths ) ) -// { -// if( setPaths.RemoveWhere( p => gamePaths.Contains( p ) ) > 0 ) -// { -// changed = true; -// } -// -// if( setPaths.Count == 0 && option.OptionFiles.Remove( relName ) ) -// { -// changed = true; -// } -// } -// else -// { -// changed = gamePaths -// .Aggregate( changed, ( current, gamePath ) => current | option.AddFile( relName, gamePath ) ); -// } -// } -// -// if( changed ) -// { -// _fullFilenameList = null; -// _selector.SaveCurrentMod(); -// var idx = Penumbra.ModManager.Mods.IndexOf( Mod.Data ); -// // Since files may have changed, we need to recompute effective files. -// foreach( var collection in Penumbra.CollectionManager -// .Where( c => c.HasCache && c[ idx ].Settings?.Enabled == true ) ) -// { -// collection.CalculateEffectiveFileList( false, collection == Penumbra.CollectionManager.Default ); -// } -// -// // If the mod is enabled in the current collection, its conflicts may have changed. -// if( Mod.Settings.Enabled ) -// { -// _selector.Cache.TriggerFilterReset(); -// } -// } -// } -// -// private void DrawAddToGroupButton() -// { -// if( ImGui.Button( ButtonAddToGroup ) ) -// { -// HandleSelectedFilesButton( false ); -// } -// } -// -// private void DrawRemoveFromGroupButton() -// { -// if( ImGui.Button( ButtonRemoveFromGroup ) ) -// { -// HandleSelectedFilesButton( true ); -// } -// } -// -// private void DrawGamePathInput() -// { -// ImGui.SetNextItemWidth( -1 ); -// ImGui.InputTextWithHint( LabelGamePathsEditBox, "Hover for help...", ref _currentGamePaths, -// 128 ); -// ImGuiCustom.HoverTooltip( TooltipGamePathsEdit ); -// } -// -// private void DrawGroupRow() -// { -// if( _selectedGroup == null ) -// { -// SelectGroup(); -// } -// -// if( _selectedOption == null ) -// { -// SelectOption(); -// } -// -// if( !DrawEditGroupSelector() ) -// { -// return; -// } -// -// ImGui.SameLine(); -// if( !DrawEditOptionSelector() ) -// { -// return; -// } -// -// ImGui.SameLine(); -// DrawAddToGroupButton(); -// ImGui.SameLine(); -// DrawRemoveFromGroupButton(); -// ImGui.SameLine(); -// DrawGamePathInput(); -// } -// -// private void DrawFileAndGamePaths( int idx ) -// { -// void Selectable( uint colorNormal, uint colorReplace ) -// { -// var loc = _fullFilenameList![ idx ].color; -// if( loc == colorNormal ) -// { -// loc = colorReplace; -// } -// -// using var colors = ImGuiRaii.PushColor( ImGuiCol.Text, loc ); -// ImGui.Selectable( _fullFilenameList[ idx ].name.FullName, ref _fullFilenameList[ idx ].selected ); -// } -// -// const float indentWidth = 30f; -// if( _selectedOption == null ) -// { -// Selectable( 0, ColorGreen ); -// return; -// } -// -// var fileName = _fullFilenameList![ idx ].relName; -// var optionFiles = ( ( ConfigModule.Option )_selectedOption ).OptionFiles; -// if( optionFiles.TryGetValue( fileName, out var gamePaths ) ) -// { -// Selectable( 0, ColorGreen ); -// -// using var indent = ImGuiRaii.PushIndent( indentWidth ); -// foreach( var gamePath in gamePaths.ToArray() ) -// { -// var tmp = gamePath.ToString(); -// var old = tmp; -// if( ImGui.InputText( $"##{fileName}_{gamePath}", ref tmp, 128, ImGuiInputTextFlags.EnterReturnsTrue ) -// && tmp != old ) -// { -// gamePaths.Remove( gamePath ); -// if( tmp.Length > 0 && Utf8GamePath.FromString( tmp, out var p, true ) ) -// { -// gamePaths.Add( p ); -// } -// else if( gamePaths.Count == 0 ) -// { -// optionFiles.Remove( fileName ); -// } -// -// _selector.SaveCurrentMod(); -// _selector.ReloadCurrentMod(); -// } -// } -// } -// else -// { -// Selectable( ColorYellow, ColorRed ); -// } -// } -// -// private void DrawMultiSelectorCheckBox( OptionGroup group, int idx, int flag, string label ) -// { -// var enabled = ( flag & ( 1 << idx ) ) != 0; -// var oldEnabled = enabled; -// if( ImGui.Checkbox( label, ref enabled ) && oldEnabled != enabled ) -// { -// Penumbra.CollectionManager.Current.SetModSetting( Mod.Data.Index, group.GroupName, -// Mod.Settings.Settings[ group.GroupName ] ^ ( 1 << idx ) ); -// // If the mod is enabled, recalculate files and filters. -// if( Mod.Settings.Enabled ) -// { -// _selector.Cache.TriggerFilterReset(); -// } -// } -// } -// -// private void DrawMultiSelector( OptionGroup group ) -// { -// if( group.Options.Count == 0 ) -// { -// return; -// } -// -// ImGuiCustom.BeginFramedGroup( group.GroupName ); -// using var raii = ImGuiRaii.DeferredEnd( ImGuiCustom.EndFramedGroup ); -// for( var i = 0; i < group.Options.Count; ++i ) -// { -// DrawMultiSelectorCheckBox( group, i, Mod.Settings.Settings[ group.GroupName ], -// $"{group.Options[ i ].OptionName}##{group.GroupName}" ); -// } -// } -// -// private void DrawSingleSelector( OptionGroup group ) -// { -// if( group.Options.Count < 2 ) -// { -// return; -// } -// -// var code = Mod.Settings.Settings[ group.GroupName ]; -// if( ImGui.Combo( group.GroupName, ref code -// , group.Options.Select( x => x.OptionName ).ToArray(), group.Options.Count ) -// && code != Mod.Settings.Settings[ group.GroupName ] ) -// { -// Penumbra.CollectionManager.Current.SetModSetting( Mod.Data.Index, group.GroupName, code ); -// if( Mod.Settings.Enabled ) -// { -// _selector.Cache.TriggerFilterReset(); -// } -// } -// } -// -// private void DrawGroupSelectors() -// { -// foreach( var g in Meta.Groups.Values.Where( g => g.SelectionType == SelectType.Single ) ) -// { -// DrawSingleSelector( g ); -// } -// -// foreach( var g in Meta.Groups.Values.Where( g => g.SelectionType == SelectType.Multi ) ) -// { -// DrawMultiSelector( g ); -// } -// } -// -// private void DrawConfigurationTab() -// { -// if( !_editMode && !Meta.HasGroupsWithConfig || !ImGui.BeginTabItem( LabelConfigurationTab ) ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTabItem ); -// if( _editMode ) -// { -// DrawGroupSelectorsEdit(); -// } -// else -// { -// DrawGroupSelectors(); -// } -// } -// -// private void DrawMetaManipulationsTab() -// { -// if( !_editMode && Mod.Data.Resources.MetaManipulations.Count == 0 || !ImGui.BeginTabItem( "Meta Manipulations" ) ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTabItem ); -// -// if( !ImGui.BeginListBox( "##MetaManipulations", AutoFillSize ) ) -// { -// return; -// } -// -// raii.Push( ImGui.EndListBox ); -// -// var manips = Mod.Data.Resources.MetaManipulations; -// var changes = false; -// if( _editMode || manips.DefaultData.Count > 0 ) -// { -// if( ImGui.CollapsingHeader( "Default" ) ) -// { -// changes = DrawMetaManipulationsTable( "##DefaultManips", manips.DefaultData, ref manips.Count ); -// } -// } -// -// foreach( var (groupName, group) in manips.GroupData ) -// { -// foreach( var (optionName, option) in group ) -// { -// if( ImGui.CollapsingHeader( $"{groupName} - {optionName}" ) ) -// { -// changes |= DrawMetaManipulationsTable( $"##{groupName}{optionName}manips", option, ref manips.Count ); -// } -// } -// } -// -// if( changes ) -// { -// Mod.Data.Resources.MetaManipulations.SaveToFile( MetaCollection.FileName( Mod.Data.BasePath ) ); -// Mod.Data.Resources.SetManipulations( Meta, Mod.Data.BasePath, false ); -// _selector.ReloadCurrentMod( true, false ); -// } -// } -// -// public void Draw( bool editMode ) -// { -// _editMode = editMode; -// if( !ImGui.BeginTabBar( LabelPluginDetails ) ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTabBar ); -// DrawAboutTab(); -// DrawChangedItemsTab(); -// -// DrawConfigurationTab(); -// if( _editMode ) -// { -// DrawFileListTabEdit(); -// } -// else -// { -// DrawFileListTab(); -// } -// -// DrawFileSwapTab(); -// DrawMetaManipulationsTab(); -// DrawConflictTab(); -// } -// } -//} \ No newline at end of file diff --git a/Penumbra/UI/ConfigWindow.ModsTab.DetailsEdit.cs b/Penumbra/UI/ConfigWindow.ModsTab.DetailsEdit.cs deleted file mode 100644 index a6024185..00000000 --- a/Penumbra/UI/ConfigWindow.ModsTab.DetailsEdit.cs +++ /dev/null @@ -1,381 +0,0 @@ -//using System.Collections.Generic; -//using System.Linq; -//using System.Numerics; -//using Dalamud.Interface; -//using FFXIVClientStructs.FFXIV.Client.UI.Misc; -//using ImGuiNET; -//using Penumbra.GameData.ByteString; -//using Penumbra.GameData.Util; -//using Penumbra.Mods; -//using Penumbra.UI.Custom; -//using Penumbra.Util; -// -//namespace Penumbra.UI; -// -//public partial class SettingsInterface -//{ -// private partial class PluginDetails -// { -// private const string LabelDescEdit = "##descedit"; -// private const string LabelNewSingleGroupEdit = "##newSingleGroup"; -// private const string LabelNewMultiGroup = "##newMultiGroup"; -// private const string LabelGamePathsEditBox = "##gamePathsEdit"; -// private const string ButtonAddToGroup = "Add to Group"; -// private const string ButtonRemoveFromGroup = "Remove from Group"; -// private const string TooltipAboutEdit = "Use Ctrl+Enter for newlines."; -// private const string TextNoOptionAvailable = "[Not Available]"; -// private const string TextDefaultGamePath = "default"; -// private static readonly Utf8String DefaultUtf8GamePath = Utf8String.FromStringUnsafe( TextDefaultGamePath, true ); -// private const char GamePathsSeparator = ';'; -// -// private static readonly string TooltipFilesTabEdit = -// $"{TooltipFilesTab}\n" -// + $"Red Files are replaced in another group or a different option in this group, but not contained in the current option."; -// -// private static readonly string TooltipGamePathsEdit = -// $"Enter all game paths to add or remove, separated by '{GamePathsSeparator}'.\n" -// + $"Use '{TextDefaultGamePath}' to add the original file path." -// + $"Use '{TextDefaultGamePath}-#' to skip the first # relative directories."; -// -// private const float MultiEditBoxWidth = 300f; -// -// private bool DrawEditGroupSelector() -// { -// ImGui.SetNextItemWidth( OptionSelectionWidth * ImGuiHelpers.GlobalScale ); -// if( Meta!.Groups.Count == 0 ) -// { -// ImGui.Combo( LabelGroupSelect, ref _selectedGroupIndex, TextNoOptionAvailable, 1 ); -// return false; -// } -// -// if( ImGui.Combo( LabelGroupSelect, ref _selectedGroupIndex -// , Meta.Groups.Values.Select( g => g.GroupName ).ToArray() -// , Meta.Groups.Count ) ) -// { -// SelectGroup(); -// SelectOption( 0 ); -// } -// -// return true; -// } -// -// private bool DrawEditOptionSelector() -// { -// ImGui.SameLine(); -// ImGui.SetNextItemWidth( OptionSelectionWidth ); -// if( ( _selectedGroup?.Options.Count ?? 0 ) == 0 ) -// { -// ImGui.Combo( LabelOptionSelect, ref _selectedOptionIndex, TextNoOptionAvailable, 1 ); -// return false; -// } -// -// var group = ( OptionGroup )_selectedGroup!; -// if( ImGui.Combo( LabelOptionSelect, ref _selectedOptionIndex, group.Options.Select( o => o.OptionName ).ToArray(), -// group.Options.Count ) ) -// { -// SelectOption(); -// } -// -// return true; -// } -// -// private void DrawFileListTabEdit() -// { -// if( ImGui.BeginTabItem( LabelFileListTab ) ) -// { -// UpdateFilenameList(); -// if( ImGui.IsItemHovered() ) -// { -// ImGui.SetTooltip( _editMode ? TooltipFilesTabEdit : TooltipFilesTab ); -// } -// -// ImGui.SetNextItemWidth( -1 ); -// if( ImGui.BeginListBox( LabelFileListHeader, AutoFillSize - Vector2.UnitY * 1.5f * ImGui.GetTextLineHeight() ) ) -// { -// for( var i = 0; i < Mod!.Data.Resources.ModFiles.Count; ++i ) -// { -// DrawFileAndGamePaths( i ); -// } -// } -// -// ImGui.EndListBox(); -// -// DrawGroupRow(); -// ImGui.EndTabItem(); -// } -// else -// { -// _fullFilenameList = null; -// } -// } -// -// private ImGuiRaii.EndStack DrawMultiSelectorEditBegin( OptionGroup group ) -// { -// var groupName = group.GroupName; -// if( ImGuiCustom.BeginFramedGroupEdit( ref groupName ) ) -// { -// if( Penumbra.ModManager.ChangeModGroup( group.GroupName, groupName, Mod.Data ) && Mod.Data.Meta.RefreshHasGroupsWithConfig() ) -// { -// _selector.Cache.TriggerFilterReset(); -// } -// } -// -// return ImGuiRaii.DeferredEnd( ImGuiCustom.EndFramedGroup ); -// } -// -// private void DrawMultiSelectorEditAdd( OptionGroup group, float nameBoxStart ) -// { -// var newOption = ""; -// ImGui.SetCursorPosX( nameBoxStart ); -// ImGui.SetNextItemWidth( MultiEditBoxWidth * ImGuiHelpers.GlobalScale ); -// if( ImGui.InputTextWithHint( $"##new_{group.GroupName}_l", "Add new option...", ref newOption, 64, -// ImGuiInputTextFlags.EnterReturnsTrue ) -// && newOption.Length != 0 ) -// { -// group.Options.Add( new ConfigModule.Option() -// { OptionName = newOption, OptionDesc = "", OptionFiles = new Dictionary< Utf8RelPath, HashSet< Utf8GamePath > >() } ); -// _selector.SaveCurrentMod(); -// if( Mod!.Data.Meta.RefreshHasGroupsWithConfig() ) -// { -// _selector.Cache.TriggerFilterReset(); -// } -// } -// } -// -// private void DrawMultiSelectorEdit( OptionGroup group ) -// { -// var nameBoxStart = CheckMarkSize; -// var flag = Mod!.Settings.Settings[ group.GroupName ]; -// -// using var raii = DrawMultiSelectorEditBegin( group ); -// for( var i = 0; i < group.Options.Count; ++i ) -// { -// var opt = group.Options[ i ]; -// var label = $"##{group.GroupName}_{i}"; -// DrawMultiSelectorCheckBox( group, i, flag, label ); -// -// ImGui.SameLine(); -// var newName = opt.OptionName; -// -// if( nameBoxStart == CheckMarkSize ) -// { -// nameBoxStart = ImGui.GetCursorPosX(); -// } -// -// ImGui.SetNextItemWidth( MultiEditBoxWidth * ImGuiHelpers.GlobalScale ); -// if( ImGui.InputText( $"{label}_l", ref newName, 64, ImGuiInputTextFlags.EnterReturnsTrue ) ) -// { -// if( newName.Length == 0 ) -// { -// Penumbra.ModManager.RemoveModOption( i, group, Mod.Data ); -// } -// else if( newName != opt.OptionName ) -// { -// group.Options[ i ] = new ConfigModule.Option() -// { OptionName = newName, OptionDesc = opt.OptionDesc, OptionFiles = opt.OptionFiles }; -// _selector.SaveCurrentMod(); -// } -// -// if( Mod!.Data.Meta.RefreshHasGroupsWithConfig() ) -// { -// _selector.Cache.TriggerFilterReset(); -// } -// } -// } -// -// DrawMultiSelectorEditAdd( group, nameBoxStart ); -// } -// -// private void DrawSingleSelectorEditGroup( OptionGroup group ) -// { -// var groupName = group.GroupName; -// if( ImGui.InputText( $"##{groupName}_add", ref groupName, 64, ImGuiInputTextFlags.EnterReturnsTrue ) ) -// { -// if( Penumbra.ModManager.ChangeModGroup( group.GroupName, groupName, Mod.Data ) && Mod.Data.Meta.RefreshHasGroupsWithConfig() ) -// { -// _selector.Cache.TriggerFilterReset(); -// } -// } -// } -// -// private float DrawSingleSelectorEdit( OptionGroup group ) -// { -// var oldSetting = Mod!.Settings.Settings[ group.GroupName ]; -// var code = oldSetting; -// if( ImGuiCustom.RenameableCombo( $"##{group.GroupName}", ref code, out var newName, -// group.Options.Select( x => x.OptionName ).ToArray(), group.Options.Count ) ) -// { -// if( code == group.Options.Count ) -// { -// if( newName.Length > 0 ) -// { -// Penumbra.CollectionManager.Current.SetModSetting(Mod.Data.Index, group.GroupName, code); -// group.Options.Add( new ConfigModule.Option() -// { -// OptionName = newName, -// OptionDesc = "", -// OptionFiles = new Dictionary< Utf8RelPath, HashSet< Utf8GamePath > >(), -// } ); -// _selector.SaveCurrentMod(); -// } -// } -// else -// { -// if( newName.Length == 0 ) -// { -// Penumbra.ModManager.RemoveModOption( code, group, Mod.Data ); -// } -// else -// { -// if( newName != group.Options[ code ].OptionName ) -// { -// group.Options[ code ] = new ConfigModule.Option() -// { -// OptionName = newName, OptionDesc = group.Options[ code ].OptionDesc, -// OptionFiles = group.Options[ code ].OptionFiles, -// }; -// _selector.SaveCurrentMod(); -// } -// } -// } -// -// if( Mod.Data.Meta.RefreshHasGroupsWithConfig() ) -// { -// _selector.Cache.TriggerFilterReset(); -// } -// } -// -// ImGui.SameLine(); -// var labelEditPos = ImGui.GetCursorPosX(); -// DrawSingleSelectorEditGroup( group ); -// -// return labelEditPos; -// } -// -// private void DrawAddSingleGroupField( float labelEditPos ) -// { -// var newGroup = ""; -// ImGui.SetCursorPosX( labelEditPos ); -// if( labelEditPos == CheckMarkSize ) -// { -// ImGui.SetNextItemWidth( MultiEditBoxWidth * ImGuiHelpers.GlobalScale ); -// } -// -// if( ImGui.InputTextWithHint( LabelNewSingleGroupEdit, "Add new Single Group...", ref newGroup, 64, -// ImGuiInputTextFlags.EnterReturnsTrue ) ) -// { -// Penumbra.ModManager.ChangeModGroup( "", newGroup, Mod.Data, SelectType.Single ); -// // Adds empty group, so can not change filters. -// } -// } -// -// private void DrawAddMultiGroupField() -// { -// var newGroup = ""; -// ImGui.SetCursorPosX( CheckMarkSize ); -// ImGui.SetNextItemWidth( MultiEditBoxWidth * ImGuiHelpers.GlobalScale ); -// if( ImGui.InputTextWithHint( LabelNewMultiGroup, "Add new Multi Group...", ref newGroup, 64, -// ImGuiInputTextFlags.EnterReturnsTrue ) ) -// { -// Penumbra.ModManager.ChangeModGroup( "", newGroup, Mod.Data, SelectType.Multi ); -// // Adds empty group, so can not change filters. -// } -// } -// -// private void DrawGroupSelectorsEdit() -// { -// var labelEditPos = CheckMarkSize; -// var groups = Meta.Groups.Values.ToArray(); -// foreach( var g in groups.Where( g => g.SelectionType == SelectType.Single ) ) -// { -// labelEditPos = DrawSingleSelectorEdit( g ); -// } -// -// DrawAddSingleGroupField( labelEditPos ); -// -// foreach( var g in groups.Where( g => g.SelectionType == SelectType.Multi ) ) -// { -// DrawMultiSelectorEdit( g ); -// } -// -// DrawAddMultiGroupField(); -// } -// -// private void DrawFileSwapTabEdit() -// { -// if( !ImGui.BeginTabItem( LabelFileSwapTab ) ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTabItem ); -// -// ImGui.SetNextItemWidth( -1 ); -// if( !ImGui.BeginListBox( LabelFileSwapHeader, AutoFillSize ) ) -// { -// return; -// } -// -// raii.Push( ImGui.EndListBox ); -// -// var swaps = Meta.FileSwaps.Keys.ToArray(); -// -// ImGui.PushFont( UiBuilder.IconFont ); -// var arrowWidth = ImGui.CalcTextSize( FontAwesomeIcon.LongArrowAltRight.ToIconString() ).X; -// ImGui.PopFont(); -// -// var width = ( ImGui.GetWindowWidth() - arrowWidth - 4 * ImGui.GetStyle().ItemSpacing.X ) / 2; -// for( var idx = 0; idx < swaps.Length + 1; ++idx ) -// { -// var key = idx == swaps.Length ? Utf8GamePath.Empty : swaps[ idx ]; -// var value = idx == swaps.Length ? FullPath.Empty : Meta.FileSwaps[ key ]; -// var keyString = key.ToString(); -// var valueString = value.ToString(); -// -// ImGui.SetNextItemWidth( width ); -// if( ImGui.InputTextWithHint( $"##swapLhs_{idx}", "Enter new file to be replaced...", ref keyString, -// GamePath.MaxGamePathLength, ImGuiInputTextFlags.EnterReturnsTrue ) ) -// { -// if( Utf8GamePath.FromString( keyString, out var newKey, true ) && newKey.CompareTo( key ) != 0 ) -// { -// if( idx < swaps.Length ) -// { -// Meta.FileSwaps.Remove( key ); -// } -// -// if( !newKey.IsEmpty ) -// { -// Meta.FileSwaps[ newKey ] = value; -// } -// -// _selector.SaveCurrentMod(); -// _selector.ReloadCurrentMod(); -// } -// } -// -// if( idx >= swaps.Length ) -// { -// continue; -// } -// -// ImGui.SameLine(); -// ImGuiCustom.PrintIcon( FontAwesomeIcon.LongArrowAltRight ); -// ImGui.SameLine(); -// -// ImGui.SetNextItemWidth( width ); -// if( ImGui.InputTextWithHint( $"##swapRhs_{idx}", "Enter new replacement path...", ref valueString, -// GamePath.MaxGamePathLength, -// ImGuiInputTextFlags.EnterReturnsTrue ) ) -// { -// var newValue = new FullPath( valueString.ToLowerInvariant() ); -// if( newValue.CompareTo( value ) != 0 ) -// { -// Meta.FileSwaps[ key ] = newValue; -// _selector.SaveCurrentMod(); -// _selector.Cache.TriggerListReset(); -// } -// } -// } -// } -// } -//} \ No newline at end of file diff --git a/Penumbra/UI/ConfigWindow.ModsTab.DetailsManipulation.cs b/Penumbra/UI/ConfigWindow.ModsTab.DetailsManipulation.cs deleted file mode 100644 index f1b63d5a..00000000 --- a/Penumbra/UI/ConfigWindow.ModsTab.DetailsManipulation.cs +++ /dev/null @@ -1,773 +0,0 @@ -//using System; -//using System.Collections.Generic; -//using System.ComponentModel; -//using System.Linq; -//using System.Numerics; -//using Dalamud.Interface; -//using ImGuiNET; -//using Penumbra.GameData.Enums; -//using Penumbra.GameData.Structs; -//using Penumbra.Meta.Files; -//using Penumbra.Meta.Manipulations; -//using Penumbra.UI.Custom; -//using ObjectType = Penumbra.GameData.Enums.ObjectType; -// -//namespace Penumbra.UI; -// -//public partial class SettingsInterface -//{ -// private partial class PluginDetails -// { -// private int _newManipTypeIdx = 0; -// private ushort _newManipSetId = 0; -// private ushort _newManipSecondaryId = 0; -// private int _newManipSubrace = 0; -// private int _newManipRace = 0; -// private int _newManipAttribute = 0; -// private int _newManipEquipSlot = 0; -// private int _newManipObjectType = 0; -// private int _newManipGender = 0; -// private int _newManipBodySlot = 0; -// private ushort _newManipVariant = 0; -// -// -// private static readonly (string, EquipSlot)[] EqpEquipSlots = -// { -// ( "Head", EquipSlot.Head ), -// ( "Body", EquipSlot.Body ), -// ( "Hands", EquipSlot.Hands ), -// ( "Legs", EquipSlot.Legs ), -// ( "Feet", EquipSlot.Feet ), -// }; -// -// private static readonly (string, EquipSlot)[] EqdpEquipSlots = -// { -// EqpEquipSlots[ 0 ], -// EqpEquipSlots[ 1 ], -// EqpEquipSlots[ 2 ], -// EqpEquipSlots[ 3 ], -// EqpEquipSlots[ 4 ], -// ( "Ears", EquipSlot.Ears ), -// ( "Neck", EquipSlot.Neck ), -// ( "Wrist", EquipSlot.Wrists ), -// ( "Left Finger", EquipSlot.LFinger ), -// ( "Right Finger", EquipSlot.RFinger ), -// }; -// -// private static readonly (string, ModelRace)[] Races = -// { -// ( ModelRace.Midlander.ToName(), ModelRace.Midlander ), -// ( ModelRace.Highlander.ToName(), ModelRace.Highlander ), -// ( ModelRace.Elezen.ToName(), ModelRace.Elezen ), -// ( ModelRace.Miqote.ToName(), ModelRace.Miqote ), -// ( ModelRace.Roegadyn.ToName(), ModelRace.Roegadyn ), -// ( ModelRace.Lalafell.ToName(), ModelRace.Lalafell ), -// ( ModelRace.AuRa.ToName(), ModelRace.AuRa ), -// ( ModelRace.Viera.ToName(), ModelRace.Viera ), -// ( ModelRace.Hrothgar.ToName(), ModelRace.Hrothgar ), -// }; -// -// private static readonly (string, Gender)[] Genders = -// { -// ( Gender.Male.ToName(), Gender.Male ), -// ( Gender.Female.ToName(), Gender.Female ), -// ( Gender.MaleNpc.ToName(), Gender.MaleNpc ), -// ( Gender.FemaleNpc.ToName(), Gender.FemaleNpc ), -// }; -// -// private static readonly (string, EstManipulation.EstType)[] EstTypes = -// { -// ( "Hair", EstManipulation.EstType.Hair ), -// ( "Face", EstManipulation.EstType.Face ), -// ( "Body", EstManipulation.EstType.Body ), -// ( "Head", EstManipulation.EstType.Head ), -// }; -// -// private static readonly (string, SubRace)[] Subraces = -// { -// ( SubRace.Midlander.ToName(), SubRace.Midlander ), -// ( SubRace.Highlander.ToName(), SubRace.Highlander ), -// ( SubRace.Wildwood.ToName(), SubRace.Wildwood ), -// ( SubRace.Duskwight.ToName(), SubRace.Duskwight ), -// ( SubRace.SeekerOfTheSun.ToName(), SubRace.SeekerOfTheSun ), -// ( SubRace.KeeperOfTheMoon.ToName(), SubRace.KeeperOfTheMoon ), -// ( SubRace.Seawolf.ToName(), SubRace.Seawolf ), -// ( SubRace.Hellsguard.ToName(), SubRace.Hellsguard ), -// ( SubRace.Plainsfolk.ToName(), SubRace.Plainsfolk ), -// ( SubRace.Dunesfolk.ToName(), SubRace.Dunesfolk ), -// ( SubRace.Raen.ToName(), SubRace.Raen ), -// ( SubRace.Xaela.ToName(), SubRace.Xaela ), -// ( SubRace.Rava.ToName(), SubRace.Rava ), -// ( SubRace.Veena.ToName(), SubRace.Veena ), -// ( SubRace.Helion.ToName(), SubRace.Helion ), -// ( SubRace.Lost.ToName(), SubRace.Lost ), -// }; -// -// private static readonly (string, RspAttribute)[] RspAttributes = -// { -// ( RspAttribute.MaleMinSize.ToFullString(), RspAttribute.MaleMinSize ), -// ( RspAttribute.MaleMaxSize.ToFullString(), RspAttribute.MaleMaxSize ), -// ( RspAttribute.FemaleMinSize.ToFullString(), RspAttribute.FemaleMinSize ), -// ( RspAttribute.FemaleMaxSize.ToFullString(), RspAttribute.FemaleMaxSize ), -// ( RspAttribute.BustMinX.ToFullString(), RspAttribute.BustMinX ), -// ( RspAttribute.BustMaxX.ToFullString(), RspAttribute.BustMaxX ), -// ( RspAttribute.BustMinY.ToFullString(), RspAttribute.BustMinY ), -// ( RspAttribute.BustMaxY.ToFullString(), RspAttribute.BustMaxY ), -// ( RspAttribute.BustMinZ.ToFullString(), RspAttribute.BustMinZ ), -// ( RspAttribute.BustMaxZ.ToFullString(), RspAttribute.BustMaxZ ), -// ( RspAttribute.MaleMinTail.ToFullString(), RspAttribute.MaleMinTail ), -// ( RspAttribute.MaleMaxTail.ToFullString(), RspAttribute.MaleMaxTail ), -// ( RspAttribute.FemaleMinTail.ToFullString(), RspAttribute.FemaleMinTail ), -// ( RspAttribute.FemaleMaxTail.ToFullString(), RspAttribute.FemaleMaxTail ), -// }; -// -// -// private static readonly (string, ObjectType)[] ImcObjectType = -// { -// ( "Equipment", ObjectType.Equipment ), -// ( "Customization", ObjectType.Character ), -// ( "Weapon", ObjectType.Weapon ), -// ( "Demihuman", ObjectType.DemiHuman ), -// ( "Monster", ObjectType.Monster ), -// }; -// -// private static readonly (string, BodySlot)[] ImcBodySlots = -// { -// ( "Hair", BodySlot.Hair ), -// ( "Face", BodySlot.Face ), -// ( "Body", BodySlot.Body ), -// ( "Tail", BodySlot.Tail ), -// ( "Ears", BodySlot.Zear ), -// }; -// -// private static bool PrintCheckBox( string name, ref bool value, bool def ) -// { -// var color = value == def ? 0 : value ? ColorDarkGreen : ColorDarkRed; -// if( color == 0 ) -// { -// return ImGui.Checkbox( name, ref value ); -// } -// -// using var colorRaii = ImGuiRaii.PushColor( ImGuiCol.Text, color ); -// var ret = ImGui.Checkbox( name, ref value ); -// return ret; -// } -// -// private bool RestrictedInputInt( string name, ref ushort value, ushort min, ushort max ) -// { -// int tmp = value; -// if( ImGui.InputInt( name, ref tmp, 0, 0, _editMode ? ImGuiInputTextFlags.EnterReturnsTrue : ImGuiInputTextFlags.ReadOnly ) -// && tmp != value -// && tmp >= min -// && tmp <= max ) -// { -// value = ( ushort )tmp; -// return true; -// } -// -// return false; -// } -// -// private static bool DefaultButton< T >( string name, ref T value, T defaultValue ) where T : IComparable< T > -// { -// var compare = defaultValue.CompareTo( value ); -// var color = compare < 0 ? ColorDarkGreen : -// compare > 0 ? ColorDarkRed : ImGui.ColorConvertFloat4ToU32( ImGui.GetStyle().Colors[ ( int )ImGuiCol.Button ] ); -// -// using var colorRaii = ImGuiRaii.PushColor( ImGuiCol.Button, color ); -// var ret = ImGui.Button( name, Vector2.UnitX * 120 ) && compare != 0; -// ImGui.SameLine(); -// return ret; -// } -// -// private bool DrawInputWithDefault( string name, ref ushort value, ushort defaultValue, ushort max ) -// => DefaultButton( $"{( _editMode ? "Set to " : "" )}Default: {defaultValue}##imc{name}", ref value, defaultValue ) -// || RestrictedInputInt( name, ref value, 0, max ); -// -// private static bool CustomCombo< T >( string label, IList< (string, T) > namesAndValues, out T value, ref int idx ) -// { -// value = idx < namesAndValues.Count ? namesAndValues[ idx ].Item2 : default!; -// -// if( !ImGui.BeginCombo( label, idx < namesAndValues.Count ? namesAndValues[ idx ].Item1 : string.Empty ) ) -// { -// return false; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndCombo ); -// -// for( var i = 0; i < namesAndValues.Count; ++i ) -// { -// if( !ImGui.Selectable( $"{namesAndValues[ i ].Item1}##{label}{i}", idx == i ) || idx == i ) -// { -// continue; -// } -// -// idx = i; -// value = namesAndValues[ i ].Item2; -// return true; -// } -// -// return false; -// } -// -// private bool DrawEqpRow( int manipIdx, IList< MetaManipulation > list ) -// { -// var ret = false; -// var id = list[ manipIdx ].Eqp; -// var val = id.Entry; -// -// -// if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) ) -// { -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup ); -// var defaults = ExpandedEqpFile.GetDefault( id.SetId ); -// var attributes = Eqp.EqpAttributes[ id.Slot ]; -// -// foreach( var flag in attributes ) -// { -// var name = flag.ToLocalName(); -// var tmp = val.HasFlag( flag ); -// if( PrintCheckBox( $"{name}##manip", ref tmp, defaults.HasFlag( flag ) ) && _editMode && tmp != val.HasFlag( flag ) ) -// { -// list[ manipIdx ] = new MetaManipulation( new EqpManipulation( tmp ? val | flag : val & ~flag, id.Slot, id.SetId ) ); -// ret = true; -// } -// } -// } -// -// ImGui.Text( ObjectType.Equipment.ToString() ); -// ImGui.TableNextColumn(); -// ImGui.Text( id.SetId.ToString() ); -// ImGui.TableNextColumn(); -// ImGui.Text( id.Slot.ToString() ); -// return ret; -// } -// -// private bool DrawGmpRow( int manipIdx, IList< MetaManipulation > list ) -// { -// var ret = false; -// var id = list[ manipIdx ].Gmp; -// var val = id.Entry; -// -// if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) ) -// { -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup ); -// var defaults = ExpandedGmpFile.GetDefault( id.SetId ); -// var enabled = val.Enabled; -// var animated = val.Animated; -// var rotationA = val.RotationA; -// var rotationB = val.RotationB; -// var rotationC = val.RotationC; -// ushort unk = val.UnknownTotal; -// -// ret |= PrintCheckBox( "Visor Enabled##manip", ref enabled, defaults.Enabled ) && enabled != val.Enabled; -// ret |= PrintCheckBox( "Visor Animated##manip", ref animated, defaults.Animated ); -// ret |= DrawInputWithDefault( "Rotation A##manip", ref rotationA, defaults.RotationA, 0x3FF ); -// ret |= DrawInputWithDefault( "Rotation B##manip", ref rotationB, defaults.RotationB, 0x3FF ); -// ret |= DrawInputWithDefault( "Rotation C##manip", ref rotationC, defaults.RotationC, 0x3FF ); -// ret |= DrawInputWithDefault( "Unknown Byte##manip", ref unk, defaults.UnknownTotal, 0xFF ); -// -// if( ret && _editMode ) -// { -// list[ manipIdx ] = new MetaManipulation( new GmpManipulation( new GmpEntry -// { -// Animated = animated, Enabled = enabled, UnknownTotal = ( byte )unk, -// RotationA = rotationA, RotationB = rotationB, RotationC = rotationC, -// }, id.SetId ) ); -// } -// } -// -// ImGui.Text( ObjectType.Equipment.ToString() ); -// ImGui.TableNextColumn(); -// ImGui.Text( id.SetId.ToString() ); -// ImGui.TableNextColumn(); -// ImGui.Text( EquipSlot.Head.ToString() ); -// return ret; -// } -// -// private static (bool, bool) GetEqdpBits( EquipSlot slot, EqdpEntry entry ) -// { -// return slot switch -// { -// EquipSlot.Head => ( entry.HasFlag( EqdpEntry.Head1 ), entry.HasFlag( EqdpEntry.Head2 ) ), -// EquipSlot.Body => ( entry.HasFlag( EqdpEntry.Body1 ), entry.HasFlag( EqdpEntry.Body2 ) ), -// EquipSlot.Hands => ( entry.HasFlag( EqdpEntry.Hands1 ), entry.HasFlag( EqdpEntry.Hands2 ) ), -// EquipSlot.Legs => ( entry.HasFlag( EqdpEntry.Legs1 ), entry.HasFlag( EqdpEntry.Legs2 ) ), -// EquipSlot.Feet => ( entry.HasFlag( EqdpEntry.Feet1 ), entry.HasFlag( EqdpEntry.Feet2 ) ), -// EquipSlot.Neck => ( entry.HasFlag( EqdpEntry.Neck1 ), entry.HasFlag( EqdpEntry.Neck2 ) ), -// EquipSlot.Ears => ( entry.HasFlag( EqdpEntry.Ears1 ), entry.HasFlag( EqdpEntry.Ears2 ) ), -// EquipSlot.Wrists => ( entry.HasFlag( EqdpEntry.Wrists1 ), entry.HasFlag( EqdpEntry.Wrists2 ) ), -// EquipSlot.RFinger => ( entry.HasFlag( EqdpEntry.RingR1 ), entry.HasFlag( EqdpEntry.RingR2 ) ), -// EquipSlot.LFinger => ( entry.HasFlag( EqdpEntry.RingL1 ), entry.HasFlag( EqdpEntry.RingL2 ) ), -// _ => ( false, false ), -// }; -// } -// -// private static EqdpEntry SetEqdpBits( EquipSlot slot, EqdpEntry value, bool bit1, bool bit2 ) -// { -// switch( slot ) -// { -// case EquipSlot.Head: -// value = bit1 ? value | EqdpEntry.Head1 : value & ~EqdpEntry.Head1; -// value = bit2 ? value | EqdpEntry.Head2 : value & ~EqdpEntry.Head2; -// return value; -// case EquipSlot.Body: -// value = bit1 ? value | EqdpEntry.Body1 : value & ~EqdpEntry.Body1; -// value = bit2 ? value | EqdpEntry.Body2 : value & ~EqdpEntry.Body2; -// return value; -// case EquipSlot.Hands: -// value = bit1 ? value | EqdpEntry.Hands1 : value & ~EqdpEntry.Hands1; -// value = bit2 ? value | EqdpEntry.Hands2 : value & ~EqdpEntry.Hands2; -// return value; -// case EquipSlot.Legs: -// value = bit1 ? value | EqdpEntry.Legs1 : value & ~EqdpEntry.Legs1; -// value = bit2 ? value | EqdpEntry.Legs2 : value & ~EqdpEntry.Legs2; -// return value; -// case EquipSlot.Feet: -// value = bit1 ? value | EqdpEntry.Feet1 : value & ~EqdpEntry.Feet1; -// value = bit2 ? value | EqdpEntry.Feet2 : value & ~EqdpEntry.Feet2; -// return value; -// case EquipSlot.Neck: -// value = bit1 ? value | EqdpEntry.Neck1 : value & ~EqdpEntry.Neck1; -// value = bit2 ? value | EqdpEntry.Neck2 : value & ~EqdpEntry.Neck2; -// return value; -// case EquipSlot.Ears: -// value = bit1 ? value | EqdpEntry.Ears1 : value & ~EqdpEntry.Ears1; -// value = bit2 ? value | EqdpEntry.Ears2 : value & ~EqdpEntry.Ears2; -// return value; -// case EquipSlot.Wrists: -// value = bit1 ? value | EqdpEntry.Wrists1 : value & ~EqdpEntry.Wrists1; -// value = bit2 ? value | EqdpEntry.Wrists2 : value & ~EqdpEntry.Wrists2; -// return value; -// case EquipSlot.RFinger: -// value = bit1 ? value | EqdpEntry.RingR1 : value & ~EqdpEntry.RingR1; -// value = bit2 ? value | EqdpEntry.RingR2 : value & ~EqdpEntry.RingR2; -// return value; -// case EquipSlot.LFinger: -// value = bit1 ? value | EqdpEntry.RingL1 : value & ~EqdpEntry.RingL1; -// value = bit2 ? value | EqdpEntry.RingL2 : value & ~EqdpEntry.RingL2; -// return value; -// } -// -// return value; -// } -// -// private bool DrawEqdpRow( int manipIdx, IList< MetaManipulation > list ) -// { -// var ret = false; -// var id = list[ manipIdx ].Eqdp; -// var val = id.Entry; -// -// if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) ) -// { -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup ); -// var defaults = ExpandedEqdpFile.GetDefault( id.FileIndex(), id.SetId ); -// var (bit1, bit2) = GetEqdpBits( id.Slot, val ); -// var (defBit1, defBit2) = GetEqdpBits( id.Slot, defaults ); -// -// ret |= PrintCheckBox( "Bit 1##manip", ref bit1, defBit1 ); -// ret |= PrintCheckBox( "Bit 2##manip", ref bit2, defBit2 ); -// -// if( ret && _editMode ) -// { -// list[ manipIdx ] = new MetaManipulation( new EqdpManipulation( SetEqdpBits( id.Slot, val, bit1, bit2 ), id.Slot, id.Gender, -// id.Race, id.SetId ) ); -// } -// } -// -// ImGui.Text( id.Slot.IsAccessory() -// ? ObjectType.Accessory.ToString() -// : ObjectType.Equipment.ToString() ); -// ImGui.TableNextColumn(); -// ImGui.Text( id.SetId.ToString() ); -// ImGui.TableNextColumn(); -// ImGui.Text( id.Slot.ToString() ); -// ImGui.TableNextColumn(); -// ImGui.Text( id.Race.ToString() ); -// ImGui.TableNextColumn(); -// ImGui.Text( id.Gender.ToString() ); -// return ret; -// } -// -// private bool DrawEstRow( int manipIdx, IList< MetaManipulation > list ) -// { -// var ret = false; -// var id = list[ manipIdx ].Est; -// var val = id.Entry; -// -// if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) ) -// { -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup ); -// var defaults = EstFile.GetDefault( id.Slot, Names.CombinedRace( id.Gender, id.Race ), id.SetId ); -// if( DrawInputWithDefault( "No Idea what this does!##manip", ref val, defaults, ushort.MaxValue ) && _editMode ) -// { -// list[ manipIdx ] = new MetaManipulation( new EstManipulation( id.Gender, id.Race, id.Slot, id.SetId, val ) ); -// ret = true; -// } -// } -// -// ImGui.Text( id.Slot.ToString() ); -// ImGui.TableNextColumn(); -// ImGui.Text( id.SetId.ToString() ); -// ImGui.TableNextColumn(); -// ImGui.TableNextColumn(); -// ImGui.Text( id.Race.ToName() ); -// ImGui.TableNextColumn(); -// ImGui.Text( id.Gender.ToName() ); -// -// return ret; -// } -// -// private bool DrawImcRow( int manipIdx, IList< MetaManipulation > list ) -// { -// var ret = false; -// var id = list[ manipIdx ].Imc; -// var val = id.Entry; -// -// if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) ) -// { -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup ); -// var defaults = new ImcFile( id.GamePath() ).GetEntry( ImcFile.PartIndex( id.EquipSlot ), id.Variant ); -// ushort materialId = val.MaterialId; -// ushort vfxId = val.VfxId; -// ushort decalId = val.DecalId; -// var soundId = ( ushort )val.SoundId; -// var attributeMask = val.AttributeMask; -// var materialAnimationId = ( ushort )val.MaterialAnimationId; -// ret |= DrawInputWithDefault( "Material Id", ref materialId, defaults.MaterialId, byte.MaxValue ); -// ret |= DrawInputWithDefault( "Vfx Id", ref vfxId, defaults.VfxId, byte.MaxValue ); -// ret |= DrawInputWithDefault( "Decal Id", ref decalId, defaults.DecalId, byte.MaxValue ); -// ret |= DrawInputWithDefault( "Sound Id", ref soundId, defaults.SoundId, 0x3F ); -// ret |= DrawInputWithDefault( "Attribute Mask", ref attributeMask, defaults.AttributeMask, 0x3FF ); -// ret |= DrawInputWithDefault( "Material Animation Id", ref materialAnimationId, defaults.MaterialAnimationId, -// byte.MaxValue ); -// -// if( ret && _editMode ) -// { -// var value = new ImcEntry( ( byte )materialId, ( byte )decalId, attributeMask, ( byte )soundId, ( byte )vfxId, -// ( byte )materialAnimationId ); -// list[ manipIdx ] = new MetaManipulation( new ImcManipulation( id, value ) ); -// } -// } -// -// ImGui.Text( id.ObjectType.ToString() ); -// ImGui.TableNextColumn(); -// ImGui.Text( id.PrimaryId.ToString() ); -// ImGui.TableNextColumn(); -// if( id.ObjectType is ObjectType.Accessory or ObjectType.Equipment ) -// { -// ImGui.Text( id.ObjectType is ObjectType.Equipment or ObjectType.Accessory -// ? id.EquipSlot.ToString() -// : id.BodySlot.ToString() ); -// } -// -// ImGui.TableNextColumn(); -// ImGui.TableNextColumn(); -// ImGui.TableNextColumn(); -// if( id.ObjectType != ObjectType.Equipment -// && id.ObjectType != ObjectType.Accessory ) -// { -// ImGui.Text( id.SecondaryId.ToString() ); -// } -// -// ImGui.TableNextColumn(); -// ImGui.Text( id.Variant.ToString() ); -// return ret; -// } -// -// private bool DrawRspRow( int manipIdx, IList< MetaManipulation > list ) -// { -// var ret = false; -// var id = list[ manipIdx ].Rsp; -// var defaults = CmpFile.GetDefault( id.SubRace, id.Attribute ); -// var val = id.Entry; -// if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) ) -// { -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup ); -// if( DefaultButton( -// $"{( _editMode ? "Set to " : "" )}Default: {defaults:F3}##scaleManip", ref val, defaults ) -// && _editMode ) -// { -// list[ manipIdx ] = new MetaManipulation( new RspManipulation( id.SubRace, id.Attribute, val ) ); -// ret = true; -// } -// -// ImGui.SetNextItemWidth( 50 * ImGuiHelpers.GlobalScale ); -// if( ImGui.InputFloat( "Scale###manip", ref val, 0, 0, "%.3f", -// _editMode ? ImGuiInputTextFlags.EnterReturnsTrue : ImGuiInputTextFlags.ReadOnly ) -// && val >= 0 -// && val <= 5 -// && _editMode ) -// { -// list[ manipIdx ] = new MetaManipulation( new RspManipulation( id.SubRace, id.Attribute, val ) ); -// ret = true; -// } -// } -// -// ImGui.Text( id.Attribute.ToUngenderedString() ); -// ImGui.TableNextColumn(); -// ImGui.TableNextColumn(); -// ImGui.TableNextColumn(); -// ImGui.Text( id.SubRace.ToString() ); -// ImGui.TableNextColumn(); -// ImGui.Text( id.Attribute.ToGender().ToString() ); -// return ret; -// } -// -// private bool DrawManipulationRow( ref int manipIdx, IList< MetaManipulation > list, ref int count ) -// { -// var type = list[ manipIdx ].ManipulationType; -// -// if( _editMode ) -// { -// ImGui.TableNextColumn(); -// using var font = ImGuiRaii.PushFont( UiBuilder.IconFont ); -// if( ImGui.Button( $"{FontAwesomeIcon.Trash.ToIconString()}##manipDelete{manipIdx}" ) ) -// { -// list.RemoveAt( manipIdx ); -// ImGui.TableNextRow(); -// --manipIdx; -// --count; -// return true; -// } -// } -// -// ImGui.TableNextColumn(); -// ImGui.Text( type.ToString() ); -// ImGui.TableNextColumn(); -// -// var changes = false; -// switch( type ) -// { -// case MetaManipulation.Type.Eqp: -// changes = DrawEqpRow( manipIdx, list ); -// ImGui.TableSetColumnIndex( 9 ); -// if( ImGui.Selectable( $"{list[ manipIdx ].Eqp.Entry}##{manipIdx}" ) ) -// { -// ImGui.OpenPopup( $"##MetaPopup{manipIdx}" ); -// } -// -// break; -// case MetaManipulation.Type.Gmp: -// changes = DrawGmpRow( manipIdx, list ); -// ImGui.TableSetColumnIndex( 9 ); -// if( ImGui.Selectable( $"{list[ manipIdx ].Gmp.Entry.Value}##{manipIdx}" ) ) -// { -// ImGui.OpenPopup( $"##MetaPopup{manipIdx}" ); -// } -// -// break; -// case MetaManipulation.Type.Eqdp: -// changes = DrawEqdpRow( manipIdx, list ); -// ImGui.TableSetColumnIndex( 9 ); -// var (bit1, bit2) = GetEqdpBits( list[ manipIdx ].Eqdp.Slot, list[ manipIdx ].Eqdp.Entry ); -// if( ImGui.Selectable( $"{bit1} {bit2}##{manipIdx}" ) ) -// { -// ImGui.OpenPopup( $"##MetaPopup{manipIdx}" ); -// } -// -// break; -// case MetaManipulation.Type.Est: -// changes = DrawEstRow( manipIdx, list ); -// ImGui.TableSetColumnIndex( 9 ); -// if( ImGui.Selectable( $"{list[ manipIdx ].Est.Entry}##{manipIdx}" ) ) -// { -// ImGui.OpenPopup( $"##MetaPopup{manipIdx}" ); -// } -// -// break; -// case MetaManipulation.Type.Imc: -// changes = DrawImcRow( manipIdx, list ); -// ImGui.TableSetColumnIndex( 9 ); -// if( ImGui.Selectable( $"{list[ manipIdx ].Imc.Entry.MaterialId}##{manipIdx}" ) ) -// { -// ImGui.OpenPopup( $"##MetaPopup{manipIdx}" ); -// } -// -// break; -// case MetaManipulation.Type.Rsp: -// changes = DrawRspRow( manipIdx, list ); -// ImGui.TableSetColumnIndex( 9 ); -// if( ImGui.Selectable( $"{list[ manipIdx ].Rsp.Entry}##{manipIdx}" ) ) -// { -// ImGui.OpenPopup( $"##MetaPopup{manipIdx}" ); -// } -// -// break; -// } -// -// -// ImGui.TableNextRow(); -// return changes; -// } -// -// -// private MetaManipulation.Type DrawNewTypeSelection() -// { -// ImGui.RadioButton( "IMC##newManipType", ref _newManipTypeIdx, 1 ); -// ImGui.SameLine(); -// ImGui.RadioButton( "EQDP##newManipType", ref _newManipTypeIdx, 2 ); -// ImGui.SameLine(); -// ImGui.RadioButton( "EQP##newManipType", ref _newManipTypeIdx, 3 ); -// ImGui.SameLine(); -// ImGui.RadioButton( "EST##newManipType", ref _newManipTypeIdx, 4 ); -// ImGui.SameLine(); -// ImGui.RadioButton( "GMP##newManipType", ref _newManipTypeIdx, 5 ); -// ImGui.SameLine(); -// ImGui.RadioButton( "RSP##newManipType", ref _newManipTypeIdx, 6 ); -// return ( MetaManipulation.Type )_newManipTypeIdx; -// } -// -// private bool DrawNewManipulationPopup( string popupName, IList< MetaManipulation > list, ref int count ) -// { -// var change = false; -// if( !ImGui.BeginPopup( popupName ) ) -// { -// return change; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup ); -// var manipType = DrawNewTypeSelection(); -// MetaManipulation? newManip = null; -// switch( manipType ) -// { -// case MetaManipulation.Type.Imc: -// { -// RestrictedInputInt( "Set Id##newManipImc", ref _newManipSetId, 0, ushort.MaxValue ); -// RestrictedInputInt( "Variant##newManipImc", ref _newManipVariant, 0, byte.MaxValue ); -// CustomCombo( "Object Type", ImcObjectType, out var objectType, ref _newManipObjectType ); -// ImcManipulation imc = new(); -// switch( objectType ) -// { -// case ObjectType.Equipment: -// CustomCombo( "Equipment Slot", EqdpEquipSlots, out var equipSlot, ref _newManipEquipSlot ); -// imc = new ImcManipulation( equipSlot, _newManipVariant, _newManipSetId, new ImcEntry() ); -// break; -// case ObjectType.DemiHuman: -// case ObjectType.Weapon: -// case ObjectType.Monster: -// RestrictedInputInt( "Secondary Id##newManipImc", ref _newManipSecondaryId, 0, ushort.MaxValue ); -// CustomCombo( "Body Slot", ImcBodySlots, out var bodySlot, ref _newManipBodySlot ); -// imc = new ImcManipulation( objectType, bodySlot, _newManipSetId, _newManipSecondaryId, -// _newManipVariant, new ImcEntry() ); -// break; -// } -// -// newManip = new MetaManipulation( new ImcManipulation( imc.ObjectType, imc.BodySlot, imc.PrimaryId, imc.SecondaryId, -// imc.Variant, imc.EquipSlot, ImcFile.GetDefault( imc.GamePath(), imc.EquipSlot, imc.Variant ) ) ); -// -// break; -// } -// case MetaManipulation.Type.Eqdp: -// { -// RestrictedInputInt( "Set Id##newManipEqdp", ref _newManipSetId, 0, ushort.MaxValue ); -// CustomCombo( "Equipment Slot", EqdpEquipSlots, out var equipSlot, ref _newManipEquipSlot ); -// CustomCombo( "Race", Races, out var race, ref _newManipRace ); -// CustomCombo( "Gender", Genders, out var gender, ref _newManipGender ); -// var eqdp = new EqdpManipulation( new EqdpEntry(), equipSlot, gender, race, _newManipSetId ); -// newManip = new MetaManipulation( new EqdpManipulation( ExpandedEqdpFile.GetDefault( eqdp.FileIndex(), eqdp.SetId ), -// equipSlot, gender, race, _newManipSetId ) ); -// break; -// } -// case MetaManipulation.Type.Eqp: -// { -// RestrictedInputInt( "Set Id##newManipEqp", ref _newManipSetId, 0, ushort.MaxValue ); -// CustomCombo( "Equipment Slot", EqpEquipSlots, out var equipSlot, ref _newManipEquipSlot ); -// newManip = new MetaManipulation( new EqpManipulation( ExpandedEqpFile.GetDefault( _newManipSetId ) & Eqp.Mask( equipSlot ), -// equipSlot, _newManipSetId ) ); -// break; -// } -// case MetaManipulation.Type.Est: -// { -// RestrictedInputInt( "Set Id##newManipEst", ref _newManipSetId, 0, ushort.MaxValue ); -// CustomCombo( "Est Type", EstTypes, out var estType, ref _newManipObjectType ); -// CustomCombo( "Race", Races, out var race, ref _newManipRace ); -// CustomCombo( "Gender", Genders, out var gender, ref _newManipGender ); -// newManip = new MetaManipulation( new EstManipulation( gender, race, estType, _newManipSetId, -// EstFile.GetDefault( estType, Names.CombinedRace( gender, race ), _newManipSetId ) ) ); -// break; -// } -// case MetaManipulation.Type.Gmp: -// RestrictedInputInt( "Set Id##newManipGmp", ref _newManipSetId, 0, ushort.MaxValue ); -// newManip = new MetaManipulation( new GmpManipulation( ExpandedGmpFile.GetDefault( _newManipSetId ), _newManipSetId ) ); -// break; -// case MetaManipulation.Type.Rsp: -// CustomCombo( "Subrace", Subraces, out var subRace, ref _newManipSubrace ); -// CustomCombo( "Attribute", RspAttributes, out var rspAttribute, ref _newManipAttribute ); -// newManip = new MetaManipulation( new RspManipulation( subRace, rspAttribute, -// CmpFile.GetDefault( subRace, rspAttribute ) ) ); -// break; -// } -// -// if( ImGui.Button( "Create Manipulation##newManip", Vector2.UnitX * -1 ) -// && newManip != null -// && list.All( m => !m.Equals( newManip ) ) ) -// { -// list.Add( newManip.Value ); -// change = true; -// ++count; -// ImGui.CloseCurrentPopup(); -// } -// -// return change; -// } -// -// private bool DrawMetaManipulationsTable( string label, IList< MetaManipulation > list, ref int count ) -// { -// var numRows = _editMode ? 11 : 10; -// var changes = false; -// -// -// if( list.Count > 0 -// && ImGui.BeginTable( label, numRows, -// ImGuiTableFlags.BordersInner | ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit ) ) -// { -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTable ); -// if( _editMode ) -// { -// ImGui.TableNextColumn(); -// } -// -// ImGui.TableNextColumn(); -// ImGui.TableHeader( $"Type##{label}" ); -// ImGui.TableNextColumn(); -// ImGui.TableHeader( $"Object Type##{label}" ); -// ImGui.TableNextColumn(); -// ImGui.TableHeader( $"Set##{label}" ); -// ImGui.TableNextColumn(); -// ImGui.TableHeader( $"Slot##{label}" ); -// ImGui.TableNextColumn(); -// ImGui.TableHeader( $"Race##{label}" ); -// ImGui.TableNextColumn(); -// ImGui.TableHeader( $"Gender##{label}" ); -// ImGui.TableNextColumn(); -// ImGui.TableHeader( $"Secondary ID##{label}" ); -// ImGui.TableNextColumn(); -// ImGui.TableHeader( $"Variant##{label}" ); -// ImGui.TableNextColumn(); -// ImGui.TableNextColumn(); -// ImGui.TableHeader( $"Value##{label}" ); -// ImGui.TableNextRow(); -// -// for( var i = 0; i < list.Count; ++i ) -// { -// changes |= DrawManipulationRow( ref i, list, ref count ); -// } -// } -// -// var popupName = $"##newManip{label}"; -// if( _editMode ) -// { -// changes |= DrawNewManipulationPopup( $"##newManip{label}", list, ref count ); -// if( ImGui.Button( $"Add New Manipulation##{label}", Vector2.UnitX * -1 ) ) -// { -// ImGui.OpenPopup( popupName ); -// } -// -// return changes; -// } -// -// return false; -// } -// } -//} \ No newline at end of file diff --git a/Penumbra/UI/ConfigWindow.ModsTab.Import.cs b/Penumbra/UI/ConfigWindow.ModsTab.Import.cs deleted file mode 100644 index 917408fe..00000000 --- a/Penumbra/UI/ConfigWindow.ModsTab.Import.cs +++ /dev/null @@ -1,177 +0,0 @@ -namespace Penumbra.UI; - -//public partial class ConfigWindow -//{ -// private class TabImport -// { -// private const string LabelTab = "Import Mods"; -// private const string LabelImportButton = "Import TexTools Modpacks"; -// private const string LabelFileDialog = "Pick one or more modpacks."; -// private const string LabelFileImportRunning = "Import in progress..."; -// private const string FileTypeFilter = "TexTools TTMP Modpack (*.ttmp2)|*.ttmp*|All files (*.*)|*.*"; -// private const string TooltipModpack1 = "Writing modpack to disk before extracting..."; -// -// private const uint ColorRed = 0xFF0000C8; -// private const uint ColorYellow = 0xFF00C8C8; -// -// private static readonly Vector2 ImportBarSize = new(-1, 0); -// -// private bool _isImportRunning; -// private string _errorMessage = string.Empty; -// private TexToolsImport? _texToolsImport; -// private readonly SettingsInterface _base; -// -// public readonly HashSet< string > NewMods = new(); -// -// public TabImport( SettingsInterface ui ) -// => _base = ui; -// -// public bool IsImporting() -// => _isImportRunning; -// -// private void RunImportTask() -// { -// _isImportRunning = true; -// Task.Run( async () => -// { -// try -// { -// var picker = new OpenFileDialog -// { -// Multiselect = true, -// Filter = FileTypeFilter, -// CheckFileExists = true, -// Title = LabelFileDialog, -// }; -// -// var result = await picker.ShowDialogAsync(); -// -// if( result == DialogResult.OK ) -// { -// _errorMessage = string.Empty; -// -// foreach( var fileName in picker.FileNames ) -// { -// PluginLog.Information( $"-> {fileName} START" ); -// -// try -// { -// _texToolsImport = new TexToolsImport( Penumbra.ModManager.BasePath ); -// var dir = _texToolsImport.ImportModPack( new FileInfo( fileName ) ); -// if( dir.Name.Any() ) -// { -// NewMods.Add( dir.Name ); -// } -// -// PluginLog.Information( $"-> {fileName} OK!" ); -// } -// catch( Exception ex ) -// { -// PluginLog.LogError( ex, "Failed to import modpack at {0}", fileName ); -// _errorMessage = ex.Message; -// } -// } -// -// var directory = _texToolsImport?.ExtractedDirectory; -// _texToolsImport = null; -// _base.ReloadMods(); -// if( directory != null ) -// { -// _base._menu.InstalledTab.Selector.SelectModOnUpdate( directory.Name ); -// } -// } -// } -// catch( Exception e ) -// { -// PluginLog.Error( $"Error opening file picker dialogue:\n{e}" ); -// } -// -// _isImportRunning = false; -// } ); -// } -// -// private void DrawImportButton() -// { -// if( !Penumbra.ModManager.Valid ) -// { -// using var style = ImGuiRaii.PushStyle( ImGuiStyleVar.Alpha, 0.5f ); -// ImGui.Button( LabelImportButton ); -// style.Pop(); -// -// using var color = ImGuiRaii.PushColor( ImGuiCol.Text, ColorRed ); -// ImGui.Text( "Can not import since the mod directory path is not valid." ); -// ImGui.Dummy( Vector2.UnitY * ImGui.GetTextLineHeightWithSpacing() ); -// color.Pop(); -// -// ImGui.Text( "Please set the mod directory in the settings tab." ); -// ImGui.Text( "This folder should preferably be close to the root directory of your (preferably SSD) drive, for example" ); -// color.Push( ImGuiCol.Text, ColorYellow ); -// ImGui.Text( " D:\\ffxivmods" ); -// color.Pop(); -// ImGui.Text( "You can return to this tab once you've done that." ); -// } -// else if( ImGui.Button( LabelImportButton ) ) -// { -// RunImportTask(); -// } -// } -// -// private void DrawImportProgress() -// { -// ImGui.Button( LabelFileImportRunning ); -// -// if( _texToolsImport == null ) -// { -// return; -// } -// -// switch( _texToolsImport.State ) -// { -// case ImporterState.None: break; -// case ImporterState.WritingPackToDisk: -// ImGui.Text( TooltipModpack1 ); -// break; -// case ImporterState.ExtractingModFiles: -// { -// var str = -// $"{_texToolsImport.CurrentModPack} - {_texToolsImport.CurrentProgress} of {_texToolsImport.TotalProgress} files"; -// -// ImGui.ProgressBar( _texToolsImport.Progress, ImportBarSize, str ); -// break; -// } -// case ImporterState.Done: break; -// default: throw new ArgumentOutOfRangeException(); -// } -// } -// -// private void DrawFailedImportMessage() -// { -// using var color = ImGuiRaii.PushColor( ImGuiCol.Text, ColorRed ); -// ImGui.Text( $"One or more of your modpacks failed to import:\n\t\t{_errorMessage}" ); -// } -// -// public void Draw() -// { -// if( !ImGui.BeginTabItem( LabelTab ) ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTabItem ); -// -// if( !_isImportRunning ) -// { -// DrawImportButton(); -// } -// else -// { -// DrawImportProgress(); -// } -// -// if( _errorMessage.Any() ) -// { -// DrawFailedImportMessage(); -// } -// } -// } -//} \ No newline at end of file diff --git a/Penumbra/UI/ConfigWindow.ModsTab.Panel.cs b/Penumbra/UI/ConfigWindow.ModsTab.Panel.cs deleted file mode 100644 index 63434602..00000000 --- a/Penumbra/UI/ConfigWindow.ModsTab.Panel.cs +++ /dev/null @@ -1,613 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Numerics; -using Dalamud.Interface; -using Dalamud.Logging; -using ImGuiNET; -using Penumbra.Mods; -using Penumbra.Util; - -namespace Penumbra.UI; - -public partial class SettingsInterface -{ - //private class ModPanel - //{ - // private const string LabelModPanel = "selectedModInfo"; - // private const string LabelEditName = "##editName"; - // private const string LabelEditVersion = "##editVersion"; - // private const string LabelEditAuthor = "##editAuthor"; - // private const string LabelEditWebsite = "##editWebsite"; - // private const string LabelModEnabled = "Enabled"; - // private const string LabelEditingEnabled = "Enable Editing"; - // private const string LabelOverWriteDir = "OverwriteDir"; - // private const string ButtonOpenWebsite = "Open Website"; - // private const string ButtonOpenModFolder = "Open Mod Folder"; - // private const string ButtonRenameModFolder = "Rename Mod Folder"; - // private const string ButtonEditJson = "Edit JSON"; - // private const string ButtonReloadJson = "Reload JSON"; - // private const string ButtonDeduplicate = "Deduplicate"; - // private const string ButtonNormalize = "Normalize"; - // private const string TooltipOpenModFolder = "Open the directory containing this mod in your default file explorer."; - // private const string TooltipRenameModFolder = "Rename the directory containing this mod without opening another application."; - // private const string TooltipEditJson = "Open the JSON configuration file in your default application for .json."; - // private const string TooltipReloadJson = "Reload the configuration of all mods."; - // private const string PopupRenameFolder = "Rename Folder"; - // - // private const string TooltipDeduplicate = - // "Try to find identical files and remove duplicate occurences to reduce the mods disk size.\n" - // + "Introduces an invisible single-option Group \"Duplicates\".\nExperimental - use at own risk!"; - // - // private const string TooltipNormalize = - // "Try to reduce unnecessary options or subdirectories to default options if possible.\nExperimental - use at own risk!"; - // - // private const float HeaderLineDistance = 10f; - // private static readonly Vector4 GreyColor = new(1f, 1f, 1f, 0.66f); - // - // private readonly SettingsInterface _base; - // private readonly Selector _selector; - // private readonly HashSet< string > _newMods; - // public readonly PluginDetails Details; - // - // private bool _editMode; - // private string _currentWebsite; - // private bool _validWebsite; - // - // private string _fromMaterial = string.Empty; - // private string _toMaterial = string.Empty; - // - // public ModPanel( SettingsInterface ui, Selector s, HashSet< string > newMods ) - // { - // _base = ui; - // _selector = s; - // _newMods = newMods; - // Details = new PluginDetails( _base, _selector ); - // _currentWebsite = Meta?.Website ?? ""; - // } - // - // private Mods.FullMod? Mod - // => _selector.Mod; - // - // private ModMeta? Meta - // => Mod?.Data.Meta; - // - // private void DrawName() - // { - // var name = Meta!.Name.Text; - // var modManager = Penumbra.ModManager; - // if( ImGuiCustom.InputOrText( _editMode, LabelEditName, ref name, 64 ) && modManager.RenameMod( name, Mod!.Data ) ) - // { - // _selector.SelectModOnUpdate( Mod.Data.BasePath.Name ); - // if( !modManager.TemporaryModSortOrder.ContainsKey( Mod!.Data.BasePath.Name ) ) - // { - // Mod.Data.Rename( name ); - // } - // } - // } - // - // private void DrawVersion() - // { - // if( _editMode ) - // { - // ImGui.BeginGroup(); - // using var raii = ImGuiRaii.DeferredEnd( ImGui.EndGroup ); - // ImGui.Text( "(Version " ); - // - // using var style = ImGuiRaii.PushStyle( ImGuiStyleVar.ItemSpacing, ZeroVector ); - // ImGui.SameLine(); - // var version = Meta!.Version; - // if( ImGuiCustom.ResizingTextInput( LabelEditVersion, ref version, 16 ) - // && version != Meta.Version ) - // { - // Meta.Version = version; - // _selector.SaveCurrentMod(); - // } - // - // ImGui.SameLine(); - // ImGui.Text( ")" ); - // } - // else if( Meta!.Version.Length > 0 ) - // { - // ImGui.Text( $"(Version {Meta.Version})" ); - // } - // } - // - // private void DrawAuthor() - // { - // ImGui.BeginGroup(); - // ImGui.TextColored( GreyColor, "by" ); - // - // ImGui.SameLine(); - // var author = Meta!.Author.Text; - // if( ImGuiCustom.InputOrText( _editMode, LabelEditAuthor, ref author, 64 ) - // && author != Meta.Author ) - // { - // Meta.Author = author; - // _selector.SaveCurrentMod(); - // _selector.Cache.TriggerFilterReset(); - // } - // - // ImGui.EndGroup(); - // } - // - // private void DrawWebsite() - // { - // ImGui.BeginGroup(); - // using var raii = ImGuiRaii.DeferredEnd( ImGui.EndGroup ); - // if( _editMode ) - // { - // ImGui.TextColored( GreyColor, "from" ); - // ImGui.SameLine(); - // var website = Meta!.Website; - // if( ImGuiCustom.ResizingTextInput( LabelEditWebsite, ref website, 512 ) - // && website != Meta.Website ) - // { - // Meta.Website = website; - // _selector.SaveCurrentMod(); - // } - // } - // else if( Meta!.Website.Length > 0 ) - // { - // if( _currentWebsite != Meta.Website ) - // { - // _currentWebsite = Meta.Website; - // _validWebsite = Uri.TryCreate( Meta.Website, UriKind.Absolute, out var uriResult ) - // && ( uriResult.Scheme == Uri.UriSchemeHttps || uriResult.Scheme == Uri.UriSchemeHttp ); - // } - // - // if( _validWebsite ) - // { - // if( ImGui.SmallButton( ButtonOpenWebsite ) ) - // { - // try - // { - // var process = new ProcessStartInfo( Meta.Website ) - // { - // UseShellExecute = true, - // }; - // Process.Start( process ); - // } - // catch( System.ComponentModel.Win32Exception ) - // { - // // Do nothing. - // } - // } - // - // ImGuiCustom.HoverTooltip( Meta.Website ); - // } - // else - // { - // ImGui.TextColored( GreyColor, "from" ); - // ImGui.SameLine(); - // ImGui.Text( Meta.Website ); - // } - // } - // } - // - // private void DrawHeaderLine() - // { - // DrawName(); - // ImGui.SameLine(); - // DrawVersion(); - // ImGui.SameLine(); - // DrawAuthor(); - // ImGui.SameLine(); - // DrawWebsite(); - // } - // - // private void DrawPriority() - // { - // var priority = Mod!.Settings.Priority; - // ImGui.SetNextItemWidth( 50 * ImGuiHelpers.GlobalScale ); - // if( ImGui.InputInt( "Priority", ref priority, 0 ) && priority != Mod!.Settings.Priority ) - // { - // Penumbra.CollectionManager.Current.SetModPriority( Mod.Data.Index, priority ); - // _selector.Cache.TriggerFilterReset(); - // } - // - // ImGuiCustom.HoverTooltip( - // "Higher priority mods take precedence over other mods in the case of file conflicts.\n" - // + "In case of identical priority, the alphabetically first mod takes precedence." ); - // } - // - // private void DrawEnabledMark() - // { - // var enabled = Mod!.Settings.Enabled; - // if( ImGui.Checkbox( LabelModEnabled, ref enabled ) ) - // { - // Penumbra.CollectionManager.Current.SetModState( Mod.Data.Index, enabled ); - // if( enabled ) - // { - // _newMods.Remove( Mod.Data.BasePath.Name ); - // } - // _selector.Cache.TriggerFilterReset(); - // } - // } - // - // public static bool DrawSortOrder( Mods.Mod mod, Mods.Mod.Manager manager, Selector selector ) - // { - // var currentSortOrder = mod.Order.FullPath; - // ImGui.SetNextItemWidth( 300 * ImGuiHelpers.GlobalScale ); - // if( ImGui.InputText( "Sort Order", ref currentSortOrder, 256, ImGuiInputTextFlags.EnterReturnsTrue ) ) - // { - // manager.ChangeSortOrder( mod, currentSortOrder ); - // selector.SelectModOnUpdate( mod.BasePath.Name ); - // return true; - // } - // - // return false; - // } - // - // private void DrawEditableMark() - // { - // ImGui.Checkbox( LabelEditingEnabled, ref _editMode ); - // } - // - // private void DrawOpenModFolderButton() - // { - // Mod!.Data.BasePath.Refresh(); - // if( ImGui.Button( ButtonOpenModFolder ) && Mod.Data.BasePath.Exists ) - // { - // Process.Start( new ProcessStartInfo( Mod!.Data.BasePath.FullName ) { UseShellExecute = true } ); - // } - // - // ImGuiCustom.HoverTooltip( TooltipOpenModFolder ); - // } - // - // private string _newName = ""; - // private bool _keyboardFocus = true; - // - // private void RenameModFolder( string newName ) - // { - // _newName = newName.ReplaceBadXivSymbols(); - // if( _newName.Length == 0 ) - // { - // PluginLog.Debug( "New Directory name {NewName} was empty after removing invalid symbols.", newName ); - // ImGui.CloseCurrentPopup(); - // } - // else if( !string.Equals( _newName, Mod!.Data.BasePath.Name, StringComparison.InvariantCultureIgnoreCase ) ) - // { - // var dir = Mod!.Data.BasePath; - // DirectoryInfo newDir = new(Path.Combine( dir.Parent!.FullName, _newName )); - // - // if( newDir.Exists ) - // { - // ImGui.OpenPopup( LabelOverWriteDir ); - // } - // else if( Penumbra.ModManager.RenameModFolder( Mod.Data, newDir ) ) - // { - // _selector.ReloadCurrentMod(); - // ImGui.CloseCurrentPopup(); - // } - // } - // else if( !string.Equals( _newName, Mod!.Data.BasePath.Name, StringComparison.InvariantCulture ) ) - // { - // var dir = Mod!.Data.BasePath; - // DirectoryInfo newDir = new(Path.Combine( dir.Parent!.FullName, _newName )); - // var sourceUri = new Uri( dir.FullName ); - // var targetUri = new Uri( newDir.FullName ); - // if( sourceUri.Equals( targetUri ) ) - // { - // var tmpFolder = new DirectoryInfo( TempFile.TempFileName( dir.Parent! ).FullName ); - // if( Penumbra.ModManager.RenameModFolder( Mod.Data, tmpFolder ) ) - // { - // if( !Penumbra.ModManager.RenameModFolder( Mod.Data, newDir ) ) - // { - // PluginLog.Error( "Could not recapitalize folder after renaming, reverting rename." ); - // Penumbra.ModManager.RenameModFolder( Mod.Data, dir ); - // } - // - // _selector.ReloadCurrentMod(); - // } - // - // ImGui.CloseCurrentPopup(); - // } - // else - // { - // ImGui.OpenPopup( LabelOverWriteDir ); - // } - // } - // } - // - // private static bool MergeFolderInto( DirectoryInfo source, DirectoryInfo target ) - // { - // try - // { - // foreach( var file in source.EnumerateFiles( "*", SearchOption.AllDirectories ) ) - // { - // var targetFile = new FileInfo( Path.Combine( target.FullName, file.FullName.Substring( source.FullName.Length + 1 ) ) ); - // if( targetFile.Exists ) - // { - // targetFile.Delete(); - // } - // - // targetFile.Directory?.Create(); - // file.MoveTo( targetFile.FullName ); - // } - // - // source.Delete( true ); - // return true; - // } - // catch( Exception e ) - // { - // PluginLog.Error( $"Could not merge directory {source.FullName} into {target.FullName}:\n{e}" ); - // } - // - // return false; - // } - // - // private bool OverwriteDirPopup() - // { - // var closeParent = false; - // var _ = true; - // if( !ImGui.BeginPopupModal( LabelOverWriteDir, ref _, ImGuiWindowFlags.AlwaysAutoResize ) ) - // { - // return closeParent; - // } - // - // using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup ); - // - // var dir = Mod!.Data.BasePath; - // DirectoryInfo newDir = new(Path.Combine( dir.Parent!.FullName, _newName )); - // ImGui.Text( - // $"The mod directory {newDir} already exists.\nDo you want to merge / overwrite both mods?\nThis may corrupt the resulting mod in irrecoverable ways." ); - // var buttonSize = ImGuiHelpers.ScaledVector2( 120, 0 ); - // if( ImGui.Button( "Yes", buttonSize ) && MergeFolderInto( dir, newDir ) ) - // { - // Penumbra.ModManager.RenameModFolder( Mod.Data, newDir, false ); - // - // _selector.SelectModOnUpdate( _newName ); - // - // closeParent = true; - // ImGui.CloseCurrentPopup(); - // } - // - // ImGui.SameLine(); - // - // if( ImGui.Button( "Cancel", buttonSize ) ) - // { - // _keyboardFocus = true; - // ImGui.CloseCurrentPopup(); - // } - // - // return closeParent; - // } - // - // private void DrawRenameModFolderPopup() - // { - // var _ = true; - // _keyboardFocus |= !ImGui.IsPopupOpen( PopupRenameFolder ); - // - // ImGui.SetNextWindowPos( ImGui.GetMainViewport().GetCenter(), ImGuiCond.Appearing, new Vector2( 0.5f, 1f ) ); - // if( !ImGui.BeginPopupModal( PopupRenameFolder, ref _, ImGuiWindowFlags.AlwaysAutoResize ) ) - // { - // return; - // } - // - // using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup ); - // - // if( ImGui.IsKeyPressed( ImGui.GetKeyIndex( ImGuiKey.Escape ) ) ) - // { - // ImGui.CloseCurrentPopup(); - // } - // - // var newName = Mod!.Data.BasePath.Name; - // - // if( _keyboardFocus ) - // { - // ImGui.SetKeyboardFocusHere(); - // _keyboardFocus = false; - // } - // - // if( ImGui.InputText( "New Folder Name##RenameFolderInput", ref newName, 64, ImGuiInputTextFlags.EnterReturnsTrue ) ) - // { - // RenameModFolder( newName ); - // } - // - // ImGui.TextColored( GreyColor, - // "Please restrict yourself to ascii symbols that are valid in a windows path,\nother symbols will be replaced by underscores." ); - // - // ImGui.SetNextWindowPos( ImGui.GetMainViewport().GetCenter(), ImGuiCond.Appearing, Vector2.One / 2 ); - // - // - // if( OverwriteDirPopup() ) - // { - // ImGui.CloseCurrentPopup(); - // } - // } - // - // private void DrawRenameModFolderButton() - // { - // DrawRenameModFolderPopup(); - // if( ImGui.Button( ButtonRenameModFolder ) ) - // { - // ImGui.OpenPopup( PopupRenameFolder ); - // } - // - // ImGuiCustom.HoverTooltip( TooltipRenameModFolder ); - // } - // - // private void DrawEditJsonButton() - // { - // if( ImGui.Button( ButtonEditJson ) ) - // { - // _selector.SaveCurrentMod(); - // Process.Start( new ProcessStartInfo( Mod!.Data.MetaFile.FullName ) { UseShellExecute = true } ); - // } - // - // ImGuiCustom.HoverTooltip( TooltipEditJson ); - // } - // - // private void DrawReloadJsonButton() - // { - // if( ImGui.Button( ButtonReloadJson ) ) - // { - // _selector.ReloadCurrentMod( true, false ); - // } - // - // ImGuiCustom.HoverTooltip( TooltipReloadJson ); - // } - // - // private void DrawResetMetaButton() - // { - // if( ImGui.Button( "Recompute Metadata" ) ) - // { - // _selector.ReloadCurrentMod( true, true, true ); - // } - // - // ImGuiCustom.HoverTooltip( - // "Force a recomputation of the metadata_manipulations.json file from all .meta files in the folder.\n" - // + "Also reloads the mod.\n" - // + "Be aware that this removes all manually added metadata changes." ); - // } - // - // private void DrawDeduplicateButton() - // { - // if( ImGui.Button( ButtonDeduplicate ) ) - // { - // ModCleanup.Deduplicate( Mod!.Data.BasePath, Meta! ); - // _selector.SaveCurrentMod(); - // _selector.ReloadCurrentMod( true, true, true ); - // } - // - // ImGuiCustom.HoverTooltip( TooltipDeduplicate ); - // } - // - // private void DrawNormalizeButton() - // { - // if( ImGui.Button( ButtonNormalize ) ) - // { - // ModCleanup.Normalize( Mod!.Data.BasePath, Meta! ); - // _selector.SaveCurrentMod(); - // _selector.ReloadCurrentMod( true, true, true ); - // } - // - // ImGuiCustom.HoverTooltip( TooltipNormalize ); - // } - // - // private void DrawAutoGenerateGroupsButton() - // { - // if( ImGui.Button( "Auto-Generate Groups" ) ) - // { - // ModCleanup.AutoGenerateGroups( Mod!.Data.BasePath, Meta! ); - // _selector.SaveCurrentMod(); - // _selector.ReloadCurrentMod( true, true ); - // } - // - // ImGuiCustom.HoverTooltip( "Automatically generate single-select groups from all folders (clears existing groups):\n" - // + "First subdirectory: Option Group\n" - // + "Second subdirectory: Option Name\n" - // + "Afterwards: Relative file paths.\n" - // + "Experimental - Use at own risk!" ); - // } - // - // private void DrawSplitButton() - // { - // if( ImGui.Button( "Split Mod" ) ) - // { - // ModCleanup.SplitMod( Mod!.Data ); - // } - // - // ImGuiCustom.HoverTooltip( - // "Split off all options of a mod into single mods that are placed in a collective folder.\n" - // + "Does not remove or change the mod itself, just create (potentially inefficient) copies.\n" - // + "Experimental - Use at own risk!" ); - // } - // - // private void DrawMaterialChangeRow() - // { - // ImGui.SetNextItemWidth( 150 * ImGuiHelpers.GlobalScale ); - // ImGui.InputTextWithHint( "##fromMaterial", "From Material Suffix...", ref _fromMaterial, 16 ); - // ImGui.SameLine(); - // using var font = ImGuiRaii.PushFont( UiBuilder.IconFont ); - // ImGui.Text( FontAwesomeIcon.LongArrowAltRight.ToIconString() ); - // font.Pop(); - // ImGui.SameLine(); - // ImGui.SetNextItemWidth( 150 * ImGuiHelpers.GlobalScale ); - // ImGui.InputTextWithHint( "##toMaterial", "To Material Suffix...", ref _toMaterial, 16 ); - // ImGui.SameLine(); - // var validStrings = ModelChanger.ValidStrings( _fromMaterial, _toMaterial ); - // using var alpha = ImGuiRaii.PushStyle( ImGuiStyleVar.Alpha, 0.5f, !validStrings ); - // if( ImGui.Button( "Convert" ) && validStrings ) - // { - // ModelChanger.ChangeModMaterials( Mod!.Data, _fromMaterial, _toMaterial ); - // } - // - // alpha.Pop(); - // - // ImGuiCustom.HoverTooltip( - // "Change the skin material of all models in this mod reference " - // + "from the suffix given in the first text input to " - // + "the suffix given in the second input.\n" - // + "Enter only the suffix, e.g. 'd' or 'a' or 'bibo', not the whole path.\n" - // + "This overwrites .mdl files, use at your own risk!" ); - // } - // - // private void DrawEditLine() - // { - // DrawOpenModFolderButton(); - // ImGui.SameLine(); - // DrawRenameModFolderButton(); - // ImGui.SameLine(); - // DrawEditJsonButton(); - // ImGui.SameLine(); - // DrawReloadJsonButton(); - // - // DrawResetMetaButton(); - // ImGui.SameLine(); - // DrawDeduplicateButton(); - // ImGui.SameLine(); - // DrawNormalizeButton(); - // ImGui.SameLine(); - // DrawAutoGenerateGroupsButton(); - // ImGui.SameLine(); - // DrawSplitButton(); - // - // DrawMaterialChangeRow(); - // - // DrawSortOrder( Mod!.Data, Penumbra.ModManager, _selector ); - // } - // - // public void Draw() - // { - // try - // { - // using var raii = ImGuiRaii.DeferredEnd( ImGui.EndChild ); - // var ret = ImGui.BeginChild( LabelModPanel, AutoFillSize, true ); - // - // if( !ret || Mod == null ) - // { - // return; - // } - // - // DrawHeaderLine(); - // - // // Next line with fixed distance. - // ImGuiCustom.VerticalDistance( HeaderLineDistance ); - // - // DrawEnabledMark(); - // ImGui.SameLine(); - // DrawPriority(); - // if( Penumbra.Config.ShowAdvanced ) - // { - // ImGui.SameLine(); - // DrawEditableMark(); - // } - // - // // Next line, if editable. - // if( _editMode ) - // { - // DrawEditLine(); - // } - // - // Details.Draw( _editMode ); - // } - // catch( Exception ex ) - // { - // PluginLog.LogError( ex, "Oh no" ); - // } - // } - //} -} \ No newline at end of file diff --git a/Penumbra/UI/ConfigWindow.ModsTab.Selector.cs b/Penumbra/UI/ConfigWindow.ModsTab.Selector.cs deleted file mode 100644 index 3190decb..00000000 --- a/Penumbra/UI/ConfigWindow.ModsTab.Selector.cs +++ /dev/null @@ -1,797 +0,0 @@ -//using System; -//using System.Collections.Generic; -//using System.IO; -//using System.Linq; -//using System.Numerics; -//using System.Runtime.InteropServices; -//using System.Windows.Forms.VisualStyles; -//using Dalamud.Interface; -//using Dalamud.Logging; -//using ImGuiNET; -//using Penumbra.Collections; -//using Penumbra.Importer; -//using Penumbra.Mods; -//using Penumbra.UI.Classes; -//using Penumbra.UI.Custom; -//using Penumbra.Util; -// -//namespace Penumbra.UI; -// -//public partial class SettingsInterface -//{ -// // Constants -// private partial class Selector -// { -// private const string LabelSelectorList = "##availableModList"; -// private const string LabelModFilter = "##ModFilter"; -// private const string LabelAddModPopup = "AddModPopup"; -// private const string LabelModHelpPopup = "Help##Selector"; -// -// private const string TooltipModFilter = -// "Filter mods for those containing the given substring.\nEnter c:[string] to filter for mods changing specific items.\nEnter a:[string] to filter for mods by specific authors."; -// -// private const string TooltipDelete = "Delete the selected mod"; -// private const string TooltipAdd = "Add an empty mod"; -// private const string DialogDeleteMod = "PenumbraDeleteMod"; -// private const string ButtonYesDelete = "Yes, delete it"; -// private const string ButtonNoDelete = "No, keep it"; -// -// private const float SelectorPanelWidth = 240f; -// -// private static readonly Vector2 SelectorButtonSizes = new(100, 0); -// private static readonly Vector2 HelpButtonSizes = new(40, 0); -// -// private static readonly Vector4 DeleteModNameColor = new(0.7f, 0.1f, 0.1f, 1); -// } -// -// // Buttons -// private partial class Selector -// { -// // === Delete === -// private int? _deleteIndex; -// -// private void DrawModTrashButton() -// { -// using var raii = ImGuiRaii.PushFont( UiBuilder.IconFont ); -// -// if( ImGui.Button( FontAwesomeIcon.Trash.ToIconString(), SelectorButtonSizes * _selectorScalingFactor ) && _index >= 0 ) -// { -// _deleteIndex = _index; -// } -// -// raii.Pop(); -// -// ImGuiCustom.HoverTooltip( TooltipDelete ); -// } -// -// private void DrawDeleteModal() -// { -// if( _deleteIndex == null ) -// { -// return; -// } -// -// ImGui.OpenPopup( DialogDeleteMod ); -// -// var _ = true; -// ImGui.SetNextWindowPos( ImGui.GetMainViewport().GetCenter(), ImGuiCond.Appearing, Vector2.One / 2 ); -// var ret = ImGui.BeginPopupModal( DialogDeleteMod, ref _, ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoDecoration ); -// if( !ret ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup ); -// -// if( Mod == null ) -// { -// _deleteIndex = null; -// ImGui.CloseCurrentPopup(); -// return; -// } -// -// ImGui.Text( "Are you sure you want to delete the following mod:" ); -// var halfLine = new Vector2( ImGui.GetTextLineHeight() / 2 ); -// ImGui.Dummy( halfLine ); -// ImGui.TextColored( DeleteModNameColor, Mod.Data.Meta.Name ); -// ImGui.Dummy( halfLine ); -// -// var buttonSize = ImGuiHelpers.ScaledVector2( 120, 0 ); -// if( ImGui.Button( ButtonYesDelete, buttonSize ) ) -// { -// ImGui.CloseCurrentPopup(); -// var mod = Mod; -// Cache.RemoveMod( mod ); -// Penumbra.ModManager.DeleteMod( mod.Data.BasePath ); -// ModFileSystem.InvokeChange(); -// ClearSelection(); -// } -// -// ImGui.SameLine(); -// -// if( ImGui.Button( ButtonNoDelete, buttonSize ) ) -// { -// ImGui.CloseCurrentPopup(); -// _deleteIndex = null; -// } -// } -// -// // === Add === -// private bool _modAddKeyboardFocus = true; -// -// private void DrawModAddButton() -// { -// using var raii = ImGuiRaii.PushFont( UiBuilder.IconFont ); -// -// if( ImGui.Button( FontAwesomeIcon.Plus.ToIconString(), SelectorButtonSizes * _selectorScalingFactor ) ) -// { -// _modAddKeyboardFocus = true; -// ImGui.OpenPopup( LabelAddModPopup ); -// } -// -// raii.Pop(); -// -// ImGuiCustom.HoverTooltip( TooltipAdd ); -// -// DrawModAddPopup(); -// } -// -// private void DrawModAddPopup() -// { -// if( !ImGui.BeginPopup( LabelAddModPopup ) ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup ); -// -// if( _modAddKeyboardFocus ) -// { -// ImGui.SetKeyboardFocusHere(); -// _modAddKeyboardFocus = false; -// } -// -// var newName = ""; -// if( ImGui.InputTextWithHint( "##AddMod", "New Mod Name...", ref newName, 64, ImGuiInputTextFlags.EnterReturnsTrue ) ) -// { -// try -// { -// var newDir = TexToolsImport.CreateModFolder( new DirectoryInfo( Penumbra.Config!.ModDirectory ), -// newName ); -// var modMeta = new ModMeta -// { -// Author = "Unknown", -// Name = newName.Replace( '/', '\\' ), -// Description = string.Empty, -// }; -// -// var metaFile = new FileInfo( Path.Combine( newDir.FullName, "meta.json" ) ); -// modMeta.SaveToFile( metaFile ); -// Penumbra.ModManager.AddMod( newDir ); -// ModFileSystem.InvokeChange(); -// SelectModOnUpdate( newDir.Name ); -// } -// catch( Exception e ) -// { -// PluginLog.Error( $"Could not create directory for new Mod {newName}:\n{e}" ); -// } -// -// ImGui.CloseCurrentPopup(); -// } -// -// if( ImGui.IsKeyPressed( ImGui.GetKeyIndex( ImGuiKey.Escape ) ) ) -// { -// ImGui.CloseCurrentPopup(); -// } -// } -// -// // === Help === -// private void DrawModHelpButton() -// { -// using var raii = ImGuiRaii.PushFont( UiBuilder.IconFont ); -// if( ImGui.Button( FontAwesomeIcon.QuestionCircle.ToIconString(), HelpButtonSizes * _selectorScalingFactor ) ) -// { -// ImGui.OpenPopup( LabelModHelpPopup ); -// } -// } -// -// private static void DrawModHelpPopup() -// { -// ImGui.SetNextWindowPos( ImGui.GetMainViewport().GetCenter(), ImGuiCond.Appearing, Vector2.One / 2 ); -// ImGui.SetNextWindowSize( new Vector2( 5 * SelectorPanelWidth, 34 * ImGui.GetTextLineHeightWithSpacing() ), -// ImGuiCond.Appearing ); -// var _ = true; -// if( !ImGui.BeginPopupModal( LabelModHelpPopup, ref _, ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove ) ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup ); -// -// ImGui.Dummy( Vector2.UnitY * ImGui.GetTextLineHeight() ); -// ImGui.Text( "Mod Selector" ); -// ImGui.BulletText( "Select a mod to obtain more information." ); -// ImGui.BulletText( "Mod names are colored according to their current state in the collection:" ); -// ImGui.Indent(); -// ImGui.Bullet(); -// ImGui.SameLine(); -// ImGui.Text( "Enabled in the current collection." ); -// ImGui.Bullet(); -// ImGui.SameLine(); -// ImGui.TextColored( ImGui.ColorConvertU32ToFloat4( ColorId.DisabledMod.Value() ), "Disabled in the current collection." ); -// ImGui.Bullet(); -// ImGui.SameLine(); -// ImGui.TextColored( ImGui.ColorConvertU32ToFloat4( ColorId.NewMod.Value() ), -// "Newly imported during this session. Will go away when first enabling a mod or when Penumbra is reloaded." ); -// ImGui.Bullet(); -// ImGui.SameLine(); -// ImGui.TextColored( ImGui.ColorConvertU32ToFloat4( ColorId.HandledConflictMod.Value() ), -// "Enabled and conflicting with another enabled Mod, but on different priorities (i.e. the conflict is solved)." ); -// ImGui.Bullet(); -// ImGui.SameLine(); -// ImGui.TextColored( ImGui.ColorConvertU32ToFloat4( ColorId.DisabledMod.Value() ), -// "Enabled and conflicting with another enabled Mod on the same priority." ); -// ImGui.Unindent(); -// ImGui.BulletText( "Right-click a mod to enter its sort order, which is its name by default." ); -// ImGui.Indent(); -// ImGui.BulletText( "A sort order differing from the mods name will not be displayed, it will just be used for ordering." ); -// ImGui.BulletText( -// "If the sort order string contains Forward-Slashes ('/'), the preceding substring will be turned into collapsible folders that can group mods." ); -// ImGui.BulletText( -// "Collapsible folders can contain further collapsible folders, so \"folder1/folder2/folder3/1\" will produce 3 folders\n" -// + "\t\t[folder1] -> [folder2] -> [folder3] -> [ModName],\n" -// + "where ModName will be sorted as if it was the string '1'." ); -// ImGui.Unindent(); -// ImGui.BulletText( -// "You can drag and drop mods and subfolders into existing folders. Dropping them onto mods is the same as dropping them onto the parent of the mod." ); -// ImGui.BulletText( "Right-clicking a folder opens a context menu." ); -// ImGui.Indent(); -// ImGui.BulletText( -// "You can rename folders in the context menu. Leave the text blank and press enter to merge the folder with its parent." ); -// ImGui.BulletText( "You can also enable or disable all descendant mods of a folder." ); -// ImGui.Unindent(); -// ImGui.BulletText( "Use the Filter Mods... input at the top to filter the list for mods with names containing the text." ); -// ImGui.Indent(); -// ImGui.BulletText( "You can enter c:[string] to filter for Changed Items instead." ); -// ImGui.BulletText( "You can enter a:[string] to filter for Mod Authors instead." ); -// ImGui.Unindent(); -// ImGui.BulletText( "Use the expandable menu beside the input to filter for mods fulfilling specific criteria." ); -// ImGui.Dummy( Vector2.UnitY * ImGui.GetTextLineHeight() ); -// ImGui.Text( "Mod Management" ); -// ImGui.BulletText( "You can delete the currently selected mod with the trashcan button." ); -// ImGui.BulletText( "You can add a completely empty mod with the plus button." ); -// ImGui.BulletText( "You can import TTMP-based mods in the import tab." ); -// ImGui.BulletText( -// "You can import penumbra-based mods by moving the corresponding folder into your mod directory in a file explorer, then rediscovering mods." ); -// ImGui.BulletText( -// "If you enable Advanced Options in the Settings tab, you can toggle Edit Mode to manipulate your selected mod even further." ); -// ImGui.Dummy( Vector2.UnitY * ImGui.GetTextLineHeight() ); -// ImGui.Dummy( Vector2.UnitX * 2 * SelectorPanelWidth ); -// ImGui.SameLine(); -// if( ImGui.Button( "Understood", Vector2.UnitX * SelectorPanelWidth ) ) -// { -// ImGui.CloseCurrentPopup(); -// } -// } -// -// // === Main === -// private void DrawModsSelectorButtons() -// { -// // Selector controls -// using var style = ImGuiRaii.PushStyle( ImGuiStyleVar.WindowPadding, ZeroVector ) -// .Push( ImGuiStyleVar.FrameRounding, 0 ); -// -// DrawModAddButton(); -// ImGui.SameLine(); -// DrawModHelpButton(); -// ImGui.SameLine(); -// DrawModTrashButton(); -// } -// } -// -// // Filters -// private partial class Selector -// { -// private string _modFilterInput = ""; -// -// private void DrawTextFilter() -// { -// ImGui.SetNextItemWidth( SelectorPanelWidth * _selectorScalingFactor - 22 * ImGuiHelpers.GlobalScale ); -// var tmp = _modFilterInput; -// if( ImGui.InputTextWithHint( LabelModFilter, "Filter Mods...", ref tmp, 256 ) && _modFilterInput != tmp ) -// { -// Cache.SetTextFilter( tmp ); -// _modFilterInput = tmp; -// } -// -// ImGuiCustom.HoverTooltip( TooltipModFilter ); -// } -// -// private void DrawToggleFilter() -// { -// if( ImGui.BeginCombo( "##ModStateFilter", "", -// ImGuiComboFlags.NoPreview | ImGuiComboFlags.PopupAlignLeft | ImGuiComboFlags.HeightLargest ) ) -// { -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndCombo ); -// var flags = ( int )Cache.StateFilter; -// foreach( ModFilter flag in Enum.GetValues( typeof( ModFilter ) ) ) -// { -// ImGui.CheckboxFlags( flag.ToName(), ref flags, ( int )flag ); -// } -// -// Cache.StateFilter = ( ModFilter )flags; -// } -// -// ImGuiCustom.HoverTooltip( "Filter mods for their activation status." ); -// } -// -// private void DrawModsSelectorFilter() -// { -// using var style = ImGuiRaii.PushStyle( ImGuiStyleVar.ItemSpacing, ZeroVector ); -// DrawTextFilter(); -// ImGui.SameLine(); -// DrawToggleFilter(); -// } -// } -// -// // Drag'n Drop -// private partial class Selector -// { -// private const string DraggedModLabel = "ModIndex"; -// private const string DraggedFolderLabel = "FolderName"; -// -// private readonly IntPtr _dragDropPayload = Marshal.AllocHGlobal( 4 ); -// -// private static unsafe bool IsDropping( string name ) -// => ImGui.AcceptDragDropPayload( name ).NativePtr != null; -// -// private void DragDropTarget( ModFolder folder ) -// { -// if( !ImGui.BeginDragDropTarget() ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndDragDropTarget ); -// -// if( IsDropping( DraggedModLabel ) ) -// { -// var payload = ImGui.GetDragDropPayload(); -// var modIndex = Marshal.ReadInt32( payload.Data ); -// var mod = Cache.GetMod( modIndex ).Item1; -// mod?.Data.Move( folder ); -// } -// else if( IsDropping( DraggedFolderLabel ) ) -// { -// var payload = ImGui.GetDragDropPayload(); -// var folderName = Marshal.PtrToStringUni( payload.Data ); -// if( ModFileSystem.Find( folderName!, out var droppedFolder ) -// && !ReferenceEquals( droppedFolder, folder ) -// && !folder.FullName.StartsWith( folderName!, StringComparison.InvariantCultureIgnoreCase ) ) -// { -// droppedFolder.Move( folder ); -// } -// } -// } -// -// private void DragDropSourceFolder( ModFolder folder ) -// { -// if( !ImGui.BeginDragDropSource() ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndDragDropSource ); -// -// var folderName = folder.FullName; -// var ptr = Marshal.StringToHGlobalUni( folderName ); -// ImGui.SetDragDropPayload( DraggedFolderLabel, ptr, ( uint )( folderName.Length + 1 ) * 2 ); -// ImGui.Text( $"Moving {folderName}..." ); -// } -// -// private void DragDropSourceMod( int modIndex, string modName ) -// { -// if( !ImGui.BeginDragDropSource() ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndDragDropSource ); -// -// Marshal.WriteInt32( _dragDropPayload, modIndex ); -// ImGui.SetDragDropPayload( "ModIndex", _dragDropPayload, 4 ); -// ImGui.Text( $"Moving {modName}..." ); -// } -// -// ~Selector() -// => Marshal.FreeHGlobal( _dragDropPayload ); -// } -// -// // Selection -// private partial class Selector -// { -// public Mods.FullMod? Mod { get; private set; } -// private int _index; -// private string _nextDir = string.Empty; -// -// private void SetSelection( int idx, Mods.FullMod? info ) -// { -// Mod = info; -// if( idx != _index ) -// { -// _base._menu.InstalledTab.ModPanel.Details.ResetState(); -// } -// -// _index = idx; -// _deleteIndex = null; -// } -// -// private void SetSelection( int idx ) -// { -// if( idx >= Cache.Count ) -// { -// idx = -1; -// } -// -// if( idx < 0 ) -// { -// SetSelection( 0, null ); -// } -// else -// { -// SetSelection( idx, Cache.GetMod( idx ).Item1 ); -// } -// } -// -// public void ReloadSelection() -// => SetSelection( _index, Cache.GetMod( _index ).Item1 ); -// -// public void ClearSelection() -// => SetSelection( -1 ); -// -// public void SelectModOnUpdate( string directory ) -// => _nextDir = directory; -// -// public void SelectModByDir( string name ) -// { -// var (mod, idx) = Cache.GetModByBasePath( name ); -// SetSelection( idx, mod ); -// } -// -// public void ReloadCurrentMod( bool reloadMeta = false, bool recomputeMeta = false, bool force = false ) -// { -// if( Mod == null ) -// { -// return; -// } -// -// if( _index >= 0 && Penumbra.ModManager.UpdateMod( Mod.Data, reloadMeta, recomputeMeta, force ) ) -// { -// SelectModOnUpdate( Mod.Data.BasePath.Name ); -// _base._menu.InstalledTab.ModPanel.Details.ResetState(); -// } -// } -// -// public void SaveCurrentMod() -// => Mod?.Data.SaveMeta(); -// } -// -// // Right-Clicks -// private partial class Selector -// { -// // === Mod === -// private void DrawModOrderPopup( string popupName, Mods.FullMod mod, bool firstOpen ) -// { -// if( !ImGui.BeginPopup( popupName ) ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup ); -// -// if( ModPanel.DrawSortOrder( mod.Data, Penumbra.ModManager, this ) ) -// { -// ImGui.CloseCurrentPopup(); -// } -// -// if( firstOpen ) -// { -// ImGui.SetKeyboardFocusHere( mod.Data.Order.FullPath.Length - 1 ); -// } -// } -// -// // === Folder === -// private string _newFolderName = string.Empty; -// private int _expandIndex = -1; -// private bool _expandCollapse; -// private bool _currentlyExpanding; -// -// private void ChangeStatusOfChildren( ModFolder folder, int currentIdx, bool toWhat ) -// { -// var change = false; -// var metaManips = false; -// foreach( var _ in folder.AllMods( Penumbra.ModManager.Config.SortFoldersFirst ) ) -// { -// var (mod, _, _) = Cache.GetMod( currentIdx++ ); -// if( mod != null ) -// { -// change |= mod.Settings.Enabled != toWhat; -// mod!.Settings.Enabled = toWhat; -// metaManips |= mod.Data.Resources.MetaManipulations.Count > 0; -// } -// } -// -// if( !change ) -// { -// return; -// } -// -// Cache.TriggerFilterReset(); -// var collection = Penumbra.CollectionManager.Current; -// if( collection.HasCache ) -// { -// collection.CalculateEffectiveFileList( metaManips, collection == Penumbra.CollectionManager.Default ); -// } -// -// collection.Save(); -// } -// -// private void DrawRenameFolderInput( ModFolder folder ) -// { -// ImGui.SetNextItemWidth( 150 * ImGuiHelpers.GlobalScale ); -// if( !ImGui.InputTextWithHint( "##NewFolderName", "Rename Folder...", ref _newFolderName, 64, -// ImGuiInputTextFlags.EnterReturnsTrue ) ) -// { -// return; -// } -// -// if( _newFolderName.Any() ) -// { -// folder.Rename( _newFolderName ); -// } -// else -// { -// folder.Merge( folder.Parent! ); -// } -// -// _newFolderName = string.Empty; -// } -// -// private void DrawFolderContextMenu( ModFolder folder, int currentIdx, string treeName ) -// { -// if( !ImGui.BeginPopup( treeName ) ) -// { -// return; -// } -// -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup ); -// -// if( ImGui.MenuItem( "Expand All Descendants" ) ) -// { -// _expandIndex = currentIdx; -// _expandCollapse = false; -// } -// -// if( ImGui.MenuItem( "Collapse All Descendants" ) ) -// { -// _expandIndex = currentIdx; -// _expandCollapse = true; -// } -// -// if( ImGui.MenuItem( "Enable All Descendants" ) ) -// { -// ChangeStatusOfChildren( folder, currentIdx, true ); -// } -// -// if( ImGui.MenuItem( "Disable All Descendants" ) ) -// { -// ChangeStatusOfChildren( folder, currentIdx, false ); -// } -// -// ImGuiHelpers.ScaledDummy( 0, 10 ); -// DrawRenameFolderInput( folder ); -// } -// } -// -// // Main-Interface -// private partial class Selector -// { -// private readonly SettingsInterface _base; -// public readonly ModListCache Cache; -// -// private float _selectorScalingFactor = 1; -// -// public Selector( SettingsInterface ui, IReadOnlySet< string > newMods ) -// { -// _base = ui; -// Cache = new ModListCache( Penumbra.ModManager, newMods ); -// } -// -// private void DrawCollectionButton( string label, string tooltipLabel, float size, ModCollection collection ) -// { -// if( collection == ModCollection.Empty -// || collection == Penumbra.CollectionManager.Current ) -// { -// using var _ = ImGuiRaii.PushStyle( ImGuiStyleVar.Alpha, 0.5f ); -// ImGui.Button( label, Vector2.UnitX * size ); -// } -// else if( ImGui.Button( label, Vector2.UnitX * size ) ) -// { -// _base._menu.CollectionsTab.SetCurrentCollection( collection ); -// } -// -// ImGuiCustom.HoverTooltip( -// $"Switches to the currently set {tooltipLabel} collection, if it is not set to None and it is not the current collection already." ); -// } -// -// private void DrawHeaderBar() -// { -// const float size = 200; -// -// DrawModsSelectorFilter(); -// var textSize = ImGui.CalcTextSize( "Current Collection" ).X + ImGui.GetStyle().ItemInnerSpacing.X; -// var comboSize = size * ImGui.GetIO().FontGlobalScale; -// var offset = comboSize + textSize; -// -// var buttonSize = Math.Max( ImGui.GetWindowContentRegionWidth() -// - offset -// - SelectorPanelWidth * _selectorScalingFactor -// - 3 * ImGui.GetStyle().ItemSpacing.X, 5f ); -// ImGui.SameLine(); -// DrawCollectionButton( "Default", "default", buttonSize, Penumbra.CollectionManager.Default ); -// -// -// ImGui.SameLine(); -// ImGui.SetNextItemWidth( comboSize ); -// using var style = ImGuiRaii.PushStyle( ImGuiStyleVar.ItemSpacing, Vector2.Zero ); -// _base._menu.CollectionsTab.DrawCurrentCollectionSelector( false ); -// } -// -// private void DrawFolderContent( ModFolder folder, ref int idx ) -// { -// // Collection may be manipulated. -// foreach( var item in folder.GetItems( Penumbra.ModManager.Config.SortFoldersFirst ).ToArray() ) -// { -// if( item is ModFolder sub ) -// { -// var (visible, _) = Cache.GetFolder( sub ); -// if( visible ) -// { -// DrawModFolder( sub, ref idx ); -// } -// else -// { -// idx += sub.TotalDescendantMods(); -// } -// } -// else if( item is Mods.Mod _ ) -// { -// var (mod, visible, color) = Cache.GetMod( idx ); -// if( mod != null && visible ) -// { -// DrawMod( mod, idx++, color ); -// } -// else -// { -// ++idx; -// } -// } -// } -// } -// -// private void DrawModFolder( ModFolder folder, ref int idx ) -// { -// var treeName = $"{folder.Name}##{folder.FullName}"; -// var open = ImGui.TreeNodeEx( treeName ); -// using var raii = ImGuiRaii.DeferredEnd( ImGui.TreePop, open ); -// -// if( idx == _expandIndex ) -// { -// _currentlyExpanding = true; -// } -// -// if( _currentlyExpanding ) -// { -// ImGui.SetNextItemOpen( !_expandCollapse ); -// } -// -// if( ImGui.IsItemClicked( ImGuiMouseButton.Right ) ) -// { -// _newFolderName = string.Empty; -// ImGui.OpenPopup( treeName ); -// } -// -// DrawFolderContextMenu( folder, idx, treeName ); -// DragDropTarget( folder ); -// DragDropSourceFolder( folder ); -// -// if( open ) -// { -// DrawFolderContent( folder, ref idx ); -// } -// else -// { -// idx += folder.TotalDescendantMods(); -// } -// -// if( idx == _expandIndex ) -// { -// _currentlyExpanding = false; -// _expandIndex = -1; -// } -// } -// -// private void DrawMod( Mods.FullMod mod, int modIndex, uint color ) -// { -// using var colorRaii = ImGuiRaii.PushColor( ImGuiCol.Text, color, color != 0 ); -// -// var selected = ImGui.Selectable( $"{mod.Data.Meta.Name}##{modIndex}", modIndex == _index ); -// colorRaii.Pop(); -// -// var popupName = $"##SortOrderPopup{modIndex}"; -// var firstOpen = false; -// if( ImGui.IsItemClicked( ImGuiMouseButton.Right ) ) -// { -// ImGui.OpenPopup( popupName ); -// firstOpen = true; -// } -// -// DragDropTarget( mod.Data.Order.ParentFolder ); -// DragDropSourceMod( modIndex, mod.Data.Meta.Name ); -// -// DrawModOrderPopup( popupName, mod, firstOpen ); -// -// if( selected ) -// { -// SetSelection( modIndex, mod ); -// } -// } -// -// public void Draw() -// { -// if( Cache.Update() ) -// { -// if( _nextDir.Any() ) -// { -// SelectModByDir( _nextDir ); -// _nextDir = string.Empty; -// } -// else if( Mod != null ) -// { -// SelectModByDir( Mod.Data.BasePath.Name ); -// } -// } -// -// _selectorScalingFactor = ImGuiHelpers.GlobalScale -// * ( Penumbra.Config.ScaleModSelector -// ? ImGui.GetWindowWidth() / SettingsMenu.MinSettingsSize.X -// : 1f ); -// // Selector pane -// DrawHeaderBar(); -// using var style = ImGuiRaii.PushStyle( ImGuiStyleVar.ItemSpacing, Vector2.Zero ); -// ImGui.BeginGroup(); -// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndGroup ) -// .Push( ImGui.EndChild ); -// // Inlay selector list -// if( ImGui.BeginChild( LabelSelectorList, -// new Vector2( SelectorPanelWidth * _selectorScalingFactor, -ImGui.GetFrameHeightWithSpacing() ), -// true, ImGuiWindowFlags.HorizontalScrollbar ) ) -// { -// style.Push( ImGuiStyleVar.IndentSpacing, 12.5f ); -// -// var modIndex = 0; -// DrawFolderContent( Penumbra.ModManager.StructuredMods, ref modIndex ); -// style.Pop(); -// } -// -// raii.Pop(); -// -// DrawModsSelectorButtons(); -// -// style.Pop(); -// DrawModHelpPopup(); -// -// DrawDeleteModal(); -// } -// } -//} \ No newline at end of file diff --git a/Penumbra/UI/ConfigWindow.ModsTab.cs b/Penumbra/UI/ConfigWindow.ModsTab.cs index dd5dddc7..de60ef03 100644 --- a/Penumbra/UI/ConfigWindow.ModsTab.cs +++ b/Penumbra/UI/ConfigWindow.ModsTab.cs @@ -1,18 +1,17 @@ -using System; -using System.Numerics; -using Dalamud.Interface; using ImGuiNET; using OtterGui; using OtterGui.Raii; using Penumbra.Collections; using Penumbra.Mods; using Penumbra.UI.Classes; +using System; +using System.Numerics; namespace Penumbra.UI; public partial class ConfigWindow { - public void DrawModsTab() + private void DrawModsTab() { if( !Penumbra.ModManager.Valid ) { @@ -103,12 +102,10 @@ public partial class ConfigWindow private bool _valid; private ModFileSystem.Leaf _leaf = null!; - private Mod _mod = null!; + private Mod _mod = null!; public ModPanel( ConfigWindow window ) - { - _window = window; - } + => _window = window; public void Draw( ModFileSystemSelector selector ) { diff --git a/Penumbra/UI/ConfigWindow.SettingsTab.cs b/Penumbra/UI/ConfigWindow.SettingsTab.cs index 839809e0..9278faa9 100644 --- a/Penumbra/UI/ConfigWindow.SettingsTab.cs +++ b/Penumbra/UI/ConfigWindow.SettingsTab.cs @@ -47,10 +47,13 @@ public partial class ConfigWindow DrawAdvancedSettings(); } + // Changing the base mod directory. private string? _newModDirectory; private readonly FileDialogManager _dialogManager = new(); - private bool _dialogOpen; + private bool _dialogOpen; // For toggling on/off. + // Do not change the directory without explicitly pressing enter or this button. + // Shows up only if the current input does not correspond to the current directory. private static bool DrawPressEnterWarning( string old, float width ) { using var color = ImRaii.PushColor( ImGuiCol.Button, Colors.PressEnterWarningBg ); @@ -58,9 +61,11 @@ public partial class ConfigWindow return ImGui.Button( $"Press Enter or Click Here to Save (Current Directory: {old})", w ); } + // Draw a directory picker button that toggles the directory picker. + // Selecting a directory does behave the same as writing in the text input, i.e. needs to be saved. private void DrawDirectoryPickerButton() { - if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Folder.ToIconString(), ImGui.GetFrameHeight() * Vector2.One, + if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Folder.ToIconString(), _window._iconButtonSize, "Select a directory via dialog.", false, true ) ) { if( _dialogOpen ) @@ -71,6 +76,8 @@ public partial class ConfigWindow else { _newModDirectory ??= Penumbra.Config.ModDirectory; + // Use the current input as start directory if it exists, + // otherwise the current mod directory, otherwise the current application directory. var startDir = Directory.Exists( _newModDirectory ) ? _newModDirectory : Directory.Exists( Penumbra.Config.ModDirectory ) @@ -103,13 +110,15 @@ public partial class ConfigWindow } } + // Draw the text input for the mod directory, + // as well as the directory picker button and the enter warning. private void DrawRootFolder() { _newModDirectory ??= Penumbra.Config.ModDirectory; var spacing = 3 * ImGuiHelpers.GlobalScale; using var group = ImRaii.Group(); - ImGui.SetNextItemWidth( _window._inputTextWidth.X - spacing - ImGui.GetFrameHeight() ); + ImGui.SetNextItemWidth( _window._inputTextWidth.X - spacing - _window._iconButtonSize.X ); var save = ImGui.InputText( "##rootDirectory", ref _newModDirectory, 255, ImGuiInputTextFlags.EnterReturnsTrue ); using var style = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing, new Vector2( spacing, 0 ) ); ImGui.SameLine(); @@ -127,18 +136,14 @@ public partial class ConfigWindow var pos = ImGui.GetCursorPosX(); ImGui.NewLine(); - if( Penumbra.Config.ModDirectory == _newModDirectory || _newModDirectory.Length == 0 ) - { - return; - } - - if( save || DrawPressEnterWarning( Penumbra.Config.ModDirectory, pos ) ) + if( Penumbra.Config.ModDirectory != _newModDirectory + && _newModDirectory.Length == 0 + && ( save || DrawPressEnterWarning( Penumbra.Config.ModDirectory, pos ) ) ) { Penumbra.ModManager.DiscoverMods( _newModDirectory ); } } - private static void DrawRediscoverButton() { DrawOpenDirectoryButton( 0, Penumbra.ModManager.BasePath, Penumbra.ModManager.Valid ); diff --git a/Penumbra/UI/ConfigWindow.cs b/Penumbra/UI/ConfigWindow.cs index f98ddb0a..fec85594 100644 --- a/Penumbra/UI/ConfigWindow.cs +++ b/Penumbra/UI/ConfigWindow.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Numerics; -using System.Reflection; using Dalamud.Interface; using Dalamud.Interface.Windowing; using ImGuiNET; @@ -28,7 +27,7 @@ public sealed partial class ConfigWindow : Window, IDisposable { _penumbra = penumbra; _settingsTab = new SettingsTab( this ); - _selector = new ModFileSystemSelector( _penumbra.ModFileSystem, new HashSet< Mod >() ); // TODO + _selector = new ModFileSystemSelector( _penumbra.ModFileSystem ); _modPanel = new ModPanel( this ); _collectionsTab = new CollectionsTab( this ); _effectiveTab = new EffectiveTab(); diff --git a/Penumbra/Util/ArrayExtensions.cs b/Penumbra/Util/ArrayExtensions.cs index 8cce7065..8953426f 100644 --- a/Penumbra/Util/ArrayExtensions.cs +++ b/Penumbra/Util/ArrayExtensions.cs @@ -7,36 +7,50 @@ namespace Penumbra.Util; public static class ArrayExtensions { + // Iterate over enumerables with additional index. public static IEnumerable< (T, int) > WithIndex< T >( this IEnumerable< T > list ) => list.Select( ( x, i ) => ( x, i ) ); - public static int IndexOf< T >( this IReadOnlyList< T > array, Predicate< T > predicate ) + + // Find the index of the first object fulfilling predicate's criteria in the given list. + // Returns -1 if no such object is found. + public static int IndexOf< T >( this IEnumerable< T > array, Predicate< T > predicate ) { - for( var i = 0; i < array.Count; ++i ) + var i = 0; + foreach( var obj in array ) { - if( predicate( array[ i ] ) ) + if( predicate( obj ) ) { return i; } + + ++i; } return -1; } - public static int IndexOf< T >( this IReadOnlyList< T > array, T needle ) + // Find the index of the first occurrence of needle in the given list. + // Returns -1 if needle is not contained in the list. + public static int IndexOf< T >( this IEnumerable< T > array, T needle ) where T : notnull { - for( var i = 0; i < array.Count; ++i ) + var i = 0; + foreach( var obj in array ) { - if( needle!.Equals( array[ i ] ) ) + if( needle.Equals( obj ) ) { return i; } + + ++i; } return -1; } - public static bool FindFirst< T >( this IReadOnlyList< T > array, Predicate< T > predicate, [NotNullWhen( true )] out T? result ) + // Find the first object fulfilling predicate's criteria in the given list, if one exists. + // Returns true if an object is found, false otherwise. + public static bool FindFirst< T >( this IEnumerable< T > array, Predicate< T > predicate, [NotNullWhen( true )] out T? result ) { foreach( var obj in array ) { @@ -51,7 +65,9 @@ public static class ArrayExtensions return false; } - public static bool FindFirst< T >( this IReadOnlyList< T > array, T needle, [NotNullWhen( true )] out T? result ) where T : IEquatable< T > + // Find the first occurrence of needle in the given list and return the value contained in the list in result. + // Returns true if an object is found, false otherwise. + public static bool FindFirst< T >( this IEnumerable< T > array, T needle, [NotNullWhen( true )] out T? result ) where T : notnull { foreach( var obj in array ) { diff --git a/Penumbra/Util/BinaryReaderExtensions.cs b/Penumbra/Util/BinaryReaderExtensions.cs deleted file mode 100644 index dec2d44d..00000000 --- a/Penumbra/Util/BinaryReaderExtensions.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Penumbra.Util; - -public static class BinaryReaderExtensions -{ - /// - /// Reads a structure from the current stream position. - /// - /// - /// The structure to read in to - /// The file data as a structure - public static T ReadStructure< T >( this BinaryReader br ) where T : struct - { - ReadOnlySpan< byte > data = br.ReadBytes( Unsafe.SizeOf< T >() ); - - return MemoryMarshal.Read< T >( data ); - } - - /// - /// Reads many structures from the current stream position. - /// - /// - /// The number of T to read from the stream - /// The structure to read in to - /// A list containing the structures read from the stream - public static List< T > ReadStructures< T >( this BinaryReader br, int count ) where T : struct - { - var size = Marshal.SizeOf< T >(); - var data = br.ReadBytes( size * count ); - - var list = new List< T >( count ); - - for( var i = 0; i < count; i++ ) - { - var offset = size * i; - var span = new ReadOnlySpan< byte >( data, offset, size ); - - list.Add( MemoryMarshal.Read< T >( span ) ); - } - - return list; - } - - /// - /// Seeks this BinaryReader's position to the given offset. Syntactic sugar. - /// - public static void Seek( this BinaryReader br, long offset ) - { - br.BaseStream.Position = offset; - } -} \ No newline at end of file diff --git a/Penumbra/Util/FixedUlongStringEnumConverter.cs b/Penumbra/Util/FixedUlongStringEnumConverter.cs index 1ab68e4e..750422b4 100644 --- a/Penumbra/Util/FixedUlongStringEnumConverter.cs +++ b/Penumbra/Util/FixedUlongStringEnumConverter.cs @@ -75,7 +75,7 @@ public static partial class JsonExtensions return reader; } - public static JsonReader ReadAndAssert( this JsonReader reader ) + private static JsonReader ReadAndAssert( this JsonReader reader ) { if( reader == null ) { diff --git a/Penumbra/Util/PenumbraSqPackStream.cs b/Penumbra/Util/PenumbraSqPackStream.cs index 8e26d45b..e338a1d5 100644 --- a/Penumbra/Util/PenumbraSqPackStream.cs +++ b/Penumbra/Util/PenumbraSqPackStream.cs @@ -4,6 +4,7 @@ using System.IO; using System.IO.Compression; using System.Runtime.InteropServices; using Lumina.Data.Structs; +using Lumina.Extensions; namespace Penumbra.Util;