diff --git a/Penumbra/Mods/ModFileSystem.cs b/Penumbra/Mods/ModFileSystem.cs index 03b3a1fe..220fb76e 100644 --- a/Penumbra/Mods/ModFileSystem.cs +++ b/Penumbra/Mods/ModFileSystem.cs @@ -5,11 +5,19 @@ using Penumbra.Util; namespace Penumbra.Mods { + public delegate void OnModFileSystemChange(); + public static partial class ModFileSystem { // The root folder that should be used as the base for all structured mods. public static ModFolder Root = ModFolder.CreateRoot(); + // Gets invoked every time the file system changes. + public static event OnModFileSystemChange? ModFileSystemChanged; + + internal static void InvokeChange() + => ModFileSystemChanged?.Invoke(); + // Find a specific mod folder by its path from Root. // Returns true if the folder was found, and false if not. // The out parameter will contain the furthest existing folder. @@ -69,7 +77,6 @@ namespace Penumbra.Mods // Move a mod to the filesystem location specified by sortOrder and rename its SortOrderName. // Creates all necessary Subfolders. - // Does NOT save. public static void Move( this ModData mod, string sortOrder ) { var split = sortOrder.Split( new[] { '/' }, StringSplitOptions.RemoveEmptyEntries ); @@ -79,8 +86,10 @@ namespace Penumbra.Mods folder = folder.FindOrCreateSubFolder( split[ i ] ).Item1; } - MoveNoSave( mod, folder ); - RenameNoSave( mod, split.Last() ); + if( MoveNoSave( mod, folder ) || RenameNoSave( mod, split.Last() ) ) + { + SaveMod( mod ); + } } // Moves folder to target. @@ -126,6 +135,7 @@ namespace Penumbra.Mods } config.Save(); + InvokeChange(); } // Sets and saves the sort order of a single mod, removing the entry if it is unnecessary. @@ -143,6 +153,7 @@ namespace Penumbra.Mods } config.Save(); + InvokeChange(); } private static bool RenameNoSave( this ModFolder target, string newName ) diff --git a/Penumbra/Mods/ModManager.cs b/Penumbra/Mods/ModManager.cs index a385d2c1..ecd77002 100644 --- a/Penumbra/Mods/ModManager.cs +++ b/Penumbra/Mods/ModManager.cs @@ -148,8 +148,12 @@ namespace Penumbra.Mods PluginLog.Error( $"Could not delete the mod {modFolder.Name}:\n{e}" ); } - Mods.Remove( modFolder.Name ); - Collections.RemoveModFromCaches( modFolder ); + if( Mods.TryGetValue( modFolder.Name, out var mod ) ) + { + mod.SortOrder.ParentFolder.RemoveMod( mod ); + Mods.Remove( modFolder.Name ); + Collections.RemoveModFromCaches( modFolder ); + } } } diff --git a/Penumbra/Plugin.cs b/Penumbra/Plugin.cs index 2d66a160..6deac9ae 100644 --- a/Penumbra/Plugin.cs +++ b/Penumbra/Plugin.cs @@ -80,9 +80,6 @@ namespace Penumbra SettingsInterface = new SettingsInterface( this ); - PluginInterface.UiBuilder.DisableGposeUiHide = true; - PluginInterface.UiBuilder.OnBuildUi += SettingsInterface.Draw; - if( Configuration.EnableHttpApi ) { CreateWebServer(); @@ -147,9 +144,9 @@ namespace Penumbra public void Dispose() { + SettingsInterface.Dispose(); ActorRefresher.Dispose(); PlayerWatcher.Dispose(); - PluginInterface.UiBuilder.OnBuildUi -= SettingsInterface.Draw; PluginInterface.CommandManager.RemoveHandler( CommandName ); PluginInterface.Dispose(); diff --git a/Penumbra/UI/MenuTabs/TabCollections.cs b/Penumbra/UI/MenuTabs/TabCollections.cs index 553ff63f..db4b0f13 100644 --- a/Penumbra/UI/MenuTabs/TabCollections.cs +++ b/Penumbra/UI/MenuTabs/TabCollections.cs @@ -141,22 +141,19 @@ namespace Penumbra.UI _manager.Collections.SetCurrentCollection( _collections[ idx + 1 ] ); _currentCollectionIndex = idx; - _selector.ReloadSelection(); - _selector.Cache.ResetModList(); + _selector.Cache.TriggerListReset(); + if( _selector.Mod != null ) + { + _selector.SelectModByDir( _selector.Mod.Data.BasePath.Name ); + } } public void SetCurrentCollection( ModCollection collection ) { var idx = Array.IndexOf( _collections, collection ) - 1; - if( idx >= 0 && idx != _currentCollectionIndex ) + if( idx >= 0 ) { - _manager.Collections.SetCurrentCollection( _collections[ idx + 1 ] ); - _currentCollectionIndex = idx; - _selector.Cache.ResetModList(); - if( _selector.Mod != null ) - { - _selector.SelectModByDir( _selector.Mod.Data.BasePath.Name ); - } + SetCurrentCollection( idx ); } } diff --git a/Penumbra/UI/MenuTabs/TabInstalled/ModListCache.cs b/Penumbra/UI/MenuTabs/TabInstalled/ModListCache.cs index 648bc125..76bbcde8 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/ModListCache.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/ModListCache.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using Dalamud.Plugin; @@ -5,7 +6,7 @@ using Penumbra.Mods; namespace Penumbra.UI { - public class ModListCache + public class ModListCache : IDisposable { public const uint DisabledModColor = 0xFF666666u; public const uint ConflictingModColor = 0xFFAAAAFFu; @@ -17,10 +18,13 @@ namespace Penumbra.UI private readonly List< (bool visible, uint color) > _visibleMods = new(); private readonly Dictionary< ModFolder, (bool visible, bool enabled) > _visibleFolders = new(); - private string _modFilter = ""; - private string _modFilterChanges = ""; - private string _modFilterAuthor = ""; - private ModFilter _stateFilter = ModFilterExtensions.UnfilteredStateMods; + private string _modFilter = ""; + private string _modFilterChanges = ""; + private string _modFilterAuthor = ""; + private ModFilter _stateFilter = ModFilterExtensions.UnfilteredStateMods; + private bool _listResetNecessary = false; + private bool _filterResetNecessary = false; + public ModFilter StateFilter { @@ -31,7 +35,7 @@ namespace Penumbra.UI _stateFilter = value; if( diff ) { - ResetFilters(); + TriggerFilterReset(); } } } @@ -40,11 +44,41 @@ namespace Penumbra.UI { _manager = manager; ResetModList(); + ModFileSystem.ModFileSystemChanged += TriggerListReset; + } + + public void Dispose() + { + ModFileSystem.ModFileSystemChanged -= TriggerListReset; } public int Count => _modsInOrder.Count; + + public bool Update() + { + if( _listResetNecessary ) + { + ResetModList(); + return true; + } + + if( _filterResetNecessary ) + { + ResetFilters(); + return true; + } + + return false; + } + + public void TriggerListReset() + => _listResetNecessary = true; + + public void TriggerFilterReset() + => _filterResetNecessary = true; + public void RemoveMod( Mod.Mod mod ) { var idx = _modsInOrder.IndexOf( mod ); @@ -58,7 +92,7 @@ namespace Penumbra.UI private void SetFolderAndParentsVisible( ModFolder? folder ) { - while( folder != null && (!_visibleFolders.TryGetValue(folder, out var state) || !state.visible) ) + while( folder != null && ( !_visibleFolders.TryGetValue( folder, out var state ) || !state.visible ) ) { _visibleFolders[ folder ] = ( true, true ); folder = folder.Parent; @@ -103,7 +137,7 @@ namespace Penumbra.UI ResetFilters(); } - public void ResetModList() + private void ResetModList() { _modsInOrder.Clear(); _visibleMods.Clear(); @@ -119,9 +153,12 @@ namespace Penumbra.UI _visibleMods.Add( CheckFilters( mod ) ); } } + + _listResetNecessary = false; + _filterResetNecessary = false; } - public void ResetFilters() + private void ResetFilters() { _visibleMods.Clear(); _visibleFolders.Clear(); @@ -130,6 +167,7 @@ namespace Penumbra.UI { _visibleMods.Add( CheckFilters( mod ) ); } + _filterResetNecessary = false; } public (Mod.Mod? mod, int idx) GetModByName( string name ) @@ -263,7 +301,7 @@ namespace Penumbra.UI return ret; } - ret.Item1 = true; + ret.Item1 = true; SetFolderAndParentsVisible( mod.Data.SortOrder.ParentFolder ); return ret; } diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs index 4ff83b76..48ba1c78 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs @@ -442,7 +442,7 @@ namespace Penumbra.UI // If the mod is enabled in the current collection, its conflicts may have changed. if( Mod!.Settings.Enabled ) { - _selector.Cache.ResetFilters(); + _selector.Cache.TriggerFilterReset(); } } } diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs index 546b0f11..e82f9339 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs @@ -113,7 +113,7 @@ namespace Penumbra.UI { if( _modManager.ChangeModGroup( group.GroupName, groupName, Mod.Data ) && Mod.Data.Meta.RefreshHasGroupsWithConfig() ) { - _selector.Cache.ResetFilters(); + _selector.Cache.TriggerFilterReset(); } } } @@ -132,7 +132,7 @@ namespace Penumbra.UI _selector.SaveCurrentMod(); if( Mod!.Data.Meta.RefreshHasGroupsWithConfig() ) { - _selector.Cache.ResetFilters(); + _selector.Cache.TriggerFilterReset(); } } } @@ -173,7 +173,7 @@ namespace Penumbra.UI if( Mod!.Data.Meta.RefreshHasGroupsWithConfig() ) { - _selector.Cache.ResetFilters(); + _selector.Cache.TriggerFilterReset(); } } } @@ -190,7 +190,7 @@ namespace Penumbra.UI { if( _modManager.ChangeModGroup( group.GroupName, groupName, Mod.Data ) && Mod.Data.Meta.RefreshHasGroupsWithConfig() ) { - _selector.Cache.ResetFilters(); + _selector.Cache.TriggerFilterReset(); } } } @@ -238,7 +238,7 @@ namespace Penumbra.UI if( Mod.Data.Meta.RefreshHasGroupsWithConfig() ) { - _selector.Cache.ResetFilters(); + _selector.Cache.TriggerFilterReset(); } } @@ -347,7 +347,6 @@ namespace Penumbra.UI _selector.SaveCurrentMod(); _selector.ReloadCurrentMod(); - _selector.Cache.ResetModList(); } } @@ -369,8 +368,7 @@ namespace Penumbra.UI { Meta.FileSwaps[ key ] = newValue; _selector.SaveCurrentMod(); - _selector.ReloadCurrentMod(); - _selector.Cache.ResetModList(); + _selector.Cache.TriggerListReset(); } } } diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledModPanel.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledModPanel.cs index e44138d1..38c1cf39 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledModPanel.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledModPanel.cs @@ -75,9 +75,9 @@ namespace Penumbra.UI if( Custom.ImGuiCustom.InputOrText( _editMode, LabelEditName, ref name, 64 ) && _modManager.RenameMod( name, Mod!.Data ) ) { _selector.SelectModByDir( Mod.Data.BasePath.Name ); - if( !_modManager.Config.ModSortOrder.ContainsKey( Mod!.Data.BasePath.Name ) && Mod.Data.Rename( name ) ) + if( !_modManager.Config.ModSortOrder.ContainsKey( Mod!.Data.BasePath.Name ) ) { - _selector.Cache.ResetModList(); + Mod.Data.Rename( name ); } } } @@ -122,7 +122,7 @@ namespace Penumbra.UI { Meta.Author = author; _selector.SaveCurrentMod(); - _selector.Cache.ResetFilters(); + _selector.Cache.TriggerFilterReset(); } ImGui.EndGroup(); @@ -205,7 +205,7 @@ namespace Penumbra.UI { Mod.Settings.Priority = priority; _base.SaveCurrentCollection( Mod.Data.Resources.MetaManipulations.Count > 0 ); - _selector.Cache.ResetFilters(); + _selector.Cache.TriggerFilterReset(); } if( ImGui.IsItemHovered() ) @@ -222,7 +222,7 @@ namespace Penumbra.UI { Mod.Settings.Enabled = enabled; _base.SaveCurrentCollection( Mod.Data.Resources.MetaManipulations.Count > 0 ); - _selector.Cache.ResetFilters(); + _selector.Cache.TriggerFilterReset(); } } @@ -233,7 +233,6 @@ namespace Penumbra.UI if( ImGui.InputText( "Sort Order", ref currentSortOrder, 256, ImGuiInputTextFlags.EnterReturnsTrue ) ) { manager.ChangeSortOrder( mod, currentSortOrder ); - selector.Cache.ResetModList(); selector.SelectModByDir( mod.BasePath.Name ); return true; } diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs index 1d55590e..35ccfb2e 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs @@ -99,6 +99,7 @@ namespace Penumbra.UI var mod = Mod; Cache.RemoveMod( mod ); _modManager.DeleteMod( mod.Data.BasePath ); + ModFileSystem.InvokeChange(); ClearSelection(); } @@ -166,7 +167,7 @@ namespace Penumbra.UI var metaFile = new FileInfo( Path.Combine( newDir.FullName, "meta.json" ) ); modMeta.SaveToFile( metaFile ); _modManager.AddMod( newDir ); - Cache.ResetModList(); + ModFileSystem.InvokeChange(); SelectModByDir( newDir.Name ); } catch( Exception e ) @@ -367,10 +368,7 @@ namespace Penumbra.UI var mod = Cache.GetMod( modIndex ).Item1; if( mod != null ) { - if( mod.Data.Move( folder ) ) - { - Cache.ResetModList(); - } + mod.Data.Move( folder ); } } else if( IsDropping( DraggedFolderLabel ) ) @@ -381,10 +379,7 @@ namespace Penumbra.UI && !ReferenceEquals( droppedFolder, folder ) && !folder.FullName.StartsWith( folderName, StringComparison.InvariantCultureIgnoreCase ) ) { - if( droppedFolder.Move( folder ) ) - { - Cache.ResetModList(); - } + droppedFolder.Move( folder ); } } @@ -484,7 +479,6 @@ namespace Penumbra.UI if( _index >= 0 && _modManager.UpdateMod( Mod.Data, reloadMeta, recomputeMeta ) ) { - Cache.ResetModList(); SelectModByDir( Mod.Data.BasePath.Name ); _base._menu.InstalledTab.ModPanel.Details.ResetState(); } @@ -541,7 +535,7 @@ namespace Penumbra.UI return; } - Cache.ResetFilters(); + Cache.TriggerFilterReset(); var collection = _modManager.Collections.CurrentCollection; if( collection.Cache != null ) { @@ -561,21 +555,15 @@ namespace Penumbra.UI return; } - bool changes; if( _newFolderName.Any() ) { - changes = folder.Rename( _newFolderName ); - _newFolderName = string.Empty; + folder.Rename( _newFolderName ); } else { - changes = folder.Merge( folder.Parent! ); - } - - if( changes ) - { - Cache.ResetModList(); + folder.Merge( folder.Parent! ); } + _newFolderName = string.Empty; } private void DrawFolderContextMenu( ModFolder folder, int currentIdx, string treeName ) @@ -629,21 +617,29 @@ namespace Penumbra.UI { _base._menu.CollectionsTab.SetCurrentCollection( collection ); } + if( ImGui.IsItemHovered() ) - ImGui.SetTooltip( $"Switches to the currently set {tooltipLabel} collection, if it is not set to None and it is not the current collection already." ); + { + ImGui.SetTooltip( + $"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; + const float size = 200; DrawModsSelectorFilter(); var textSize = ImGui.CalcTextSize( TabCollections.LabelCurrentCollection ).X + ImGui.GetStyle().ItemInnerSpacing.X; var comboSize = size * ImGui.GetIO().FontGlobalScale; var offset = comboSize + textSize; - var buttonSize = (ImGui.GetWindowContentRegionWidth() - offset - SelectorPanelWidth * _selectorScalingFactor - 4 * ImGui.GetStyle().ItemSpacing.X) / 2; + var buttonSize = ( ImGui.GetWindowContentRegionWidth() + - offset + - SelectorPanelWidth * _selectorScalingFactor + - 4 * ImGui.GetStyle().ItemSpacing.X ) + / 2; ImGui.SameLine(); - DrawCollectionButton("Default", "default", buttonSize, _modManager.Collections.DefaultCollection ); + DrawCollectionButton( "Default", "default", buttonSize, _modManager.Collections.DefaultCollection ); ImGui.SameLine(); DrawCollectionButton( "Forced", "forced", buttonSize, _modManager.Collections.ForcedCollection ); @@ -767,6 +763,11 @@ namespace Penumbra.UI true, ImGuiWindowFlags.HorizontalScrollbar ); ImGui.PushStyleVar( ImGuiStyleVar.IndentSpacing, 12.5f ); + if( Cache.Update() && Mod != null ) + { + SelectModByDir( Mod.Data.BasePath.Name ); + } + var modIndex = 0; DrawFolderContent( _modManager.StructuredMods, ref modIndex ); ImGui.PopStyleVar(); diff --git a/Penumbra/UI/MenuTabs/TabSettings.cs b/Penumbra/UI/MenuTabs/TabSettings.cs index 12756558..c0a3260d 100644 --- a/Penumbra/UI/MenuTabs/TabSettings.cs +++ b/Penumbra/UI/MenuTabs/TabSettings.cs @@ -107,7 +107,7 @@ namespace Penumbra.UI if( ImGui.Checkbox( LabelSortFoldersFirst, ref foldersFirst ) ) { _config.SortFoldersFirst = foldersFirst; - _base._menu.InstalledTab.Selector.Cache.ResetModList(); + _base._menu.InstalledTab.Selector.Cache.TriggerListReset(); _configChanged = true; } } diff --git a/Penumbra/UI/SettingsInterface.cs b/Penumbra/UI/SettingsInterface.cs index a387fd13..b75ef5d0 100644 --- a/Penumbra/UI/SettingsInterface.cs +++ b/Penumbra/UI/SettingsInterface.cs @@ -1,10 +1,11 @@ +using System; using System.Numerics; using Penumbra.Mods; using Penumbra.Util; namespace Penumbra.UI { - public partial class SettingsInterface + public partial class SettingsInterface : IDisposable { private const float DefaultVerticalSpace = 20f; @@ -25,8 +26,22 @@ namespace Penumbra.UI _menuBar = new MenuBar( this ); _menu = new SettingsMenu( this ); _modManager = Service< ModManager >.Get(); + + _plugin.PluginInterface.UiBuilder.DisableGposeUiHide = true; + _plugin.PluginInterface.UiBuilder.OnBuildUi += Draw; + _plugin.PluginInterface.UiBuilder.OnOpenConfigUi += OpenConfig; } + public void Dispose() + { + _menu.InstalledTab.Selector.Cache.Dispose(); + _plugin.PluginInterface.UiBuilder.OnBuildUi -= Draw; + _plugin.PluginInterface.UiBuilder.OnOpenConfigUi -= OpenConfig; + } + + private void OpenConfig( object _1, EventArgs _2 ) + => _menu.Visible = true; + public void FlipVisibility() => _menu.Visible = !_menu.Visible; @@ -44,7 +59,7 @@ namespace Penumbra.UI { _menu.InstalledTab.Selector.ClearSelection(); _modManager.DiscoverMods( _plugin.Configuration.ModDirectory ); - _menu.InstalledTab.Selector.Cache.ResetModList(); + _menu.InstalledTab.Selector.Cache.TriggerListReset(); } private void SaveCurrentCollection( bool recalculateMeta ) @@ -61,7 +76,7 @@ namespace Penumbra.UI { current.CalculateEffectiveFileList( _modManager.BasePath, recalculateMeta, current == _modManager.Collections.ActiveCollection ); - _menu.InstalledTab.Selector.Cache.ResetFilters(); + _menu.InstalledTab.Selector.Cache.TriggerFilterReset(); } } }