mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Fixes regarding collection settings, change self-conflict behaviour by iterating through options instead of through files, button to clean up collection settings.
This commit is contained in:
parent
ac19a599ed
commit
d7214cd851
8 changed files with 279 additions and 56 deletions
|
|
@ -53,6 +53,10 @@ namespace Penumbra.Meta
|
||||||
public IEnumerable< (MetaManipulation, Mod.Mod) > Manipulations
|
public IEnumerable< (MetaManipulation, Mod.Mod) > Manipulations
|
||||||
=> _currentManipulations.Select( kvp => ( kvp.Key, kvp.Value ) );
|
=> _currentManipulations.Select( kvp => ( kvp.Key, kvp.Value ) );
|
||||||
|
|
||||||
|
public IEnumerable< (GamePath, FileInfo) > Files
|
||||||
|
=> _currentFiles.Where( kvp => kvp.Value.CurrentFile != null )
|
||||||
|
.Select( kvp => ( kvp.Key, kvp.Value.CurrentFile! ) );
|
||||||
|
|
||||||
public int Count
|
public int Count
|
||||||
=> _currentManipulations.Count;
|
=> _currentManipulations.Count;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ namespace Penumbra.Mods
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateSettings()
|
public void UpdateSettings( bool forceSave )
|
||||||
{
|
{
|
||||||
if( Cache == null )
|
if( Cache == null )
|
||||||
{
|
{
|
||||||
|
|
@ -122,7 +122,7 @@ namespace Penumbra.Mods
|
||||||
changes |= mod.FixSettings();
|
changes |= mod.FixSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
if( changes )
|
if( forceSave || changes )
|
||||||
{
|
{
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
|
|
@ -133,7 +133,7 @@ namespace Penumbra.Mods
|
||||||
PluginLog.Debug( "Recalculating effective file list for {CollectionName} [{WithMetaManipulations}] [{IsActiveCollection}]", Name,
|
PluginLog.Debug( "Recalculating effective file list for {CollectionName} [{WithMetaManipulations}] [{IsActiveCollection}]", Name,
|
||||||
withMetaManipulations, activeCollection );
|
withMetaManipulations, activeCollection );
|
||||||
Cache ??= new ModCollectionCache( Name, modDir );
|
Cache ??= new ModCollectionCache( Name, modDir );
|
||||||
UpdateSettings();
|
UpdateSettings( false );
|
||||||
Cache.CalculateEffectiveFileList();
|
Cache.CalculateEffectiveFileList();
|
||||||
if( withMetaManipulations )
|
if( withMetaManipulations )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,16 @@
|
||||||
using System;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Lumina.Data.Parsing;
|
using System.Runtime.InteropServices;
|
||||||
|
using Dalamud.Logging;
|
||||||
using Penumbra.GameData.Util;
|
using Penumbra.GameData.Util;
|
||||||
using Penumbra.Meta;
|
using Penumbra.Meta;
|
||||||
using Penumbra.Mod;
|
using Penumbra.Mod;
|
||||||
|
using Penumbra.Structs;
|
||||||
|
using Penumbra.Util;
|
||||||
|
|
||||||
namespace Penumbra.Mods
|
namespace Penumbra.Mods
|
||||||
{
|
{
|
||||||
|
|
@ -13,25 +18,80 @@ namespace Penumbra.Mods
|
||||||
// It will only be setup if a collection gets activated in any way.
|
// It will only be setup if a collection gets activated in any way.
|
||||||
public class ModCollectionCache
|
public class ModCollectionCache
|
||||||
{
|
{
|
||||||
|
// Shared caches to avoid allocations.
|
||||||
|
private static readonly BitArray FileSeen = new( 256 );
|
||||||
|
private static readonly Dictionary< GamePath, Mod.Mod > RegisteredFiles = new( 256 );
|
||||||
|
|
||||||
public readonly Dictionary< string, Mod.Mod > AvailableMods = new();
|
public readonly Dictionary< string, Mod.Mod > AvailableMods = new();
|
||||||
|
|
||||||
public readonly Dictionary< GamePath, FileInfo > ResolvedFiles = new();
|
public readonly Dictionary< GamePath, FileInfo > ResolvedFiles = new();
|
||||||
public readonly Dictionary< GamePath, GamePath > SwappedFiles = new();
|
public readonly Dictionary< GamePath, GamePath > SwappedFiles = new();
|
||||||
|
public readonly HashSet< FileInfo > MissingFiles = new();
|
||||||
public readonly MetaManager MetaManipulations;
|
public readonly MetaManager MetaManipulations;
|
||||||
|
|
||||||
public ModCollectionCache( string collectionName, DirectoryInfo tempDir )
|
public ModCollectionCache( string collectionName, DirectoryInfo tempDir )
|
||||||
=> MetaManipulations = new MetaManager( collectionName, ResolvedFiles, tempDir );
|
=> MetaManipulations = new MetaManager( collectionName, ResolvedFiles, tempDir );
|
||||||
|
|
||||||
private void AddFiles( Dictionary< GamePath, Mod.Mod > registeredFiles, Mod.Mod mod )
|
private static void ResetFileSeen( int size )
|
||||||
{
|
{
|
||||||
foreach( var file in mod.Data.Resources.ModFiles )
|
if( size < FileSeen.Length )
|
||||||
{
|
{
|
||||||
var gamePaths = mod.GetFiles( file );
|
FileSeen.Length = size;
|
||||||
foreach( var gamePath in gamePaths )
|
FileSeen.SetAll( false );
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if( !registeredFiles.TryGetValue( gamePath, out var oldMod ) )
|
FileSeen.SetAll( false );
|
||||||
|
FileSeen.Length = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CalculateEffectiveFileList()
|
||||||
{
|
{
|
||||||
registeredFiles.Add( gamePath, mod );
|
ResolvedFiles.Clear();
|
||||||
|
SwappedFiles.Clear();
|
||||||
|
MissingFiles.Clear();
|
||||||
|
RegisteredFiles.Clear();
|
||||||
|
|
||||||
|
foreach( var mod in AvailableMods.Values
|
||||||
|
.Where( m => m.Settings.Enabled )
|
||||||
|
.OrderByDescending( m => m.Settings.Priority ) )
|
||||||
|
{
|
||||||
|
mod.Cache.ClearFileConflicts();
|
||||||
|
AddFiles( mod );
|
||||||
|
AddSwaps( mod );
|
||||||
|
}
|
||||||
|
|
||||||
|
AddMetaFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void AddFiles( Mod.Mod mod )
|
||||||
|
{
|
||||||
|
ResetFileSeen( mod.Data.Resources.ModFiles.Count );
|
||||||
|
// Iterate in reverse so that later groups take precedence before earlier ones.
|
||||||
|
foreach( var group in mod.Data.Meta.Groups.Values.Reverse() )
|
||||||
|
{
|
||||||
|
switch( group.SelectionType )
|
||||||
|
{
|
||||||
|
case SelectType.Single:
|
||||||
|
AddFilesForSingle( group, mod );
|
||||||
|
break;
|
||||||
|
case SelectType.Multi:
|
||||||
|
AddFilesForMulti( group, mod );
|
||||||
|
break;
|
||||||
|
default: throw new InvalidEnumArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddRemainingFiles( mod );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddFile( Mod.Mod mod, GamePath gamePath, FileInfo file )
|
||||||
|
{
|
||||||
|
if( !RegisteredFiles.TryGetValue( gamePath, out var oldMod ) )
|
||||||
|
{
|
||||||
|
RegisteredFiles.Add( gamePath, mod );
|
||||||
ResolvedFiles[ gamePath ] = file;
|
ResolvedFiles[ gamePath ] = file;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -39,16 +99,125 @@ namespace Penumbra.Mods
|
||||||
mod.Cache.AddConflict( oldMod, gamePath );
|
mod.Cache.AddConflict( oldMod, gamePath );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddMissingFile( FileInfo file )
|
||||||
|
{
|
||||||
|
switch( file.Extension.ToLowerInvariant() )
|
||||||
|
{
|
||||||
|
case ".meta":
|
||||||
|
case ".rgsp":
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
MissingFiles.Add( file );
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddSwaps( Dictionary< GamePath, Mod.Mod > registeredFiles, Mod.Mod mod )
|
private void AddPathsForOption( Option option, Mod.Mod mod, bool enabled )
|
||||||
|
{
|
||||||
|
foreach( var (file, paths) in option.OptionFiles )
|
||||||
|
{
|
||||||
|
var fullPath = Path.Combine( mod.Data.BasePath.FullName, file );
|
||||||
|
var idx = mod.Data.Resources.ModFiles.IndexOf( f => f.FullName == fullPath );
|
||||||
|
if( idx < 0 )
|
||||||
|
{
|
||||||
|
AddMissingFile( new FileInfo( fullPath ) );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var registeredFile = mod.Data.Resources.ModFiles[ idx ];
|
||||||
|
registeredFile.Refresh();
|
||||||
|
if( !registeredFile.Exists )
|
||||||
|
{
|
||||||
|
AddMissingFile( registeredFile );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSeen.Set( idx, true );
|
||||||
|
if( enabled )
|
||||||
|
{
|
||||||
|
foreach( var path in paths )
|
||||||
|
{
|
||||||
|
AddFile( mod, path, registeredFile );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddFilesForSingle( OptionGroup singleGroup, Mod.Mod mod )
|
||||||
|
{
|
||||||
|
Debug.Assert( singleGroup.SelectionType == SelectType.Single );
|
||||||
|
|
||||||
|
if( !mod.Settings.Settings.TryGetValue( singleGroup.GroupName, out var setting ) )
|
||||||
|
{
|
||||||
|
setting = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for( var i = 0; i < singleGroup.Options.Count; ++i )
|
||||||
|
{
|
||||||
|
AddPathsForOption( singleGroup.Options[ i ], mod, setting == i );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddFilesForMulti( OptionGroup multiGroup, Mod.Mod mod )
|
||||||
|
{
|
||||||
|
Debug.Assert( multiGroup.SelectionType == SelectType.Multi );
|
||||||
|
|
||||||
|
if( !mod.Settings.Settings.TryGetValue( multiGroup.GroupName, out var setting ) )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also iterate options in reverse so that later options take precedence before earlier ones.
|
||||||
|
for( var i = multiGroup.Options.Count - 1; i >= 0; --i )
|
||||||
|
{
|
||||||
|
AddPathsForOption( multiGroup.Options[ i ], mod, ( setting & ( 1 << i ) ) != 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddRemainingFiles( Mod.Mod mod )
|
||||||
|
{
|
||||||
|
for( var i = 0; i < mod.Data.Resources.ModFiles.Count; ++i )
|
||||||
|
{
|
||||||
|
if( FileSeen.Get( i ) )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var file = mod.Data.Resources.ModFiles[ i ];
|
||||||
|
file.Refresh();
|
||||||
|
if( file.Exists )
|
||||||
|
{
|
||||||
|
AddFile( mod, new GamePath( file, mod.Data.BasePath ), file );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MissingFiles.Add( file );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddMetaFiles()
|
||||||
|
{
|
||||||
|
foreach( var (gamePath, file) in MetaManipulations.Files )
|
||||||
|
{
|
||||||
|
if( RegisteredFiles.TryGetValue( gamePath, out var mod ) )
|
||||||
|
{
|
||||||
|
PluginLog.Warning(
|
||||||
|
$"The meta manipulation file {gamePath} was already completely replaced by {mod.Data.Meta.Name}. This is probably a mistake. Using the custom file {file.FullName}." );
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolvedFiles[ gamePath ] = file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddSwaps( Mod.Mod mod )
|
||||||
{
|
{
|
||||||
foreach( var swap in mod.Data.Meta.FileSwaps )
|
foreach( var swap in mod.Data.Meta.FileSwaps )
|
||||||
{
|
{
|
||||||
if( !registeredFiles.TryGetValue( swap.Key, out var oldMod ) )
|
if( !RegisteredFiles.TryGetValue( swap.Key, out var oldMod ) )
|
||||||
{
|
{
|
||||||
registeredFiles.Add( swap.Key, mod );
|
RegisteredFiles.Add( swap.Key, mod );
|
||||||
SwappedFiles.Add( swap.Key, swap.Value );
|
SwappedFiles.Add( swap.Key, swap.Value );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -86,22 +255,6 @@ namespace Penumbra.Mods
|
||||||
MetaManipulations.WriteNewFiles();
|
MetaManipulations.WriteNewFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CalculateEffectiveFileList()
|
|
||||||
{
|
|
||||||
ResolvedFiles.Clear();
|
|
||||||
SwappedFiles.Clear();
|
|
||||||
|
|
||||||
var registeredFiles = new Dictionary< GamePath, Mod.Mod >();
|
|
||||||
foreach( var mod in AvailableMods.Values
|
|
||||||
.Where( m => m.Settings.Enabled )
|
|
||||||
.OrderByDescending( m => m.Settings.Priority ) )
|
|
||||||
{
|
|
||||||
mod.Cache.ClearFileConflicts();
|
|
||||||
AddFiles( registeredFiles, mod );
|
|
||||||
AddSwaps( registeredFiles, mod );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveMod( DirectoryInfo basePath )
|
public void RemoveMod( DirectoryInfo basePath )
|
||||||
{
|
{
|
||||||
if( AvailableMods.TryGetValue( basePath.Name, out var mod ) )
|
if( AvailableMods.TryGetValue( basePath.Name, out var mod ) )
|
||||||
|
|
@ -121,7 +274,7 @@ namespace Penumbra.Mods
|
||||||
private class PriorityComparer : IComparer< Mod.Mod >
|
private class PriorityComparer : IComparer< Mod.Mod >
|
||||||
{
|
{
|
||||||
public int Compare( Mod.Mod? x, Mod.Mod? y )
|
public int Compare( Mod.Mod? x, Mod.Mod? y )
|
||||||
=> (x?.Settings.Priority ?? 0).CompareTo( y?.Settings.Priority ?? 0 );
|
=> ( x?.Settings.Priority ?? 0 ).CompareTo( y?.Settings.Priority ?? 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly PriorityComparer Comparer = new();
|
private static readonly PriorityComparer Comparer = new();
|
||||||
|
|
|
||||||
|
|
@ -43,12 +43,14 @@ namespace Penumbra.Structs
|
||||||
|
|
||||||
private bool ApplySingleGroupFiles( RelPath relPath, int selection, HashSet< GamePath > paths )
|
private bool ApplySingleGroupFiles( RelPath relPath, int selection, HashSet< GamePath > paths )
|
||||||
{
|
{
|
||||||
|
// Selection contains the path, merge all GamePaths for this config.
|
||||||
if( Options[ selection ].OptionFiles.TryGetValue( relPath, out var groupPaths ) )
|
if( Options[ selection ].OptionFiles.TryGetValue( relPath, out var groupPaths ) )
|
||||||
{
|
{
|
||||||
paths.UnionWith( groupPaths );
|
paths.UnionWith( groupPaths );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the group contains the file in another selection, return true to skip it for default files.
|
||||||
for( var i = 0; i < Options.Count; ++i )
|
for( var i = 0; i < Options.Count; ++i )
|
||||||
{
|
{
|
||||||
if( i == selection )
|
if( i == selection )
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Numerics;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
|
|
@ -53,6 +54,15 @@ namespace Penumbra.UI.Custom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static partial class ImGuiCustom
|
||||||
|
{
|
||||||
|
public static bool DisableButton( string label, bool condition )
|
||||||
|
{
|
||||||
|
using var alpha = ImGuiRaii.PushStyle( ImGuiStyleVar.Alpha, 0.5f, !condition );
|
||||||
|
return ImGui.Button( label ) && condition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static partial class ImGuiCustom
|
public static partial class ImGuiCustom
|
||||||
{
|
{
|
||||||
public static void PrintIcon( FontAwesomeIcon icon )
|
public static void PrintIcon( FontAwesomeIcon icon )
|
||||||
|
|
|
||||||
|
|
@ -87,13 +87,25 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
if( _manager.Collections.AddCollection( _newCollectionName, settings ) )
|
if( _manager.Collections.AddCollection( _newCollectionName, settings ) )
|
||||||
{
|
{
|
||||||
_manager.Collections.SetCurrentCollection( _manager.Collections.Collections[ _newCollectionName ] );
|
|
||||||
UpdateNames();
|
UpdateNames();
|
||||||
|
SetCurrentCollection( _manager.Collections.Collections[_newCollectionName], true );
|
||||||
}
|
}
|
||||||
|
|
||||||
_newCollectionName = string.Empty;
|
_newCollectionName = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawCleanCollectionButton()
|
||||||
|
{
|
||||||
|
if( ImGui.Button( "Clean Settings" ) )
|
||||||
|
{
|
||||||
|
var changes = ModFunctions.CleanUpCollection( _manager.Collections.CurrentCollection.Settings,
|
||||||
|
_manager.BasePath.EnumerateDirectories() );
|
||||||
|
_manager.Collections.CurrentCollection.UpdateSettings( forceSave: changes );
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiCustom.HoverTooltip( "Remove all stored settings for mods not currently available and fix invalid settings.\nUse at own risk." );
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawNewCollectionInput()
|
private void DrawNewCollectionInput()
|
||||||
{
|
{
|
||||||
ImGui.InputTextWithHint( "##New Collection", "New Collection", ref _newCollectionName, 64 );
|
ImGui.InputTextWithHint( "##New Collection", "New Collection", ref _newCollectionName, 64 );
|
||||||
|
|
@ -113,26 +125,31 @@ namespace Penumbra.UI
|
||||||
|
|
||||||
style.Pop();
|
style.Pop();
|
||||||
|
|
||||||
if( _manager.Collections.Collections.Count > 1
|
var deleteCondition = _manager.Collections.Collections.Count > 1
|
||||||
&& _manager.Collections.CurrentCollection.Name != ModCollection.DefaultCollection )
|
&& _manager.Collections.CurrentCollection.Name != ModCollection.DefaultCollection;
|
||||||
{
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if( ImGui.Button( "Delete Current Collection" ) )
|
if( ImGuiCustom.DisableButton( "Delete Current Collection", deleteCondition) )
|
||||||
{
|
{
|
||||||
_manager.Collections.RemoveCollection( _manager.Collections.CurrentCollection.Name );
|
_manager.Collections.RemoveCollection( _manager.Collections.CurrentCollection.Name );
|
||||||
|
SetCurrentCollection( _manager.Collections.CurrentCollection, true );
|
||||||
UpdateNames();
|
UpdateNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( Penumbra.Config.ShowAdvanced )
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
DrawCleanCollectionButton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetCurrentCollection( int idx )
|
private void SetCurrentCollection( int idx, bool force )
|
||||||
{
|
{
|
||||||
if( idx == _currentCollectionIndex )
|
if( !force && idx == _currentCollectionIndex )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_manager.Collections.SetCurrentCollection( _collections[ idx + 1 ] );
|
_manager.Collections.SetCurrentCollection( _collections[idx + 1] );
|
||||||
_currentCollectionIndex = idx;
|
_currentCollectionIndex = idx;
|
||||||
_selector.Cache.TriggerListReset();
|
_selector.Cache.TriggerListReset();
|
||||||
if( _selector.Mod != null )
|
if( _selector.Mod != null )
|
||||||
|
|
@ -141,12 +158,12 @@ namespace Penumbra.UI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetCurrentCollection( ModCollection collection )
|
public void SetCurrentCollection( ModCollection collection, bool force = false )
|
||||||
{
|
{
|
||||||
var idx = Array.IndexOf( _collections, collection ) - 1;
|
var idx = Array.IndexOf( _collections, collection ) - 1;
|
||||||
if( idx >= 0 )
|
if( idx >= 0 )
|
||||||
{
|
{
|
||||||
SetCurrentCollection( idx );
|
SetCurrentCollection( idx, force );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,7 +176,7 @@ namespace Penumbra.UI
|
||||||
|
|
||||||
if( combo )
|
if( combo )
|
||||||
{
|
{
|
||||||
SetCurrentCollection( index );
|
SetCurrentCollection( index, false );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,18 +222,14 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
ImGui.InputTextWithHint( "##New Character", "New Character Name", ref _newCharacterName, 32 );
|
ImGui.InputTextWithHint( "##New Character", "New Character Name", ref _newCharacterName, 32 );
|
||||||
|
|
||||||
using var style = ImGuiRaii.PushStyle( ImGuiStyleVar.Alpha, 0.5f, _newCharacterName.Length == 0 );
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if( ImGui.Button( "Create New Character Collection" ) && _newCharacterName.Length > 0 )
|
if( ImGuiCustom.DisableButton( "Create New Character Collection", _newCharacterName.Length > 0 ))
|
||||||
{
|
{
|
||||||
_manager.Collections.CreateCharacterCollection( _newCharacterName );
|
_manager.Collections.CreateCharacterCollection( _newCharacterName );
|
||||||
_currentCharacterIndices[ _newCharacterName ] = 0;
|
_currentCharacterIndices[ _newCharacterName ] = 0;
|
||||||
_newCharacterName = string.Empty;
|
_newCharacterName = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
style.Pop();
|
|
||||||
|
|
||||||
ImGuiCustom.HoverTooltip(
|
ImGuiCustom.HoverTooltip(
|
||||||
"A character collection will be used whenever you manually redraw a character with the Name you have set up.\n"
|
"A character collection will be used whenever you manually redraw a character with the Name you have set up.\n"
|
||||||
+ "If you enable automatic character redraws in the Settings tab, penumbra will try to use Character collections for corresponding characters automatically.\n" );
|
+ "If you enable automatic character redraws in the Settings tab, penumbra will try to use Character collections for corresponding characters automatically.\n" );
|
||||||
|
|
|
||||||
|
|
@ -342,6 +342,34 @@ namespace Penumbra.UI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawDebugTabMissingFiles()
|
||||||
|
{
|
||||||
|
if( !ImGui.CollapsingHeader( "Missing Files##Debug" ) )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var manager = Service<ModManager>.Get();
|
||||||
|
var cache = manager.Collections.CurrentCollection.Cache;
|
||||||
|
if( cache == null || !ImGui.BeginTable( "##MissingFilesDebugList", 1, ImGuiTableFlags.RowBg, -Vector2.UnitX))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTable );
|
||||||
|
|
||||||
|
foreach( var file in cache.MissingFiles )
|
||||||
|
{
|
||||||
|
ImGui.TableNextRow();
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
if( ImGui.Selectable( file.FullName ) )
|
||||||
|
{
|
||||||
|
ImGui.SetClipboardText( file.FullName );
|
||||||
|
}
|
||||||
|
ImGuiCustom.HoverTooltip( "Click to copy to clipboard." );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawDebugTab()
|
private void DrawDebugTab()
|
||||||
{
|
{
|
||||||
if( !ImGui.BeginTabItem( "Debug Tab" ) )
|
if( !ImGui.BeginTabItem( "Debug Tab" ) )
|
||||||
|
|
@ -353,6 +381,8 @@ namespace Penumbra.UI
|
||||||
|
|
||||||
DrawDebugTabGeneral();
|
DrawDebugTabGeneral();
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
|
DrawDebugTabMissingFiles();
|
||||||
|
ImGui.NewLine();
|
||||||
DrawDebugTabRedraw();
|
DrawDebugTabRedraw();
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
DrawDebugTabPlayers();
|
DrawDebugTabPlayers();
|
||||||
|
|
|
||||||
|
|
@ -72,5 +72,16 @@ namespace Penumbra.Util
|
||||||
|
|
||||||
array.Swap( idx1, idx2 );
|
array.Swap( idx1, idx2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int IndexOf< T >( this IList< T > array, Func< T, bool > predicate )
|
||||||
|
{
|
||||||
|
for( var i = 0; i < array.Count; ++i )
|
||||||
|
{
|
||||||
|
if( predicate.Invoke( array[ i ] ) )
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue