From a6661f15e87ad8b8b15d386943d08e20001c9848 Mon Sep 17 00:00:00 2001 From: Exter-N Date: Thu, 30 May 2024 20:46:04 +0200 Subject: [PATCH 1/4] Display the additional path data in ResourceTree --- .../Interop/MaterialPreview/MaterialInfo.cs | 11 +++++--- .../ResolveContext.PathResolution.cs | 2 +- .../Interop/ResourceTree/ResolveContext.cs | 27 +++++++------------ Penumbra/Interop/ResourceTree/ResourceNode.cs | 4 +++ .../UI/AdvancedWindow/ResourceTreeViewer.cs | 8 ++++-- 5 files changed, 28 insertions(+), 24 deletions(-) diff --git a/Penumbra/Interop/MaterialPreview/MaterialInfo.cs b/Penumbra/Interop/MaterialPreview/MaterialInfo.cs index 61e7c764..f7e6caf0 100644 --- a/Penumbra/Interop/MaterialPreview/MaterialInfo.cs +++ b/Penumbra/Interop/MaterialPreview/MaterialInfo.cs @@ -3,8 +3,9 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Render; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; -using Penumbra.Interop.ResourceTree; +using Penumbra.Interop.PathResolving; using Penumbra.String; +using static Penumbra.Interop.Structs.StructExtensions; using Model = Penumbra.GameData.Interop.Model; namespace Penumbra.Interop.MaterialPreview; @@ -78,8 +79,12 @@ public readonly record struct MaterialInfo(ObjectIndex ObjectIndex, DrawObjectTy continue; var mtrlHandle = material->MaterialResourceHandle; - var path = ResolveContext.GetResourceHandlePath(&mtrlHandle->ResourceHandle); - if (path == needle) + if (mtrlHandle == null) + continue; + + PathDataHandler.Split(mtrlHandle->ResourceHandle.FileName.AsSpan(), out var path, out _); + var fileName = ByteString.FromSpanUnsafe(path, true); + if (fileName == needle) result.Add(new MaterialInfo(index, type, i, j)); } } diff --git a/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs b/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs index d5b4fa39..236c7051 100644 --- a/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs +++ b/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs @@ -155,7 +155,7 @@ internal partial record ResolveContext var imcFileData = imc->GetDataSpan(); if (imcFileData.IsEmpty) { - Penumbra.Log.Warning($"IMC resource handle with path {GetResourceHandlePath(imc, false)} doesn't have a valid data span"); + Penumbra.Log.Warning($"IMC resource handle with path {imc->FileName.AsByteString()} doesn't have a valid data span"); return variant.Id; } diff --git a/Penumbra/Interop/ResourceTree/ResolveContext.cs b/Penumbra/Interop/ResourceTree/ResolveContext.cs index 7c8da41f..e38bf4f6 100644 --- a/Penumbra/Interop/ResourceTree/ResolveContext.cs +++ b/Penumbra/Interop/ResourceTree/ResolveContext.cs @@ -111,12 +111,18 @@ internal unsafe partial record ResolveContext( if (resourceHandle == null) throw new ArgumentNullException(nameof(resourceHandle)); - var fullPath = Utf8GamePath.FromByteString(GetResourceHandlePath(resourceHandle), out var p) ? new FullPath(p) : FullPath.Empty; + var fileName = resourceHandle->FileName.AsSpan(); + var additionalData = ByteString.Empty; + if (PathDataHandler.Split(fileName, out fileName, out var data)) + additionalData = ByteString.FromSpanUnsafe(data, false).Clone(); + + var fullPath = Utf8GamePath.FromSpan(fileName, out var p) ? new FullPath(p.Clone()) : FullPath.Empty; var node = new ResourceNode(type, objectAddress, (nint)resourceHandle, GetResourceHandleLength(resourceHandle), this) { - GamePath = gamePath, - FullPath = fullPath, + GamePath = gamePath, + FullPath = fullPath, + AdditionalData = additionalData, }; if (autoAdd) Global.Nodes.Add((gamePath, (nint)resourceHandle), node); @@ -365,21 +371,6 @@ internal unsafe partial record ResolveContext( return i >= 0 && i < array.Length ? array[i] : null; } - internal static ByteString GetResourceHandlePath(ResourceHandle* handle, bool stripPrefix = true) - { - if (handle == null) - return ByteString.Empty; - - var name = handle->FileName.AsByteString(); - if (name.IsEmpty) - return ByteString.Empty; - - if (stripPrefix && PathDataHandler.Split(name.Span, out var path, out _)) - name = ByteString.FromSpanUnsafe(path, name.IsNullTerminated, name.IsAsciiLowerCase, name.IsAscii); - - return name; - } - private static ulong GetResourceHandleLength(ResourceHandle* handle) { if (handle == null) diff --git a/Penumbra/Interop/ResourceTree/ResourceNode.cs b/Penumbra/Interop/ResourceTree/ResourceNode.cs index 7ec75893..e74edb91 100644 --- a/Penumbra/Interop/ResourceTree/ResourceNode.cs +++ b/Penumbra/Interop/ResourceTree/ResourceNode.cs @@ -1,4 +1,5 @@ using Penumbra.Api.Enums; +using Penumbra.String; using Penumbra.String.Classes; using ChangedItemIcon = Penumbra.UI.ChangedItemDrawer.ChangedItemIcon; @@ -15,6 +16,7 @@ public class ResourceNode : ICloneable public readonly nint ResourceHandle; public Utf8GamePath[] PossibleGamePaths; public FullPath FullPath; + public ByteString AdditionalData; public readonly ulong Length; public readonly List Children; internal ResolveContext? ResolveContext; @@ -40,6 +42,7 @@ public class ResourceNode : ICloneable ObjectAddress = objectAddress; ResourceHandle = resourceHandle; PossibleGamePaths = Array.Empty(); + AdditionalData = ByteString.Empty; Length = length; Children = new List(); ResolveContext = resolveContext; @@ -56,6 +59,7 @@ public class ResourceNode : ICloneable ResourceHandle = other.ResourceHandle; PossibleGamePaths = other.PossibleGamePaths; FullPath = other.FullPath; + AdditionalData = other.AdditionalData; Length = other.Length; Children = other.Children; ResolveContext = other.ResolveContext; diff --git a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs index d31f3e52..3ada77df 100644 --- a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs +++ b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs @@ -5,6 +5,7 @@ using OtterGui.Raii; using OtterGui; using Penumbra.Interop.ResourceTree; using Penumbra.UI.Classes; +using Penumbra.String; namespace Penumbra.UI.AdvancedWindow; @@ -177,6 +178,9 @@ public class ResourceTreeViewer return NodeVisibility.Hidden; } + string GetAdditionalDataSuffix(ByteString data) + => !debugMode || data.IsEmpty ? string.Empty : $"\n\nAdditional Data: {data}"; + foreach (var (resourceNode, index) in resourceNodes.WithIndex()) { var visibility = GetNodeVisibility(resourceNode); @@ -260,13 +264,13 @@ public class ResourceTreeViewer ImGui.Selectable(resourceNode.FullPath.ToPath(), false, 0, new Vector2(ImGui.GetContentRegionAvail().X, cellHeight)); if (ImGui.IsItemClicked()) ImGui.SetClipboardText(resourceNode.FullPath.ToPath()); - ImGuiUtil.HoverTooltip($"{resourceNode.FullPath.ToPath()}\n\nClick to copy to clipboard."); + ImGuiUtil.HoverTooltip($"{resourceNode.FullPath.ToPath()}\n\nClick to copy to clipboard.{GetAdditionalDataSuffix(resourceNode.AdditionalData)}"); } else { ImGui.Selectable("(unavailable)", false, ImGuiSelectableFlags.Disabled, new Vector2(ImGui.GetContentRegionAvail().X, cellHeight)); - ImGuiUtil.HoverTooltip("The actual path to this file is unavailable.\nIt may be managed by another plug-in."); + ImGuiUtil.HoverTooltip($"The actual path to this file is unavailable.\nIt may be managed by another plug-in.{GetAdditionalDataSuffix(resourceNode.AdditionalData)}"); } mutedColor.Dispose(); From f4bdbcac5338f6bb7deb5e021ca2e4d6236de739 Mon Sep 17 00:00:00 2001 From: Exter-N Date: Thu, 30 May 2024 23:09:26 +0200 Subject: [PATCH 2/4] Make Resource Trees honor Incognito Mode --- Penumbra/Interop/ResourceTree/ResourceTree.cs | 26 ++++++----- .../ResourceTree/ResourceTreeFactory.cs | 43 ++++++++++--------- Penumbra/Services/StaticServiceManager.cs | 2 + Penumbra/UI/AdvancedWindow/ModEditWindow.cs | 6 +-- .../UI/AdvancedWindow/ResourceTreeViewer.cs | 17 +++++--- .../ResourceTreeViewerFactory.cs | 13 ++++++ Penumbra/UI/ChangedItemDrawer.cs | 7 ++- Penumbra/UI/CollectionTab/CollectionPanel.cs | 11 +++-- .../UI/CollectionTab/CollectionSelector.cs | 8 ++-- Penumbra/UI/CollectionTab/InheritanceUi.cs | 4 +- Penumbra/UI/IncognitoService.cs | 29 +++++++++++++ Penumbra/UI/Tabs/CollectionsTab.cs | 25 ++++------- Penumbra/UI/Tabs/OnScreenTab.cs | 7 +-- 13 files changed, 123 insertions(+), 75 deletions(-) create mode 100644 Penumbra/UI/AdvancedWindow/ResourceTreeViewerFactory.cs create mode 100644 Penumbra/UI/IncognitoService.cs diff --git a/Penumbra/Interop/ResourceTree/ResourceTree.cs b/Penumbra/Interop/ResourceTree/ResourceTree.cs index dac86e44..fc8c805a 100644 --- a/Penumbra/Interop/ResourceTree/ResourceTree.cs +++ b/Penumbra/Interop/ResourceTree/ResourceTree.cs @@ -15,6 +15,7 @@ namespace Penumbra.Interop.ResourceTree; public class ResourceTree { public readonly string Name; + public readonly string AnonymizedName; public readonly int GameObjectIndex; public readonly nint GameObjectAddress; public readonly nint DrawObjectAddress; @@ -22,6 +23,7 @@ public class ResourceTree public readonly bool PlayerRelated; public readonly bool Networked; public readonly string CollectionName; + public readonly string AnonymizedCollectionName; public readonly List Nodes; public readonly HashSet FlatNodes; @@ -29,18 +31,20 @@ public class ResourceTree public CustomizeData CustomizeData; public GenderRace RaceCode; - public ResourceTree(string name, int gameObjectIndex, nint gameObjectAddress, nint drawObjectAddress, bool localPlayerRelated, bool playerRelated, bool networked, string collectionName) + public ResourceTree(string name, string anonymizedName, int gameObjectIndex, nint gameObjectAddress, nint drawObjectAddress, bool localPlayerRelated, bool playerRelated, bool networked, string collectionName, string anonymizedCollectionName) { - Name = name; - GameObjectIndex = gameObjectIndex; - GameObjectAddress = gameObjectAddress; - DrawObjectAddress = drawObjectAddress; - LocalPlayerRelated = localPlayerRelated; - Networked = networked; - PlayerRelated = playerRelated; - CollectionName = collectionName; - Nodes = new List(); - FlatNodes = new HashSet(); + Name = name; + AnonymizedName = anonymizedName; + GameObjectIndex = gameObjectIndex; + GameObjectAddress = gameObjectAddress; + DrawObjectAddress = drawObjectAddress; + LocalPlayerRelated = localPlayerRelated; + Networked = networked; + PlayerRelated = playerRelated; + CollectionName = collectionName; + AnonymizedCollectionName = anonymizedCollectionName; + Nodes = new List(); + FlatNodes = new HashSet(); } public void ProcessPostfix(Action action) diff --git a/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs b/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs index ae7187f0..a722e344 100644 --- a/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs +++ b/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs @@ -72,10 +72,10 @@ public class ResourceTreeFactory( return null; var localPlayerRelated = cache.IsLocalPlayerRelated(character); - var (name, related) = GetCharacterName(character, cache); + var (name, anonymizedName, related) = GetCharacterName(character); var networked = character.ObjectId != Dalamud.Game.ClientState.Objects.Types.GameObject.InvalidGameObjectId; - var tree = new ResourceTree(name, character.ObjectIndex, (nint)gameObjStruct, (nint)drawObjStruct, localPlayerRelated, related, - networked, collectionResolveData.ModCollection.Name); + var tree = new ResourceTree(name, anonymizedName, character.ObjectIndex, (nint)gameObjStruct, (nint)drawObjStruct, localPlayerRelated, related, + networked, collectionResolveData.ModCollection.Name, collectionResolveData.ModCollection.AnonymizedName); var globalContext = new GlobalResolveContext(identifier, collectionResolveData.ModCollection, cache, (flags & Flags.WithUiData) != 0); using (var _ = pathState.EnterInternalResolve()) @@ -157,27 +157,30 @@ public class ResourceTreeFactory( } } - private unsafe (string Name, bool PlayerRelated) GetCharacterName(Dalamud.Game.ClientState.Objects.Types.Character character, - TreeBuildCache cache) + private unsafe (string Name, string AnonymizedName, bool PlayerRelated) GetCharacterName(Dalamud.Game.ClientState.Objects.Types.Character character) { var identifier = actors.FromObject((GameObject*)character.Address, out var owner, true, false, false); - switch (identifier.Type) - { - case IdentifierType.Player: return (identifier.PlayerName.ToString(), true); - case IdentifierType.Owned: - var ownerChara = objects.Objects.CreateObjectReference(owner) as Dalamud.Game.ClientState.Objects.Types.Character; - if (ownerChara != null) - { - var ownerName = GetCharacterName(ownerChara, cache); - return ($"[{ownerName.Name}] {character.Name} ({identifier.Kind.ToName()})", ownerName.PlayerRelated); - } - - break; - } - - return ($"{character.Name} ({identifier.Kind.ToName()})", false); + var identifierStr = identifier.ToString(); + return (identifierStr, identifier.Incognito(identifierStr), IsPlayerRelated(identifier, owner)); } + private unsafe bool IsPlayerRelated(Dalamud.Game.ClientState.Objects.Types.Character? character) + { + if (character == null) + return false; + + var identifier = actors.FromObject((GameObject*)character.Address, out var owner, true, false, false); + return IsPlayerRelated(identifier, owner); + } + + private bool IsPlayerRelated(ActorIdentifier identifier, Actor owner) + => identifier.Type switch + { + IdentifierType.Player => true, + IdentifierType.Owned => IsPlayerRelated(objects.Objects.CreateObjectReference(owner) as Dalamud.Game.ClientState.Objects.Types.Character), + _ => false, + }; + [Flags] public enum Flags { diff --git a/Penumbra/Services/StaticServiceManager.cs b/Penumbra/Services/StaticServiceManager.cs index 0c6648ba..146d7ee0 100644 --- a/Penumbra/Services/StaticServiceManager.cs +++ b/Penumbra/Services/StaticServiceManager.cs @@ -149,6 +149,7 @@ public static class StaticServiceManager private static ServiceManager AddInterface(this ServiceManager services) => services.AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() @@ -181,6 +182,7 @@ public static class StaticServiceManager .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton(p => new Diagnostics(p)); private static ServiceManager AddModEditor(this ServiceManager services) diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs index 6b48a048..af01047b 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs @@ -585,7 +585,8 @@ public partial class ModEditWindow : Window, IDisposable Configuration config, ModEditor editor, ResourceTreeFactory resourceTreeFactory, MetaFileManager metaFileManager, StainService stainService, ActiveCollections activeCollections, ModMergeTab modMergeTab, CommunicatorService communicator, TextureManager textures, ModelManager models, IDragDropManager dragDropManager, - ChangedItemDrawer changedItemDrawer, ObjectManager objects, IFramework framework, CharacterBaseDestructor characterBaseDestructor) + ResourceTreeViewerFactory resourceTreeViewerFactory, ObjectManager objects, IFramework framework, + CharacterBaseDestructor characterBaseDestructor) : base(WindowBaseLabel) { _performance = performance; @@ -618,8 +619,7 @@ public partial class ModEditWindow : Window, IDisposable _center = new CombinedTexture(_left, _right); _textureSelectCombo = new TextureDrawer.PathSelectCombo(textures, editor, () => GetPlayerResourcesOfType(ResourceType.Tex)); _resourceTreeFactory = resourceTreeFactory; - _quickImportViewer = - new ResourceTreeViewer(_config, resourceTreeFactory, changedItemDrawer, 2, OnQuickImportRefresh, DrawQuickImportActions); + _quickImportViewer = resourceTreeViewerFactory.Create(2, OnQuickImportRefresh, DrawQuickImportActions); _communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.ModEditWindow); IsOpen = _config is { OpenWindowAtStart: true, Ephemeral.AdvancedEditingOpen: true }; } diff --git a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs index 3ada77df..c0c49e47 100644 --- a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs +++ b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs @@ -6,6 +6,7 @@ using OtterGui; using Penumbra.Interop.ResourceTree; using Penumbra.UI.Classes; using Penumbra.String; +using Penumbra.UI.Tabs; namespace Penumbra.UI.AdvancedWindow; @@ -17,6 +18,7 @@ public class ResourceTreeViewer private readonly Configuration _config; private readonly ResourceTreeFactory _treeFactory; private readonly ChangedItemDrawer _changedItemDrawer; + private readonly IncognitoService _incognito; private readonly int _actionCapacity; private readonly Action _onRefresh; private readonly Action _drawActions; @@ -29,11 +31,12 @@ public class ResourceTreeViewer private Task? _task; public ResourceTreeViewer(Configuration config, ResourceTreeFactory treeFactory, ChangedItemDrawer changedItemDrawer, - int actionCapacity, Action onRefresh, Action drawActions) + IncognitoService incognito, int actionCapacity, Action onRefresh, Action drawActions) { _config = config; _treeFactory = treeFactory; _changedItemDrawer = changedItemDrawer; + _incognito = incognito; _actionCapacity = actionCapacity; _onRefresh = onRefresh; _drawActions = drawActions; @@ -75,7 +78,7 @@ public class ResourceTreeViewer using (var c = ImRaii.PushColor(ImGuiCol.Text, CategoryColor(category).Value())) { - var isOpen = ImGui.CollapsingHeader($"{tree.Name}##{index}", index == 0 ? ImGuiTreeNodeFlags.DefaultOpen : 0); + var isOpen = ImGui.CollapsingHeader($"{(_incognito.IncognitoMode ? tree.AnonymizedName : tree.Name)}###{index}", index == 0 ? ImGuiTreeNodeFlags.DefaultOpen : 0); if (debugMode) { using var _ = ImRaii.PushFont(UiBuilder.MonoFont); @@ -89,7 +92,7 @@ public class ResourceTreeViewer using var id = ImRaii.PushId(index); - ImGui.TextUnformatted($"Collection: {tree.CollectionName}"); + ImGui.TextUnformatted($"Collection: {(_incognito.IncognitoMode ? tree.AnonymizedCollectionName : tree.CollectionName)}"); using var table = ImRaii.Table("##ResourceTree", _actionCapacity > 0 ? 4 : 3, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg); @@ -137,10 +140,14 @@ public class ResourceTreeViewer ImGui.SameLine(0, checkPadding); - _changedItemDrawer.DrawTypeFilter(ref _typeFilter, -yOffset); + ImGui.SetCursorPosY(ImGui.GetCursorPosY() - yOffset); + using (ImRaii.Child("##typeFilter", new Vector2(ImGui.GetContentRegionAvail().X, ChangedItemDrawer.TypeFilterIconSize.Y))) + _changedItemDrawer.DrawTypeFilter(ref _typeFilter); - ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - checkSpacing - ImGui.GetFrameHeightWithSpacing()); ImGui.InputTextWithHint("##TreeNameFilter", "Filter by Character/Entity Name...", ref _nameFilter, 128); + ImGui.SameLine(0, checkSpacing); + _incognito.DrawToggle(); } private Task RefreshCharacterList() diff --git a/Penumbra/UI/AdvancedWindow/ResourceTreeViewerFactory.cs b/Penumbra/UI/AdvancedWindow/ResourceTreeViewerFactory.cs new file mode 100644 index 00000000..91dab6cb --- /dev/null +++ b/Penumbra/UI/AdvancedWindow/ResourceTreeViewerFactory.cs @@ -0,0 +1,13 @@ +using Penumbra.Interop.ResourceTree; + +namespace Penumbra.UI.AdvancedWindow; + +public class ResourceTreeViewerFactory( + Configuration config, + ResourceTreeFactory treeFactory, + ChangedItemDrawer changedItemDrawer, + IncognitoService incognito) +{ + public ResourceTreeViewer Create(int actionCapacity, Action onRefresh, Action drawActions) + => new(config, treeFactory, changedItemDrawer, incognito, actionCapacity, onRefresh, drawActions); +} diff --git a/Penumbra/UI/ChangedItemDrawer.cs b/Penumbra/UI/ChangedItemDrawer.cs index 638afef0..29a1f291 100644 --- a/Penumbra/UI/ChangedItemDrawer.cs +++ b/Penumbra/UI/ChangedItemDrawer.cs @@ -212,7 +212,7 @@ public class ChangedItemDrawer : IDisposable return; var typeFilter = _config.Ephemeral.ChangedItemFilter; - if (DrawTypeFilter(ref typeFilter, 0.0f)) + if (DrawTypeFilter(ref typeFilter)) { _config.Ephemeral.ChangedItemFilter = typeFilter; _config.Ephemeral.Save(); @@ -220,7 +220,7 @@ public class ChangedItemDrawer : IDisposable } /// Draw a header line with the different icon types to filter them. - public bool DrawTypeFilter(ref ChangedItemIcon typeFilter, float yOffset) + public bool DrawTypeFilter(ref ChangedItemIcon typeFilter) { var ret = false; using var _ = ImRaii.PushId("ChangedItemIconFilter"); @@ -233,7 +233,6 @@ public class ChangedItemDrawer : IDisposable var ret = false; var icon = _icons[type]; var flag = typeFilter.HasFlag(type); - ImGui.SetCursorPosY(ImGui.GetCursorPosY() + yOffset); ImGui.Image(icon.ImGuiHandle, size, Vector2.Zero, Vector2.One, flag ? Vector4.One : new Vector4(0.6f, 0.3f, 0.3f, 1f)); if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) { @@ -267,7 +266,7 @@ public class ChangedItemDrawer : IDisposable ImGui.SameLine(); } - ImGui.SetCursorPos(new Vector2(ImGui.GetContentRegionMax().X - size.X, ImGui.GetCursorPosY() + yOffset)); + ImGui.SetCursorPosX(ImGui.GetContentRegionMax().X - size.X); ImGui.Image(_icons[AllFlags].ImGuiHandle, size, Vector2.Zero, Vector2.One, typeFilter == 0 ? new Vector4(0.6f, 0.3f, 0.3f, 1f) : typeFilter == AllFlags ? new Vector4(0.75f, 0.75f, 0.75f, 1f) : new Vector4(0.5f, 0.5f, 1f, 1f)); diff --git a/Penumbra/UI/CollectionTab/CollectionPanel.cs b/Penumbra/UI/CollectionTab/CollectionPanel.cs index bb22e6a7..cb4dbe20 100644 --- a/Penumbra/UI/CollectionTab/CollectionPanel.cs +++ b/Penumbra/UI/CollectionTab/CollectionPanel.cs @@ -31,6 +31,7 @@ public sealed class CollectionPanel : IDisposable private readonly InheritanceUi _inheritanceUi; private readonly ModStorage _mods; private readonly FilenameService _fileNames; + private readonly IncognitoService _incognito; private readonly IFontHandle _nameFont; private static readonly IReadOnlyDictionary Buttons = CreateButtons(); @@ -41,7 +42,8 @@ public sealed class CollectionPanel : IDisposable private int _draggedIndividualAssignment = -1; public CollectionPanel(DalamudPluginInterface pi, CommunicatorService communicator, CollectionManager manager, - CollectionSelector selector, ActorManager actors, ITargetManager targets, ModStorage mods, FilenameService fileNames) + CollectionSelector selector, ActorManager actors, ITargetManager targets, ModStorage mods, FilenameService fileNames, + IncognitoService incognito) { _collections = manager.Storage; _active = manager.Active; @@ -50,8 +52,9 @@ public sealed class CollectionPanel : IDisposable _targets = targets; _mods = mods; _fileNames = fileNames; + _incognito = incognito; _individualAssignmentUi = new IndividualAssignmentUi(communicator, actors, manager); - _inheritanceUi = new InheritanceUi(manager, _selector); + _inheritanceUi = new InheritanceUi(manager, incognito); _nameFont = pi.UiBuilder.FontAtlas.NewGameFontHandle(new GameFontStyle(GameFontFamilyAndSize.Jupiter23)); } @@ -415,7 +418,7 @@ public sealed class CollectionPanel : IDisposable /// Respect incognito mode for names of identifiers. private string Name(ActorIdentifier id, string? name) - => _selector.IncognitoMode && id.Type is IdentifierType.Player or IdentifierType.Owned + => _incognito.IncognitoMode && id.Type is IdentifierType.Player or IdentifierType.Owned ? id.Incognito(name) : name ?? id.ToString(); @@ -423,7 +426,7 @@ public sealed class CollectionPanel : IDisposable private string Name(ModCollection? collection) => collection == null ? "Unassigned" : collection == ModCollection.Empty ? "Use No Mods" : - _selector.IncognitoMode ? collection.AnonymizedName : collection.Name; + _incognito.IncognitoMode ? collection.AnonymizedName : collection.Name; private void DrawIndividualButton(string intro, Vector2 width, string tooltip, char suffix, params ActorIdentifier[] identifiers) { diff --git a/Penumbra/UI/CollectionTab/CollectionSelector.cs b/Penumbra/UI/CollectionTab/CollectionSelector.cs index fac85d4d..24d3f591 100644 --- a/Penumbra/UI/CollectionTab/CollectionSelector.cs +++ b/Penumbra/UI/CollectionTab/CollectionSelector.cs @@ -17,13 +17,12 @@ public sealed class CollectionSelector : ItemSelector, IDisposabl private readonly CollectionStorage _storage; private readonly ActiveCollections _active; private readonly TutorialService _tutorial; + private readonly IncognitoService _incognito; private ModCollection? _dragging; - public bool IncognitoMode; - public CollectionSelector(Configuration config, CommunicatorService communicator, CollectionStorage storage, ActiveCollections active, - TutorialService tutorial) + TutorialService tutorial, IncognitoService incognito) : base([], Flags.Delete | Flags.Add | Flags.Duplicate | Flags.Filter) { _config = config; @@ -31,6 +30,7 @@ public sealed class CollectionSelector : ItemSelector, IDisposabl _storage = storage; _active = active; _tutorial = tutorial; + _incognito = incognito; _communicator.CollectionChange.Subscribe(OnCollectionChange, CollectionChange.Priority.CollectionSelector); // Set items. @@ -109,7 +109,7 @@ public sealed class CollectionSelector : ItemSelector, IDisposabl } private string Name(ModCollection collection) - => IncognitoMode ? collection.AnonymizedName : collection.Name; + => _incognito.IncognitoMode ? collection.AnonymizedName : collection.Name; private void OnCollectionChange(CollectionType type, ModCollection? old, ModCollection? @new, string _3) { diff --git a/Penumbra/UI/CollectionTab/InheritanceUi.cs b/Penumbra/UI/CollectionTab/InheritanceUi.cs index 2290592d..418fe52c 100644 --- a/Penumbra/UI/CollectionTab/InheritanceUi.cs +++ b/Penumbra/UI/CollectionTab/InheritanceUi.cs @@ -9,7 +9,7 @@ using Penumbra.UI.Classes; namespace Penumbra.UI.CollectionTab; -public class InheritanceUi(CollectionManager collectionManager, CollectionSelector selector) : IUiService +public class InheritanceUi(CollectionManager collectionManager, IncognitoService incognito) : IUiService { private const int InheritedCollectionHeight = 9; private const string InheritanceDragDropLabel = "##InheritanceMove"; @@ -312,5 +312,5 @@ public class InheritanceUi(CollectionManager collectionManager, CollectionSelect } private string Name(ModCollection collection) - => selector.IncognitoMode ? collection.AnonymizedName : collection.Name; + => incognito.IncognitoMode ? collection.AnonymizedName : collection.Name; } diff --git a/Penumbra/UI/IncognitoService.cs b/Penumbra/UI/IncognitoService.cs new file mode 100644 index 00000000..d4b1828f --- /dev/null +++ b/Penumbra/UI/IncognitoService.cs @@ -0,0 +1,29 @@ +using Dalamud.Interface.Utility; +using Dalamud.Interface; +using ImGuiNET; +using OtterGui; +using Penumbra.UI.Classes; +using OtterGui.Raii; + +namespace Penumbra.UI; + +public class IncognitoService(TutorialService tutorial) +{ + public bool IncognitoMode; + + public void DrawToggle(float? buttonWidth = null) + { + using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale); + using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.FolderExpanded.Value()) + .Push(ImGuiCol.Border, ColorId.FolderExpanded.Value()); + if (ImGuiUtil.DrawDisabledButton( + $"{(IncognitoMode ? FontAwesomeIcon.Eye : FontAwesomeIcon.EyeSlash).ToIconString()}###IncognitoMode", + new Vector2(buttonWidth ?? ImGui.GetFrameHeightWithSpacing(), ImGui.GetFrameHeight()), string.Empty, false, true)) + IncognitoMode = !IncognitoMode; + var hovered = ImGui.IsItemHovered(); + tutorial.OpenTutorial(BasicTutorialSteps.Incognito); + color.Pop(2); + if (hovered) + ImGui.SetTooltip(IncognitoMode ? "Toggle incognito mode off." : "Toggle incognito mode on."); + } +} diff --git a/Penumbra/UI/Tabs/CollectionsTab.cs b/Penumbra/UI/Tabs/CollectionsTab.cs index fe1471b3..1eaece50 100644 --- a/Penumbra/UI/Tabs/CollectionsTab.cs +++ b/Penumbra/UI/Tabs/CollectionsTab.cs @@ -21,6 +21,7 @@ public sealed class CollectionsTab : IDisposable, ITab private readonly CollectionSelector _selector; private readonly CollectionPanel _panel; private readonly TutorialService _tutorial; + private readonly IncognitoService _incognito; public enum PanelMode { @@ -40,13 +41,14 @@ public sealed class CollectionsTab : IDisposable, ITab } } - public CollectionsTab(DalamudPluginInterface pi, Configuration configuration, CommunicatorService communicator, + public CollectionsTab(DalamudPluginInterface pi, Configuration configuration, CommunicatorService communicator, IncognitoService incognito, CollectionManager collectionManager, ModStorage modStorage, ActorManager actors, ITargetManager targets, TutorialService tutorial, FilenameService fileNames) { - _config = configuration.Ephemeral; - _tutorial = tutorial; - _selector = new CollectionSelector(configuration, communicator, collectionManager.Storage, collectionManager.Active, _tutorial); - _panel = new CollectionPanel(pi, communicator, collectionManager, _selector, actors, targets, modStorage, fileNames); + _config = configuration.Ephemeral; + _tutorial = tutorial; + _incognito = incognito; + _selector = new CollectionSelector(configuration, communicator, collectionManager.Storage, collectionManager.Active, _tutorial, incognito); + _panel = new CollectionPanel(pi, communicator, collectionManager, _selector, actors, targets, modStorage, fileNames, incognito); } public void Dispose() @@ -116,18 +118,7 @@ public sealed class CollectionsTab : IDisposable, ITab _tutorial.OpenTutorial(BasicTutorialSteps.CollectionDetails); ImGui.SameLine(); - style.Push(ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale); - color.Push(ImGuiCol.Text, ColorId.FolderExpanded.Value()) - .Push(ImGuiCol.Border, ColorId.FolderExpanded.Value()); - if (ImGuiUtil.DrawDisabledButton( - $"{(_selector.IncognitoMode ? FontAwesomeIcon.Eye : FontAwesomeIcon.EyeSlash).ToIconString()}###IncognitoMode", - buttonSize with { X = withSpacing }, string.Empty, false, true)) - _selector.IncognitoMode = !_selector.IncognitoMode; - var hovered = ImGui.IsItemHovered(); - _tutorial.OpenTutorial(BasicTutorialSteps.Incognito); - color.Pop(2); - if (hovered) - ImGui.SetTooltip(_selector.IncognitoMode ? "Toggle incognito mode off." : "Toggle incognito mode on."); + _incognito.DrawToggle(withSpacing); } private void DrawPanel() diff --git a/Penumbra/UI/Tabs/OnScreenTab.cs b/Penumbra/UI/Tabs/OnScreenTab.cs index 09772d8e..787e07a1 100644 --- a/Penumbra/UI/Tabs/OnScreenTab.cs +++ b/Penumbra/UI/Tabs/OnScreenTab.cs @@ -1,18 +1,15 @@ using OtterGui.Widgets; -using Penumbra.Interop.ResourceTree; using Penumbra.UI.AdvancedWindow; namespace Penumbra.UI.Tabs; public class OnScreenTab : ITab { - private readonly Configuration _config; private readonly ResourceTreeViewer _viewer; - public OnScreenTab(Configuration config, ResourceTreeFactory treeFactory, ChangedItemDrawer changedItemDrawer) + public OnScreenTab(ResourceTreeViewerFactory resourceTreeViewerFactory) { - _config = config; - _viewer = new ResourceTreeViewer(_config, treeFactory, changedItemDrawer, 0, delegate { }, delegate { }); + _viewer = resourceTreeViewerFactory.Create(0, delegate { }, delegate { }); } public ReadOnlySpan Label From c7046ec0069bef1ee9d98602589740ed0d3a74bb Mon Sep 17 00:00:00 2001 From: Exter-N Date: Fri, 31 May 2024 01:05:05 +0200 Subject: [PATCH 3/4] ResourceTree: Add name/path filter --- Penumbra/Interop/ResourceTree/ResourceNode.cs | 2 - .../ResourceTree/ResourceTreeFactory.cs | 3 - .../UI/AdvancedWindow/ResourceTreeViewer.cs | 74 +++++++++++++++---- 3 files changed, 60 insertions(+), 19 deletions(-) diff --git a/Penumbra/Interop/ResourceTree/ResourceNode.cs b/Penumbra/Interop/ResourceTree/ResourceNode.cs index e74edb91..9c911791 100644 --- a/Penumbra/Interop/ResourceTree/ResourceNode.cs +++ b/Penumbra/Interop/ResourceTree/ResourceNode.cs @@ -10,7 +10,6 @@ public class ResourceNode : ICloneable public string? Name; public string? FallbackName; public ChangedItemIcon Icon; - public ChangedItemIcon DescendentIcons; public readonly ResourceType Type; public readonly nint ObjectAddress; public readonly nint ResourceHandle; @@ -53,7 +52,6 @@ public class ResourceNode : ICloneable Name = other.Name; FallbackName = other.FallbackName; Icon = other.Icon; - DescendentIcons = other.DescendentIcons; Type = other.Type; ObjectAddress = other.ObjectAddress; ResourceHandle = other.ResourceHandle; diff --git a/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs b/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs index a722e344..5a190e52 100644 --- a/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs +++ b/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs @@ -116,9 +116,6 @@ public class ResourceTreeFactory( { if (node.Name == parent?.Name) node.Name = null; - - if (parent != null) - parent.DescendentIcons |= node.Icon | node.DescendentIcons; }); } diff --git a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs index c0c49e47..5f376b26 100644 --- a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs +++ b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs @@ -24,9 +24,12 @@ public class ResourceTreeViewer private readonly Action _drawActions; private readonly HashSet _unfolded; + private readonly Dictionary _filterCache; + private TreeCategory _categoryFilter; private ChangedItemDrawer.ChangedItemIcon _typeFilter; private string _nameFilter; + private string _nodeFilter; private Task? _task; @@ -40,11 +43,14 @@ public class ResourceTreeViewer _actionCapacity = actionCapacity; _onRefresh = onRefresh; _drawActions = drawActions; - _unfolded = new HashSet(); + _unfolded = []; + + _filterCache = []; _categoryFilter = AllCategories; _typeFilter = ChangedItemDrawer.AllFlags; _nameFilter = string.Empty; + _nodeFilter = string.Empty; } public void Draw() @@ -107,7 +113,7 @@ public class ResourceTreeViewer (_actionCapacity - 1) * 3 * ImGuiHelpers.GlobalScale + _actionCapacity * ImGui.GetFrameHeight()); ImGui.TableHeadersRow(); - DrawNodes(tree.Nodes, 0, unchecked(tree.DrawObjectAddress * 31)); + DrawNodes(tree.Nodes, 0, unchecked(tree.DrawObjectAddress * 31), 0); } } } @@ -140,14 +146,22 @@ public class ResourceTreeViewer ImGui.SameLine(0, checkPadding); + var filterChanged = false; ImGui.SetCursorPosY(ImGui.GetCursorPosY() - yOffset); using (ImRaii.Child("##typeFilter", new Vector2(ImGui.GetContentRegionAvail().X, ChangedItemDrawer.TypeFilterIconSize.Y))) - _changedItemDrawer.DrawTypeFilter(ref _typeFilter); + filterChanged |= _changedItemDrawer.DrawTypeFilter(ref _typeFilter); - ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - checkSpacing - ImGui.GetFrameHeightWithSpacing()); - ImGui.InputTextWithHint("##TreeNameFilter", "Filter by Character/Entity Name...", ref _nameFilter, 128); + var fieldWidth = (ImGui.GetContentRegionAvail().X - checkSpacing * 2.0f - ImGui.GetFrameHeightWithSpacing()) / 2.0f; + ImGui.SetNextItemWidth(fieldWidth); + filterChanged |= ImGui.InputTextWithHint("##TreeNameFilter", "Filter by Character/Entity Name...", ref _nameFilter, 128); + ImGui.SameLine(0, checkSpacing); + ImGui.SetNextItemWidth(fieldWidth); + filterChanged |= ImGui.InputTextWithHint("##NodeFilter", "Filter by Item/Part Name or Path...", ref _nodeFilter, 128); ImGui.SameLine(0, checkSpacing); _incognito.DrawToggle(); + + if (filterChanged) + _filterCache.Clear(); } private Task RefreshCharacterList() @@ -161,36 +175,68 @@ public class ResourceTreeViewer } finally { + _filterCache.Clear(); _unfolded.Clear(); _onRefresh(); } }); - private void DrawNodes(IEnumerable resourceNodes, int level, nint pathHash) + private void DrawNodes(IEnumerable resourceNodes, int level, nint pathHash, ChangedItemDrawer.ChangedItemIcon parentFilterIcon) { var debugMode = _config.DebugMode; var frameHeight = ImGui.GetFrameHeight(); var cellHeight = _actionCapacity > 0 ? frameHeight : 0.0f; - NodeVisibility GetNodeVisibility(ResourceNode node) + bool MatchesFilter(ResourceNode node, ChangedItemDrawer.ChangedItemIcon filterIcon) + { + if (!_typeFilter.HasFlag(filterIcon)) + return false; + + if (_nodeFilter.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)); + } + + NodeVisibility CalculateNodeVisibility(nint nodePathHash, ResourceNode node, ChangedItemDrawer.ChangedItemIcon parentFilterIcon) { if (node.Internal && !debugMode) return NodeVisibility.Hidden; - if (_typeFilter.HasFlag(node.Icon)) + var filterIcon = node.Icon != 0 ? node.Icon : parentFilterIcon; + if (MatchesFilter(node, filterIcon)) return NodeVisibility.Visible; - if ((_typeFilter & node.DescendentIcons) != 0) - return NodeVisibility.DescendentsOnly; + + foreach (var child in node.Children) + { + if (GetNodeVisibility(unchecked(nodePathHash * 31 + child.ResourceHandle), child, filterIcon) != NodeVisibility.Hidden) + return NodeVisibility.DescendentsOnly; + } return NodeVisibility.Hidden; } + NodeVisibility GetNodeVisibility(nint nodePathHash, ResourceNode node, ChangedItemDrawer.ChangedItemIcon parentFilterIcon) + { + if (!_filterCache.TryGetValue(nodePathHash, out var visibility)) + { + visibility = CalculateNodeVisibility(nodePathHash, node, parentFilterIcon); + _filterCache.Add(nodePathHash, visibility); + } + return visibility; + } + string GetAdditionalDataSuffix(ByteString data) => !debugMode || data.IsEmpty ? string.Empty : $"\n\nAdditional Data: {data}"; foreach (var (resourceNode, index) in resourceNodes.WithIndex()) { - var visibility = GetNodeVisibility(resourceNode); + var nodePathHash = unchecked(pathHash + resourceNode.ResourceHandle); + + var visibility = GetNodeVisibility(nodePathHash, resourceNode, parentFilterIcon); if (visibility == NodeVisibility.Hidden) continue; @@ -199,14 +245,14 @@ public class ResourceTreeViewer using var mutedColor = ImRaii.PushColor(ImGuiCol.Text, textColorInternal, resourceNode.Internal); - var nodePathHash = unchecked(pathHash + resourceNode.ResourceHandle); + var filterIcon = resourceNode.Icon != 0 ? resourceNode.Icon : parentFilterIcon; using var id = ImRaii.PushId(index); ImGui.TableNextColumn(); var unfolded = _unfolded.Contains(nodePathHash); using (var indent = ImRaii.PushIndent(level)) { - var hasVisibleChildren = resourceNode.Children.Any(child => GetNodeVisibility(child) != NodeVisibility.Hidden); + var hasVisibleChildren = resourceNode.Children.Any(child => GetNodeVisibility(unchecked(nodePathHash * 31 + child.ResourceHandle), child, filterIcon) != NodeVisibility.Hidden); var unfoldable = hasVisibleChildren && visibility != NodeVisibility.DescendentsOnly; if (unfoldable) { @@ -291,7 +337,7 @@ public class ResourceTreeViewer } if (unfolded) - DrawNodes(resourceNode.Children, level + 1, unchecked(nodePathHash * 31)); + DrawNodes(resourceNode.Children, level + 1, unchecked(nodePathHash * 31), filterIcon); } } From 3deda68eeca875ebd6403359f52877ec7d53b55a Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sun, 2 Jun 2024 01:03:51 +0200 Subject: [PATCH 4/4] Small updates. --- OtterGui | 2 +- Penumbra.GameData | 2 +- Penumbra/Services/StaticServiceManager.cs | 2 -- .../UI/AdvancedWindow/ResourceTreeViewer.cs | 2 +- .../ResourceTreeViewerFactory.cs | 3 +- Penumbra/UI/IncognitoService.cs | 29 +++++++++---------- .../ModsTab/Groups/ImcModGroupEditDrawer.cs | 2 +- .../ModsTab/Groups/MultiModGroupEditDrawer.cs | 2 +- .../Groups/SingleModGroupEditDrawer.cs | 2 +- 9 files changed, 21 insertions(+), 25 deletions(-) diff --git a/OtterGui b/OtterGui index 0b5afffd..becacbca 160000 --- a/OtterGui +++ b/OtterGui @@ -1 +1 @@ -Subproject commit 0b5afffda19d3e16aec9e8682d18c8f11f67f1c6 +Subproject commit becacbca4f35595d16ff40dc9639cfa24be3461f diff --git a/Penumbra.GameData b/Penumbra.GameData index 29b71cf7..fed687b5 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit 29b71cf7b3cc68995d38f0954fa38c4b9500a81d +Subproject commit fed687b536b7c709484db251b690b8821c5ef403 diff --git a/Penumbra/Services/StaticServiceManager.cs b/Penumbra/Services/StaticServiceManager.cs index 146d7ee0..0c6648ba 100644 --- a/Penumbra/Services/StaticServiceManager.cs +++ b/Penumbra/Services/StaticServiceManager.cs @@ -149,7 +149,6 @@ public static class StaticServiceManager private static ServiceManager AddInterface(this ServiceManager services) => services.AddSingleton() .AddSingleton() - .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() @@ -182,7 +181,6 @@ public static class StaticServiceManager .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() .AddSingleton(p => new Diagnostics(p)); private static ServiceManager AddModEditor(this ServiceManager services) diff --git a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs index 5f376b26..7315f136 100644 --- a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs +++ b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs @@ -158,7 +158,7 @@ public class ResourceTreeViewer ImGui.SetNextItemWidth(fieldWidth); filterChanged |= ImGui.InputTextWithHint("##NodeFilter", "Filter by Item/Part Name or Path...", ref _nodeFilter, 128); ImGui.SameLine(0, checkSpacing); - _incognito.DrawToggle(); + _incognito.DrawToggle(ImGui.GetFrameHeightWithSpacing()); if (filterChanged) _filterCache.Clear(); diff --git a/Penumbra/UI/AdvancedWindow/ResourceTreeViewerFactory.cs b/Penumbra/UI/AdvancedWindow/ResourceTreeViewerFactory.cs index 91dab6cb..ea64c0bf 100644 --- a/Penumbra/UI/AdvancedWindow/ResourceTreeViewerFactory.cs +++ b/Penumbra/UI/AdvancedWindow/ResourceTreeViewerFactory.cs @@ -1,3 +1,4 @@ +using OtterGui.Services; using Penumbra.Interop.ResourceTree; namespace Penumbra.UI.AdvancedWindow; @@ -6,7 +7,7 @@ public class ResourceTreeViewerFactory( Configuration config, ResourceTreeFactory treeFactory, ChangedItemDrawer changedItemDrawer, - IncognitoService incognito) + IncognitoService incognito) : IService { public ResourceTreeViewer Create(int actionCapacity, Action onRefresh, Action drawActions) => new(config, treeFactory, changedItemDrawer, incognito, actionCapacity, onRefresh, drawActions); diff --git a/Penumbra/UI/IncognitoService.cs b/Penumbra/UI/IncognitoService.cs index d4b1828f..d58ea1ec 100644 --- a/Penumbra/UI/IncognitoService.cs +++ b/Penumbra/UI/IncognitoService.cs @@ -1,29 +1,26 @@ -using Dalamud.Interface.Utility; using Dalamud.Interface; -using ImGuiNET; -using OtterGui; using Penumbra.UI.Classes; using OtterGui.Raii; +using OtterGui.Services; +using OtterGui.Text; namespace Penumbra.UI; -public class IncognitoService(TutorialService tutorial) +public class IncognitoService(TutorialService tutorial) : IService { public bool IncognitoMode; - public void DrawToggle(float? buttonWidth = null) + public void DrawToggle(float width) { - using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale); - using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.FolderExpanded.Value()) - .Push(ImGuiCol.Border, ColorId.FolderExpanded.Value()); - if (ImGuiUtil.DrawDisabledButton( - $"{(IncognitoMode ? FontAwesomeIcon.Eye : FontAwesomeIcon.EyeSlash).ToIconString()}###IncognitoMode", - new Vector2(buttonWidth ?? ImGui.GetFrameHeightWithSpacing(), ImGui.GetFrameHeight()), string.Empty, false, true)) - IncognitoMode = !IncognitoMode; - var hovered = ImGui.IsItemHovered(); + var color = ColorId.FolderExpanded.Value(); + using (ImRaii.PushFrameBorder(ImUtf8.GlobalScale, color)) + { + var tt = IncognitoMode ? "Toggle incognito mode off."u8 : "Toggle incognito mode on."u8; + var icon = IncognitoMode ? FontAwesomeIcon.EyeSlash : FontAwesomeIcon.Eye; + if (ImUtf8.IconButton(icon, tt, new Vector2(width, ImUtf8.FrameHeight), false, color)) + IncognitoMode = !IncognitoMode; + } + tutorial.OpenTutorial(BasicTutorialSteps.Incognito); - color.Pop(2); - if (hovered) - ImGui.SetTooltip(IncognitoMode ? "Toggle incognito mode off." : "Toggle incognito mode on."); } } diff --git a/Penumbra/UI/ModsTab/Groups/ImcModGroupEditDrawer.cs b/Penumbra/UI/ModsTab/Groups/ImcModGroupEditDrawer.cs index b10f123c..b129d275 100644 --- a/Penumbra/UI/ModsTab/Groups/ImcModGroupEditDrawer.cs +++ b/Penumbra/UI/ModsTab/Groups/ImcModGroupEditDrawer.cs @@ -118,7 +118,7 @@ public readonly struct ImcModGroupEditDrawer(ModGroupEditDrawer editor, ImcModGr : validName ? "Add a new option to this group."u8 : "Please enter a name for the new option."u8; - if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, !validName || dis)) + if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, default, !validName || dis)) { editor.ModManager.OptionEditor.ImcEditor.AddOption(group, cache, name); editor.NewOptionName = null; diff --git a/Penumbra/UI/ModsTab/Groups/MultiModGroupEditDrawer.cs b/Penumbra/UI/ModsTab/Groups/MultiModGroupEditDrawer.cs index e6701a03..f0275853 100644 --- a/Penumbra/UI/ModsTab/Groups/MultiModGroupEditDrawer.cs +++ b/Penumbra/UI/ModsTab/Groups/MultiModGroupEditDrawer.cs @@ -54,7 +54,7 @@ public readonly struct MultiModGroupEditDrawer(ModGroupEditDrawer editor, MultiM var validName = name.Length > 0; if (ImUtf8.IconButton(FontAwesomeIcon.Plus, validName ? "Add a new option to this group."u8 - : "Please enter a name for the new option."u8, !validName)) + : "Please enter a name for the new option."u8, default, !validName)) { editor.ModManager.OptionEditor.MultiEditor.AddOption(group, name); editor.NewOptionName = null; diff --git a/Penumbra/UI/ModsTab/Groups/SingleModGroupEditDrawer.cs b/Penumbra/UI/ModsTab/Groups/SingleModGroupEditDrawer.cs index 75fbc63a..be2dbd73 100644 --- a/Penumbra/UI/ModsTab/Groups/SingleModGroupEditDrawer.cs +++ b/Penumbra/UI/ModsTab/Groups/SingleModGroupEditDrawer.cs @@ -59,7 +59,7 @@ public readonly struct SingleModGroupEditDrawer(ModGroupEditDrawer editor, Singl var validName = name.Length > 0; if (ImUtf8.IconButton(FontAwesomeIcon.Plus, validName ? "Add a new option to this group."u8 - : "Please enter a name for the new option."u8, !validName)) + : "Please enter a name for the new option."u8, default, !validName)) { editor.ModManager.OptionEditor.SingleEditor.AddOption(group, name); editor.NewOptionName = null;