Update Filter things.

This commit is contained in:
Ottermandias 2026-01-16 21:03:03 +01:00
parent 27e059658d
commit 3b4cab2a1a
22 changed files with 809 additions and 402 deletions

View file

@ -22,6 +22,9 @@ public class Configuration : IPluginConfiguration, ISavable, IService
[JsonIgnore] [JsonIgnore]
public readonly EphemeralConfig Ephemeral; public readonly EphemeralConfig Ephemeral;
[JsonIgnore]
public readonly FilterConfig Filters;
[JsonIgnore] [JsonIgnore]
public readonly UiConfig Ui; public readonly UiConfig Ui;
@ -122,11 +125,12 @@ public class Configuration : IPluginConfiguration, ISavable, IService
/// Includes adding new colors and migrating from old versions. /// Includes adding new colors and migrating from old versions.
/// </summary> /// </summary>
public Configuration(CharacterUtility utility, ConfigMigrationService migrator, SaveService saveService, EphemeralConfig ephemeral, public Configuration(CharacterUtility utility, ConfigMigrationService migrator, SaveService saveService, EphemeralConfig ephemeral,
UiConfig ui) UiConfig ui, FilterConfig filters)
{ {
_saveService = saveService; _saveService = saveService;
Ephemeral = ephemeral; Ephemeral = ephemeral;
Ui = ui; Ui = ui;
Filters = filters;
Load(utility, migrator); Load(utility, migrator);
} }

View file

@ -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<int>() != 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);
}
}
}

View file

@ -30,7 +30,6 @@ public class EphemeralConfig : ISavable, IService
public RecordType ResourceWatcherRecordTypes { get; set; } = ResourceWatcher.AllRecords; public RecordType ResourceWatcherRecordTypes { get; set; } = ResourceWatcher.AllRecords;
public CollectionPanelMode CollectionPanel { get; set; } = CollectionPanelMode.SimpleAssignment; public CollectionPanelMode CollectionPanel { get; set; } = CollectionPanelMode.SimpleAssignment;
public TabType SelectedTab { get; set; } = TabType.Settings; public TabType SelectedTab { get; set; } = TabType.Settings;
public ChangedItemIconFlag ChangedItemFilter { get; set; } = ChangedItemFlagExtensions.DefaultFlags;
public bool FixMainWindow { get; set; } = false; public bool FixMainWindow { get; set; } = false;
public HashSet<string> AdvancedEditingOpenForModPaths { get; set; } = []; public HashSet<string> AdvancedEditingOpenForModPaths { get; set; } = [];
public bool ForceRedrawOnFileChange { get; set; } = false; public bool ForceRedrawOnFileChange { get; set; } = false;

View file

@ -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<uint>() is { } modTypeFilter
? (ModTypeFilter)modTypeFilter
: ModTypeFilterExtensions.UnfilteredStateMods;
_modFilter = mods["ModFilter"]?.Value<string>() ?? string.Empty;
_modChangedItemTypeFilter = mods["ChangedItemTypeFilter"]?.Value<uint>() 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>() ?? 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>() ?? string.Empty;
_changedItemModFilter = changedItems["ModFilter"]?.Value<string>() ?? string.Empty;
_changedItemTypeFilter = changedItems["TypeFilter"]?.Value<uint>() 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>() ?? string.Empty;
_effectiveChangesFilePathFilter = effectiveChanges["FilePathFilter"]?.Value<string>() ?? 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>() ?? string.Empty;
_onScreenItemFilter = onScreen["ItemFilter"]?.Value<string>() ?? string.Empty;
_onScreenTypeFilter = onScreen["TypeFilter"]?.Value<uint>() 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>() ?? 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<bool>() ?? false;
_resourceLoggerMaxEntries = resourceWatcher["MaxEntries"]?.Value<int>() ?? 500;
_resourceLoggerStoreOnlyMatching = resourceWatcher["StoreOnlyMatching"]?.Value<bool>() ?? true;
_resourceLoggerWriteToLog = resourceWatcher["WriteToLog"]?.Value<bool>() ?? false;
_resourceLoggerLogFilter = resourceWatcher["LogFilter"]?.Value<string>() ?? string.Empty;
_resourceLoggerPathFilter = resourceWatcher["PathFilter"]?.Value<string>() ?? string.Empty;
_resourceLoggerCollectionFilter = resourceWatcher["CollectionFilter"]?.Value<string>() ?? string.Empty;
_resourceLoggerObjectFilter = resourceWatcher["ObjectFilter"]?.Value<string>() ?? string.Empty;
_resourceLoggerOriginalPathFilter = resourceWatcher["OriginalPathFilter"]?.Value<string>() ?? string.Empty;
_resourceLoggerResourceFilter = resourceWatcher["ResourceFilter"]?.Value<string>() ?? string.Empty;
_resourceLoggerCrcFilter = resourceWatcher["CrcFilter"]?.Value<string>() ?? string.Empty;
_resourceLoggerRefFilter = resourceWatcher["RefFilter"]?.Value<string>() ?? string.Empty;
_resourceLoggerThreadFilter = resourceWatcher["ThreadFilter"]?.Value<string>() ?? string.Empty;
_resourceLoggerRecordFilter = resourceWatcher["RecordFilter"]?.Value<uint>() is { } recordFilter
? (RecordType)recordFilter
: RecordTypeExtensions.All;
_resourceLoggerCustomFilter = resourceWatcher["CustomFilter"]?.Value<uint>() is { } customFilter
? (BoolEnum)customFilter
: BoolEnumExtensions.All;
_resourceLoggerSyncFilter = resourceWatcher["SyncFilter"]?.Value<uint>() is { } syncFilter
? (BoolEnum)syncFilter
: BoolEnumExtensions.All;
_resourceLoggerCategoryFilter = resourceWatcher["CategoryFilter"]?.Value<uint>() is { } categoryFilter
? (ResourceCategoryFlag)categoryFilter
: ResourceExtensions.AllResourceCategories;
_resourceLoggerTypeFilter = resourceWatcher["TypeFilter"]?.Value<uint>() is { } typeFilter
? (ResourceTypeFlag)typeFilter
: ResourceExtensions.AllResourceTypes;
_resourceLoggerLoadStateFilter = resourceWatcher["LoadStateFilter"]?.Value<uint>() is { } loadStateFilter
? (LoadStateFlag)loadStateFilter
: LoadStateExtensions.All;
}
#endregion
}

View file

@ -1,73 +1,27 @@
using Dalamud.Interface.ImGuiNotification;
using Luna; using Luna;
using Luna.Generators;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Penumbra.Services; using Penumbra.Services;
namespace Penumbra; 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) public UiConfig(SaveService saveService)
: base(saveService, TimeSpan.FromMinutes(5))
{ {
_saveService = saveService;
Load(); Load();
} }
private TwoPanelWidth _collectionsTabScale = new(0.25f, ScalingMode.Percentage); protected override void AddData(JsonTextWriter j)
public TwoPanelWidth CollectionTabScale
{ {
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.WritePropertyName("CollectionsTab");
j.WriteStartObject(); j.WriteStartObject();
j.WritePropertyName("Mode"); j.WritePropertyName("Mode");
j.WriteValue(CollectionTabScale.Mode.ToString()); j.WriteValue(CollectionsTabScale.Mode.ToString());
j.WritePropertyName("Width"); j.WritePropertyName("Width");
j.WriteValue(CollectionTabScale.Width); j.WriteValue(CollectionsTabScale.Width);
j.WriteEndObject(); j.WriteEndObject();
j.WritePropertyName("ModsTab"); j.WritePropertyName("ModsTab");
j.WriteStartObject(); j.WriteStartObject();
@ -76,33 +30,27 @@ public class UiConfig : ISavable, IService
j.WritePropertyName("Width"); j.WritePropertyName("Width");
j.WriteValue(ModTabScale.Width); j.WriteValue(ModTabScale.Width);
j.WriteEndObject(); j.WriteEndObject();
j.WriteEndObject();
} }
private void Load() protected override void LoadData(JObject j)
{ {
if (!File.Exists(_saveService.FileNames.UiConfigFile)) if (j["CollectionsTab"] is JObject collections)
return; _collectionsTabScale = new TwoPanelWidth(collections["Width"].ValueOr(float.NaN),
collections["Mode"].TextEnum(ScalingMode.Percentage));
try if (j["ModsTab"] is JObject mods)
{ _modTabScale = new TwoPanelWidth(mods["Width"].ValueOr(float.NaN), mods["Mode"].TextEnum(ScalingMode.Percentage));
var text = File.ReadAllText(_saveService.FileNames.UiConfigFile);
var jObj = JObject.Parse(text);
if (jObj["Version"]?.Value<int>() 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);
}
} }
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);
} }

View file

@ -8,7 +8,6 @@ using Penumbra.Interop.Services;
using Penumbra.Mods.Editor; using Penumbra.Mods.Editor;
using Penumbra.Mods.Manager; using Penumbra.Mods.Manager;
using Penumbra.Mods.Settings; using Penumbra.Mods.Settings;
using Penumbra.UI;
using Penumbra.UI.Classes; using Penumbra.UI.Classes;
using Penumbra.UI.ResourceWatcher; using Penumbra.UI.ResourceWatcher;
using Penumbra.UI.Tabs; using Penumbra.UI.Tabs;
@ -126,8 +125,8 @@ public class ConfigMigrationService(SaveService saveService, BackupService backu
_data["ResourceWatcherRecordTypes"]?.ToObject<RecordType>() ?? _config.Ephemeral.ResourceWatcherRecordTypes; _data["ResourceWatcherRecordTypes"]?.ToObject<RecordType>() ?? _config.Ephemeral.ResourceWatcherRecordTypes;
_config.Ephemeral.CollectionPanel = _data["CollectionPanel"]?.ToObject<CollectionPanelMode>() ?? _config.Ephemeral.CollectionPanel; _config.Ephemeral.CollectionPanel = _data["CollectionPanel"]?.ToObject<CollectionPanelMode>() ?? _config.Ephemeral.CollectionPanel;
_config.Ephemeral.SelectedTab = _data["SelectedTab"]?.ToObject<TabType>() ?? _config.Ephemeral.SelectedTab; _config.Ephemeral.SelectedTab = _data["SelectedTab"]?.ToObject<TabType>() ?? _config.Ephemeral.SelectedTab;
_config.Ephemeral.ChangedItemFilter = _data["ChangedItemFilter"]?.ToObject<ChangedItemIconFlag>() _config.Filters.ChangedItemTypeFilter = _data["ChangedItemFilter"]?.ToObject<ChangedItemIconFlag>()
?? _config.Ephemeral.ChangedItemFilter; ?? _config.Filters.ChangedItemTypeFilter;
_config.Ephemeral.FixMainWindow = _data["FixMainWindow"]?.ToObject<bool>() ?? _config.Ephemeral.FixMainWindow; _config.Ephemeral.FixMainWindow = _data["FixMainWindow"]?.ToObject<bool>() ?? _config.Ephemeral.FixMainWindow;
_config.Ephemeral.Save(); _config.Ephemeral.Save();
} }

View file

@ -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 LocalDataDirectory = Path.Combine(pi.ConfigDirectory.FullName, "mod_data");
public readonly string EphemeralConfigFile = Path.Combine(pi.ConfigDirectory.FullName, "ephemeral_config.json"); 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 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 OldFilesystemFile = Path.Combine(pi.ConfigDirectory.FullName, "sort_order.json");
public readonly string ActiveCollectionsFile = Path.Combine(pi.ConfigDirectory.FullName, "active_collections.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"); public readonly string PredefinedTagFile = Path.Combine(pi.ConfigDirectory.FullName, "predefined_tags.json");

View file

@ -39,11 +39,9 @@ public class ResourceTreeViewer(
private readonly Dictionary<nint, NodeVisibility> _filterCache = []; private readonly Dictionary<nint, NodeVisibility> _filterCache = [];
private readonly Dictionary<FullPath, IWritable?> _writableCache = []; private readonly Dictionary<FullPath, IWritable?> _writableCache = [];
private TreeCategory _categoryFilter = AllCategories; private TreeCategory _categoryFilter = AllCategories;
private ChangedItemIconFlag _typeFilter = ChangedItemFlagExtensions.AllFlags;
private string _nameFilter = string.Empty; private string _note = string.Empty;
private string _nodeFilter = string.Empty;
private string _note = string.Empty;
private Task<ResourceTree[]>? _task; private Task<ResourceTree[]>? _task;
@ -73,7 +71,7 @@ public class ResourceTreeViewer(
foreach (var (index, tree) in _task.Result.Index()) foreach (var (index, tree) in _task.Result.Index())
{ {
var category = Classify(tree); 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; continue;
using (ImGuiColor.Text.Push(CategoryColor(category).Value())) using (ImGuiColor.Text.Push(CategoryColor(category).Value()))
@ -198,15 +196,31 @@ public class ResourceTreeViewer(
Im.Cursor.Y -= yOffset; Im.Cursor.Y -= yOffset;
using (Im.Child.Begin("##typeFilter"u8, new Vector2(Im.ContentRegion.Available.X, ChangedItemDrawer.TypeFilterIconSize.Y))) 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; var fieldWidth = (Im.ContentRegion.Available.X - checkSpacing * 2.0f - Im.Style.FrameHeightWithSpacing) / 2.0f;
Im.Item.SetNextWidth(fieldWidth); 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.Line.Same(0, checkSpacing);
Im.Item.SetNextWidth(fieldWidth); 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); Im.Line.Same(0, checkSpacing);
incognito.DrawToggle(Im.Style.FrameHeightWithSpacing); incognito.DrawToggle(Im.Style.FrameHeightWithSpacing);
@ -353,7 +367,9 @@ public class ResourceTreeViewer(
if (hasMod && Im.Item.RightClicked() && Im.Io.KeyControl) if (hasMod && Im.Item.RightClicked() && Im.Io.KeyControl)
communicator.SelectTab.Invoke(new SelectTab.Arguments(TabType.Mods, mod)); 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 else
{ {
@ -411,16 +427,17 @@ public class ResourceTreeViewer(
bool MatchesFilter(ResourceNode node, ChangedItemIconFlag filterIcon) bool MatchesFilter(ResourceNode node, ChangedItemIconFlag filterIcon)
{ {
if (!_typeFilter.HasFlag(filterIcon)) if (!config.Filters.OnScreenTypeFilter.HasFlag(filterIcon))
return false; return false;
if (_nodeFilter.Length == 0) if (config.Filters.OnScreenItemFilter.Length == 0)
return true; return true;
return node.Name != null && node.Name.Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase) return node.Name != null && node.Name.Contains(config.Filters.OnScreenItemFilter, StringComparison.OrdinalIgnoreCase)
|| node.FullPath.FullName.Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase) || node.FullPath.FullName.Contains(config.Filters.OnScreenItemFilter, StringComparison.OrdinalIgnoreCase)
|| node.FullPath.InternalName.ToString().Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase) || node.FullPath.InternalName.ToString().Contains(config.Filters.OnScreenItemFilter, StringComparison.OrdinalIgnoreCase)
|| Array.Exists(node.PossibleGamePaths, path => path.Path.ToString().Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase)); || Array.Exists(node.PossibleGamePaths,
path => path.Path.ToString().Contains(config.Filters.OnScreenItemFilter, StringComparison.OrdinalIgnoreCase));
} }
void DrawActions(ResourceNode resourceNode, Vector2 buttonSize) void DrawActions(ResourceNode resourceNode, Vector2 buttonSize)
@ -489,7 +506,9 @@ public class ResourceTreeViewer(
private static void HeaderInteraction(ResourceTree tree) 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) if (tree.GameObjectAddress == nint.Zero)
return; return;
@ -513,7 +532,8 @@ public class ResourceTreeViewer(
private static void ResourceInteraction(ResourceNode node) 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); true, Im.Font.Mono);
if (node.ResourceHandle == nint.Zero) if (node.ResourceHandle == nint.Zero)

View file

@ -15,7 +15,8 @@ namespace Penumbra.UI.Classes;
public class ChangedItemDrawer : IDisposable, IUiService 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<char> input, out ChangedItemIconFlag slot) public static bool TryParseIndex(ReadOnlySpan<char> input, out ChangedItemIconFlag slot)
{ {
@ -83,9 +84,15 @@ public class ChangedItemDrawer : IDisposable, IUiService
} }
/// <summary> Check if a changed item should be drawn based on its category. </summary> /// <summary> Check if a changed item should be drawn based on its category. </summary>
public bool FilterChangedItem(string name, IIdentifiedObjectData data, string filter) public bool FilterChangedItemGlobal(string name, IIdentifiedObjectData data, string filter)
=> (_config.Ephemeral.ChangedItemFilter == ChangedItemFlagExtensions.AllFlags => (_config.Filters.ChangedItemTypeFilter == ChangedItemFlagExtensions.AllFlags
|| _config.Ephemeral.ChangedItemFilter.HasFlag(data.GetIcon().ToFlag())) || _config.Filters.ChangedItemTypeFilter.HasFlag(data.GetIcon().ToFlag()))
&& (filter.Length is 0 || !data.IsFilteredOut(name, filter));
/// <summary> Check if a changed item should be drawn based on its category. </summary>
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)); && (filter.Length is 0 || !data.IsFilteredOut(name, filter));
/// <summary> Draw the icon corresponding to the category of a changed item. </summary> /// <summary> Draw the icon corresponding to the category of a changed item. </summary>
@ -160,31 +167,33 @@ public class ChangedItemDrawer : IDisposable, IUiService
} }
/// <summary> Draw a header line with the different icon types to filter them. </summary> /// <summary> Draw a header line with the different icon types to filter them. </summary>
public void DrawTypeFilter() public void DrawTypeFilter(bool global)
{ {
if (_config.HideChangedItemFilters) if (_config.HideChangedItemFilters)
return; return;
var typeFilter = _config.Ephemeral.ChangedItemFilter; var typeFilter = global ? _config.Filters.ChangedItemTypeFilter : _config.Filters.ModChangedItemTypeFilter;
if (DrawTypeFilter(ref typeFilter)) if (DrawTypeFilter(typeFilter, out typeFilter))
{ {
_config.Ephemeral.ChangedItemFilter = typeFilter; if (global)
_config.Ephemeral.Save(); _config.Filters.ChangedItemTypeFilter = typeFilter;
else
_config.Filters.ModChangedItemTypeFilter = typeFilter;
} }
} }
/// <summary> Draw a header line with the different icon types to filter them. </summary> /// <summary> Draw a header line with the different icon types to filter them. </summary>
public bool DrawTypeFilter(ref ChangedItemIconFlag typeFilter) public bool DrawTypeFilter(ChangedItemIconFlag typeFilter, out ChangedItemIconFlag newTypeFilter)
{ {
var ret = false; var ret = false;
using var _ = Im.Id.Push("ChangedItemIconFilter"u8); using var _ = Im.Id.Push("ChangedItemIconFilter"u8);
var size = TypeFilterIconSize; var size = TypeFilterIconSize;
using var style = ImStyleDouble.ItemSpacing.Push(Vector2.Zero); using var style = ImStyleDouble.ItemSpacing.Push(Vector2.Zero);
newTypeFilter = typeFilter;
foreach (var iconType in ChangedItemFlagExtensions.Order) foreach (var iconType in ChangedItemFlagExtensions.Order)
{ {
ret |= DrawIcon(iconType, ref typeFilter); ret |= DrawIcon(iconType, ref newTypeFilter);
Im.Line.Same(); Im.Line.Same();
} }
@ -198,30 +207,30 @@ public class ChangedItemDrawer : IDisposable, IUiService
}); });
if (Im.Item.Clicked()) if (Im.Item.Clicked())
{ {
typeFilter = typeFilter is ChangedItemFlagExtensions.AllFlags ? 0 : ChangedItemFlagExtensions.AllFlags; newTypeFilter = typeFilter is ChangedItemFlagExtensions.AllFlags ? 0 : ChangedItemFlagExtensions.AllFlags;
ret = true; ret = true;
} }
return ret; return ret;
bool DrawIcon(ChangedItemIconFlag type, ref ChangedItemIconFlag typeFilter) bool DrawIcon(ChangedItemIconFlag type, ref ChangedItemIconFlag filter)
{ {
var localRet = false; var localRet = false;
var icon = _icons[type]; 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)); 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()) if (Im.Item.Clicked())
{ {
typeFilter = flag ? typeFilter & ~type : typeFilter | type; filter = flag ? filter & ~type : filter | type;
localRet = true; localRet = true;
} }
using var popup = Im.Popup.BeginContextItem($"{type}"); using var popup = Im.Popup.BeginContextItem($"{type}");
if (popup) if (popup)
if (Im.Menu.Item("Enable Only This"u8)) if (Im.Menu.Item("Enable Only This"u8))
{ {
typeFilter = type; filter = type;
localRet = true; localRet = true;
Im.Popup.CloseCurrent(); Im.Popup.CloseCurrent();
} }

View file

@ -50,7 +50,7 @@ public static class ChangedItemFlagExtensions
public const ChangedItemIconFlag AllFlags = (ChangedItemIconFlag)0x01FFFF; public const ChangedItemIconFlag AllFlags = (ChangedItemIconFlag)0x01FFFF;
public static readonly int NumCategories = Order.Count; public static readonly int NumCategories = Order.Count;
public const ChangedItemIconFlag DefaultFlags = AllFlags & ~ChangedItemIconFlag.Offhand; public const ChangedItemIconFlag DefaultFlags = AllFlags;
public static ReadOnlySpan<byte> ToDescription(this ChangedItemIconFlag iconFlag) public static ReadOnlySpan<byte> ToDescription(this ChangedItemIconFlag iconFlag)
=> iconFlag switch => iconFlag switch

View file

@ -5,6 +5,12 @@ namespace Penumbra.UI.CollectionTab;
public sealed class CollectionFilter : TextFilterBase<CollectionSelector.Entry>, IUiService public sealed class CollectionFilter : TextFilterBase<CollectionSelector.Entry>, IUiService
{ {
public CollectionFilter(FilterConfig filterConfig)
{
Set(filterConfig.CollectionFilter);
FilterChanged += () => filterConfig.CollectionFilter = Text;
}
public override bool WouldBeVisible(in CollectionSelector.Entry item, int globalIndex) public override bool WouldBeVisible(in CollectionSelector.Entry item, int globalIndex)
=> base.WouldBeVisible(in item, globalIndex) || WouldBeVisible(item.AnonymousName.Utf16); => base.WouldBeVisible(in item, globalIndex) || WouldBeVisible(item.AnonymousName.Utf16);

View file

@ -11,24 +11,12 @@ using Penumbra.UI.Classes;
namespace Penumbra.UI.MainWindow; 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( public sealed class ChangedItemsTab(
CollectionManager collectionManager, CollectionManager collectionManager,
CollectionSelectHeader collectionHeader, CollectionSelectHeader collectionHeader,
ChangedItemDrawer drawer, ChangedItemDrawer drawer,
CommunicatorService communicator) CommunicatorService communicator,
FilterConfig filterConfig)
: ITab<TabType> : ITab<TabType>
{ {
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
@ -39,14 +27,14 @@ public sealed class ChangedItemsTab(
private Vector2 _buttonSize; 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<Item> private sealed class ChangedItemFilter(ChangedItemDrawer drawer, FilterConfig filterConfig) : IFilter<Item>
{ {
public bool WouldBeVisible(in Item item, int globalIndex) public bool WouldBeVisible(in Item item, int globalIndex)
=> drawer.FilterChangedItem(item.Name, item.Data, uiState.ChangedItemTabNameFilter) => drawer.FilterChangedItemGlobal(item.Name, item.Data, filterConfig.ChangedItemItemFilter)
&& (uiState.ChangedItemTabModFilter.Length is 0 && (filterConfig.ChangedItemModFilter.Length is 0
|| item.Mods.Any(m => m.Name.Contains(uiState.ChangedItemTabModFilter, StringComparison.OrdinalIgnoreCase))); || item.Mods.Any(m => m.Name.Contains(filterConfig.ChangedItemModFilter, StringComparison.OrdinalIgnoreCase)));
public event Action? FilterChanged; public event Action? FilterChanged;
@ -56,29 +44,36 @@ public sealed class ChangedItemsTab(
- 450 * Im.Style.GlobalScale - 450 * Im.Style.GlobalScale
- Im.Style.ItemSpacing.X; - Im.Style.ItemSpacing.X;
Im.Item.SetNextWidth(450 * Im.Style.GlobalScale); 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.Line.Same();
Im.Item.SetNextWidth(varWidth); Im.Item.SetNextWidth(varWidth);
ret |= Im.Input.Text("##changedItemsModFilter"u8, ref uiState.ChangedItemTabModFilter, "Filter Mods..."u8); filter = filterConfig.ChangedItemModFilter;
if (!ret) if (Im.Input.Text("##changedItemsModFilter"u8, ref filter, "Filter Mods..."u8))
return false; {
ret = true;
filterConfig.ChangedItemModFilter = filter;
}
FilterChanged?.Invoke(); if (ret)
return true; FilterChanged?.Invoke();
return ret;
} }
public void Clear() public void Clear()
{ {
uiState.ChangedItemTabModFilter = string.Empty; filterConfig.ChangedItemModFilter = string.Empty;
uiState.ChangedItemTabNameFilter = string.Empty; filterConfig.ChangedItemItemFilter = string.Empty;
uiState.ChangedItemTabCategoryFilter = ChangedItemFlagExtensions.DefaultFlags; filterConfig.ChangedItemTypeFilter = ChangedItemFlagExtensions.DefaultFlags;
FilterChanged?.Invoke(); FilterChanged?.Invoke();
} }
public bool IsEmpty public bool IsEmpty
=> uiState.ChangedItemTabModFilter.Length is 0 => filterConfig.ChangedItemModFilter.Length is 0
&& uiState.ChangedItemTabNameFilter.Length is 0 && filterConfig.ChangedItemItemFilter.Length is 0
&& uiState.ChangedItemTabCategoryFilter is ChangedItemFlagExtensions.DefaultFlags; && filterConfig.ChangedItemTypeFilter is ChangedItemFlagExtensions.DefaultFlags;
} }
private readonly record struct Item(string Label, IIdentifiedObjectData Data, SingleArray<IMod> Mods) private readonly record struct Item(string Label, IIdentifiedObjectData Data, SingleArray<IMod> Mods)
@ -124,7 +119,7 @@ public sealed class ChangedItemsTab(
public void DrawContent() public void DrawContent()
{ {
collectionHeader.Draw(true); collectionHeader.Draw(true);
drawer.DrawTypeFilter(); drawer.DrawTypeFilter(true);
_filter.DrawFilter("##Filter"u8, Im.ContentRegion.Available); _filter.DrawFilter("##Filter"u8, Im.ContentRegion.Available);
using var child = Im.Child.Begin("##changedItemsChild"u8, Im.ContentRegion.Available); using var child = Im.Child.Begin("##changedItemsChild"u8, Im.ContentRegion.Available);
if (!child) if (!child)

View file

@ -15,7 +15,8 @@ namespace Penumbra.UI.MainWindow;
public sealed class EffectiveTab( public sealed class EffectiveTab(
CollectionManager collectionManager, CollectionManager collectionManager,
CollectionSelectHeader collectionHeader, CollectionSelectHeader collectionHeader,
CommunicatorService communicatorService) CommunicatorService communicatorService,
FilterConfig filterConfig)
: ITab<TabType> : ITab<TabType>
{ {
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
@ -31,7 +32,7 @@ public sealed class EffectiveTab(
public TabType Identifier public TabType Identifier
=> TabType.EffectiveChanges; => TabType.EffectiveChanges;
private readonly PairFilter<Item> _filter = new(new GamePathFilter(), new FullPathFilter()); private readonly PairFilter<Item> _filter = new(new GamePathFilter(filterConfig), new FullPathFilter(filterConfig));
private sealed class Cache : BasicFilterCache<Item>, IPanel private sealed class Cache : BasicFilterCache<Item>, 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)); 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); 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 }); Filter.Filter2.DrawFilter("Filter file path..."u8, Im.ContentRegion.Available with { Y = Im.Style.FrameHeight });
} }
private void DrawTable() private void DrawTable()
@ -143,12 +143,24 @@ public sealed class EffectiveTab(
private sealed class GamePathFilter : RegexFilterBase<Item> private sealed class GamePathFilter : RegexFilterBase<Item>
{ {
public GamePathFilter(FilterConfig config)
{
Set(config.EffectiveChangesGamePathFilter);
FilterChanged += () => config.EffectiveChangesGamePathFilter = Text;
}
protected override string ToFilterString(in Item item, int globalIndex) protected override string ToFilterString(in Item item, int globalIndex)
=> item.GamePath.Utf16; => item.GamePath.Utf16;
} }
private sealed class FullPathFilter : RegexFilterBase<Item> private sealed class FullPathFilter : RegexFilterBase<Item>
{ {
public FullPathFilter(FilterConfig config)
{
Set(config.EffectiveChangesFilePathFilter);
FilterChanged += () => config.EffectiveChangesFilePathFilter = Text;
}
protected override string ToFilterString(in Item item, int globalIndex) protected override string ToFilterString(in Item item, int globalIndex)
=> item.FilePath.FullName; => item.FilePath.FullName;
} }

View file

@ -110,7 +110,7 @@ public class ModPanelChangedItemsTab(
{ {
foreach (var (s, i) in _lastSelected.ChangedItems) 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)); Data.Add(Container.Single(s, i));
} }
@ -124,7 +124,7 @@ public class ModPanelChangedItemsTab(
if (i is not IdentifiedItem item) if (i is not IdentifiedItem item)
continue; continue;
if (!drawer.FilterChangedItem(s, item, string.Empty)) if (!drawer.FilterChangedItemMod(s, item, string.Empty))
continue; continue;
if (tmp.TryGetValue((item.Item.PrimaryId, item.Item.Type), out var p)) if (tmp.TryGetValue((item.Item.PrimaryId, item.Item.Type), out var p))
@ -160,7 +160,7 @@ public class ModPanelChangedItemsTab(
if (i is IdentifiedItem) if (i is IdentifiedItem)
continue; continue;
if (!drawer.FilterChangedItem(s, i, string.Empty)) if (!drawer.FilterChangedItemMod(s, i, string.Empty))
continue; continue;
while (sortedTmpIdx < sortedTmp.Length while (sortedTmpIdx < sortedTmp.Length
@ -221,9 +221,9 @@ public class ModPanelChangedItemsTab(
_id = Im.Id.Current; _id = Im.Id.Current;
_mod = fileSystem.Selection.Selection!.GetValue<Mod>()!; _mod = fileSystem.Selection.Selection!.GetValue<Mod>()!;
var cache = CacheManager.Instance.GetOrCreateCache(_id, () => new ChangedItemsCache()); 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(); Im.Separator();
_buttonSize = new Vector2(Im.Style.ItemSpacing.Y + Im.Style.FrameHeight); _buttonSize = new Vector2(Im.Style.ItemSpacing.Y + Im.Style.FrameHeight);
using var style = ImStyleDouble.CellPadding.Push(Vector2.Zero) using var style = ImStyleDouble.CellPadding.Push(Vector2.Zero)

View file

@ -9,11 +9,25 @@ using static ImSharp.Im;
namespace Penumbra.UI.ModsTab.Selector; namespace Penumbra.UI.ModsTab.Selector;
public sealed class ModFilter(ModManager modManager, ActiveCollections collections) public sealed class ModFilter : TokenizedFilter<ModFilterTokenType, ModFileSystemCache.ModData, ModFilterToken>,
: TokenizedFilter<ModFilterTokenType, ModFileSystemCache.ModData, ModFilterToken>, IFileSystemFilter<ModFileSystemCache.ModData>
IFileSystemFilter<ModFileSystemCache.ModData>
{ {
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 public ModTypeFilter StateFilter
=> _stateFilter; => _stateFilter;
@ -156,8 +170,8 @@ public sealed class ModFilter(ModManager modManager, ActiveCollections collectio
private bool CheckStateFilters(Mod mod) private bool CheckStateFilters(Mod mod)
{ {
var (settings, collection) = collections.Current.GetActualSettings(mod.Index); var (settings, collection) = _collections.Current.GetActualSettings(mod.Index);
var isNew = modManager.IsNew(mod); var isNew = _modManager.IsNew(mod);
// Handle mod details. // Handle mod details.
if (CheckFlags(mod.TotalFileCount, ModTypeFilter.HasNoFiles, ModTypeFilter.HasFiles) if (CheckFlags(mod.TotalFileCount, ModTypeFilter.HasNoFiles, ModTypeFilter.HasFiles)
|| CheckFlags(mod.TotalSwapCount, ModTypeFilter.HasNoFileSwaps, ModTypeFilter.HasFileSwaps) || CheckFlags(mod.TotalSwapCount, ModTypeFilter.HasNoFileSwaps, ModTypeFilter.HasFileSwaps)
@ -182,7 +196,7 @@ public sealed class ModFilter(ModManager modManager, ActiveCollections collectio
} }
// Handle Inheritance // Handle Inheritance
if (collection == collections.Current) if (collection == _collections.Current)
{ {
if (!_stateFilter.HasFlag(ModTypeFilter.Uninherited)) if (!_stateFilter.HasFlag(ModTypeFilter.Uninherited))
return false; return false;
@ -213,7 +227,7 @@ public sealed class ModFilter(ModManager modManager, ActiveCollections collectio
return false; return false;
// Conflicts can only be relevant if the mod is enabled. // 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.Count > 0)
{ {
if (conflicts.Any(c => !c.Solved)) if (conflicts.Any(c => !c.Solved))

View file

@ -17,12 +17,10 @@ public sealed class ModFileSystemDrawer : FileSystemDrawer<ModFileSystemCache.Mo
public readonly FileDialogService FileService; public readonly FileDialogService FileService;
public readonly TutorialService Tutorial; public readonly TutorialService Tutorial;
public readonly CommunicatorService Communicator; public readonly CommunicatorService Communicator;
public readonly GlobalModImporter GlobalModImporter;
public ModFileSystemDrawer(ModFileSystem fileSystem, ModManager modManager, CollectionManager collectionManager, Configuration config, public ModFileSystemDrawer(ModFileSystem fileSystem, ModManager modManager, CollectionManager collectionManager, Configuration config,
ModImportManager modImport, FileDialogService fileService, TutorialService tutorial, CommunicatorService communicator, ModImportManager modImport, FileDialogService fileService, TutorialService tutorial, CommunicatorService communicator)
GlobalModImporter globalModImporter) : base(fileSystem, new ModFilter(modManager, collectionManager.Active, config.Filters))
: base(fileSystem, new ModFilter(modManager, collectionManager.Active))
{ {
ModManager = modManager; ModManager = modManager;
CollectionManager = collectionManager; CollectionManager = collectionManager;
@ -31,7 +29,6 @@ public sealed class ModFileSystemDrawer : FileSystemDrawer<ModFileSystemCache.Mo
FileService = fileService; FileService = fileService;
Tutorial = tutorial; Tutorial = tutorial;
Communicator = communicator; Communicator = communicator;
GlobalModImporter = globalModImporter;
SortMode = Config.SortMode; SortMode = Config.SortMode;
MainContext.AddButton(new ClearTemporarySettingsButton(this), 105); MainContext.AddButton(new ClearTemporarySettingsButton(this), 105);

View file

@ -30,6 +30,15 @@ public enum RecordType : byte
ResourceComplete = 0x10, ResourceComplete = 0x10,
} }
public static partial class RecordTypeExtensions
{
public const RecordType All = RecordType.Request
| RecordType.ResourceLoad
| RecordType.FileLoad
| RecordType.Destruction
| RecordType.ResourceComplete;
}
internal unsafe struct Record() internal unsafe struct Record()
{ {
public DateTime Time; public DateTime Time;

View file

@ -43,7 +43,7 @@ public sealed class ResourceWatcher : IDisposable, ITab<TabType>
_resources = resources; _resources = resources;
_destructor = destructor; _destructor = destructor;
_loader = loader; _loader = loader;
_table = new ResourceWatcherTable(new ResourceWatcherConfig(), _records); _table = new ResourceWatcherTable(config.Filters, _records);
_resources.ResourceRequested += OnResourceRequested; _resources.ResourceRequested += OnResourceRequested;
_destructor.Subscribe(OnResourceDestroyed, ResourceHandleDestructor.Priority.ResourceWatcher); _destructor.Subscribe(OnResourceDestroyed, ResourceHandleDestructor.Priority.ResourceWatcher);
_loader.ResourceLoaded += OnResourceLoaded; _loader.ResourceLoaded += OnResourceLoaded;

View file

@ -9,33 +9,6 @@ using Penumbra.UI.Classes;
namespace Penumbra.UI.ResourceWatcher; 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<RecordType>().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<LoadStateFlag>().Or();
public void Save()
{ }
}
[Flags] [Flags]
public enum BoolEnum : byte public enum BoolEnum : byte
{ {
@ -44,6 +17,11 @@ public enum BoolEnum : byte
Unknown = 0x04, Unknown = 0x04,
} }
public static class BoolEnumExtensions
{
public const BoolEnum All = BoolEnum.True | BoolEnum.False | BoolEnum.Unknown;
}
[Flags] [Flags]
public enum LoadStateFlag : byte public enum LoadStateFlag : byte
{ {
@ -55,6 +33,16 @@ public enum LoadStateFlag : byte
None = 0xFF, 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) internal sealed unsafe class CachedRecord(Record record)
{ {
public readonly Record Record = record; public readonly Record Record = record;
@ -84,23 +72,23 @@ internal sealed class ResourceWatcherTable : TableBase<CachedRecord, TableCache<
public bool WouldBeVisible(Record record) public bool WouldBeVisible(Record record)
=> Columns.OfType<ICheckRecord>().All(column => column.WouldBeVisible(record)); => Columns.OfType<ICheckRecord>().All(column => column.WouldBeVisible(record));
public ResourceWatcherTable(ResourceWatcherConfig config, IReadOnlyList<Record> records) public ResourceWatcherTable(FilterConfig filterConfig, IReadOnlyList<Record> records)
: base(new StringU8("##records"u8), : base(new StringU8("##records"u8),
new PathColumn(config) { Label = new StringU8("Path"u8) }, new PathColumn(filterConfig) { Label = new StringU8("Path"u8) },
new RecordTypeColumn(config) { Label = new StringU8("Record"u8) }, new RecordTypeColumn(filterConfig) { Label = new StringU8("Record"u8) },
new CollectionColumn(config) { Label = new StringU8("Collection"u8) }, new CollectionColumn(filterConfig) { Label = new StringU8("Collection"u8) },
new ObjectColumn(config) { Label = new StringU8("Game Object"u8) }, new ObjectColumn(filterConfig) { Label = new StringU8("Game Object"u8) },
new CustomLoadColumn(config) { Label = new StringU8("Custom"u8) }, new CustomLoadColumn(filterConfig) { Label = new StringU8("Custom"u8) },
new SynchronousLoadColumn(config) { Label = new StringU8("Sync"u8) }, new SynchronousLoadColumn(filterConfig) { Label = new StringU8("Sync"u8) },
new OriginalPathColumn(config) { Label = new StringU8("Original Path"u8) }, new OriginalPathColumn(filterConfig) { Label = new StringU8("Original Path"u8) },
new ResourceCategoryColumn(config) { Label = new StringU8("Category"u8) }, new ResourceCategoryColumn(filterConfig) { Label = new StringU8("Category"u8) },
new ResourceTypeColumn(config) { Label = new StringU8("Type"u8) }, new ResourceTypeColumn(filterConfig) { Label = new StringU8("Type"u8) },
new HandleColumn(config) { Label = new StringU8("Resource"u8) }, new HandleColumn(filterConfig) { Label = new StringU8("Resource"u8) },
new LoadStateColumn(config) { Label = new StringU8("State"u8) }, new LoadStateColumn(filterConfig) { Label = new StringU8("State"u8) },
new RefCountColumn(config) { Label = new StringU8("#Ref"u8) }, new RefCountColumn(filterConfig) { Label = new StringU8("#Ref"u8) },
new DateColumn { Label = new StringU8("Time"u8) }, new DateColumn { Label = new StringU8("Time"u8) },
new Crc64Column(config) { Label = new StringU8("Crc64"u8) }, new Crc64Column(filterConfig) { Label = new StringU8("Crc64"u8) },
new OsThreadColumn(config) { Label = new StringU8("TID"u8) } new OsThreadColumn(filterConfig) { Label = new StringU8("TID"u8) }
) )
{ {
_records = records; _records = records;
@ -149,26 +137,15 @@ internal sealed class ResourceWatcherTable : TableBase<CachedRecord, TableCache<
private sealed class PathColumn : TextColumn<CachedRecord>, ICheckRecord private sealed class PathColumn : TextColumn<CachedRecord>, ICheckRecord
{ {
private readonly ResourceWatcherConfig _config; public PathColumn(FilterConfig config)
public PathColumn(ResourceWatcherConfig config)
{ {
_config = config;
UnscaledWidth = 300; UnscaledWidth = 300;
Filter.Set(config.PathFilter); Filter.Set(config.ResourceLoggerPathFilter);
Filter.FilterChanged += OnFilterChanged; Filter.FilterChanged += () => config.ResourceLoggerPathFilter = Filter.Text;
}
private void OnFilterChanged()
{
_config.PathFilter = Filter.Text;
_config.Save();
} }
public override void DrawColumn(in CachedRecord item, int globalIndex) 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) protected override string ComparisonText(in CachedRecord item, int globalIndex)
=> item.PathU16; => item.PathU16;
@ -182,20 +159,11 @@ internal sealed class ResourceWatcherTable : TableBase<CachedRecord, TableCache<
private sealed class RecordTypeColumn : FlagColumn<RecordType, CachedRecord>, ICheckRecord private sealed class RecordTypeColumn : FlagColumn<RecordType, CachedRecord>, ICheckRecord
{ {
private readonly ResourceWatcherConfig _config; public RecordTypeColumn(FilterConfig config)
public RecordTypeColumn(ResourceWatcherConfig config)
{ {
_config = config;
UnscaledWidth = 80; UnscaledWidth = 80;
Filter.LoadValue(config.RecordFilter); Filter.LoadValue(config.ResourceLoggerRecordFilter);
Filter.FilterChanged += OnFilterChanged; Filter.FilterChanged += () => config.ResourceLoggerRecordFilter = Filter.FilterValue;
}
private void OnFilterChanged()
{
_config.RecordFilter = Filter.FilterValue;
_config.Save();
} }
protected override StringU8 DisplayString(in CachedRecord item, int globalIndex) protected override StringU8 DisplayString(in CachedRecord item, int globalIndex)
@ -225,20 +193,11 @@ internal sealed class ResourceWatcherTable : TableBase<CachedRecord, TableCache<
private sealed class Crc64Column : TextColumn<CachedRecord>, ICheckRecord private sealed class Crc64Column : TextColumn<CachedRecord>, ICheckRecord
{ {
private readonly ResourceWatcherConfig _config; public Crc64Column(FilterConfig config)
public Crc64Column(ResourceWatcherConfig config)
{ {
_config = config;
UnscaledWidth = 17 * 8; UnscaledWidth = 17 * 8;
Filter.Set(config.CrcFilter); Filter.Set(config.ResourceLoggerCrcFilter);
Filter.FilterChanged += OnFilterChanged; Filter.FilterChanged += () => config.ResourceLoggerCrcFilter = Filter.Text;
}
private void OnFilterChanged()
{
_config.CrcFilter = Filter.Text;
_config.Save();
} }
public override int Compare(in CachedRecord lhs, int lhsGlobalIndex, in CachedRecord rhs, int rhsGlobalIndex) public override int Compare(in CachedRecord lhs, int lhsGlobalIndex, in CachedRecord rhs, int rhsGlobalIndex)
@ -266,20 +225,11 @@ internal sealed class ResourceWatcherTable : TableBase<CachedRecord, TableCache<
private sealed class CollectionColumn : TextColumn<CachedRecord>, ICheckRecord private sealed class CollectionColumn : TextColumn<CachedRecord>, ICheckRecord
{ {
private readonly ResourceWatcherConfig _config; public CollectionColumn(FilterConfig config)
public CollectionColumn(ResourceWatcherConfig config)
{ {
_config = config;
UnscaledWidth = 80; UnscaledWidth = 80;
Filter.Set(config.CollectionFilter); Filter.Set(config.ResourceLoggerCollectionFilter);
Filter.FilterChanged += OnFilterChanged; Filter.FilterChanged += () => config.ResourceLoggerCollectionFilter = Filter.Text;
}
private void OnFilterChanged()
{
_config.CollectionFilter = Filter.Text;
_config.Save();
} }
protected override string ComparisonText(in CachedRecord item, int globalIndex) protected override string ComparisonText(in CachedRecord item, int globalIndex)
@ -294,20 +244,11 @@ internal sealed class ResourceWatcherTable : TableBase<CachedRecord, TableCache<
private sealed class ObjectColumn : TextColumn<CachedRecord>, ICheckRecord private sealed class ObjectColumn : TextColumn<CachedRecord>, ICheckRecord
{ {
private readonly ResourceWatcherConfig _config; public ObjectColumn(FilterConfig config)
public ObjectColumn(ResourceWatcherConfig config)
{ {
_config = config;
UnscaledWidth = 150; UnscaledWidth = 150;
Filter.Set(config.ObjectFilter); Filter.Set(config.ResourceLoggerObjectFilter);
Filter.FilterChanged += OnFilterChanged; Filter.FilterChanged += () => config.ResourceLoggerObjectFilter = Filter.Text;
}
private void OnFilterChanged()
{
_config.ObjectFilter = Filter.Text;
_config.Save();
} }
protected override string ComparisonText(in CachedRecord item, int globalIndex) protected override string ComparisonText(in CachedRecord item, int globalIndex)
@ -322,20 +263,11 @@ internal sealed class ResourceWatcherTable : TableBase<CachedRecord, TableCache<
private sealed class OriginalPathColumn : TextColumn<CachedRecord>, ICheckRecord private sealed class OriginalPathColumn : TextColumn<CachedRecord>, ICheckRecord
{ {
private readonly ResourceWatcherConfig _config; public OriginalPathColumn(FilterConfig config)
public OriginalPathColumn(ResourceWatcherConfig config)
{ {
_config = config;
UnscaledWidth = 200; UnscaledWidth = 200;
Filter.Set(config.OriginalPathFilter); Filter.Set(config.ResourceLoggerOriginalPathFilter);
Filter.FilterChanged += OnFilterChanged; Filter.FilterChanged += () => config.ResourceLoggerOriginalPathFilter = Filter.Text;
}
private void OnFilterChanged()
{
_config.OriginalPathFilter = Filter.Text;
_config.Save();
} }
public override void DrawColumn(in CachedRecord item, int globalIndex) public override void DrawColumn(in CachedRecord item, int globalIndex)
@ -355,20 +287,11 @@ internal sealed class ResourceWatcherTable : TableBase<CachedRecord, TableCache<
private sealed class ResourceCategoryColumn : FlagColumn<ResourceCategoryFlag, CachedRecord>, ICheckRecord private sealed class ResourceCategoryColumn : FlagColumn<ResourceCategoryFlag, CachedRecord>, ICheckRecord
{ {
private readonly ResourceWatcherConfig _config; public ResourceCategoryColumn(FilterConfig config)
public ResourceCategoryColumn(ResourceWatcherConfig config)
{ {
_config = config;
UnscaledWidth = 80; UnscaledWidth = 80;
Filter.LoadValue(config.CategoryFilter); Filter.LoadValue(config.ResourceLoggerCategoryFilter);
Filter.FilterChanged += OnFilterChanged; Filter.FilterChanged += () => config.ResourceLoggerCategoryFilter = Filter.FilterValue;
}
private void OnFilterChanged()
{
_config.CategoryFilter = Filter.FilterValue;
_config.Save();
} }
protected override StringU8 DisplayString(in CachedRecord item, int globalIndex) protected override StringU8 DisplayString(in CachedRecord item, int globalIndex)
@ -386,20 +309,11 @@ internal sealed class ResourceWatcherTable : TableBase<CachedRecord, TableCache<
private sealed class ResourceTypeColumn : FlagColumn<ResourceTypeFlag, CachedRecord>, ICheckRecord private sealed class ResourceTypeColumn : FlagColumn<ResourceTypeFlag, CachedRecord>, ICheckRecord
{ {
private readonly ResourceWatcherConfig _config; public ResourceTypeColumn(FilterConfig config)
public ResourceTypeColumn(ResourceWatcherConfig config)
{ {
_config = config;
UnscaledWidth = 50; UnscaledWidth = 50;
Filter.LoadValue(config.TypeFilter); Filter.LoadValue(config.ResourceLoggerTypeFilter);
Filter.FilterChanged += OnFilterChanged; Filter.FilterChanged += () => config.ResourceLoggerTypeFilter = Filter.FilterValue;
}
private void OnFilterChanged()
{
_config.TypeFilter = Filter.FilterValue;
_config.Save();
} }
protected override IReadOnlyList<(ResourceTypeFlag Value, StringU8 Name)> EnumData { get; } = protected override IReadOnlyList<(ResourceTypeFlag Value, StringU8 Name)> EnumData { get; } =
@ -417,20 +331,11 @@ internal sealed class ResourceWatcherTable : TableBase<CachedRecord, TableCache<
private sealed class LoadStateColumn : FlagColumn<LoadStateFlag, CachedRecord>, ICheckRecord private sealed class LoadStateColumn : FlagColumn<LoadStateFlag, CachedRecord>, ICheckRecord
{ {
private readonly ResourceWatcherConfig _config; public LoadStateColumn(FilterConfig config)
public LoadStateColumn(ResourceWatcherConfig config)
{ {
_config = config;
UnscaledWidth = 50; UnscaledWidth = 50;
Filter.LoadValue(config.LoadStateFilter); Filter.LoadValue(config.ResourceLoggerLoadStateFilter);
Filter.FilterChanged += OnFilterChanged; Filter.FilterChanged += () => config.ResourceLoggerLoadStateFilter = Filter.FilterValue;
}
private void OnFilterChanged()
{
_config.LoadStateFilter = Filter.FilterValue;
_config.Save();
} }
public override void DrawColumn(in CachedRecord item, int globalIndex) public override void DrawColumn(in CachedRecord item, int globalIndex)
@ -474,39 +379,30 @@ internal sealed class ResourceWatcherTable : TableBase<CachedRecord, TableCache<
protected override LoadStateFlag GetValue(in CachedRecord item, int globalIndex) protected override LoadStateFlag GetValue(in CachedRecord item, int globalIndex)
=> GetValue(item.Record.LoadState); => GetValue(item.Record.LoadState);
public bool WouldBeVisible(in Record record) public bool WouldBeVisible(in Record record)
=> Filter.FilterValue.HasFlag(GetValue(record.LoadState)); => Filter.FilterValue.HasFlag(GetValue(record.LoadState));
private static LoadStateFlag GetValue(LoadState value) private static LoadStateFlag GetValue(LoadState value)
=> value switch => value switch
{ {
LoadState.None => LoadStateFlag.None, LoadState.None => LoadStateFlag.None,
LoadState.Success => LoadStateFlag.Success, LoadState.Success => LoadStateFlag.Success,
LoadState.FailedSubResource => LoadStateFlag.FailedSub, LoadState.FailedSubResource => LoadStateFlag.FailedSub,
<= LoadState.Constructed => LoadStateFlag.Unknown, <= LoadState.Constructed => LoadStateFlag.Unknown,
< LoadState.Success => LoadStateFlag.Async, < LoadState.Success => LoadStateFlag.Async,
> LoadState.Success => LoadStateFlag.Failed, > LoadState.Success => LoadStateFlag.Failed,
}; };
} }
private sealed class HandleColumn : TextColumn<CachedRecord>, ICheckRecord private sealed class HandleColumn : TextColumn<CachedRecord>, ICheckRecord
{ {
private readonly ResourceWatcherConfig _config; public HandleColumn(FilterConfig config)
public HandleColumn(ResourceWatcherConfig config)
{ {
_config = config;
UnscaledWidth = 120; UnscaledWidth = 120;
Filter.Set(config.ResourceFilter); Filter.Set(config.ResourceLoggerResourceFilter);
Filter.FilterChanged += OnFilterChanged; Filter.FilterChanged += () => config.ResourceLoggerResourceFilter = Filter.Text;
}
private void OnFilterChanged()
{
_config.ResourceFilter = Filter.Text;
_config.Save();
} }
public override unsafe void DrawColumn(in CachedRecord item, int globalIndex) public override unsafe void DrawColumn(in CachedRecord item, int globalIndex)
@ -569,20 +465,11 @@ internal sealed class ResourceWatcherTable : TableBase<CachedRecord, TableCache<
private sealed class CustomLoadColumn : OptBoolColumn, ICheckRecord private sealed class CustomLoadColumn : OptBoolColumn, ICheckRecord
{ {
private readonly ResourceWatcherConfig _config; public CustomLoadColumn(FilterConfig config)
public CustomLoadColumn(ResourceWatcherConfig config)
: base(60f) : base(60f)
{ {
_config = config; Filter.LoadValue(config.ResourceLoggerCustomFilter);
Filter.LoadValue(config.CustomFilter); Filter.FilterChanged += () => config.ResourceLoggerCustomFilter = Filter.FilterValue;
Filter.FilterChanged += OnFilterChanged;
}
private void OnFilterChanged()
{
_config.CustomFilter = Filter.FilterValue;
_config.Save();
} }
protected override BoolEnum GetValue(in CachedRecord item, int globalIndex) protected override BoolEnum GetValue(in CachedRecord item, int globalIndex)
@ -594,20 +481,11 @@ internal sealed class ResourceWatcherTable : TableBase<CachedRecord, TableCache<
private sealed class SynchronousLoadColumn : OptBoolColumn, ICheckRecord private sealed class SynchronousLoadColumn : OptBoolColumn, ICheckRecord
{ {
private readonly ResourceWatcherConfig _config; public SynchronousLoadColumn(FilterConfig config)
public SynchronousLoadColumn(ResourceWatcherConfig config)
: base(45) : base(45)
{ {
_config = config; Filter.LoadValue(config.ResourceLoggerSyncFilter);
Filter.LoadValue(config.SyncFilter); Filter.FilterChanged += () => config.ResourceLoggerSyncFilter = Filter.FilterValue;
Filter.FilterChanged += OnFilterChanged;
}
private void OnFilterChanged()
{
_config.SyncFilter = Filter.FilterValue;
_config.Save();
} }
protected override BoolEnum GetValue(in CachedRecord item, int globalIndex) protected override BoolEnum GetValue(in CachedRecord item, int globalIndex)
@ -619,20 +497,11 @@ internal sealed class ResourceWatcherTable : TableBase<CachedRecord, TableCache<
private sealed class RefCountColumn : NumberColumn<uint, CachedRecord>, ICheckRecord private sealed class RefCountColumn : NumberColumn<uint, CachedRecord>, ICheckRecord
{ {
private readonly ResourceWatcherConfig _config; public RefCountColumn(FilterConfig config)
public RefCountColumn(ResourceWatcherConfig config)
{ {
_config = config;
UnscaledWidth = 60; UnscaledWidth = 60;
Filter.Set(config.RefFilter); Filter.Set(config.ResourceLoggerRefFilter);
Filter.FilterChanged += OnFilterChanged; Filter.FilterChanged += () => config.ResourceLoggerRefFilter = Filter.Text;
}
private void OnFilterChanged()
{
_config.RefFilter = Filter.Text;
_config.Save();
} }
public override uint ToValue(in CachedRecord item, int globalIndex) public override uint ToValue(in CachedRecord item, int globalIndex)
@ -650,20 +519,11 @@ internal sealed class ResourceWatcherTable : TableBase<CachedRecord, TableCache<
private sealed class OsThreadColumn : NumberColumn<uint, CachedRecord>, ICheckRecord private sealed class OsThreadColumn : NumberColumn<uint, CachedRecord>, ICheckRecord
{ {
private readonly ResourceWatcherConfig _config; public OsThreadColumn(FilterConfig config)
public OsThreadColumn(ResourceWatcherConfig config)
{ {
_config = config;
UnscaledWidth = 60; UnscaledWidth = 60;
Filter.Set(config.ThreadFilter); Filter.Set(config.ResourceLoggerThreadFilter);
Filter.FilterChanged += OnFilterChanged; Filter.FilterChanged += () => config.ResourceLoggerThreadFilter = Filter.Text;
}
private void OnFilterChanged()
{
_config.ThreadFilter = Filter.Text;
_config.Save();
} }
public override uint ToValue(in CachedRecord item, int globalIndex) public override uint ToValue(in CachedRecord item, int globalIndex)

View file

@ -43,10 +43,10 @@ public sealed class CollectionsTab : TwoPanelLayout, ITab<TabType>
} }
public void DrawContent() public void DrawContent()
=> Draw(_config.CollectionTabScale); => Draw(_config.CollectionsTabScale);
protected override void SetWidth(float width, ScalingMode mode) protected override void SetWidth(float width, ScalingMode mode)
=> _config.CollectionTabScale = new TwoPanelWidth(width, mode); => _config.CollectionsTabScale = new TwoPanelWidth(width, mode);
public void PostTabButton() public void PostTabButton()
=> _tutorial.OpenTutorial(BasicTutorialSteps.Collections); => _tutorial.OpenTutorial(BasicTutorialSteps.Collections);

View file

@ -20,10 +20,16 @@ public sealed class ResourceTab(Configuration config, ResourceManagerService res
public bool IsVisible public bool IsVisible
=> config.DebugMode; => config.DebugMode;
public readonly ResourceFilter Filter = new(); public readonly ResourceFilter Filter = new(config.Filters);
public sealed class ResourceFilter : Utf8FilterBase<ResourceHandle> public sealed class ResourceFilter : Utf8FilterBase<ResourceHandle>
{ {
public ResourceFilter(FilterConfig filterConfig)
{
Set(new StringU8(filterConfig.ResourceManagerFilter));
FilterChanged += () => filterConfig.ResourceManagerFilter = Text.ToString();
}
protected override ReadOnlySpan<byte> ToFilterString(in ResourceHandle item, int globalIndex) protected override ReadOnlySpan<byte> ToFilterString(in ResourceHandle item, int globalIndex)
=> item.FileName.AsSpan(); => item.FileName.AsSpan();
} }

View file

@ -432,7 +432,8 @@ public sealed class SettingsTab : ITab<TabType>
_config.HideChangedItemFilters = v; _config.HideChangedItemFilters = v;
if (v) if (v)
{ {
_config.Ephemeral.ChangedItemFilter = ChangedItemFlagExtensions.AllFlags; _config.Filters.ModChangedItemTypeFilter = ChangedItemFlagExtensions.AllFlags;
_config.Filters.ChangedItemTypeFilter = ChangedItemFlagExtensions.AllFlags;
_config.Ephemeral.Save(); _config.Ephemeral.Save();
} }
}); });