From 3b4cab2a1aec57743e1779f8d9364dac9977ef70 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Fri, 16 Jan 2026 21:03:03 +0100 Subject: [PATCH] Update Filter things. --- Penumbra/Config/Configuration.cs | 6 +- Penumbra/Config/ConfigurationFile.cs | 60 +++ Penumbra/Config/EphemeralConfig.cs | 1 - Penumbra/Config/FilterConfig.cs | 467 ++++++++++++++++++ Penumbra/Config/UiConfig.cs | 100 +--- Penumbra/Services/ConfigMigrationService.cs | 5 +- Penumbra/Services/FilenameService.cs | 1 + .../UI/AdvancedWindow/ResourceTreeViewer.cs | 56 ++- Penumbra/UI/Classes/ChangedItemDrawer.cs | 49 +- Penumbra/UI/Classes/ChangedItemIconFlag.cs | 2 +- Penumbra/UI/CollectionTab/CollectionFilter.cs | 6 + Penumbra/UI/MainWindow/ChangedItemsTab.cs | 59 +-- Penumbra/UI/MainWindow/EffectiveTab.cs | 18 +- .../UI/ModsTab/ModPanelChangedItemsTab.cs | 10 +- .../UI/ModsTab/Selector/Filter/ModFilter.cs | 30 +- .../ModsTab/Selector/ModFileSystemDrawer.cs | 7 +- Penumbra/UI/ResourceWatcher/Record.cs | 9 + .../UI/ResourceWatcher/ResourceWatcher.cs | 2 +- .../ResourceWatcher/ResourceWatcherTable.cs | 308 ++++-------- Penumbra/UI/Tabs/CollectionsTab.cs | 4 +- Penumbra/UI/Tabs/ResourceTab.cs | 8 +- Penumbra/UI/Tabs/SettingsTab.cs | 3 +- 22 files changed, 809 insertions(+), 402 deletions(-) create mode 100644 Penumbra/Config/ConfigurationFile.cs create mode 100644 Penumbra/Config/FilterConfig.cs diff --git a/Penumbra/Config/Configuration.cs b/Penumbra/Config/Configuration.cs index 16dde140..1e75378b 100644 --- a/Penumbra/Config/Configuration.cs +++ b/Penumbra/Config/Configuration.cs @@ -22,6 +22,9 @@ public class Configuration : IPluginConfiguration, ISavable, IService [JsonIgnore] public readonly EphemeralConfig Ephemeral; + [JsonIgnore] + public readonly FilterConfig Filters; + [JsonIgnore] public readonly UiConfig Ui; @@ -122,11 +125,12 @@ public class Configuration : IPluginConfiguration, ISavable, IService /// Includes adding new colors and migrating from old versions. /// public Configuration(CharacterUtility utility, ConfigMigrationService migrator, SaveService saveService, EphemeralConfig ephemeral, - UiConfig ui) + UiConfig ui, FilterConfig filters) { _saveService = saveService; Ephemeral = ephemeral; Ui = ui; + Filters = filters; Load(utility, migrator); } diff --git a/Penumbra/Config/ConfigurationFile.cs b/Penumbra/Config/ConfigurationFile.cs new file mode 100644 index 00000000..2e2f4afc --- /dev/null +++ b/Penumbra/Config/ConfigurationFile.cs @@ -0,0 +1,60 @@ +using Dalamud.Interface.ImGuiNotification; +using Luna; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Penumbra.Services; + +namespace Penumbra; + +public abstract class ConfigurationFile(SaveService saveService, TimeSpan? saveDelay = null) : ISavable, IService +{ + public abstract int CurrentVersion { get; } + + [JsonIgnore] + protected readonly SaveService SaveService = saveService; + + public virtual void Save() + => SaveService.DelaySave(this, SaveDelay); + + protected TimeSpan SaveDelay { get; set; } = saveDelay ?? TimeSpan.FromMinutes(1); + + public virtual void Save(StreamWriter writer) + { + using var j = new JsonTextWriter(writer); + j.Formatting = Formatting.Indented; + j.WriteStartObject(); + j.WritePropertyName("Version"); + j.WriteValue(CurrentVersion); + AddData(j); + j.WriteEndObject(); + } + + protected abstract void AddData(JsonTextWriter j); + protected abstract void LoadData(JObject j); + + public abstract string ToFilePath(FilenameService fileNames); + + protected virtual void Load() + { + var fileName = ToFilePath(SaveService.FileNames); + var logName = ((ISavable)this).LogName(fileName); + if (!File.Exists(fileName)) + return; + + try + { + Penumbra.Log.Debug($"Reading {logName}..."); + var text = File.ReadAllText(fileName); + var jObj = JObject.Parse(text); + if (jObj["Version"]?.Value() != CurrentVersion) + throw new Exception("Unsupported version."); + + LoadData(jObj); + } + catch (Exception ex) + { + Penumbra.Messager.NotificationMessage(ex, $"Error reading {logName}, reverting to default.", + $"Error reading {logName}", NotificationType.Error); + } + } +} diff --git a/Penumbra/Config/EphemeralConfig.cs b/Penumbra/Config/EphemeralConfig.cs index 2eb8d038..509c32dd 100644 --- a/Penumbra/Config/EphemeralConfig.cs +++ b/Penumbra/Config/EphemeralConfig.cs @@ -30,7 +30,6 @@ public class EphemeralConfig : ISavable, IService public RecordType ResourceWatcherRecordTypes { get; set; } = ResourceWatcher.AllRecords; public CollectionPanelMode CollectionPanel { get; set; } = CollectionPanelMode.SimpleAssignment; public TabType SelectedTab { get; set; } = TabType.Settings; - public ChangedItemIconFlag ChangedItemFilter { get; set; } = ChangedItemFlagExtensions.DefaultFlags; public bool FixMainWindow { get; set; } = false; public HashSet AdvancedEditingOpenForModPaths { get; set; } = []; public bool ForceRedrawOnFileChange { get; set; } = false; diff --git a/Penumbra/Config/FilterConfig.cs b/Penumbra/Config/FilterConfig.cs new file mode 100644 index 00000000..44325038 --- /dev/null +++ b/Penumbra/Config/FilterConfig.cs @@ -0,0 +1,467 @@ +using Luna.Generators; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Penumbra.Enums; +using Penumbra.Services; +using Penumbra.UI.Classes; +using Penumbra.UI.ModsTab.Selector; +using Penumbra.UI.ResourceWatcher; + +namespace Penumbra; + +public sealed partial class FilterConfig : ConfigurationFile +{ + public override int CurrentVersion + => 1; + + public FilterConfig(SaveService saveService) + : base(saveService) + { + Load(); + } + + protected override void AddData(JsonTextWriter j) + { + WriteModsTab(j); + WriteCollectionsTab(j); + WriteChangedItemsTab(j); + WriteEffectiveChangesTab(j); + WriteOnScreenTab(j); + WriteResourceManagerTab(j); + WriteResourceWatcherTab(j); + } + + protected override void LoadData(JObject j) + { + LoadModsTab(j); + LoadCollectionsTab(j); + LoadChangedItemsTab(j); + LoadEffectiveChangesTab(j); + LoadOnScreenTab(j); + LoadResourceManagerTab(j); + LoadResourceWatcherTab(j); + } + + public override string ToFilePath(FilenameService fileNames) + => fileNames.FilterFile; + + + #region Mods Tab + + [ConfigProperty] + private ModTypeFilter _modTypeFilter = ModTypeFilterExtensions.UnfilteredStateMods; + + [ConfigProperty] + private string _modFilter = string.Empty; + + [ConfigProperty] + private ChangedItemIconFlag _modChangedItemTypeFilter = ChangedItemFlagExtensions.DefaultFlags; + + private void WriteModsTab(JsonTextWriter j) + { + if (ModTypeFilter is ModTypeFilterExtensions.UnfilteredStateMods + && ModFilter.Length is 0 + && ModChangedItemTypeFilter is ChangedItemFlagExtensions.DefaultFlags) + return; + + j.WritePropertyName("Mods"); + j.WriteStartObject(); + if (ModTypeFilter is not ModTypeFilterExtensions.UnfilteredStateMods) + { + j.WritePropertyName("TypeFilter"); + j.WriteValue((uint)ModTypeFilter); + } + + if (ModFilter.Length > 0) + { + j.WritePropertyName("ModFilter"); + j.WriteValue(ModFilter); + } + + if (ModChangedItemTypeFilter is not ChangedItemFlagExtensions.DefaultFlags) + { + j.WritePropertyName("ChangedItemTypeFilter"); + j.WriteValue((uint)ModChangedItemTypeFilter); + } + + j.WriteEndObject(); + } + + private void LoadModsTab(JObject j) + { + if (j["Mods"] is not JObject mods) + return; + + _modTypeFilter = mods["TypeFilter"]?.Value() is { } modTypeFilter + ? (ModTypeFilter)modTypeFilter + : ModTypeFilterExtensions.UnfilteredStateMods; + _modFilter = mods["ModFilter"]?.Value() ?? string.Empty; + _modChangedItemTypeFilter = mods["ChangedItemTypeFilter"]?.Value() is { } changedItemFilter + ? (ChangedItemIconFlag)changedItemFilter + : ChangedItemFlagExtensions.DefaultFlags; + } + + #endregion + + #region Collections Tab + + [ConfigProperty] + private string _collectionFilter = string.Empty; + + private void WriteCollectionsTab(JsonTextWriter j) + { + if (CollectionFilter.Length is 0) + return; + + j.WritePropertyName("Collections"); + j.WriteStartObject(); + if (CollectionFilter.Length > 0) + { + j.WritePropertyName("CollectionFilter"); + j.WriteValue(CollectionFilter); + } + + j.WriteEndObject(); + } + + private void LoadCollectionsTab(JObject j) + { + if (j["Collections"] is JObject collections) + _collectionFilter = collections["CollectionFilter"]?.Value() ?? string.Empty; + } + + #endregion + + #region Changed Items Tab + + // Changed Items tab + [ConfigProperty] + private string _changedItemItemFilter = string.Empty; + + [ConfigProperty] + private string _changedItemModFilter = string.Empty; + + [ConfigProperty] + private ChangedItemIconFlag _changedItemTypeFilter = ChangedItemFlagExtensions.DefaultFlags; + + private void WriteChangedItemsTab(JsonTextWriter j) + { + if (ChangedItemItemFilter.Length is 0 + && ChangedItemModFilter.Length is 0 + && ChangedItemTypeFilter is ChangedItemFlagExtensions.DefaultFlags) + return; + + j.WritePropertyName("ChangedItems"); + j.WriteStartObject(); + if (ChangedItemItemFilter.Length > 0) + { + j.WritePropertyName("ItemFilter"); + j.WriteValue(ChangedItemItemFilter); + } + + if (ChangedItemModFilter.Length > 0) + { + j.WritePropertyName("ModFilter"); + j.WriteValue(ChangedItemModFilter); + } + + if (ChangedItemTypeFilter is not ChangedItemFlagExtensions.DefaultFlags) + { + j.WritePropertyName("TypeFilter"); + j.WriteValue((uint)ChangedItemTypeFilter); + } + + j.WriteEndObject(); + } + + private void LoadChangedItemsTab(JObject j) + { + if (j["ChangedItems"] is not JObject changedItems) + return; + + _changedItemItemFilter = changedItems["ItemFilter"]?.Value() ?? string.Empty; + _changedItemModFilter = changedItems["ModFilter"]?.Value() ?? string.Empty; + _changedItemTypeFilter = changedItems["TypeFilter"]?.Value() is { } typeFilter + ? (ChangedItemIconFlag)typeFilter + : ChangedItemFlagExtensions.DefaultFlags; + } + + #endregion + + #region Effective Changes tab + + [ConfigProperty] + private string _effectiveChangesGamePathFilter = string.Empty; + + [ConfigProperty] + private string _effectiveChangesFilePathFilter = string.Empty; + + private void WriteEffectiveChangesTab(JsonTextWriter j) + { + if (EffectiveChangesGamePathFilter.Length is 0 && EffectiveChangesFilePathFilter.Length is 0) + return; + + j.WritePropertyName("EffectiveChanges"); + j.WriteStartObject(); + if (EffectiveChangesGamePathFilter.Length > 0) + { + j.WritePropertyName("GamePathFilter"); + j.WriteValue(EffectiveChangesGamePathFilter); + } + + if (EffectiveChangesFilePathFilter.Length > 0) + { + j.WritePropertyName("FilePathFilter"); + j.WriteValue(EffectiveChangesFilePathFilter); + } + + j.WriteEndObject(); + } + + private void LoadEffectiveChangesTab(JObject j) + { + if (j["EffectiveChanges"] is not JObject effectiveChanges) + return; + + _effectiveChangesGamePathFilter = effectiveChanges["GamePathFilter"]?.Value() ?? string.Empty; + _effectiveChangesFilePathFilter = effectiveChanges["FilePathFilter"]?.Value() ?? string.Empty; + } + + #endregion + + #region On-Screen tab + + [ConfigProperty] + private string _onScreenCharacterFilter = string.Empty; + + [ConfigProperty] + private string _onScreenItemFilter = string.Empty; + + [ConfigProperty] + private ChangedItemIconFlag _onScreenTypeFilter = ChangedItemFlagExtensions.DefaultFlags; + + private void WriteOnScreenTab(JsonTextWriter j) + { + if (OnScreenCharacterFilter.Length is 0 + && OnScreenItemFilter.Length is 0 + && OnScreenTypeFilter is ChangedItemFlagExtensions.DefaultFlags) + return; + + j.WritePropertyName("OnScreen"); + j.WriteStartObject(); + if (OnScreenCharacterFilter.Length > 0) + { + j.WritePropertyName("CharacterFilter"); + j.WriteValue(OnScreenCharacterFilter); + } + + if (OnScreenItemFilter.Length > 0) + { + j.WritePropertyName("ItemFilter"); + j.WriteValue(OnScreenItemFilter); + } + + if (OnScreenTypeFilter is not ChangedItemFlagExtensions.DefaultFlags) + { + j.WritePropertyName("TypeFilter"); + j.WriteValue((uint)OnScreenTypeFilter); + } + + j.WriteEndObject(); + } + + private void LoadOnScreenTab(JObject j) + { + if (j["OnScreen"] is not JObject onScreen) + return; + + _onScreenCharacterFilter = onScreen["CharacterFilter"]?.Value() ?? string.Empty; + _onScreenItemFilter = onScreen["ItemFilter"]?.Value() ?? string.Empty; + _onScreenTypeFilter = onScreen["TypeFilter"]?.Value() is { } typeFilter + ? (ChangedItemIconFlag)typeFilter + : ChangedItemFlagExtensions.DefaultFlags; + } + + #endregion + + #region Resource Manager tab + + [ConfigProperty] + private string _resourceManagerFilter = string.Empty; + + private void WriteResourceManagerTab(JsonTextWriter j) + { + if (ResourceManagerFilter.Length is 0) + return; + + j.WritePropertyName("ResourceManager"); + j.WriteStartObject(); + if (ResourceManagerFilter.Length > 0) + { + j.WritePropertyName("PathFilter"); + j.WriteValue(ResourceManagerFilter); + } + + j.WriteEndObject(); + } + + private void LoadResourceManagerTab(JObject j) + { + if (j["ResourceManager"] is JObject resourceManager) + _resourceManagerFilter = resourceManager["PathFilter"]?.Value() ?? string.Empty; + } + + #endregion + + #region + + [ConfigProperty] + private bool _resourceLoggerEnabled = false; + + [ConfigProperty] + private int _resourceLoggerMaxEntries = 500; + + [ConfigProperty] + private bool _resourceLoggerStoreOnlyMatching = true; + + [ConfigProperty] + private bool _resourceLoggerWriteToLog = false; + + [ConfigProperty] + private string _resourceLoggerLogFilter = string.Empty; + + [ConfigProperty] + private string _resourceLoggerPathFilter = string.Empty; + + [ConfigProperty] + private string _resourceLoggerCollectionFilter = string.Empty; + + [ConfigProperty] + private string _resourceLoggerObjectFilter = string.Empty; + + [ConfigProperty] + private string _resourceLoggerOriginalPathFilter = string.Empty; + + [ConfigProperty] + private string _resourceLoggerResourceFilter = string.Empty; + + [ConfigProperty] + private string _resourceLoggerCrcFilter = string.Empty; + + [ConfigProperty] + private string _resourceLoggerRefFilter = string.Empty; + + [ConfigProperty] + private string _resourceLoggerThreadFilter = string.Empty; + + [ConfigProperty] + private RecordType _resourceLoggerRecordFilter = RecordTypeExtensions.All; + + [ConfigProperty] + private BoolEnum _resourceLoggerCustomFilter = BoolEnumExtensions.All; + + [ConfigProperty] + private BoolEnum _resourceLoggerSyncFilter = BoolEnumExtensions.All; + + [ConfigProperty] + private ResourceCategoryFlag _resourceLoggerCategoryFilter = ResourceExtensions.AllResourceCategories; + + [ConfigProperty] + private ResourceTypeFlag _resourceLoggerTypeFilter = ResourceExtensions.AllResourceTypes; + + [ConfigProperty] + private LoadStateFlag _resourceLoggerLoadStateFilter = LoadStateExtensions.All; + + private void WriteResourceWatcherTab(JsonTextWriter j) + { + var jObj = new JObject(); + + if (ResourceLoggerEnabled) + jObj["Enabled"] = true; + if (ResourceLoggerWriteToLog) + jObj["WriteToLog"] = true; + if (ResourceLoggerMaxEntries is not 500) + jObj["MaxEntries"] = ResourceLoggerMaxEntries; + if (!ResourceLoggerStoreOnlyMatching) + jObj["StoreOnlyMatching"] = false; + if (ResourceLoggerLogFilter.Length > 0) + jObj["LogFilter"] = ResourceLoggerLogFilter; + if (ResourceLoggerPathFilter.Length > 0) + jObj["PathFilter"] = ResourceLoggerPathFilter; + if (ResourceLoggerCollectionFilter.Length > 0) + jObj["CollectionFilter"] = ResourceLoggerCollectionFilter; + if (ResourceLoggerObjectFilter.Length > 0) + jObj["ObjectFilter"] = ResourceLoggerObjectFilter; + if (ResourceLoggerOriginalPathFilter.Length > 0) + jObj["OriginalPathFilter"] = ResourceLoggerOriginalPathFilter; + if (ResourceLoggerResourceFilter.Length > 0) + jObj["ResourceFilter"] = ResourceLoggerResourceFilter; + if (ResourceLoggerCrcFilter.Length > 0) + jObj["CrcFilter"] = ResourceLoggerCrcFilter; + if (ResourceLoggerRefFilter.Length > 0) + jObj["RefFilter"] = ResourceLoggerRefFilter; + if (ResourceLoggerThreadFilter.Length > 0) + jObj["ThreadFilter"] = ResourceLoggerThreadFilter; + + if (ResourceLoggerRecordFilter is not RecordTypeExtensions.All) + jObj["RecordFilter"] = (uint)ResourceLoggerRecordFilter; + if (ResourceLoggerCustomFilter is not BoolEnumExtensions.All) + jObj["CustomFilter"] = (uint)ResourceLoggerCustomFilter; + if (ResourceLoggerSyncFilter is not BoolEnumExtensions.All) + jObj["SyncFilter"] = (uint)ResourceLoggerSyncFilter; + if (ResourceLoggerCategoryFilter != ResourceExtensions.AllResourceCategories) + jObj["CategoryFilter"] = (uint)ResourceLoggerCategoryFilter; + if (ResourceLoggerTypeFilter != ResourceExtensions.AllResourceTypes) + jObj["TypeFilter"] = (uint)ResourceLoggerTypeFilter; + if (ResourceLoggerLoadStateFilter is not LoadStateExtensions.All) + jObj["LoadStateFilter"] = (uint)ResourceLoggerLoadStateFilter; + + if (jObj.Count is not 0) + { + j.WritePropertyName("ResourceWatcher"); + jObj.WriteTo(j); + } + } + + private void LoadResourceWatcherTab(JObject j) + { + if (j["ResourceWatcher"] is not JObject resourceWatcher) + return; + + _resourceLoggerEnabled = resourceWatcher["Enabled"]?.Value() ?? false; + _resourceLoggerMaxEntries = resourceWatcher["MaxEntries"]?.Value() ?? 500; + _resourceLoggerStoreOnlyMatching = resourceWatcher["StoreOnlyMatching"]?.Value() ?? true; + _resourceLoggerWriteToLog = resourceWatcher["WriteToLog"]?.Value() ?? false; + + _resourceLoggerLogFilter = resourceWatcher["LogFilter"]?.Value() ?? string.Empty; + _resourceLoggerPathFilter = resourceWatcher["PathFilter"]?.Value() ?? string.Empty; + _resourceLoggerCollectionFilter = resourceWatcher["CollectionFilter"]?.Value() ?? string.Empty; + _resourceLoggerObjectFilter = resourceWatcher["ObjectFilter"]?.Value() ?? string.Empty; + _resourceLoggerOriginalPathFilter = resourceWatcher["OriginalPathFilter"]?.Value() ?? string.Empty; + _resourceLoggerResourceFilter = resourceWatcher["ResourceFilter"]?.Value() ?? string.Empty; + _resourceLoggerCrcFilter = resourceWatcher["CrcFilter"]?.Value() ?? string.Empty; + _resourceLoggerRefFilter = resourceWatcher["RefFilter"]?.Value() ?? string.Empty; + _resourceLoggerThreadFilter = resourceWatcher["ThreadFilter"]?.Value() ?? string.Empty; + + _resourceLoggerRecordFilter = resourceWatcher["RecordFilter"]?.Value() is { } recordFilter + ? (RecordType)recordFilter + : RecordTypeExtensions.All; + _resourceLoggerCustomFilter = resourceWatcher["CustomFilter"]?.Value() is { } customFilter + ? (BoolEnum)customFilter + : BoolEnumExtensions.All; + _resourceLoggerSyncFilter = resourceWatcher["SyncFilter"]?.Value() is { } syncFilter + ? (BoolEnum)syncFilter + : BoolEnumExtensions.All; + _resourceLoggerCategoryFilter = resourceWatcher["CategoryFilter"]?.Value() is { } categoryFilter + ? (ResourceCategoryFlag)categoryFilter + : ResourceExtensions.AllResourceCategories; + _resourceLoggerTypeFilter = resourceWatcher["TypeFilter"]?.Value() is { } typeFilter + ? (ResourceTypeFlag)typeFilter + : ResourceExtensions.AllResourceTypes; + _resourceLoggerLoadStateFilter = resourceWatcher["LoadStateFilter"]?.Value() is { } loadStateFilter + ? (LoadStateFlag)loadStateFilter + : LoadStateExtensions.All; + } + + #endregion +} diff --git a/Penumbra/Config/UiConfig.cs b/Penumbra/Config/UiConfig.cs index 7233b1ab..08c46467 100644 --- a/Penumbra/Config/UiConfig.cs +++ b/Penumbra/Config/UiConfig.cs @@ -1,73 +1,27 @@ -using Dalamud.Interface.ImGuiNotification; using Luna; +using Luna.Generators; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Penumbra.Services; namespace Penumbra; -public class UiConfig : ISavable, IService +public sealed partial class UiConfig : ConfigurationFile { - public const int CurrentVersion = 1; - - [JsonIgnore] - private readonly SaveService _saveService; - public UiConfig(SaveService saveService) + : base(saveService, TimeSpan.FromMinutes(5)) { - _saveService = saveService; Load(); } - private TwoPanelWidth _collectionsTabScale = new(0.25f, ScalingMode.Percentage); - - public TwoPanelWidth CollectionTabScale + protected override void AddData(JsonTextWriter j) { - get => _collectionsTabScale; - set - { - if (value == _collectionsTabScale) - return; - - _collectionsTabScale = value; - Save(); - } - } - - private TwoPanelWidth _modTabScale = new(0.3f, ScalingMode.Percentage); - - public TwoPanelWidth ModTabScale - { - get => _modTabScale; - set - { - if (value == _modTabScale) - return; - - _modTabScale = value; - Save(); - } - } - - public string ToFilePath(FilenameService fileNames) - => fileNames.UiConfigFile; - - public void Save() - => _saveService.DelaySave(this); - - public void Save(StreamWriter writer) - { - using var j = new JsonTextWriter(writer); - j.Formatting = Formatting.Indented; - j.WriteStartObject(); - j.WritePropertyName("Version"); - j.WriteValue(CurrentVersion); j.WritePropertyName("CollectionsTab"); j.WriteStartObject(); j.WritePropertyName("Mode"); - j.WriteValue(CollectionTabScale.Mode.ToString()); + j.WriteValue(CollectionsTabScale.Mode.ToString()); j.WritePropertyName("Width"); - j.WriteValue(CollectionTabScale.Width); + j.WriteValue(CollectionsTabScale.Width); j.WriteEndObject(); j.WritePropertyName("ModsTab"); j.WriteStartObject(); @@ -76,33 +30,27 @@ public class UiConfig : ISavable, IService j.WritePropertyName("Width"); j.WriteValue(ModTabScale.Width); j.WriteEndObject(); - j.WriteEndObject(); } - private void Load() + protected override void LoadData(JObject j) { - if (!File.Exists(_saveService.FileNames.UiConfigFile)) - return; + if (j["CollectionsTab"] is JObject collections) + _collectionsTabScale = new TwoPanelWidth(collections["Width"].ValueOr(float.NaN), + collections["Mode"].TextEnum(ScalingMode.Percentage)); - try - { - var text = File.ReadAllText(_saveService.FileNames.UiConfigFile); - var jObj = JObject.Parse(text); - if (jObj["Version"]?.Value() is not CurrentVersion) - throw new Exception("Unsupported version."); - - if (jObj["CollectionsTab"] is JObject collections) - _collectionsTabScale = new TwoPanelWidth(collections["Width"].ValueOr(float.NaN), - collections["Mode"].TextEnum(ScalingMode.Percentage)); - - if (jObj["ModsTab"] is JObject mods) - _modTabScale = new TwoPanelWidth(mods["Width"].ValueOr(float.NaN), mods["Mode"].TextEnum(ScalingMode.Percentage)); - } - catch (Exception ex) - { - Penumbra.Messager.NotificationMessage(ex, - "Error reading UI Configuration, reverting to default.", - "Error reading UI Configuration", NotificationType.Error); - } + if (j["ModsTab"] is JObject mods) + _modTabScale = new TwoPanelWidth(mods["Width"].ValueOr(float.NaN), mods["Mode"].TextEnum(ScalingMode.Percentage)); } + + public override int CurrentVersion + => 1; + + public override string ToFilePath(FilenameService fileNames) + => fileNames.UiConfigFile; + + [ConfigProperty] + private TwoPanelWidth _collectionsTabScale = new(0.25f, ScalingMode.Percentage); + + [ConfigProperty] + private TwoPanelWidth _modTabScale = new(0.3f, ScalingMode.Percentage); } diff --git a/Penumbra/Services/ConfigMigrationService.cs b/Penumbra/Services/ConfigMigrationService.cs index 2e55ed25..f5c35c14 100644 --- a/Penumbra/Services/ConfigMigrationService.cs +++ b/Penumbra/Services/ConfigMigrationService.cs @@ -8,7 +8,6 @@ using Penumbra.Interop.Services; using Penumbra.Mods.Editor; using Penumbra.Mods.Manager; using Penumbra.Mods.Settings; -using Penumbra.UI; using Penumbra.UI.Classes; using Penumbra.UI.ResourceWatcher; using Penumbra.UI.Tabs; @@ -126,8 +125,8 @@ public class ConfigMigrationService(SaveService saveService, BackupService backu _data["ResourceWatcherRecordTypes"]?.ToObject() ?? _config.Ephemeral.ResourceWatcherRecordTypes; _config.Ephemeral.CollectionPanel = _data["CollectionPanel"]?.ToObject() ?? _config.Ephemeral.CollectionPanel; _config.Ephemeral.SelectedTab = _data["SelectedTab"]?.ToObject() ?? _config.Ephemeral.SelectedTab; - _config.Ephemeral.ChangedItemFilter = _data["ChangedItemFilter"]?.ToObject() - ?? _config.Ephemeral.ChangedItemFilter; + _config.Filters.ChangedItemTypeFilter = _data["ChangedItemFilter"]?.ToObject() + ?? _config.Filters.ChangedItemTypeFilter; _config.Ephemeral.FixMainWindow = _data["FixMainWindow"]?.ToObject() ?? _config.Ephemeral.FixMainWindow; _config.Ephemeral.Save(); } diff --git a/Penumbra/Services/FilenameService.cs b/Penumbra/Services/FilenameService.cs index 54b7e5b5..66398aa7 100644 --- a/Penumbra/Services/FilenameService.cs +++ b/Penumbra/Services/FilenameService.cs @@ -11,6 +11,7 @@ public sealed class FilenameService(IDalamudPluginInterface pi) : BaseFilePathPr public readonly string LocalDataDirectory = Path.Combine(pi.ConfigDirectory.FullName, "mod_data"); public readonly string EphemeralConfigFile = Path.Combine(pi.ConfigDirectory.FullName, "ephemeral_config.json"); public readonly string UiConfigFile = Path.Combine(pi.ConfigDirectory.FullName, "ui_config.json"); + public readonly string FilterFile = Path.Combine(pi.ConfigDirectory.FullName, "filters.json"); public readonly string OldFilesystemFile = Path.Combine(pi.ConfigDirectory.FullName, "sort_order.json"); public readonly string ActiveCollectionsFile = Path.Combine(pi.ConfigDirectory.FullName, "active_collections.json"); public readonly string PredefinedTagFile = Path.Combine(pi.ConfigDirectory.FullName, "predefined_tags.json"); diff --git a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs index 7db1aabd..0674e5c5 100644 --- a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs +++ b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs @@ -39,11 +39,9 @@ public class ResourceTreeViewer( private readonly Dictionary _filterCache = []; private readonly Dictionary _writableCache = []; - private TreeCategory _categoryFilter = AllCategories; - private ChangedItemIconFlag _typeFilter = ChangedItemFlagExtensions.AllFlags; - private string _nameFilter = string.Empty; - private string _nodeFilter = string.Empty; - private string _note = string.Empty; + private TreeCategory _categoryFilter = AllCategories; + + private string _note = string.Empty; private Task? _task; @@ -73,7 +71,7 @@ public class ResourceTreeViewer( foreach (var (index, tree) in _task.Result.Index()) { var category = Classify(tree); - if (!_categoryFilter.HasFlag(category) || !tree.Name.Contains(_nameFilter, StringComparison.OrdinalIgnoreCase)) + if (!_categoryFilter.HasFlag(category) || !tree.Name.Contains(config.Filters.OnScreenCharacterFilter, StringComparison.OrdinalIgnoreCase)) continue; using (ImGuiColor.Text.Push(CategoryColor(category).Value())) @@ -198,15 +196,31 @@ public class ResourceTreeViewer( Im.Cursor.Y -= yOffset; using (Im.Child.Begin("##typeFilter"u8, new Vector2(Im.ContentRegion.Available.X, ChangedItemDrawer.TypeFilterIconSize.Y))) { - filterChanged |= changedItemDrawer.DrawTypeFilter(ref _typeFilter); + if (changedItemDrawer.DrawTypeFilter(config.Filters.OnScreenTypeFilter, out var newTypeFilter)) + { + filterChanged = true; + config.Filters.OnScreenTypeFilter = newTypeFilter; + } } var fieldWidth = (Im.ContentRegion.Available.X - checkSpacing * 2.0f - Im.Style.FrameHeightWithSpacing) / 2.0f; Im.Item.SetNextWidth(fieldWidth); - filterChanged |= Im.Input.Text("##TreeNameFilter"u8, ref _nameFilter, "Filter by Character/Entity Name..."u8); + var filter = config.Filters.OnScreenCharacterFilter; + if (Im.Input.Text("##TreeNameFilter"u8, ref filter, "Filter by Character/Entity Name..."u8)) + { + filterChanged = true; + config.Filters.OnScreenCharacterFilter = filter; + } + Im.Line.Same(0, checkSpacing); Im.Item.SetNextWidth(fieldWidth); - filterChanged |= Im.Input.Text("##NodeFilter"u8, ref _nodeFilter, "Filter by Item/Part Name or Path..."u8); + filter = config.Filters.OnScreenItemFilter; + if (Im.Input.Text("##NodeFilter"u8, ref filter, "Filter by Item/Part Name or Path..."u8)) + { + filterChanged = true; + config.Filters.OnScreenItemFilter = filter; + } + Im.Line.Same(0, checkSpacing); incognito.DrawToggle(Im.Style.FrameHeightWithSpacing); @@ -353,7 +367,9 @@ public class ResourceTreeViewer( if (hasMod && Im.Item.RightClicked() && Im.Io.KeyControl) communicator.SelectTab.Invoke(new SelectTab.Arguments(TabType.Mods, mod)); - Im.Tooltip.OnHover(default, $"{resourceNode.FullPath.ToPath()}\n\nClick to copy to clipboard.{(hasMod ? "\nControl + Right-Click to jump to mod." : string.Empty)}{GetAdditionalDataSuffix(resourceNode.AdditionalData)}", true); + Im.Tooltip.OnHover(default, + $"{resourceNode.FullPath.ToPath()}\n\nClick to copy to clipboard.{(hasMod ? "\nControl + Right-Click to jump to mod." : string.Empty)}{GetAdditionalDataSuffix(resourceNode.AdditionalData)}", + true); } else { @@ -411,16 +427,17 @@ public class ResourceTreeViewer( bool MatchesFilter(ResourceNode node, ChangedItemIconFlag filterIcon) { - if (!_typeFilter.HasFlag(filterIcon)) + if (!config.Filters.OnScreenTypeFilter.HasFlag(filterIcon)) return false; - if (_nodeFilter.Length == 0) + if (config.Filters.OnScreenItemFilter.Length == 0) return true; - return node.Name != null && node.Name.Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase) - || node.FullPath.FullName.Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase) - || node.FullPath.InternalName.ToString().Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase) - || Array.Exists(node.PossibleGamePaths, path => path.Path.ToString().Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase)); + return node.Name != null && node.Name.Contains(config.Filters.OnScreenItemFilter, StringComparison.OrdinalIgnoreCase) + || node.FullPath.FullName.Contains(config.Filters.OnScreenItemFilter, StringComparison.OrdinalIgnoreCase) + || node.FullPath.InternalName.ToString().Contains(config.Filters.OnScreenItemFilter, StringComparison.OrdinalIgnoreCase) + || Array.Exists(node.PossibleGamePaths, + path => path.Path.ToString().Contains(config.Filters.OnScreenItemFilter, StringComparison.OrdinalIgnoreCase)); } void DrawActions(ResourceNode resourceNode, Vector2 buttonSize) @@ -489,7 +506,9 @@ public class ResourceTreeViewer( private static void HeaderInteraction(ResourceTree tree) { - Im.Tooltip.OnHover(default, $"Object Index: {tree.GameObjectIndex}\nObject Address: 0x{tree.GameObjectAddress:X16}\nDraw Object Address: 0x{tree.DrawObjectAddress:X16}", true, Im.Font.Mono); + Im.Tooltip.OnHover(default, + $"Object Index: {tree.GameObjectIndex}\nObject Address: 0x{tree.GameObjectAddress:X16}\nDraw Object Address: 0x{tree.DrawObjectAddress:X16}", + true, Im.Font.Mono); if (tree.GameObjectAddress == nint.Zero) return; @@ -513,7 +532,8 @@ public class ResourceTreeViewer( private static void ResourceInteraction(ResourceNode node) { - Im.Tooltip.OnHover(default, $"Resource Type: {node.Type}\nObject Address: 0x{node.ObjectAddress:X16}\nResource Handle: 0x{node.ResourceHandle:X16}\nLength: 0x{node.Length:X16}", + Im.Tooltip.OnHover(default, + $"Resource Type: {node.Type}\nObject Address: 0x{node.ObjectAddress:X16}\nResource Handle: 0x{node.ResourceHandle:X16}\nLength: 0x{node.Length:X16}", true, Im.Font.Mono); if (node.ResourceHandle == nint.Zero) diff --git a/Penumbra/UI/Classes/ChangedItemDrawer.cs b/Penumbra/UI/Classes/ChangedItemDrawer.cs index 6ccaef5b..579a90e1 100644 --- a/Penumbra/UI/Classes/ChangedItemDrawer.cs +++ b/Penumbra/UI/Classes/ChangedItemDrawer.cs @@ -15,7 +15,8 @@ namespace Penumbra.UI.Classes; public class ChangedItemDrawer : IDisposable, IUiService { - private static readonly string[] LowerNames = ChangedItemFlagExtensions.Order.Select(f => f.ToDescription().ToString().ToLowerInvariant()).ToArray(); + private static readonly string[] LowerNames = + ChangedItemFlagExtensions.Order.Select(f => f.ToDescription().ToString().ToLowerInvariant()).ToArray(); public static bool TryParseIndex(ReadOnlySpan input, out ChangedItemIconFlag slot) { @@ -83,9 +84,15 @@ public class ChangedItemDrawer : IDisposable, IUiService } /// Check if a changed item should be drawn based on its category. - public bool FilterChangedItem(string name, IIdentifiedObjectData data, string filter) - => (_config.Ephemeral.ChangedItemFilter == ChangedItemFlagExtensions.AllFlags - || _config.Ephemeral.ChangedItemFilter.HasFlag(data.GetIcon().ToFlag())) + public bool FilterChangedItemGlobal(string name, IIdentifiedObjectData data, string filter) + => (_config.Filters.ChangedItemTypeFilter == ChangedItemFlagExtensions.AllFlags + || _config.Filters.ChangedItemTypeFilter.HasFlag(data.GetIcon().ToFlag())) + && (filter.Length is 0 || !data.IsFilteredOut(name, filter)); + + /// Check if a changed item should be drawn based on its category. + public bool FilterChangedItemMod(string name, IIdentifiedObjectData data, string filter) + => (_config.Filters.ModChangedItemTypeFilter == ChangedItemFlagExtensions.AllFlags + || _config.Filters.ModChangedItemTypeFilter.HasFlag(data.GetIcon().ToFlag())) && (filter.Length is 0 || !data.IsFilteredOut(name, filter)); /// Draw the icon corresponding to the category of a changed item. @@ -160,31 +167,33 @@ public class ChangedItemDrawer : IDisposable, IUiService } /// Draw a header line with the different icon types to filter them. - public void DrawTypeFilter() + public void DrawTypeFilter(bool global) { if (_config.HideChangedItemFilters) return; - var typeFilter = _config.Ephemeral.ChangedItemFilter; - if (DrawTypeFilter(ref typeFilter)) + var typeFilter = global ? _config.Filters.ChangedItemTypeFilter : _config.Filters.ModChangedItemTypeFilter; + if (DrawTypeFilter(typeFilter, out typeFilter)) { - _config.Ephemeral.ChangedItemFilter = typeFilter; - _config.Ephemeral.Save(); + if (global) + _config.Filters.ChangedItemTypeFilter = typeFilter; + else + _config.Filters.ModChangedItemTypeFilter = typeFilter; } } /// Draw a header line with the different icon types to filter them. - public bool DrawTypeFilter(ref ChangedItemIconFlag typeFilter) + public bool DrawTypeFilter(ChangedItemIconFlag typeFilter, out ChangedItemIconFlag newTypeFilter) { var ret = false; using var _ = Im.Id.Push("ChangedItemIconFilter"u8); var size = TypeFilterIconSize; using var style = ImStyleDouble.ItemSpacing.Push(Vector2.Zero); - + newTypeFilter = typeFilter; foreach (var iconType in ChangedItemFlagExtensions.Order) { - ret |= DrawIcon(iconType, ref typeFilter); + ret |= DrawIcon(iconType, ref newTypeFilter); Im.Line.Same(); } @@ -198,30 +207,30 @@ public class ChangedItemDrawer : IDisposable, IUiService }); if (Im.Item.Clicked()) { - typeFilter = typeFilter is ChangedItemFlagExtensions.AllFlags ? 0 : ChangedItemFlagExtensions.AllFlags; - ret = true; + newTypeFilter = typeFilter is ChangedItemFlagExtensions.AllFlags ? 0 : ChangedItemFlagExtensions.AllFlags; + ret = true; } return ret; - bool DrawIcon(ChangedItemIconFlag type, ref ChangedItemIconFlag typeFilter) + bool DrawIcon(ChangedItemIconFlag type, ref ChangedItemIconFlag filter) { var localRet = false; var icon = _icons[type]; - var flag = typeFilter.HasFlag(type); + var flag = filter.HasFlag(type); Im.Image.Draw(icon.Id(), size, Vector2.Zero, Vector2.One, flag ? Vector4.One : new Vector4(0.6f, 0.3f, 0.3f, 1f)); if (Im.Item.Clicked()) { - typeFilter = flag ? typeFilter & ~type : typeFilter | type; - localRet = true; + filter = flag ? filter & ~type : filter | type; + localRet = true; } using var popup = Im.Popup.BeginContextItem($"{type}"); if (popup) if (Im.Menu.Item("Enable Only This"u8)) { - typeFilter = type; - localRet = true; + filter = type; + localRet = true; Im.Popup.CloseCurrent(); } diff --git a/Penumbra/UI/Classes/ChangedItemIconFlag.cs b/Penumbra/UI/Classes/ChangedItemIconFlag.cs index 6cc0842f..bd651ca4 100644 --- a/Penumbra/UI/Classes/ChangedItemIconFlag.cs +++ b/Penumbra/UI/Classes/ChangedItemIconFlag.cs @@ -50,7 +50,7 @@ public static class ChangedItemFlagExtensions public const ChangedItemIconFlag AllFlags = (ChangedItemIconFlag)0x01FFFF; public static readonly int NumCategories = Order.Count; - public const ChangedItemIconFlag DefaultFlags = AllFlags & ~ChangedItemIconFlag.Offhand; + public const ChangedItemIconFlag DefaultFlags = AllFlags; public static ReadOnlySpan ToDescription(this ChangedItemIconFlag iconFlag) => iconFlag switch diff --git a/Penumbra/UI/CollectionTab/CollectionFilter.cs b/Penumbra/UI/CollectionTab/CollectionFilter.cs index 57d0a44f..c572efdf 100644 --- a/Penumbra/UI/CollectionTab/CollectionFilter.cs +++ b/Penumbra/UI/CollectionTab/CollectionFilter.cs @@ -5,6 +5,12 @@ namespace Penumbra.UI.CollectionTab; public sealed class CollectionFilter : TextFilterBase, IUiService { + public CollectionFilter(FilterConfig filterConfig) + { + Set(filterConfig.CollectionFilter); + FilterChanged += () => filterConfig.CollectionFilter = Text; + } + public override bool WouldBeVisible(in CollectionSelector.Entry item, int globalIndex) => base.WouldBeVisible(in item, globalIndex) || WouldBeVisible(item.AnonymousName.Utf16); diff --git a/Penumbra/UI/MainWindow/ChangedItemsTab.cs b/Penumbra/UI/MainWindow/ChangedItemsTab.cs index f226f134..845b97a0 100644 --- a/Penumbra/UI/MainWindow/ChangedItemsTab.cs +++ b/Penumbra/UI/MainWindow/ChangedItemsTab.cs @@ -11,24 +11,12 @@ using Penumbra.UI.Classes; namespace Penumbra.UI.MainWindow; -public class UiState : ISavable, IService -{ - public string ChangedItemTabNameFilter = string.Empty; - public string ChangedItemTabModFilter = string.Empty; - public ChangedItemIconFlag ChangedItemTabCategoryFilter = ChangedItemFlagExtensions.DefaultFlags; - - public string ToFilePath(FilenameService fileNames) - => "uiState"; - - public void Save(StreamWriter writer) - { } -} - public sealed class ChangedItemsTab( CollectionManager collectionManager, CollectionSelectHeader collectionHeader, ChangedItemDrawer drawer, - CommunicatorService communicator) + CommunicatorService communicator, + FilterConfig filterConfig) : ITab { public ReadOnlySpan Label @@ -39,14 +27,14 @@ public sealed class ChangedItemsTab( private Vector2 _buttonSize; - private readonly ChangedItemFilter _filter = new(drawer, new UiState()); + private readonly ChangedItemFilter _filter = new(drawer, filterConfig); - private sealed class ChangedItemFilter(ChangedItemDrawer drawer, UiState uiState) : IFilter + private sealed class ChangedItemFilter(ChangedItemDrawer drawer, FilterConfig filterConfig) : IFilter { public bool WouldBeVisible(in Item item, int globalIndex) - => drawer.FilterChangedItem(item.Name, item.Data, uiState.ChangedItemTabNameFilter) - && (uiState.ChangedItemTabModFilter.Length is 0 - || item.Mods.Any(m => m.Name.Contains(uiState.ChangedItemTabModFilter, StringComparison.OrdinalIgnoreCase))); + => drawer.FilterChangedItemGlobal(item.Name, item.Data, filterConfig.ChangedItemItemFilter) + && (filterConfig.ChangedItemModFilter.Length is 0 + || item.Mods.Any(m => m.Name.Contains(filterConfig.ChangedItemModFilter, StringComparison.OrdinalIgnoreCase))); public event Action? FilterChanged; @@ -56,29 +44,36 @@ public sealed class ChangedItemsTab( - 450 * Im.Style.GlobalScale - Im.Style.ItemSpacing.X; Im.Item.SetNextWidth(450 * Im.Style.GlobalScale); - var ret = Im.Input.Text("##changedItemsFilter"u8, ref uiState.ChangedItemTabNameFilter, "Filter Item..."u8); + var filter = filterConfig.ChangedItemItemFilter; + var ret = Im.Input.Text("##changedItemsFilter"u8, ref filter, "Filter Item..."u8); + if (ret) + filterConfig.ChangedItemItemFilter = filter; Im.Line.Same(); Im.Item.SetNextWidth(varWidth); - ret |= Im.Input.Text("##changedItemsModFilter"u8, ref uiState.ChangedItemTabModFilter, "Filter Mods..."u8); - if (!ret) - return false; + filter = filterConfig.ChangedItemModFilter; + if (Im.Input.Text("##changedItemsModFilter"u8, ref filter, "Filter Mods..."u8)) + { + ret = true; + filterConfig.ChangedItemModFilter = filter; + } - FilterChanged?.Invoke(); - return true; + if (ret) + FilterChanged?.Invoke(); + return ret; } public void Clear() { - uiState.ChangedItemTabModFilter = string.Empty; - uiState.ChangedItemTabNameFilter = string.Empty; - uiState.ChangedItemTabCategoryFilter = ChangedItemFlagExtensions.DefaultFlags; + filterConfig.ChangedItemModFilter = string.Empty; + filterConfig.ChangedItemItemFilter = string.Empty; + filterConfig.ChangedItemTypeFilter = ChangedItemFlagExtensions.DefaultFlags; FilterChanged?.Invoke(); } public bool IsEmpty - => uiState.ChangedItemTabModFilter.Length is 0 - && uiState.ChangedItemTabNameFilter.Length is 0 - && uiState.ChangedItemTabCategoryFilter is ChangedItemFlagExtensions.DefaultFlags; + => filterConfig.ChangedItemModFilter.Length is 0 + && filterConfig.ChangedItemItemFilter.Length is 0 + && filterConfig.ChangedItemTypeFilter is ChangedItemFlagExtensions.DefaultFlags; } private readonly record struct Item(string Label, IIdentifiedObjectData Data, SingleArray Mods) @@ -124,7 +119,7 @@ public sealed class ChangedItemsTab( public void DrawContent() { collectionHeader.Draw(true); - drawer.DrawTypeFilter(); + drawer.DrawTypeFilter(true); _filter.DrawFilter("##Filter"u8, Im.ContentRegion.Available); using var child = Im.Child.Begin("##changedItemsChild"u8, Im.ContentRegion.Available); if (!child) diff --git a/Penumbra/UI/MainWindow/EffectiveTab.cs b/Penumbra/UI/MainWindow/EffectiveTab.cs index 44244aef..216aed00 100644 --- a/Penumbra/UI/MainWindow/EffectiveTab.cs +++ b/Penumbra/UI/MainWindow/EffectiveTab.cs @@ -15,7 +15,8 @@ namespace Penumbra.UI.MainWindow; public sealed class EffectiveTab( CollectionManager collectionManager, CollectionSelectHeader collectionHeader, - CommunicatorService communicatorService) + CommunicatorService communicatorService, + FilterConfig filterConfig) : ITab { public ReadOnlySpan Label @@ -31,7 +32,7 @@ public sealed class EffectiveTab( public TabType Identifier => TabType.EffectiveChanges; - private readonly PairFilter _filter = new(new GamePathFilter(), new FullPathFilter()); + private readonly PairFilter _filter = new(new GamePathFilter(filterConfig), new FullPathFilter(filterConfig)); private sealed class Cache : BasicFilterCache, IPanel { @@ -111,7 +112,6 @@ public sealed class EffectiveTab( Filter.Filter1.DrawFilter("Filter game path..."u8, new Vector2(_gamePathSize + Im.Style.CellPadding.X, Im.Style.FrameHeight)); Im.Line.Same(0, _arrowSize + 2 * Im.Style.CellPadding.X); Filter.Filter2.DrawFilter("Filter file path..."u8, Im.ContentRegion.Available with { Y = Im.Style.FrameHeight }); - } private void DrawTable() @@ -143,12 +143,24 @@ public sealed class EffectiveTab( private sealed class GamePathFilter : RegexFilterBase { + public GamePathFilter(FilterConfig config) + { + Set(config.EffectiveChangesGamePathFilter); + FilterChanged += () => config.EffectiveChangesGamePathFilter = Text; + } + protected override string ToFilterString(in Item item, int globalIndex) => item.GamePath.Utf16; } private sealed class FullPathFilter : RegexFilterBase { + public FullPathFilter(FilterConfig config) + { + Set(config.EffectiveChangesFilePathFilter); + FilterChanged += () => config.EffectiveChangesFilePathFilter = Text; + } + protected override string ToFilterString(in Item item, int globalIndex) => item.FilePath.FullName; } diff --git a/Penumbra/UI/ModsTab/ModPanelChangedItemsTab.cs b/Penumbra/UI/ModsTab/ModPanelChangedItemsTab.cs index 201caf44..0b3be587 100644 --- a/Penumbra/UI/ModsTab/ModPanelChangedItemsTab.cs +++ b/Penumbra/UI/ModsTab/ModPanelChangedItemsTab.cs @@ -110,7 +110,7 @@ public class ModPanelChangedItemsTab( { foreach (var (s, i) in _lastSelected.ChangedItems) { - if (drawer.FilterChangedItem(s, i, string.Empty)) + if (drawer.FilterChangedItemMod(s, i, string.Empty)) Data.Add(Container.Single(s, i)); } @@ -124,7 +124,7 @@ public class ModPanelChangedItemsTab( if (i is not IdentifiedItem item) continue; - if (!drawer.FilterChangedItem(s, item, string.Empty)) + if (!drawer.FilterChangedItemMod(s, item, string.Empty)) continue; if (tmp.TryGetValue((item.Item.PrimaryId, item.Item.Type), out var p)) @@ -160,7 +160,7 @@ public class ModPanelChangedItemsTab( if (i is IdentifiedItem) continue; - if (!drawer.FilterChangedItem(s, i, string.Empty)) + if (!drawer.FilterChangedItemMod(s, i, string.Empty)) continue; while (sortedTmpIdx < sortedTmp.Length @@ -221,9 +221,9 @@ public class ModPanelChangedItemsTab( _id = Im.Id.Current; _mod = fileSystem.Selection.Selection!.GetValue()!; var cache = CacheManager.Instance.GetOrCreateCache(_id, () => new ChangedItemsCache()); - drawer.DrawTypeFilter(); + drawer.DrawTypeFilter(false); - cache.Update(_mod, drawer, config.Ephemeral.ChangedItemFilter, config.ChangedItemDisplay); + cache.Update(_mod, drawer, config.Filters.ModChangedItemTypeFilter, config.ChangedItemDisplay); Im.Separator(); _buttonSize = new Vector2(Im.Style.ItemSpacing.Y + Im.Style.FrameHeight); using var style = ImStyleDouble.CellPadding.Push(Vector2.Zero) diff --git a/Penumbra/UI/ModsTab/Selector/Filter/ModFilter.cs b/Penumbra/UI/ModsTab/Selector/Filter/ModFilter.cs index b018dd5a..05d74a88 100644 --- a/Penumbra/UI/ModsTab/Selector/Filter/ModFilter.cs +++ b/Penumbra/UI/ModsTab/Selector/Filter/ModFilter.cs @@ -9,11 +9,25 @@ using static ImSharp.Im; namespace Penumbra.UI.ModsTab.Selector; -public sealed class ModFilter(ModManager modManager, ActiveCollections collections) - : TokenizedFilter, - IFileSystemFilter +public sealed class ModFilter : TokenizedFilter, + IFileSystemFilter { - private ModTypeFilter _stateFilter = ModTypeFilterExtensions.UnfilteredStateMods; + private ModTypeFilter _stateFilter; + private readonly ModManager _modManager; + private readonly ActiveCollections _collections; + + public ModFilter(ModManager modManager, ActiveCollections collections, FilterConfig filterConfig) + { + _modManager = modManager; + _collections = collections; + _stateFilter = filterConfig.ModTypeFilter; + Set(filterConfig.ModFilter); + FilterChanged += () => + { + filterConfig.ModFilter = Text; + filterConfig.ModTypeFilter = StateFilter; + }; + } public ModTypeFilter StateFilter => _stateFilter; @@ -156,8 +170,8 @@ public sealed class ModFilter(ModManager modManager, ActiveCollections collectio private bool CheckStateFilters(Mod mod) { - var (settings, collection) = collections.Current.GetActualSettings(mod.Index); - var isNew = modManager.IsNew(mod); + var (settings, collection) = _collections.Current.GetActualSettings(mod.Index); + var isNew = _modManager.IsNew(mod); // Handle mod details. if (CheckFlags(mod.TotalFileCount, ModTypeFilter.HasNoFiles, ModTypeFilter.HasFiles) || CheckFlags(mod.TotalSwapCount, ModTypeFilter.HasNoFileSwaps, ModTypeFilter.HasFileSwaps) @@ -182,7 +196,7 @@ public sealed class ModFilter(ModManager modManager, ActiveCollections collectio } // Handle Inheritance - if (collection == collections.Current) + if (collection == _collections.Current) { if (!_stateFilter.HasFlag(ModTypeFilter.Uninherited)) return false; @@ -213,7 +227,7 @@ public sealed class ModFilter(ModManager modManager, ActiveCollections collectio return false; // Conflicts can only be relevant if the mod is enabled. - var conflicts = collections.Current.Conflicts(mod); + var conflicts = _collections.Current.Conflicts(mod); if (conflicts.Count > 0) { if (conflicts.Any(c => !c.Solved)) diff --git a/Penumbra/UI/ModsTab/Selector/ModFileSystemDrawer.cs b/Penumbra/UI/ModsTab/Selector/ModFileSystemDrawer.cs index b1dad750..7ad61722 100644 --- a/Penumbra/UI/ModsTab/Selector/ModFileSystemDrawer.cs +++ b/Penumbra/UI/ModsTab/Selector/ModFileSystemDrawer.cs @@ -17,12 +17,10 @@ public sealed class ModFileSystemDrawer : FileSystemDrawer _resources = resources; _destructor = destructor; _loader = loader; - _table = new ResourceWatcherTable(new ResourceWatcherConfig(), _records); + _table = new ResourceWatcherTable(config.Filters, _records); _resources.ResourceRequested += OnResourceRequested; _destructor.Subscribe(OnResourceDestroyed, ResourceHandleDestructor.Priority.ResourceWatcher); _loader.ResourceLoaded += OnResourceLoaded; diff --git a/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs b/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs index 6385b52a..51cd0388 100644 --- a/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs +++ b/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs @@ -9,33 +9,6 @@ using Penumbra.UI.Classes; namespace Penumbra.UI.ResourceWatcher; -public class ResourceWatcherConfig -{ - public int Version = 1; - public bool Enabled = false; - public int MaxEntries = 500; - public bool StoreOnlyMatching = true; - public bool WriteToLog = false; - public string LogFilter = string.Empty; - public string PathFilter = string.Empty; - public string CollectionFilter = string.Empty; - public string ObjectFilter = string.Empty; - public string OriginalPathFilter = string.Empty; - public string ResourceFilter = string.Empty; - public string CrcFilter = string.Empty; - public string RefFilter = string.Empty; - public string ThreadFilter = string.Empty; - public RecordType RecordFilter = Enum.GetValues().Or(); - public BoolEnum CustomFilter = BoolEnum.True | BoolEnum.False | BoolEnum.Unknown; - public BoolEnum SyncFilter = BoolEnum.True | BoolEnum.False | BoolEnum.Unknown; - public ResourceCategoryFlag CategoryFilter = ResourceExtensions.AllResourceCategories; - public ResourceTypeFlag TypeFilter = ResourceExtensions.AllResourceTypes; - public LoadStateFlag LoadStateFilter = Enum.GetValues().Or(); - - public void Save() - { } -} - [Flags] public enum BoolEnum : byte { @@ -44,6 +17,11 @@ public enum BoolEnum : byte Unknown = 0x04, } +public static class BoolEnumExtensions +{ + public const BoolEnum All = BoolEnum.True | BoolEnum.False | BoolEnum.Unknown; +} + [Flags] public enum LoadStateFlag : byte { @@ -55,6 +33,16 @@ public enum LoadStateFlag : byte None = 0xFF, } +public static class LoadStateExtensions +{ + public const LoadStateFlag All = LoadStateFlag.Success + | LoadStateFlag.Async + | LoadStateFlag.Failed + | LoadStateFlag.FailedSub + | LoadStateFlag.Unknown + | LoadStateFlag.None; +} + internal sealed unsafe class CachedRecord(Record record) { public readonly Record Record = record; @@ -84,23 +72,23 @@ internal sealed class ResourceWatcherTable : TableBase Columns.OfType().All(column => column.WouldBeVisible(record)); - public ResourceWatcherTable(ResourceWatcherConfig config, IReadOnlyList records) + public ResourceWatcherTable(FilterConfig filterConfig, IReadOnlyList records) : base(new StringU8("##records"u8), - new PathColumn(config) { Label = new StringU8("Path"u8) }, - new RecordTypeColumn(config) { Label = new StringU8("Record"u8) }, - new CollectionColumn(config) { Label = new StringU8("Collection"u8) }, - new ObjectColumn(config) { Label = new StringU8("Game Object"u8) }, - new CustomLoadColumn(config) { Label = new StringU8("Custom"u8) }, - new SynchronousLoadColumn(config) { Label = new StringU8("Sync"u8) }, - new OriginalPathColumn(config) { Label = new StringU8("Original Path"u8) }, - new ResourceCategoryColumn(config) { Label = new StringU8("Category"u8) }, - new ResourceTypeColumn(config) { Label = new StringU8("Type"u8) }, - new HandleColumn(config) { Label = new StringU8("Resource"u8) }, - new LoadStateColumn(config) { Label = new StringU8("State"u8) }, - new RefCountColumn(config) { Label = new StringU8("#Ref"u8) }, - new DateColumn { Label = new StringU8("Time"u8) }, - new Crc64Column(config) { Label = new StringU8("Crc64"u8) }, - new OsThreadColumn(config) { Label = new StringU8("TID"u8) } + new PathColumn(filterConfig) { Label = new StringU8("Path"u8) }, + new RecordTypeColumn(filterConfig) { Label = new StringU8("Record"u8) }, + new CollectionColumn(filterConfig) { Label = new StringU8("Collection"u8) }, + new ObjectColumn(filterConfig) { Label = new StringU8("Game Object"u8) }, + new CustomLoadColumn(filterConfig) { Label = new StringU8("Custom"u8) }, + new SynchronousLoadColumn(filterConfig) { Label = new StringU8("Sync"u8) }, + new OriginalPathColumn(filterConfig) { Label = new StringU8("Original Path"u8) }, + new ResourceCategoryColumn(filterConfig) { Label = new StringU8("Category"u8) }, + new ResourceTypeColumn(filterConfig) { Label = new StringU8("Type"u8) }, + new HandleColumn(filterConfig) { Label = new StringU8("Resource"u8) }, + new LoadStateColumn(filterConfig) { Label = new StringU8("State"u8) }, + new RefCountColumn(filterConfig) { Label = new StringU8("#Ref"u8) }, + new DateColumn { Label = new StringU8("Time"u8) }, + new Crc64Column(filterConfig) { Label = new StringU8("Crc64"u8) }, + new OsThreadColumn(filterConfig) { Label = new StringU8("TID"u8) } ) { _records = records; @@ -149,26 +137,15 @@ internal sealed class ResourceWatcherTable : TableBase, ICheckRecord { - private readonly ResourceWatcherConfig _config; - - public PathColumn(ResourceWatcherConfig config) + public PathColumn(FilterConfig config) { - _config = config; UnscaledWidth = 300; - Filter.Set(config.PathFilter); - Filter.FilterChanged += OnFilterChanged; - } - - private void OnFilterChanged() - { - _config.PathFilter = Filter.Text; - _config.Save(); + Filter.Set(config.ResourceLoggerPathFilter); + Filter.FilterChanged += () => config.ResourceLoggerPathFilter = Filter.Text; } public override void DrawColumn(in CachedRecord item, int globalIndex) - { - DrawByteString(item.Record.Path, 290 * Im.Style.GlobalScale); - } + => DrawByteString(item.Record.Path, 290 * Im.Style.GlobalScale); protected override string ComparisonText(in CachedRecord item, int globalIndex) => item.PathU16; @@ -182,20 +159,11 @@ internal sealed class ResourceWatcherTable : TableBase, ICheckRecord { - private readonly ResourceWatcherConfig _config; - - public RecordTypeColumn(ResourceWatcherConfig config) + public RecordTypeColumn(FilterConfig config) { - _config = config; UnscaledWidth = 80; - Filter.LoadValue(config.RecordFilter); - Filter.FilterChanged += OnFilterChanged; - } - - private void OnFilterChanged() - { - _config.RecordFilter = Filter.FilterValue; - _config.Save(); + Filter.LoadValue(config.ResourceLoggerRecordFilter); + Filter.FilterChanged += () => config.ResourceLoggerRecordFilter = Filter.FilterValue; } protected override StringU8 DisplayString(in CachedRecord item, int globalIndex) @@ -225,20 +193,11 @@ internal sealed class ResourceWatcherTable : TableBase, ICheckRecord { - private readonly ResourceWatcherConfig _config; - - public Crc64Column(ResourceWatcherConfig config) + public Crc64Column(FilterConfig config) { - _config = config; UnscaledWidth = 17 * 8; - Filter.Set(config.CrcFilter); - Filter.FilterChanged += OnFilterChanged; - } - - private void OnFilterChanged() - { - _config.CrcFilter = Filter.Text; - _config.Save(); + Filter.Set(config.ResourceLoggerCrcFilter); + Filter.FilterChanged += () => config.ResourceLoggerCrcFilter = Filter.Text; } public override int Compare(in CachedRecord lhs, int lhsGlobalIndex, in CachedRecord rhs, int rhsGlobalIndex) @@ -266,20 +225,11 @@ internal sealed class ResourceWatcherTable : TableBase, ICheckRecord { - private readonly ResourceWatcherConfig _config; - - public CollectionColumn(ResourceWatcherConfig config) + public CollectionColumn(FilterConfig config) { - _config = config; UnscaledWidth = 80; - Filter.Set(config.CollectionFilter); - Filter.FilterChanged += OnFilterChanged; - } - - private void OnFilterChanged() - { - _config.CollectionFilter = Filter.Text; - _config.Save(); + Filter.Set(config.ResourceLoggerCollectionFilter); + Filter.FilterChanged += () => config.ResourceLoggerCollectionFilter = Filter.Text; } protected override string ComparisonText(in CachedRecord item, int globalIndex) @@ -294,20 +244,11 @@ internal sealed class ResourceWatcherTable : TableBase, ICheckRecord { - private readonly ResourceWatcherConfig _config; - - public ObjectColumn(ResourceWatcherConfig config) + public ObjectColumn(FilterConfig config) { - _config = config; UnscaledWidth = 150; - Filter.Set(config.ObjectFilter); - Filter.FilterChanged += OnFilterChanged; - } - - private void OnFilterChanged() - { - _config.ObjectFilter = Filter.Text; - _config.Save(); + Filter.Set(config.ResourceLoggerObjectFilter); + Filter.FilterChanged += () => config.ResourceLoggerObjectFilter = Filter.Text; } protected override string ComparisonText(in CachedRecord item, int globalIndex) @@ -322,20 +263,11 @@ internal sealed class ResourceWatcherTable : TableBase, ICheckRecord { - private readonly ResourceWatcherConfig _config; - - public OriginalPathColumn(ResourceWatcherConfig config) + public OriginalPathColumn(FilterConfig config) { - _config = config; UnscaledWidth = 200; - Filter.Set(config.OriginalPathFilter); - Filter.FilterChanged += OnFilterChanged; - } - - private void OnFilterChanged() - { - _config.OriginalPathFilter = Filter.Text; - _config.Save(); + Filter.Set(config.ResourceLoggerOriginalPathFilter); + Filter.FilterChanged += () => config.ResourceLoggerOriginalPathFilter = Filter.Text; } public override void DrawColumn(in CachedRecord item, int globalIndex) @@ -355,20 +287,11 @@ internal sealed class ResourceWatcherTable : TableBase, ICheckRecord { - private readonly ResourceWatcherConfig _config; - - public ResourceCategoryColumn(ResourceWatcherConfig config) + public ResourceCategoryColumn(FilterConfig config) { - _config = config; UnscaledWidth = 80; - Filter.LoadValue(config.CategoryFilter); - Filter.FilterChanged += OnFilterChanged; - } - - private void OnFilterChanged() - { - _config.CategoryFilter = Filter.FilterValue; - _config.Save(); + Filter.LoadValue(config.ResourceLoggerCategoryFilter); + Filter.FilterChanged += () => config.ResourceLoggerCategoryFilter = Filter.FilterValue; } protected override StringU8 DisplayString(in CachedRecord item, int globalIndex) @@ -386,20 +309,11 @@ internal sealed class ResourceWatcherTable : TableBase, ICheckRecord { - private readonly ResourceWatcherConfig _config; - - public ResourceTypeColumn(ResourceWatcherConfig config) + public ResourceTypeColumn(FilterConfig config) { - _config = config; UnscaledWidth = 50; - Filter.LoadValue(config.TypeFilter); - Filter.FilterChanged += OnFilterChanged; - } - - private void OnFilterChanged() - { - _config.TypeFilter = Filter.FilterValue; - _config.Save(); + Filter.LoadValue(config.ResourceLoggerTypeFilter); + Filter.FilterChanged += () => config.ResourceLoggerTypeFilter = Filter.FilterValue; } protected override IReadOnlyList<(ResourceTypeFlag Value, StringU8 Name)> EnumData { get; } = @@ -417,20 +331,11 @@ internal sealed class ResourceWatcherTable : TableBase, ICheckRecord { - private readonly ResourceWatcherConfig _config; - - public LoadStateColumn(ResourceWatcherConfig config) + public LoadStateColumn(FilterConfig config) { - _config = config; UnscaledWidth = 50; - Filter.LoadValue(config.LoadStateFilter); - Filter.FilterChanged += OnFilterChanged; - } - - private void OnFilterChanged() - { - _config.LoadStateFilter = Filter.FilterValue; - _config.Save(); + Filter.LoadValue(config.ResourceLoggerLoadStateFilter); + Filter.FilterChanged += () => config.ResourceLoggerLoadStateFilter = Filter.FilterValue; } public override void DrawColumn(in CachedRecord item, int globalIndex) @@ -474,39 +379,30 @@ internal sealed class ResourceWatcherTable : TableBase GetValue(item.Record.LoadState); - + public bool WouldBeVisible(in Record record) => Filter.FilterValue.HasFlag(GetValue(record.LoadState)); private static LoadStateFlag GetValue(LoadState value) - => value switch - { - LoadState.None => LoadStateFlag.None, - LoadState.Success => LoadStateFlag.Success, - LoadState.FailedSubResource => LoadStateFlag.FailedSub, - <= LoadState.Constructed => LoadStateFlag.Unknown, - < LoadState.Success => LoadStateFlag.Async, - > LoadState.Success => LoadStateFlag.Failed, - }; + => value switch + { + LoadState.None => LoadStateFlag.None, + LoadState.Success => LoadStateFlag.Success, + LoadState.FailedSubResource => LoadStateFlag.FailedSub, + <= LoadState.Constructed => LoadStateFlag.Unknown, + < LoadState.Success => LoadStateFlag.Async, + > LoadState.Success => LoadStateFlag.Failed, + }; } private sealed class HandleColumn : TextColumn, ICheckRecord { - private readonly ResourceWatcherConfig _config; - - public HandleColumn(ResourceWatcherConfig config) + public HandleColumn(FilterConfig config) { - _config = config; UnscaledWidth = 120; - Filter.Set(config.ResourceFilter); - Filter.FilterChanged += OnFilterChanged; - } - - private void OnFilterChanged() - { - _config.ResourceFilter = Filter.Text; - _config.Save(); + Filter.Set(config.ResourceLoggerResourceFilter); + Filter.FilterChanged += () => config.ResourceLoggerResourceFilter = Filter.Text; } public override unsafe void DrawColumn(in CachedRecord item, int globalIndex) @@ -569,20 +465,11 @@ internal sealed class ResourceWatcherTable : TableBase config.ResourceLoggerCustomFilter = Filter.FilterValue; } protected override BoolEnum GetValue(in CachedRecord item, int globalIndex) @@ -594,20 +481,11 @@ internal sealed class ResourceWatcherTable : TableBase config.ResourceLoggerSyncFilter = Filter.FilterValue; } protected override BoolEnum GetValue(in CachedRecord item, int globalIndex) @@ -619,20 +497,11 @@ internal sealed class ResourceWatcherTable : TableBase, ICheckRecord { - private readonly ResourceWatcherConfig _config; - - public RefCountColumn(ResourceWatcherConfig config) + public RefCountColumn(FilterConfig config) { - _config = config; UnscaledWidth = 60; - Filter.Set(config.RefFilter); - Filter.FilterChanged += OnFilterChanged; - } - - private void OnFilterChanged() - { - _config.RefFilter = Filter.Text; - _config.Save(); + Filter.Set(config.ResourceLoggerRefFilter); + Filter.FilterChanged += () => config.ResourceLoggerRefFilter = Filter.Text; } public override uint ToValue(in CachedRecord item, int globalIndex) @@ -650,20 +519,11 @@ internal sealed class ResourceWatcherTable : TableBase, ICheckRecord { - private readonly ResourceWatcherConfig _config; - - public OsThreadColumn(ResourceWatcherConfig config) + public OsThreadColumn(FilterConfig config) { - _config = config; UnscaledWidth = 60; - Filter.Set(config.ThreadFilter); - Filter.FilterChanged += OnFilterChanged; - } - - private void OnFilterChanged() - { - _config.ThreadFilter = Filter.Text; - _config.Save(); + Filter.Set(config.ResourceLoggerThreadFilter); + Filter.FilterChanged += () => config.ResourceLoggerThreadFilter = Filter.Text; } public override uint ToValue(in CachedRecord item, int globalIndex) diff --git a/Penumbra/UI/Tabs/CollectionsTab.cs b/Penumbra/UI/Tabs/CollectionsTab.cs index ffcc5d8a..5317785b 100644 --- a/Penumbra/UI/Tabs/CollectionsTab.cs +++ b/Penumbra/UI/Tabs/CollectionsTab.cs @@ -43,10 +43,10 @@ public sealed class CollectionsTab : TwoPanelLayout, ITab } public void DrawContent() - => Draw(_config.CollectionTabScale); + => Draw(_config.CollectionsTabScale); protected override void SetWidth(float width, ScalingMode mode) - => _config.CollectionTabScale = new TwoPanelWidth(width, mode); + => _config.CollectionsTabScale = new TwoPanelWidth(width, mode); public void PostTabButton() => _tutorial.OpenTutorial(BasicTutorialSteps.Collections); diff --git a/Penumbra/UI/Tabs/ResourceTab.cs b/Penumbra/UI/Tabs/ResourceTab.cs index 0b4341a2..91cbcb82 100644 --- a/Penumbra/UI/Tabs/ResourceTab.cs +++ b/Penumbra/UI/Tabs/ResourceTab.cs @@ -20,10 +20,16 @@ public sealed class ResourceTab(Configuration config, ResourceManagerService res public bool IsVisible => config.DebugMode; - public readonly ResourceFilter Filter = new(); + public readonly ResourceFilter Filter = new(config.Filters); public sealed class ResourceFilter : Utf8FilterBase { + public ResourceFilter(FilterConfig filterConfig) + { + Set(new StringU8(filterConfig.ResourceManagerFilter)); + FilterChanged += () => filterConfig.ResourceManagerFilter = Text.ToString(); + } + protected override ReadOnlySpan ToFilterString(in ResourceHandle item, int globalIndex) => item.FileName.AsSpan(); } diff --git a/Penumbra/UI/Tabs/SettingsTab.cs b/Penumbra/UI/Tabs/SettingsTab.cs index ee0dd249..22d9d1b1 100644 --- a/Penumbra/UI/Tabs/SettingsTab.cs +++ b/Penumbra/UI/Tabs/SettingsTab.cs @@ -432,7 +432,8 @@ public sealed class SettingsTab : ITab _config.HideChangedItemFilters = v; if (v) { - _config.Ephemeral.ChangedItemFilter = ChangedItemFlagExtensions.AllFlags; + _config.Filters.ModChangedItemTypeFilter = ChangedItemFlagExtensions.AllFlags; + _config.Filters.ChangedItemTypeFilter = ChangedItemFlagExtensions.AllFlags; _config.Ephemeral.Save(); } });