Change everything in collection caches to use IMod and introduce TemporaryMod.

This commit is contained in:
Ottermandias 2022-06-18 16:00:20 +02:00
parent c578bd3a49
commit fc767589a2
18 changed files with 129 additions and 74 deletions

View file

@ -378,7 +378,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
if( group.Type == SelectType.Single ) if( group.Type == SelectType.Single )
{ {
var name = optionNames[ ^1 ]; var name = optionNames[ ^1 ];
var optionIdx = group.IndexOf( o => o.Name == name ); var optionIdx = group.IndexOf( o => o.Name == optionNames[^1] );
if( optionIdx < 0 ) if( optionIdx < 0 )
{ {
return PenumbraApiEc.OptionMissing; return PenumbraApiEc.OptionMissing;

View file

@ -65,8 +65,8 @@ public partial class ModCollection
internal IReadOnlyDictionary< Utf8GamePath, ModPath > ResolvedFiles internal IReadOnlyDictionary< Utf8GamePath, ModPath > ResolvedFiles
=> _cache?.ResolvedFiles ?? new Dictionary< Utf8GamePath, ModPath >(); => _cache?.ResolvedFiles ?? new Dictionary< Utf8GamePath, ModPath >();
internal IReadOnlyDictionary< string, (SingleArray< Mod >, object?) > ChangedItems internal IReadOnlyDictionary< string, (SingleArray< IMod >, object?) > ChangedItems
=> _cache?.ChangedItems ?? new Dictionary< string, (SingleArray< Mod >, object?) >(); => _cache?.ChangedItems ?? new Dictionary< string, (SingleArray< IMod >, object?) >();
internal IEnumerable< SingleArray< ModConflicts > > AllConflicts internal IEnumerable< SingleArray< ModConflicts > > AllConflicts
=> _cache?.AllConflicts ?? Array.Empty< SingleArray< ModConflicts > >(); => _cache?.AllConflicts ?? Array.Empty< SingleArray< ModConflicts > >();

View file

@ -11,8 +11,8 @@ using Penumbra.Util;
namespace Penumbra.Collections; namespace Penumbra.Collections;
public record struct ModPath( Mod Mod, FullPath Path ); public record struct ModPath( IMod Mod, FullPath Path );
public record ModConflicts( Mod Mod2, List< object > Conflicts, bool HasPriority, bool Solved ); public record ModConflicts( IMod Mod2, List< object > Conflicts, bool HasPriority, bool Solved );
public partial class ModCollection public partial class ModCollection
{ {
@ -21,15 +21,15 @@ public partial class ModCollection
private class Cache : IDisposable private class Cache : IDisposable
{ {
private readonly ModCollection _collection; private readonly ModCollection _collection;
private readonly SortedList< string, (SingleArray< Mod >, object?) > _changedItems = new(); private readonly SortedList< string, (SingleArray< IMod >, object?) > _changedItems = new();
public readonly Dictionary< Utf8GamePath, ModPath > ResolvedFiles = new(); public readonly Dictionary< Utf8GamePath, ModPath > ResolvedFiles = new();
public readonly MetaManager MetaManipulations; public readonly MetaManager MetaManipulations;
private readonly Dictionary< Mod, SingleArray< ModConflicts > > _conflicts = new(); private readonly Dictionary< IMod, SingleArray< ModConflicts > > _conflicts = new();
public IEnumerable< SingleArray< ModConflicts > > AllConflicts public IEnumerable< SingleArray< ModConflicts > > AllConflicts
=> _conflicts.Values; => _conflicts.Values;
public SingleArray< ModConflicts > Conflicts( Mod mod ) public SingleArray< ModConflicts > Conflicts( IMod mod )
=> _conflicts.TryGetValue( mod, out var c ) ? c : new SingleArray< ModConflicts >(); => _conflicts.TryGetValue( mod, out var c ) ? c : new SingleArray< ModConflicts >();
// Count the number of changes of the effective file list. // Count the number of changes of the effective file list.
@ -38,7 +38,7 @@ public partial class ModCollection
private int _changedItemsSaveCounter = -1; private int _changedItemsSaveCounter = -1;
// Obtain currently changed items. Computes them if they haven't been computed before. // Obtain currently changed items. Computes them if they haven't been computed before.
public IReadOnlyDictionary< string, (SingleArray< Mod >, object?) > ChangedItems public IReadOnlyDictionary< string, (SingleArray< IMod >, object?) > ChangedItems
{ {
get get
{ {
@ -178,13 +178,13 @@ public partial class ModCollection
} }
} }
public void ReloadMod( Mod mod, bool addMetaChanges ) public void ReloadMod( IMod mod, bool addMetaChanges )
{ {
RemoveMod( mod, addMetaChanges ); RemoveMod( mod, addMetaChanges );
AddMod( mod, addMetaChanges ); AddMod( mod, addMetaChanges );
} }
public void RemoveMod( Mod mod, bool addMetaChanges ) public void RemoveMod( IMod mod, bool addMetaChanges )
{ {
var conflicts = Conflicts( mod ); var conflicts = Conflicts( mod );
@ -243,37 +243,40 @@ public partial class ModCollection
// Add all files and possibly manipulations of a given mod according to its settings in this collection. // Add all files and possibly manipulations of a given mod according to its settings in this collection.
public void AddMod( Mod mod, bool addMetaChanges ) public void AddMod( IMod mod, bool addMetaChanges )
{ {
var settings = _collection[ mod.Index ].Settings; if( mod.Index >= 0 )
if( settings is not { Enabled: true } )
{ {
return; var settings = _collection[ mod.Index ].Settings;
} if( settings is not { Enabled: true } )
foreach( var (group, groupIndex) in mod.Groups.WithIndex().OrderByDescending( g => g.Item1.Priority ) )
{
if( group.Count == 0 )
{ {
continue; return;
} }
var config = settings.Settings[ groupIndex ]; foreach( var (group, groupIndex) in mod.Groups.WithIndex().OrderByDescending( g => g.Item1.Priority ) )
switch( group.Type )
{ {
case SelectType.Single: if( group.Count == 0 )
AddSubMod( group[ ( int )config ], mod );
break;
case SelectType.Multi:
{ {
foreach( var (option, _) in group.WithIndex() continue;
.OrderByDescending( p => group.OptionPriority( p.Item2 ) ) }
.Where( p => ( ( 1 << p.Item2 ) & config ) != 0 ) )
{
AddSubMod( option, mod );
}
break; var config = settings.Settings[ groupIndex ];
switch( group.Type )
{
case SelectType.Single:
AddSubMod( group[ ( int )config ], mod );
break;
case SelectType.Multi:
{
foreach( var (option, _) in group.WithIndex()
.OrderByDescending( p => group.OptionPriority( p.Item2 ) )
.Where( p => ( ( 1 << p.Item2 ) & config ) != 0 ) )
{
AddSubMod( option, mod );
}
break;
}
} }
} }
} }
@ -297,7 +300,7 @@ public partial class ModCollection
} }
// Add all files and possibly manipulations of a specific submod // Add all files and possibly manipulations of a specific submod
private void AddSubMod( ISubMod subMod, Mod parentMod ) private void AddSubMod( ISubMod subMod, IMod parentMod )
{ {
foreach( var (path, file) in subMod.Files.Concat( subMod.FileSwaps ) ) foreach( var (path, file) in subMod.Files.Concat( subMod.FileSwaps ) )
{ {
@ -320,7 +323,7 @@ public partial class ModCollection
// For different mods, higher mod priority takes precedence before option group priority, // For different mods, higher mod priority takes precedence before option group priority,
// which takes precedence before option priority, which takes precedence before ordering. // which takes precedence before option priority, which takes precedence before ordering.
// Inside the same mod, conflicts are not recorded. // Inside the same mod, conflicts are not recorded.
private void AddFile( Utf8GamePath path, FullPath file, Mod mod ) private void AddFile( Utf8GamePath path, FullPath file, IMod mod )
{ {
if( ResolvedFiles.TryAdd( path, new ModPath( mod, file ) ) ) if( ResolvedFiles.TryAdd( path, new ModPath( mod, file ) ) )
{ {
@ -343,7 +346,7 @@ public partial class ModCollection
// Remove all empty conflict sets for a given mod with the given conflicts. // Remove all empty conflict sets for a given mod with the given conflicts.
// If transitive is true, also removes the corresponding version of the other mod. // If transitive is true, also removes the corresponding version of the other mod.
private void RemoveEmptyConflicts( Mod mod, SingleArray< ModConflicts > oldConflicts, bool transitive ) private void RemoveEmptyConflicts( IMod mod, SingleArray< ModConflicts > oldConflicts, bool transitive )
{ {
var changedConflicts = oldConflicts.Remove( c => var changedConflicts = oldConflicts.Remove( c =>
{ {
@ -372,10 +375,10 @@ public partial class ModCollection
// Add a new conflict between the added mod and the existing mod. // Add a new conflict between the added mod and the existing mod.
// Update all other existing conflicts between the existing mod and other mods if necessary. // Update all other existing conflicts between the existing mod and other mods if necessary.
// Returns if the added mod takes priority before the existing mod. // Returns if the added mod takes priority before the existing mod.
private bool AddConflict( object data, Mod addedMod, Mod existingMod ) private bool AddConflict( object data, IMod addedMod, IMod existingMod )
{ {
var addedPriority = addedMod.Index >= 0 ? _collection[ addedMod.Index ].Settings!.Priority : int.MaxValue; var addedPriority = addedMod.Index >= 0 ? _collection[ addedMod.Index ].Settings!.Priority : addedMod.Priority;
var existingPriority = existingMod.Index >= 0 ? _collection[ existingMod.Index ].Settings!.Priority : int.MaxValue; var existingPriority = existingMod.Index >= 0 ? _collection[ existingMod.Index ].Settings!.Priority : existingMod.Priority;
if( existingPriority < addedPriority ) if( existingPriority < addedPriority )
{ {
@ -417,7 +420,7 @@ public partial class ModCollection
// For different mods, higher mod priority takes precedence before option group priority, // For different mods, higher mod priority takes precedence before option group priority,
// which takes precedence before option priority, which takes precedence before ordering. // which takes precedence before option priority, which takes precedence before ordering.
// Inside the same mod, conflicts are not recorded. // Inside the same mod, conflicts are not recorded.
private void AddManipulation( MetaManipulation manip, Mod mod ) private void AddManipulation( MetaManipulation manip, IMod mod )
{ {
if( !MetaManipulations.TryGetValue( manip, out var existingMod ) ) if( !MetaManipulations.TryGetValue( manip, out var existingMod ) )
{ {
@ -463,7 +466,7 @@ public partial class ModCollection
{ {
if( !_changedItems.TryGetValue( name, out var data ) ) if( !_changedItems.TryGetValue( name, out var data ) )
{ {
_changedItems.Add( name, ( new SingleArray< Mod >( modPath.Mod ), obj ) ); _changedItems.Add( name, ( new SingleArray< IMod >( modPath.Mod ), obj ) );
} }
else if( !data.Item1.Contains( modPath.Mod ) ) else if( !data.Item1.Contains( modPath.Mod ) )
{ {

View file

@ -14,7 +14,7 @@ public partial class MetaManager
public struct MetaManagerCmp : IDisposable public struct MetaManagerCmp : IDisposable
{ {
public CmpFile? File = null; public CmpFile? File = null;
public readonly Dictionary< RspManipulation, Mod > Manipulations = new(); public readonly Dictionary< RspManipulation, IMod > Manipulations = new();
public MetaManagerCmp() public MetaManagerCmp()
{ } { }
@ -39,7 +39,7 @@ public partial class MetaManager
Manipulations.Clear(); Manipulations.Clear();
} }
public bool ApplyMod( RspManipulation m, Mod mod ) public bool ApplyMod( RspManipulation m, IMod mod )
{ {
#if USE_CMP #if USE_CMP
Manipulations[ m ] = mod; Manipulations[ m ] = mod;

View file

@ -17,7 +17,7 @@ public partial class MetaManager
{ {
public readonly ExpandedEqdpFile?[] Files = new ExpandedEqdpFile?[CharacterUtility.NumEqdpFiles - 2]; // TODO: female Hrothgar public readonly ExpandedEqdpFile?[] Files = new ExpandedEqdpFile?[CharacterUtility.NumEqdpFiles - 2]; // TODO: female Hrothgar
public readonly Dictionary< EqdpManipulation, Mod > Manipulations = new(); public readonly Dictionary< EqdpManipulation, IMod > Manipulations = new();
public MetaManagerEqdp() public MetaManagerEqdp()
{ } { }
@ -51,7 +51,7 @@ public partial class MetaManager
Manipulations.Clear(); Manipulations.Clear();
} }
public bool ApplyMod( EqdpManipulation m, Mod mod ) public bool ApplyMod( EqdpManipulation m, IMod mod )
{ {
#if USE_EQDP #if USE_EQDP
Manipulations[ m ] = mod; Manipulations[ m ] = mod;

View file

@ -14,7 +14,7 @@ public partial class MetaManager
public struct MetaManagerEqp : IDisposable public struct MetaManagerEqp : IDisposable
{ {
public ExpandedEqpFile? File = null; public ExpandedEqpFile? File = null;
public readonly Dictionary< EqpManipulation, Mod > Manipulations = new(); public readonly Dictionary< EqpManipulation, IMod > Manipulations = new();
public MetaManagerEqp() public MetaManagerEqp()
{ } { }
@ -39,7 +39,7 @@ public partial class MetaManager
Manipulations.Clear(); Manipulations.Clear();
} }
public bool ApplyMod( EqpManipulation m, Mod mod ) public bool ApplyMod( EqpManipulation m, IMod mod )
{ {
#if USE_EQP #if USE_EQP
Manipulations[ m ] = mod; Manipulations[ m ] = mod;

View file

@ -18,7 +18,7 @@ public partial class MetaManager
public EstFile? BodyFile = null; public EstFile? BodyFile = null;
public EstFile? HeadFile = null; public EstFile? HeadFile = null;
public readonly Dictionary< EstManipulation, Mod > Manipulations = new(); public readonly Dictionary< EstManipulation, IMod > Manipulations = new();
public MetaManagerEst() public MetaManagerEst()
{ } { }
@ -51,7 +51,7 @@ public partial class MetaManager
Manipulations.Clear(); Manipulations.Clear();
} }
public bool ApplyMod( EstManipulation m, Mod mod ) public bool ApplyMod( EstManipulation m, IMod mod )
{ {
#if USE_EST #if USE_EST
Manipulations[ m ] = mod; Manipulations[ m ] = mod;

View file

@ -14,7 +14,7 @@ public partial class MetaManager
public struct MetaManagerGmp : IDisposable public struct MetaManagerGmp : IDisposable
{ {
public ExpandedGmpFile? File = null; public ExpandedGmpFile? File = null;
public readonly Dictionary< GmpManipulation, Mod > Manipulations = new(); public readonly Dictionary< GmpManipulation, IMod > Manipulations = new();
public MetaManagerGmp() public MetaManagerGmp()
{ } { }
@ -38,7 +38,7 @@ public partial class MetaManager
} }
} }
public bool ApplyMod( GmpManipulation m, Mod mod ) public bool ApplyMod( GmpManipulation m, IMod mod )
{ {
#if USE_GMP #if USE_GMP
Manipulations[ m ] = mod; Manipulations[ m ] = mod;

View file

@ -18,7 +18,7 @@ public partial class MetaManager
public readonly struct MetaManagerImc : IDisposable public readonly struct MetaManagerImc : IDisposable
{ {
public readonly Dictionary< Utf8GamePath, ImcFile > Files = new(); public readonly Dictionary< Utf8GamePath, ImcFile > Files = new();
public readonly Dictionary< ImcManipulation, Mod > Manipulations = new(); public readonly Dictionary< ImcManipulation, IMod > Manipulations = new();
private readonly ModCollection _collection; private readonly ModCollection _collection;
private static int _imcManagerCount; private static int _imcManagerCount;
@ -65,7 +65,7 @@ public partial class MetaManager
Manipulations.Clear(); Manipulations.Clear();
} }
public bool ApplyMod( ImcManipulation m, Mod mod ) public bool ApplyMod( ImcManipulation m, IMod mod )
{ {
#if USE_IMC #if USE_IMC
Manipulations[ m ] = mod; Manipulations[ m ] = mod;

View file

@ -30,7 +30,7 @@ public partial class MetaManager : IDisposable
} }
} }
public bool TryGetValue( MetaManipulation manip, [NotNullWhen(true)] out Mod? mod ) public bool TryGetValue( MetaManipulation manip, [NotNullWhen(true)] out IMod? mod )
{ {
mod = manip.ManipulationType switch mod = manip.ManipulationType switch
{ {
@ -86,7 +86,7 @@ public partial class MetaManager : IDisposable
Imc.Dispose(); Imc.Dispose();
} }
public bool ApplyMod( MetaManipulation m, Mod mod ) public bool ApplyMod( MetaManipulation m, IMod mod )
{ {
return m.ManipulationType switch return m.ManipulationType switch
{ {

View file

@ -0,0 +1,19 @@
using System.Collections.Generic;
using OtterGui.Classes;
namespace Penumbra.Mods;
public interface IMod
{
LowerString Name { get; }
public int Index { get; }
public int Priority { get; }
public int TotalManipulations { get; }
public ISubMod Default { get; }
public IReadOnlyList< IModGroup > Groups { get; }
public IEnumerable< ISubMod > AllSubMods { get; }
}

View file

@ -1,13 +1,10 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using Penumbra.GameData.ByteString;
using Penumbra.Util; using Penumbra.Util;
namespace Penumbra.Mods; namespace Penumbra.Mods;
public partial class Mod public partial class Mod : IMod
{ {
public partial class Editor : IDisposable public partial class Editor : IDisposable
{ {
@ -15,7 +12,7 @@ public partial class Mod
public Editor( Mod mod, int groupIdx, int optionIdx ) public Editor( Mod mod, int groupIdx, int optionIdx )
{ {
_mod = mod; _mod = mod;
SetSubMod( groupIdx, optionIdx ); SetSubMod( groupIdx, optionIdx );
GroupIdx = groupIdx; GroupIdx = groupIdx;
_subMod = _mod._default; _subMod = _mod._default;

View file

@ -17,6 +17,10 @@ public partial class Mod
public DirectoryInfo ModPath { get; private set; } public DirectoryInfo ModPath { get; private set; }
public int Index { get; private set; } = -1; public int Index { get; private set; } = -1;
// Unused if Index < 0 but used for special temporary mods.
public int Priority
=> 0;
private Mod( DirectoryInfo modPath ) private Mod( DirectoryInfo modPath )
=> ModPath = modPath; => ModPath = modPath;
@ -30,7 +34,7 @@ public partial class Mod
} }
var mod = new Mod( modPath ); var mod = new Mod( modPath );
if( !mod.Reload(out _) ) if( !mod.Reload( out _ ) )
{ {
// Can not be base path not existing because that is checked before. // Can not be base path not existing because that is checked before.
PluginLog.Error( $"Mod at {modPath} without name is not supported." ); PluginLog.Error( $"Mod at {modPath} without name is not supported." );
@ -40,15 +44,17 @@ public partial class Mod
return mod; return mod;
} }
private bool Reload(out MetaChangeType metaChange) private bool Reload( out MetaChangeType metaChange )
{ {
metaChange = MetaChangeType.Deletion; metaChange = MetaChangeType.Deletion;
ModPath.Refresh(); ModPath.Refresh();
if( !ModPath.Exists ) if( !ModPath.Exists )
{
return false; return false;
}
metaChange = LoadMeta(); metaChange = LoadMeta();
if( metaChange.HasFlag(MetaChangeType.Deletion) || Name.Length == 0 ) if( metaChange.HasFlag( MetaChangeType.Deletion ) || Name.Length == 0 )
{ {
return false; return false;
} }

View file

@ -24,10 +24,11 @@ public enum MetaChangeType : ushort
public sealed partial class Mod public sealed partial class Mod
{ {
public static readonly Mod ForcedFiles = new(new DirectoryInfo( "." )) public static readonly TemporaryMod ForcedFiles = new()
{ {
Name = "Forced Files", Name = "Forced Files",
Index = -1, Index = -1,
Priority = int.MaxValue,
}; };
public const uint CurrentFileVersion = 1; public const uint CurrentFileVersion = 1;

View file

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using OtterGui.Classes;
namespace Penumbra.Mods;
public sealed partial class Mod
{
public class TemporaryMod : IMod
{
public LowerString Name { get; init; } = LowerString.Empty;
public int Index { get; init; } = -2;
public int Priority { get; init; } = int.MaxValue;
public int TotalManipulations
=> Default.Manipulations.Count;
public ISubMod Default { get; } = new SubMod();
public IReadOnlyList< IModGroup > Groups
=> Array.Empty< IModGroup >();
public IEnumerable< ISubMod > AllSubMods
=> new[] { Default };
}
}

View file

@ -23,13 +23,13 @@ public partial class ConfigWindow
private void DrawChangedItemTab() private void DrawChangedItemTab()
{ {
// Functions in here for less pollution. // Functions in here for less pollution.
bool FilterChangedItem( KeyValuePair< string, (SingleArray< Mod >, object?) > item ) bool FilterChangedItem( KeyValuePair< string, (SingleArray< IMod >, object?) > item )
=> ( _changedItemFilter.IsEmpty => ( _changedItemFilter.IsEmpty
|| ChangedItemName( item.Key, item.Value.Item2 ) || ChangedItemName( item.Key, item.Value.Item2 )
.Contains( _changedItemFilter.Lower, StringComparison.InvariantCultureIgnoreCase ) ) .Contains( _changedItemFilter.Lower, StringComparison.InvariantCultureIgnoreCase ) )
&& ( _changedItemModFilter.IsEmpty || item.Value.Item1.Any( m => m.Name.Contains( _changedItemModFilter ) ) ); && ( _changedItemModFilter.IsEmpty || item.Value.Item1.Any( m => m.Name.Contains( _changedItemModFilter ) ) );
void DrawChangedItemColumn( KeyValuePair< string, (SingleArray< Mod >, object?) > item ) void DrawChangedItemColumn( KeyValuePair< string, (SingleArray< IMod >, object?) > item )
{ {
ImGui.TableNextColumn(); ImGui.TableNextColumn();
DrawChangedItem( item.Key, item.Value.Item2, false ); DrawChangedItem( item.Key, item.Value.Item2, false );

View file

@ -112,7 +112,7 @@ public partial class ConfigWindow
{ {
// We can treat all meta manipulations the same, // We can treat all meta manipulations the same,
// we are only really interested in their ToString function here. // we are only really interested in their ToString function here.
static (object, Mod) Convert< T >( KeyValuePair< T, Mod > kvp ) static (object, IMod) Convert< T >( KeyValuePair< T, IMod > kvp )
=> ( kvp.Key!, kvp.Value ); => ( kvp.Key!, kvp.Value );
var it = m.Cmp.Manipulations.Select( Convert ) var it = m.Cmp.Manipulations.Select( Convert )
@ -183,7 +183,7 @@ public partial class ConfigWindow
} }
// Draw a line for a unfiltered/unconverted manipulation and mod-index pair. // Draw a line for a unfiltered/unconverted manipulation and mod-index pair.
private static void DrawLine( (object, Mod) pair ) private static void DrawLine( (object, IMod) pair )
{ {
var (manipulation, mod) = pair; var (manipulation, mod) = pair;
ImGui.TableNextColumn(); ImGui.TableNextColumn();

View file

@ -128,16 +128,19 @@ public partial class ConfigWindow
foreach( var conflict in Penumbra.CollectionManager.Current.Conflicts( _mod ) ) foreach( var conflict in Penumbra.CollectionManager.Current.Conflicts( _mod ) )
{ {
if( ImGui.Selectable( conflict.Mod2.Name ) ) if( ImGui.Selectable( conflict.Mod2.Name ) && conflict.Mod2 is Mod mod )
{ {
_window._selector.SelectByValue( conflict.Mod2 ); _window._selector.SelectByValue( mod );
} }
ImGui.SameLine(); ImGui.SameLine();
using( var color = ImRaii.PushColor( ImGuiCol.Text, using( var color = ImRaii.PushColor( ImGuiCol.Text,
conflict.HasPriority ? ColorId.HandledConflictMod.Value() : ColorId.ConflictingMod.Value() ) ) conflict.HasPriority ? ColorId.HandledConflictMod.Value() : ColorId.ConflictingMod.Value() ) )
{ {
ImGui.TextUnformatted( $"(Priority {Penumbra.CollectionManager.Current[ conflict.Mod2.Index ].Settings!.Priority})" ); var priority = conflict.Mod2.Index < 0
? conflict.Mod2.Priority
: Penumbra.CollectionManager.Current[conflict.Mod2.Index].Settings!.Priority;
ImGui.TextUnformatted( $"(Priority {priority})" );
} }
using var indent = ImRaii.PushIndent( 30f ); using var indent = ImRaii.PushIndent( 30f );