diff --git a/OtterGui b/OtterGui index a78f17dd..baee5028 160000 --- a/OtterGui +++ b/OtterGui @@ -1 +1 @@ -Subproject commit a78f17dd1bc4cbe7e9d6c04af828ff1adac4bd6f +Subproject commit baee502862a5e8cdfa407f703ce98abad5cc623b diff --git a/Penumbra/Collections/ModCollection.Changes.cs b/Penumbra/Collections/ModCollection.Changes.cs index f4c1b32e..114efe63 100644 --- a/Penumbra/Collections/ModCollection.Changes.cs +++ b/Penumbra/Collections/ModCollection.Changes.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using Penumbra.Mods; namespace Penumbra.Collections; @@ -6,10 +8,12 @@ namespace Penumbra.Collections; // Different types a mod setting can change: public enum ModSettingChange { - Inheritance, // it was set to inherit from other collections or not inherit anymore - EnableState, // it was enabled or disabled - Priority, // its priority was changed - Setting, // a specific setting was changed + Inheritance, // it was set to inherit from other collections or not inherit anymore + EnableState, // it was enabled or disabled + Priority, // its priority was changed + Setting, // a specific setting was changed + MultiInheritance, // multiple mods were set to inherit from other collections or not inherit anymore. + MultiEnableState, // multiple mods were enabled or disabled at once. } public partial class ModCollection @@ -28,7 +32,7 @@ public partial class ModCollection } } - // Set the enabled state mod idx to newValue if it differs from the current priority. + // Set the enabled state mod idx to newValue if it differs from the current enabled state. // If mod idx is currently inherited, stop the inheritance. public void SetModState( int idx, bool newValue ) { @@ -41,6 +45,37 @@ public partial class ModCollection } } + // Enable or disable the mod inheritance of every mod in mods. + public void SetMultipleModInheritances( IEnumerable< Mod > mods, bool inherit ) + { + if( mods.Aggregate( false, ( current, mod ) => current | FixInheritance( mod.Index, inherit ) ) ) + { + ModSettingChanged.Invoke( ModSettingChange.MultiInheritance, -1, -1, null, false ); + } + } + + // Set the enabled state of every mod in mods to the new value. + // If the mod is currently inherited, stop the inheritance. + public void SetMultipleModStates( IEnumerable< Mod > mods, bool newValue ) + { + var changes = false; + foreach( var mod in mods ) + { + var oldValue = _settings[ mod.Index ]?.Enabled ?? this[ mod.Index ].Settings?.Enabled ?? false; + if( newValue != oldValue ) + { + FixInheritance( mod.Index, false ); + _settings[ mod.Index ]!.Enabled = newValue; + changes = true; + } + } + + if( changes ) + { + ModSettingChanged.Invoke( ModSettingChange.MultiEnableState, -1, -1, null, false ); + } + } + // Set the priority of mod idx to newValue if it differs from the current priority. // If mod idx is currently inherited, stop the inheritance. public void SetModPriority( int idx, int newValue ) diff --git a/Penumbra/Mods/Mod2.Meta.cs b/Penumbra/Mods/Mod2.Meta.cs index 0bc6f72d..8a440829 100644 --- a/Penumbra/Mods/Mod2.Meta.cs +++ b/Penumbra/Mods/Mod2.Meta.cs @@ -3,7 +3,7 @@ using System.IO; using Dalamud.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using Penumbra.Util; +using OtterGui; namespace Penumbra.Mods; diff --git a/Penumbra/Mods/ModFileSystemA.cs b/Penumbra/Mods/ModFileSystemA.cs new file mode 100644 index 00000000..1bece099 --- /dev/null +++ b/Penumbra/Mods/ModFileSystemA.cs @@ -0,0 +1,39 @@ +using System.IO; +using OtterGui.Filesystem; + +namespace Penumbra.Mods; + +public sealed class ModFileSystemA : FileSystem< Mod > +{ + public void Save() + => SaveToFile( new FileInfo( Mod.Manager.SortOrderFile ), SaveMod, true ); + + public static ModFileSystemA Load() + { + var x = new ModFileSystemA(); + if( x.Load( new FileInfo( Mod.Manager.SortOrderFile ), Penumbra.ModManager.Mods, ModToIdentifier, ModToName ) ) + { + x.Save(); + } + + x.Changed += ( _1, _2, _3, _4 ) => x.Save(); + + return x; + } + + private static string ModToIdentifier( Mod mod ) + => mod.BasePath.Name; + + private static string ModToName( Mod mod ) + => mod.Meta.Name.Text; + + private static (string, bool) SaveMod( Mod mod, string fullPath ) + { + if( fullPath == ModToName( mod ) ) + { + return ( string.Empty, false ); + } + + return ( ModToIdentifier( mod ), true ); + } +} \ No newline at end of file diff --git a/Penumbra/Mods/ModMeta.cs b/Penumbra/Mods/ModMeta.cs index 68469515..253839ce 100644 --- a/Penumbra/Mods/ModMeta.cs +++ b/Penumbra/Mods/ModMeta.cs @@ -5,6 +5,7 @@ using System.Linq; using Dalamud.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using OtterGui; using Penumbra.GameData.ByteString; using Penumbra.Util; diff --git a/Penumbra/UI/Colors.cs b/Penumbra/UI/Colors.cs new file mode 100644 index 00000000..013af594 --- /dev/null +++ b/Penumbra/UI/Colors.cs @@ -0,0 +1,10 @@ +namespace Penumbra.UI; + +public static class Colors +{ + public const uint DefaultTextColor = 0xFFFFFFFFu; + public const uint NewModColor = 0xFF66DD66u; + public const uint DisabledModColor = 0xFF666666u; + public const uint ConflictingModColor = 0xFFAAAAFFu; + public const uint HandledConflictModColor = 0xFF88DDDDu; +} \ No newline at end of file diff --git a/Penumbra/UI/MenuTabs/ModFileSystemSelector.cs b/Penumbra/UI/MenuTabs/ModFileSystemSelector.cs new file mode 100644 index 00000000..c80a0c47 --- /dev/null +++ b/Penumbra/UI/MenuTabs/ModFileSystemSelector.cs @@ -0,0 +1,330 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using Dalamud.Interface; +using ImGuiNET; +using OtterGui; +using OtterGui.Filesystem; +using OtterGui.FileSystem.Selector; +using OtterGui.Raii; +using Penumbra.Collections; +using Penumbra.Mods; + +namespace Penumbra.UI; + +public sealed class ModFileSystemSelector : FileSystemSelector< Mod, ModState > +{ + private readonly IReadOnlySet< Mod > _newMods = new HashSet(); + private LowerString _modFilter = LowerString.Empty; + private LowerString _modFilterAuthor = LowerString.Empty; + private LowerString _modFilterChanges = LowerString.Empty; + private LowerString _modFilterName = LowerString.Empty; + private ModFilter _stateFilter = ModFilterExtensions.UnfilteredStateMods; + + public ModFilter StateFilter + { + get => _stateFilter; + set + { + var diff = _stateFilter != value; + _stateFilter = value; + if( diff ) + { + SetFilterDirty(); + } + } + } + + protected override bool ChangeFilter( string filterValue ) + { + if( filterValue.StartsWith( "c:", StringComparison.InvariantCultureIgnoreCase ) ) + { + _modFilterChanges = new LowerString( filterValue[ 2.. ] ); + _modFilter = LowerString.Empty; + _modFilterAuthor = LowerString.Empty; + _modFilterName = LowerString.Empty; + } + else if( filterValue.StartsWith( "a:", StringComparison.InvariantCultureIgnoreCase ) ) + { + _modFilterAuthor = new LowerString( filterValue[ 2.. ] ); + _modFilter = LowerString.Empty; + _modFilterChanges = LowerString.Empty; + _modFilterName = LowerString.Empty; + } + else if( filterValue.StartsWith( "n:", StringComparison.InvariantCultureIgnoreCase ) ) + { + _modFilterName = new LowerString( filterValue[ 2.. ] ); + _modFilter = LowerString.Empty; + _modFilterChanges = LowerString.Empty; + _modFilterAuthor = LowerString.Empty; + } + else + { + _modFilter = new LowerString( filterValue ); + _modFilterAuthor = LowerString.Empty; + _modFilterChanges = LowerString.Empty; + _modFilterName = LowerString.Empty; + } + + return true; + } + + private bool CheckFlags( int count, ModFilter hasNoFlag, ModFilter hasFlag ) + { + if( count == 0 ) + { + if( StateFilter.HasFlag( hasNoFlag ) ) + { + return false; + } + } + else if( StateFilter.HasFlag( hasFlag ) ) + { + return false; + } + + return true; + } + + private ModState GetModState( Mod mod, ModSettings? settings ) + { + if( settings?.Enabled != true ) + { + return new ModState { Color = ImGui.GetColorU32( ImGuiCol.TextDisabled ) }; + } + + return new ModState { Color = ImGui.GetColorU32( ImGuiCol.Text ) }; + } + + protected override bool ApplyFiltersAndState( FileSystem< Mod >.IPath path, out ModState state ) + { + if( path is ModFileSystemA.Folder f ) + { + return base.ApplyFiltersAndState( f, out state ); + } + + return ApplyFiltersAndState( ( ModFileSystemA.Leaf )path, out state ); + } + + private bool CheckPath( string path, Mod mod ) + => _modFilter.IsEmpty + || path.Contains( _modFilter.Lower, StringComparison.InvariantCultureIgnoreCase ) + || mod.Meta.Name.Contains( _modFilter ); + + private bool CheckName( Mod mod ) + => _modFilterName.IsEmpty || mod.Meta.Name.Contains( _modFilterName ); + + private bool CheckAuthor( Mod mod ) + => _modFilterAuthor.IsEmpty || mod.Meta.Author.Contains( _modFilterAuthor ); + + private bool CheckItems( Mod mod ) + => _modFilterChanges.IsEmpty || mod.LowerChangedItemsString.Contains( _modFilterChanges.Lower ); + + private bool ApplyFiltersAndState( ModFileSystemA.Leaf leaf, out ModState state ) + { + state = new ModState { Color = Colors.DefaultTextColor }; + var mod = leaf.Value; + var (settings, collection) = Current[ mod.Index ]; + // Check string filters. + if( !( CheckPath( leaf.FullName(), mod ) + && CheckName( mod ) + && CheckAuthor( mod ) + && CheckItems( mod ) ) ) + { + return true; + } + + var isNew = _newMods.Contains( mod ); + if( CheckFlags( mod.Resources.ModFiles.Count, ModFilter.HasNoFiles, ModFilter.HasFiles ) + || CheckFlags( mod.Meta.FileSwaps.Count, ModFilter.HasNoFileSwaps, ModFilter.HasFileSwaps ) + || CheckFlags( mod.Resources.MetaManipulations.Count, ModFilter.HasNoMetaManipulations, ModFilter.HasMetaManipulations ) + || CheckFlags( mod.Meta.HasGroupsWithConfig ? 1 : 0, ModFilter.HasNoConfig, ModFilter.HasConfig ) + || CheckFlags( isNew ? 1 : 0, ModFilter.IsNew, ModFilter.NotNew ) ) + { + return true; + } + + if( settings == null ) + { + state.Color = Colors.DisabledModColor; + if( !StateFilter.HasFlag( ModFilter.Undefined ) ) + { + return true; + } + + settings = new ModSettings(); + } + + + if( !settings.Enabled ) + { + state.Color = Colors.DisabledModColor; + if( !StateFilter.HasFlag( ModFilter.Disabled ) ) + { + return true; + } + } + else + { + if( !StateFilter.HasFlag( ModFilter.Enabled ) ) + { + return true; + } + + var conflicts = Penumbra.CollectionManager.Current.ModConflicts( mod.Index ).ToList(); + if( conflicts.Count > 0 ) + { + if( conflicts.Any( c => !c.Solved ) ) + { + if( !StateFilter.HasFlag( ModFilter.UnsolvedConflict ) ) + { + return true; + } + + state.Color = Colors.ConflictingModColor; + } + else + { + if( !StateFilter.HasFlag( ModFilter.SolvedConflict ) ) + { + return true; + } + + state.Color = Colors.HandledConflictModColor; + } + } + else if( !StateFilter.HasFlag( ModFilter.NoConflict ) ) + { + return true; + } + } + + if( collection == Current ) + { + if( !StateFilter.HasFlag( ModFilter.Uninherited ) ) + { + return true; + } + } + else + { + if( !StateFilter.HasFlag( ModFilter.Inherited ) ) + { + return true; + } + } + + if( isNew ) + { + state.Color = Colors.NewModColor; + } + + return false; + } + + + protected override float CustomFilters( float width ) + { + var pos = ImGui.GetCursorPos(); + var remainingWidth = width - ImGui.GetFrameHeight(); + var comboPos = new Vector2( pos.X + remainingWidth, pos.Y ); + ImGui.SetCursorPos( comboPos ); + using var combo = ImRaii.Combo( "##filterCombo", string.Empty, + ImGuiComboFlags.NoPreview | ImGuiComboFlags.PopupAlignLeft | ImGuiComboFlags.HeightLargest ); + + if( combo ) + { + ImGui.Text( "A" ); + ImGui.Text( "B" ); + ImGui.Text( "C" ); + } + + combo.Dispose(); + ImGui.SetCursorPos( pos ); + return remainingWidth; + } + + + public ModFileSystemSelector( ModFileSystemA fileSystem ) + : base( fileSystem ) + { + SubscribeRightClickFolder( EnableDescendants, 10 ); + SubscribeRightClickFolder( DisableDescendants, 10 ); + SubscribeRightClickFolder( InheritDescendants, 15 ); + SubscribeRightClickFolder( OwnDescendants, 15 ); + AddButton( AddNewModButton, 0 ); + AddButton( DeleteModButton, 1000 ); + } + + private static ModCollection Current + => Penumbra.CollectionManager.Current; + + private static void EnableDescendants( ModFileSystemA.Folder folder ) + { + if( ImGui.MenuItem( "Enable Descendants" ) ) + { + SetDescendants( folder, true, false ); + } + } + + private static void DisableDescendants( ModFileSystemA.Folder folder ) + { + if( ImGui.MenuItem( "Disable Descendants" ) ) + { + SetDescendants( folder, false, false ); + } + } + + private static void InheritDescendants( ModFileSystemA.Folder folder ) + { + if( ImGui.MenuItem( "Inherit Descendants" ) ) + { + SetDescendants( folder, true, true ); + } + } + + private static void OwnDescendants( ModFileSystemA.Folder folder ) + { + if( ImGui.MenuItem( "Stop Inheriting Descendants" ) ) + { + SetDescendants( folder, false, true ); + } + } + + private static void AddNewModButton( Vector2 size ) + { + if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Plus.ToIconString(), size, "Create a new, empty mod of a given name.", false, true ) ) + { } + } + + private void DeleteModButton( Vector2 size ) + { + if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Trash.ToIconString(), size, + "Delete the currently selected mod entirely from your drive.", SelectedLeaf == null, true ) ) + { } + } + + private static void SetDescendants( ModFileSystemA.Folder folder, bool enabled, bool inherit = false ) + { + var mods = folder.GetAllDescendants( SortMode.Lexicographical ).OfType< ModFileSystemA.Leaf >().Select( l => l.Value ); + if( inherit ) + { + Current.SetMultipleModInheritances( mods, enabled ); + } + else + { + Current.SetMultipleModStates( mods, enabled ); + } + } + + public override SortMode SortMode + => Penumbra.Config.SortFoldersFirst ? SortMode.FoldersFirst : SortMode.Lexicographical; + + protected override void DrawLeafName( FileSystem< Mod >.Leaf leaf, in ModState state, bool selected ) + { + var flags = selected ? ImGuiTreeNodeFlags.Selected | LeafFlags : LeafFlags; + using var c = ImRaii.PushColor( ImGuiCol.Text, state.Color ); + using var _ = ImRaii.TreeNode( leaf.Value.Meta.Name, flags ); + } +} \ No newline at end of file diff --git a/Penumbra/UI/MenuTabs/TabBrowser.cs b/Penumbra/UI/MenuTabs/TabBrowser.cs index e88f88d6..6e00a9af 100644 --- a/Penumbra/UI/MenuTabs/TabBrowser.cs +++ b/Penumbra/UI/MenuTabs/TabBrowser.cs @@ -1,24 +1,37 @@ -using System.Diagnostics; -using ImGuiNET; +using System.Runtime.InteropServices; +using OtterGui.Raii; +using Penumbra.Mods; -namespace Penumbra.UI +namespace Penumbra.UI; + +[StructLayout( LayoutKind.Sequential, Pack = 1 )] +public struct ModState { - public partial class SettingsInterface - { - private class TabBrowser - { - [Conditional( "LEAVEMEALONE" )] - public void Draw() - { - var ret = ImGui.BeginTabItem( "Available Mods" ); - if( !ret ) - { - return; - } + public uint Color; +} - ImGui.Text( "woah" ); - ImGui.EndTabItem(); +public partial class SettingsInterface +{ + private class TabBrowser + { + private readonly ModFileSystemA _fileSystem; + private readonly ModFileSystemSelector _selector; + + public TabBrowser() + { + _fileSystem = ModFileSystemA.Load(); + _selector = new ModFileSystemSelector( _fileSystem ); + } + + public void Draw() + { + using var ret = ImRaii.TabItem( "Available Mods" ); + if( !ret ) + { + return; } + + _selector.Draw( 400 ); } } } \ No newline at end of file diff --git a/Penumbra/UI/MenuTabs/TabDebug.cs b/Penumbra/UI/MenuTabs/TabDebug.cs index bfa7d1b8..18353834 100644 --- a/Penumbra/UI/MenuTabs/TabDebug.cs +++ b/Penumbra/UI/MenuTabs/TabDebug.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Numerics; using ImGuiNET; +using OtterGui.Raii; using Penumbra.Api; using Penumbra.UI.Custom; using CharacterUtility = Penumbra.Interop.CharacterUtility; @@ -12,6 +13,17 @@ namespace Penumbra.UI; public partial class SettingsInterface { + private string ImGuiIdTester = string.Empty; + + private void DrawImGuiIdTester() + { + ImGui.SetNextItemWidth( 200 ); + ImGui.InputText( "##abc1", ref ImGuiIdTester, 32 ); + ImGui.SameLine(); + ImGui.Text( ImGui.GetID( ImGuiIdTester ).ToString( "X" ) ); + } + + private static void PrintValue( string name, string value ) { ImGui.TableNextRow(); @@ -289,6 +301,16 @@ public partial class SettingsInterface } } + private void DrawDebugTabUtility() + { + if( !ImGui.CollapsingHeader( "Utilities##Debug" ) ) + { + return; + } + + DrawImGuiIdTester(); + } + private void DrawDebugTab() { if( !ImGui.BeginTabItem( "Debug Tab" ) ) @@ -324,5 +346,7 @@ public partial class SettingsInterface ImGui.NewLine(); DrawDebugTabIpc(); ImGui.NewLine(); + DrawDebugTabUtility(); + ImGui.NewLine(); } } \ No newline at end of file diff --git a/Penumbra/UI/MenuTabs/TabEffective.cs b/Penumbra/UI/MenuTabs/TabEffective.cs index 8420cb5c..e9a962e7 100644 --- a/Penumbra/UI/MenuTabs/TabEffective.cs +++ b/Penumbra/UI/MenuTabs/TabEffective.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using Dalamud.Interface; using ImGuiNET; +using OtterGui; using Penumbra.Collections; using Penumbra.GameData.ByteString; using Penumbra.GameData.Util; diff --git a/Penumbra/UI/MenuTabs/TabInstalled/ModFilter.cs b/Penumbra/UI/MenuTabs/TabInstalled/ModFilter.cs index a90f620c..a5a0b6d8 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/ModFilter.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/ModFilter.cs @@ -1,50 +1,55 @@ using System; -namespace Penumbra.UI +namespace Penumbra.UI; + +[Flags] +public enum ModFilter { - [Flags] - public enum ModFilter - { - Enabled = 1 << 0, - Disabled = 1 << 1, - NoConflict = 1 << 2, - SolvedConflict = 1 << 3, - UnsolvedConflict = 1 << 4, - HasNoMetaManipulations = 1 << 5, - HasMetaManipulations = 1 << 6, - HasNoFileSwaps = 1 << 7, - HasFileSwaps = 1 << 8, - HasConfig = 1 << 9, - HasNoConfig = 1 << 10, - HasNoFiles = 1 << 11, - HasFiles = 1 << 12, - IsNew = 1 << 13, - NotNew = 1 << 14, - }; + Enabled = 1 << 0, + Disabled = 1 << 1, + NoConflict = 1 << 2, + SolvedConflict = 1 << 3, + UnsolvedConflict = 1 << 4, + HasNoMetaManipulations = 1 << 5, + HasMetaManipulations = 1 << 6, + HasNoFileSwaps = 1 << 7, + HasFileSwaps = 1 << 8, + HasConfig = 1 << 9, + HasNoConfig = 1 << 10, + HasNoFiles = 1 << 11, + HasFiles = 1 << 12, + IsNew = 1 << 13, + NotNew = 1 << 14, + Inherited = 1 << 15, + Uninherited = 1 << 16, + Undefined = 1 << 17, +}; - public static class ModFilterExtensions - { - public const ModFilter UnfilteredStateMods = ( ModFilter )( ( 1 << 15 ) - 1 ); +public static class ModFilterExtensions +{ + public const ModFilter UnfilteredStateMods = ( ModFilter )( ( 1 << 18 ) - 1 ); - public static string ToName( this ModFilter filter ) - => filter switch - { - ModFilter.Enabled => "Enabled", - ModFilter.Disabled => "Disabled", - ModFilter.NoConflict => "No Conflicts", - ModFilter.SolvedConflict => "Solved Conflicts", - ModFilter.UnsolvedConflict => "Unsolved Conflicts", - ModFilter.HasNoMetaManipulations => "No Meta Manipulations", - ModFilter.HasMetaManipulations => "Meta Manipulations", - ModFilter.HasNoFileSwaps => "No File Swaps", - ModFilter.HasFileSwaps => "File Swaps", - ModFilter.HasNoConfig => "No Configuration", - ModFilter.HasConfig => "Configuration", - ModFilter.HasNoFiles => "No Files", - ModFilter.HasFiles => "Files", - ModFilter.IsNew => "Newly Imported", - ModFilter.NotNew => "Not Newly Imported", - _ => throw new ArgumentOutOfRangeException( nameof( filter ), filter, null ), - }; - } + public static string ToName( this ModFilter filter ) + => filter switch + { + ModFilter.Enabled => "Enabled", + ModFilter.Disabled => "Disabled", + ModFilter.NoConflict => "No Conflicts", + ModFilter.SolvedConflict => "Solved Conflicts", + ModFilter.UnsolvedConflict => "Unsolved Conflicts", + ModFilter.HasNoMetaManipulations => "No Meta Manipulations", + ModFilter.HasMetaManipulations => "Meta Manipulations", + ModFilter.HasNoFileSwaps => "No File Swaps", + ModFilter.HasFileSwaps => "File Swaps", + ModFilter.HasNoConfig => "No Configuration", + ModFilter.HasConfig => "Configuration", + ModFilter.HasNoFiles => "No Files", + ModFilter.HasFiles => "Files", + ModFilter.IsNew => "Newly Imported", + ModFilter.NotNew => "Not Newly Imported", + ModFilter.Inherited => "Inherited Configuration", + ModFilter.Uninherited => "Own Configuration", + ModFilter.Undefined => "Not Configured", + _ => throw new ArgumentOutOfRangeException( nameof( filter ), filter, null ), + }; } \ No newline at end of file diff --git a/Penumbra/UI/MenuTabs/TabInstalled/ModListCache.cs b/Penumbra/UI/MenuTabs/TabInstalled/ModListCache.cs index 7c35efea..1b9b48f9 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/ModListCache.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/ModListCache.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using Dalamud.Logging; +using OtterGui; using Penumbra.Mods; using Penumbra.Util; @@ -9,12 +10,7 @@ namespace Penumbra.UI; public class ModListCache : IDisposable { - public const uint NewModColor = 0xFF66DD66u; - public const uint DisabledModColor = 0xFF666666u; - public const uint ConflictingModColor = 0xFFAAAAFFu; - public const uint HandledConflictModColor = 0xFF88DDDDu; - - private readonly Mods.Mod.Manager _manager; + private readonly Mod.Manager _manager; private readonly List< FullMod > _modsInOrder = new(); private readonly List< (bool visible, uint color) > _visibleMods = new(); @@ -24,10 +20,11 @@ public class ModListCache : IDisposable private LowerString _modFilter = LowerString.Empty; private LowerString _modFilterAuthor = LowerString.Empty; private LowerString _modFilterChanges = LowerString.Empty; - private ModFilter _stateFilter = ModFilterExtensions.UnfilteredStateMods; - private bool _listResetNecessary; - private bool _filterResetNecessary; + private bool _listResetNecessary; + private bool _filterResetNecessary; + + private ModFilter _stateFilter = ModFilterExtensions.UnfilteredStateMods; public ModFilter StateFilter { @@ -43,7 +40,7 @@ public class ModListCache : IDisposable } } - public ModListCache( Mods.Mod.Manager manager, IReadOnlySet< string > newMods ) + public ModListCache( Mod.Manager manager, IReadOnlySet< string > newMods ) { _manager = manager; _newMods = newMods; @@ -281,7 +278,7 @@ public class ModListCache : IDisposable return ret; } - ret.Item2 = ret.Item2 == 0 ? DisabledModColor : ret.Item2; + ret.Item2 = ret.Item2 == 0 ? Colors.DisabledModColor : ret.Item2; } if( mod.Settings.Enabled && !StateFilter.HasFlag( ModFilter.Enabled ) ) @@ -299,7 +296,7 @@ public class ModListCache : IDisposable return ret; } - ret.Item2 = ret.Item2 == 0 ? ConflictingModColor : ret.Item2; + ret.Item2 = ret.Item2 == 0 ? Colors.ConflictingModColor : ret.Item2; } else { @@ -308,7 +305,7 @@ public class ModListCache : IDisposable return ret; } - ret.Item2 = ret.Item2 == 0 ? HandledConflictModColor : ret.Item2; + ret.Item2 = ret.Item2 == 0 ? Colors.HandledConflictModColor : ret.Item2; } } else if( !StateFilter.HasFlag( ModFilter.NoConflict ) ) @@ -319,7 +316,7 @@ public class ModListCache : IDisposable ret.Item1 = true; if( isNew ) { - ret.Item2 = NewModColor; + ret.Item2 = Colors.NewModColor; } SetFolderAndParentsVisible( mod.Data.Order.ParentFolder ); diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs index 6ccf9d3f..cc773a28 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs @@ -217,18 +217,18 @@ public partial class SettingsInterface ImGui.Text( "Enabled in the current collection." ); ImGui.Bullet(); ImGui.SameLine(); - ImGui.TextColored( ImGui.ColorConvertU32ToFloat4( ModListCache.DisabledModColor ), "Disabled in the current collection." ); + ImGui.TextColored( ImGui.ColorConvertU32ToFloat4( Colors.DisabledModColor ), "Disabled in the current collection." ); ImGui.Bullet(); ImGui.SameLine(); - ImGui.TextColored( ImGui.ColorConvertU32ToFloat4( ModListCache.NewModColor ), + ImGui.TextColored( ImGui.ColorConvertU32ToFloat4( Colors.NewModColor ), "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( ModListCache.HandledConflictModColor ), + ImGui.TextColored( ImGui.ColorConvertU32ToFloat4( Colors.HandledConflictModColor ), "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( ModListCache.ConflictingModColor ), + ImGui.TextColored( ImGui.ColorConvertU32ToFloat4( Colors.ConflictingModColor ), "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." ); diff --git a/Penumbra/UI/MenuTabs/TabSettings.cs b/Penumbra/UI/MenuTabs/TabSettings.cs index ee11e961..fe36cebb 100644 --- a/Penumbra/UI/MenuTabs/TabSettings.cs +++ b/Penumbra/UI/MenuTabs/TabSettings.cs @@ -235,7 +235,8 @@ public partial class SettingsInterface } ImGui.SameLine(); - ImGuiComponents.HelpMarker( "Enables other applications, e.g. Anamnesis, to use some Penumbra functions, like requesting redraws." ); + ImGuiComponents.HelpMarker( + "Enables other applications, e.g. Anamnesis, to use some Penumbra functions, like requesting redraws." ); } private static void DrawReloadResourceButton() @@ -342,7 +343,6 @@ public partial class SettingsInterface } using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTabItem ); - DrawRootFolder(); DrawRediscoverButton(); diff --git a/Penumbra/Util/Backup.cs b/Penumbra/Util/Backup.cs index 878731d7..a096e238 100644 --- a/Penumbra/Util/Backup.cs +++ b/Penumbra/Util/Backup.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; +using System.Linq; using Dalamud.Logging; namespace Penumbra.Util; @@ -119,7 +120,7 @@ public static class Backup { using var fileStream = File.Open( fileName, FileMode.Create ); using var zip = new ZipArchive( fileStream, ZipArchiveMode.Create ); - foreach( var file in files ) + foreach( var file in files.Where( f => File.Exists( f.FullName ) ) ) { zip.CreateEntryFromFile( file.FullName, Path.GetRelativePath( configDirectory, file.FullName ), CompressionLevel.Optimal ); } diff --git a/Penumbra/Util/LowerString.cs b/Penumbra/Util/LowerString.cs deleted file mode 100644 index 4942a5bc..00000000 --- a/Penumbra/Util/LowerString.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; -using ImGuiNET; -using Newtonsoft.Json; - -namespace Penumbra.Util; - -[JsonConverter( typeof( Converter ) )] -public readonly struct LowerString : IEquatable< LowerString >, IComparable< LowerString > -{ - public static readonly LowerString Empty = new(string.Empty); - - public readonly string Text = string.Empty; - public readonly string Lower = string.Empty; - - public LowerString( string text ) - { - Text = string.Intern( text ); - Lower = string.Intern( text.ToLowerInvariant() ); - } - - - public int Length - => Text.Length; - - public int Count - => Length; - - public bool Equals( LowerString other ) - => string.Equals( Lower, other.Lower, StringComparison.InvariantCulture ); - - public bool Equals( string other ) - => string.Equals( Lower, other, StringComparison.InvariantCultureIgnoreCase ); - - public int CompareTo( LowerString other ) - => string.Compare( Lower, other.Lower, StringComparison.InvariantCulture ); - - public int CompareTo( string other ) - => string.Compare( Lower, other, StringComparison.InvariantCultureIgnoreCase ); - - public bool Contains( LowerString other ) - => Lower.Contains( other.Lower, StringComparison.InvariantCulture ); - - public bool Contains( string other ) - => Lower.Contains( other, StringComparison.InvariantCultureIgnoreCase ); - - public bool StartsWith( LowerString other ) - => Lower.StartsWith( other.Lower, StringComparison.InvariantCulture ); - - public bool StartsWith( string other ) - => Lower.StartsWith( other, StringComparison.InvariantCultureIgnoreCase ); - - public bool EndsWith( LowerString other ) - => Lower.EndsWith( other.Lower, StringComparison.InvariantCulture ); - - public bool EndsWith( string other ) - => Lower.EndsWith( other, StringComparison.InvariantCultureIgnoreCase ); - - public override string ToString() - => Text; - - public static implicit operator string( LowerString s ) - => s.Text; - - public static implicit operator LowerString( string s ) - => new(s); - - private class Converter : JsonConverter< LowerString > - { - public override void WriteJson( JsonWriter writer, LowerString value, JsonSerializer serializer ) - { - writer.WriteValue( value.Text ); - } - - public override LowerString ReadJson( JsonReader reader, Type objectType, LowerString existingValue, bool hasExistingValue, - JsonSerializer serializer ) - { - if( reader.Value is string text ) - { - return new LowerString( text ); - } - - return existingValue; - } - } - - public static bool InputWithHint( string label, string hint, ref LowerString s, uint maxLength = 128, - ImGuiInputTextFlags flags = ImGuiInputTextFlags.None ) - { - var tmp = s.Text; - if( !ImGui.InputTextWithHint( label, hint, ref tmp, maxLength, flags ) || tmp == s.Text ) - { - return false; - } - - s = new LowerString( tmp ); - return true; - } - - public override bool Equals( object? obj ) - => obj is LowerString lowerString && Equals( lowerString ); - - public override int GetHashCode() - => Text.GetHashCode(); - - public static bool operator ==( LowerString lhs, LowerString rhs ) - => lhs.Equals( rhs ); - - public static bool operator !=( LowerString lhs, LowerString rhs ) - => lhs.Equals( rhs ); - - public static bool operator ==( LowerString lhs, string rhs ) - => lhs.Equals( rhs ); - - public static bool operator !=( LowerString lhs, string rhs ) - => lhs.Equals( rhs ); - - public static bool operator ==( string lhs, LowerString rhs ) - => rhs.Equals( lhs ); - - public static bool operator !=( string lhs, LowerString rhs ) - => rhs.Equals( lhs ); -} \ No newline at end of file diff --git a/Penumbra/Util/ModelChanger.cs b/Penumbra/Util/ModelChanger.cs index 75c03c3e..fd9c4eb3 100644 --- a/Penumbra/Util/ModelChanger.cs +++ b/Penumbra/Util/ModelChanger.cs @@ -26,7 +26,7 @@ public static class ModelChanger && Encoding.UTF8.GetByteCount( to ) == to.Length; - [Conditional( "Debug" )] + [Conditional( "DEBUG" )] private static void WriteBackup( string name, byte[] text ) => File.WriteAllBytes( name + ".bak", text );