diff --git a/Penumbra/Import/Textures/TextureDrawer.cs b/Penumbra/Import/Textures/TextureDrawer.cs index b077f6fd..fead989e 100644 --- a/Penumbra/Import/Textures/TextureDrawer.cs +++ b/Penumbra/Import/Textures/TextureDrawer.cs @@ -1,12 +1,16 @@ +using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Numerics; using Dalamud.Interface; using ImGuiNET; using Lumina.Data.Files; using OtterGui; using OtterGui.Raii; +using OtterGui.Widgets; using OtterTex; +using Penumbra.Mods; using Penumbra.String.Classes; using Penumbra.UI; using Penumbra.UI.Classes; @@ -33,41 +37,6 @@ public static class TextureDrawer } } - public static void PathSelectBox(TextureManager textures, Texture current, string label, string tooltip, IEnumerable<(string, bool)> paths, - int skipPrefix) - { - ImGui.SetNextItemWidth(-0.0001f); - var startPath = current.Path.Length > 0 ? current.Path : "Choose a modded texture from this mod here..."; - using var combo = ImRaii.Combo(label, startPath); - if (combo) - foreach (var ((path, game), idx) in paths.WithIndex()) - { - if (game) - { - if (!textures.GameFileExists(path)) - continue; - } - else if (!File.Exists(path)) - { - continue; - } - - using var id = ImRaii.PushId(idx); - using (var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.FolderExpanded.Value(), game)) - { - var p = game ? $"--> {path}" : path[skipPrefix..]; - if (ImGui.Selectable(p, path == startPath) && path != startPath) - current.Load(textures, path); - } - - ImGuiUtil.HoverTooltip(game - ? "This is a game path and refers to an unmanipulated file from your game data." - : "This is a path to a modded file on your file system."); - } - - ImGuiUtil.HoverTooltip(tooltip); - } - public static void PathInputBox(TextureManager textures, Texture current, ref string? tmpPath, string label, string hint, string tooltip, string startPath, FileDialogService fileDialog, string defaultModImportPath) { @@ -136,4 +105,53 @@ public static class TextureDrawer break; } } + + public sealed class PathSelectCombo : FilterComboCache<(string, bool)> + { + private int _skipPrefix = 0; + + public PathSelectCombo(TextureManager textures, ModEditor editor) + : base(() => CreateFiles(textures, editor)) + { } + + protected override string ToString((string, bool) obj) + => obj.Item1; + + protected override bool DrawSelectable(int globalIdx, bool selected) + { + var (path, game) = Items[globalIdx]; + bool ret; + using (var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.FolderExpanded.Value(), game)) + { + var equals = string.Equals(CurrentSelection.Item1, path, StringComparison.OrdinalIgnoreCase); + var p = game ? $"--> {path}" : path[_skipPrefix..]; + ret = ImGui.Selectable(p, selected) && !equals; + } + + ImGuiUtil.HoverTooltip(game + ? "This is a game path and refers to an unmanipulated file from your game data." + : "This is a path to a modded file on your file system."); + return ret; + } + + private static IReadOnlyList<(string, bool)> CreateFiles(TextureManager textures, ModEditor editor) + => editor.Files.Tex.SelectMany(f => f.SubModUsage.Select(p => (p.Item2.ToString(), true)) + .Prepend((f.File.FullName, false))) + .Where(p => p.Item2 ? textures.GameFileExists(p.Item1) : File.Exists(p.Item1)) + .ToList(); + + public bool Draw(string label, string tooltip, string current, int skipPrefix, out string newPath) + { + _skipPrefix = skipPrefix; + var startPath = current.Length > 0 ? current : "Choose a modded texture from this mod here..."; + if (!Draw(label, startPath, tooltip, -0.0001f, ImGui.GetTextLineHeightWithSpacing())) + { + newPath = current; + return false; + } + + newPath = CurrentSelection.Item1; + return true; + } + } } diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Textures.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Textures.cs index 07607d11..32cd400f 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Textures.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Textures.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using ImGuiNET; using OtterGui; using OtterGui.Raii; -using OtterGui.Tasks; using OtterTex; using Penumbra.Import.Textures; @@ -16,9 +15,10 @@ public partial class ModEditWindow { private readonly TextureManager _textures; - private readonly Texture _left = new(); - private readonly Texture _right = new(); - private readonly CombinedTexture _center; + private readonly Texture _left = new(); + private readonly Texture _right = new(); + private readonly CombinedTexture _center; + private readonly TextureDrawer.PathSelectCombo _textureSelectCombo; private bool _overlayCollapsed = true; private bool _addMipMaps = true; @@ -47,11 +47,10 @@ public partial class ModEditWindow TextureDrawer.PathInputBox(_textures, tex, ref tex.TmpPath, "##input", "Import Image...", "Can import game paths as well as your own files.", _mod!.ModPath.FullName, _fileDialog, _config.DefaultModImportPath); - var files = _editor.Files.Tex.SelectMany(f => f.SubModUsage.Select(p => (p.Item2.ToString(), true)) - .Prepend((f.File.FullName, false))); - TextureDrawer.PathSelectBox(_textures, tex, "##combo", - "Select the textures included in this mod on your drive or the ones they replace from the game files.", files, - _mod.ModPath.FullName.Length + 1); + if (_textureSelectCombo.Draw("##combo", + "Select the textures included in this mod on your drive or the ones they replace from the game files.", tex.Path, + _mod.ModPath.FullName.Length + 1, out var newPath) && newPath != tex.Path) + tex.Load(_textures, newPath); if (tex == _left) _center.DrawMatrixInputLeft(size.X); diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs index a36dcf14..8b870937 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs @@ -520,22 +520,22 @@ public partial class ModEditWindow : Window, IDisposable public ModEditWindow(PerformanceTracker performance, FileDialogService fileDialog, ItemSwapTab itemSwapTab, IDataManager gameData, Configuration config, ModEditor editor, ResourceTreeFactory resourceTreeFactory, MetaFileManager metaFileManager, - StainService stainService, ActiveCollections activeCollections, UiBuilder uiBuilder, DalamudServices dalamud, ModMergeTab modMergeTab, + StainService stainService, ActiveCollections activeCollections, DalamudServices dalamud, ModMergeTab modMergeTab, CommunicatorService communicator, TextureManager textures) : base(WindowBaseLabel) { - _performance = performance; - _itemSwapTab = itemSwapTab; - _config = config; - _editor = editor; - _metaFileManager = metaFileManager; - _stainService = stainService; - _activeCollections = activeCollections; - _dalamud = dalamud; - _modMergeTab = modMergeTab; - _communicator = communicator; - _textures = textures; - _fileDialog = fileDialog; + _performance = performance; + _itemSwapTab = itemSwapTab; + _config = config; + _editor = editor; + _metaFileManager = metaFileManager; + _stainService = stainService; + _activeCollections = activeCollections; + _dalamud = dalamud; + _modMergeTab = modMergeTab; + _communicator = communicator; + _textures = textures; + _fileDialog = fileDialog; _materialTab = new FileEditor(this, gameData, config, _fileDialog, "Materials", ".mtrl", () => _editor.Files.Mtrl, DrawMaterialPanel, () => _mod?.ModPath.FullName ?? string.Empty, bytes => new MtrlTab(this, new MtrlFile(bytes))); @@ -544,8 +544,9 @@ public partial class ModEditWindow : Window, IDisposable _shaderPackageTab = new FileEditor(this, gameData, config, _fileDialog, "Shaders", ".shpk", () => _editor.Files.Shpk, DrawShaderPackagePanel, () => _mod?.ModPath.FullName ?? string.Empty, bytes => new ShpkTab(_fileDialog, bytes)); - _center = new CombinedTexture(_left, _right); - _quickImportViewer = new ResourceTreeViewer(_config, resourceTreeFactory, 2, OnQuickImportRefresh, DrawQuickImportActions); + _center = new CombinedTexture(_left, _right); + _textureSelectCombo = new TextureDrawer.PathSelectCombo(textures, editor); + _quickImportViewer = new ResourceTreeViewer(_config, resourceTreeFactory, 2, OnQuickImportRefresh, DrawQuickImportActions); _communicator.ModPathChanged.Subscribe(OnModPathChanged, ModPathChanged.Priority.ModEditWindow); }