From 85500f0e9d89cda2e26e00730b28409a82d883db Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sat, 18 Nov 2023 13:15:33 +0100 Subject: [PATCH] Improve Multi Mod selection. --- Penumbra/Services/FilenameService.cs | 28 ++---- Penumbra/Services/ServiceManager.cs | 1 + Penumbra/UI/ModsTab/ModPanel.cs | 51 ++--------- Penumbra/UI/ModsTab/MultiModPanel.cs | 125 +++++++++++++++++++++++++++ 4 files changed, 139 insertions(+), 66 deletions(-) create mode 100644 Penumbra/UI/ModsTab/MultiModPanel.cs diff --git a/Penumbra/Services/FilenameService.cs b/Penumbra/Services/FilenameService.cs index e525c1f4..cf99c6c8 100644 --- a/Penumbra/Services/FilenameService.cs +++ b/Penumbra/Services/FilenameService.cs @@ -5,26 +5,15 @@ using Penumbra.Mods; namespace Penumbra.Services; -public class FilenameService +public class FilenameService(DalamudPluginInterface pi) { - public readonly string ConfigDirectory; - public readonly string CollectionDirectory; - public readonly string LocalDataDirectory; - public readonly string ConfigFile; - public readonly string EphemeralConfigFile; - public readonly string FilesystemFile; - public readonly string ActiveCollectionsFile; - - public FilenameService(DalamudPluginInterface pi) - { - ConfigDirectory = pi.ConfigDirectory.FullName; - CollectionDirectory = Path.Combine(pi.ConfigDirectory.FullName, "collections"); - LocalDataDirectory = Path.Combine(pi.ConfigDirectory.FullName, "mod_data"); - ConfigFile = pi.ConfigFile.FullName; - FilesystemFile = Path.Combine(pi.ConfigDirectory.FullName, "sort_order.json"); - ActiveCollectionsFile = Path.Combine(pi.ConfigDirectory.FullName, "active_collections.json"); - EphemeralConfigFile = Path.Combine(pi.ConfigDirectory.FullName, "ephemeral_config.json"); - } + public readonly string ConfigDirectory = pi.ConfigDirectory.FullName; + public readonly string CollectionDirectory = Path.Combine(pi.ConfigDirectory.FullName, "collections"); + public readonly string LocalDataDirectory = Path.Combine(pi.ConfigDirectory.FullName, "mod_data"); + public readonly string ConfigFile = pi.ConfigFile.FullName; + public readonly string EphemeralConfigFile = Path.Combine(pi.ConfigDirectory.FullName, "ephemeral_config.json"); + public readonly string FilesystemFile = Path.Combine(pi.ConfigDirectory.FullName, "sort_order.json"); + public readonly string ActiveCollectionsFile = Path.Combine(pi.ConfigDirectory.FullName, "active_collections.json"); /// Obtain the path of a collection file given its name. public string CollectionFile(ModCollection collection) @@ -34,7 +23,6 @@ public class FilenameService public string CollectionFile(string collectionName) => Path.Combine(CollectionDirectory, $"{collectionName}.json"); - /// Obtain the path of the local data file given a mod directory. Returns an empty string if the mod is temporary. public string LocalDataFile(Mod mod) => LocalDataFile(mod.ModPath.FullName); diff --git a/Penumbra/Services/ServiceManager.cs b/Penumbra/Services/ServiceManager.cs index 227f65d7..73be8834 100644 --- a/Penumbra/Services/ServiceManager.cs +++ b/Penumbra/Services/ServiceManager.cs @@ -149,6 +149,7 @@ public static class ServiceManager .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() diff --git a/Penumbra/UI/ModsTab/ModPanel.cs b/Penumbra/UI/ModsTab/ModPanel.cs index 59c9d279..15961ff3 100644 --- a/Penumbra/UI/ModsTab/ModPanel.cs +++ b/Penumbra/UI/ModsTab/ModPanel.cs @@ -1,26 +1,24 @@ -using Dalamud.Interface; using Dalamud.Plugin; -using ImGuiNET; -using OtterGui; -using OtterGui.Raii; using Penumbra.Mods; -using Penumbra.Mods.Manager; using Penumbra.UI.AdvancedWindow; namespace Penumbra.UI.ModsTab; public class ModPanel : IDisposable { + private readonly MultiModPanel _multiModPanel; private readonly ModFileSystemSelector _selector; private readonly ModEditWindow _editWindow; private readonly ModPanelHeader _header; private readonly ModPanelTabBar _tabs; - public ModPanel(DalamudPluginInterface pi, ModFileSystemSelector selector, ModEditWindow editWindow, ModPanelTabBar tabs) + public ModPanel(DalamudPluginInterface pi, ModFileSystemSelector selector, ModEditWindow editWindow, ModPanelTabBar tabs, + MultiModPanel multiModPanel) { _selector = selector; _editWindow = editWindow; _tabs = tabs; + _multiModPanel = multiModPanel; _header = new ModPanelHeader(pi); _selector.SelectionChanged += OnSelectionChange; } @@ -29,7 +27,7 @@ public class ModPanel : IDisposable { if (!_valid) { - DrawMultiSelection(); + _multiModPanel.Draw(); return; } @@ -43,45 +41,6 @@ public class ModPanel : IDisposable _header.Dispose(); } - private void DrawMultiSelection() - { - if (_selector.SelectedPaths.Count == 0) - return; - - var sizeType = ImGui.GetFrameHeight(); - var availableSizePercent = (ImGui.GetContentRegionAvail().X - sizeType - 4 * ImGui.GetStyle().CellPadding.X) / 100; - var sizeMods = availableSizePercent * 35; - var sizeFolders = availableSizePercent * 65; - - ImGui.NewLine(); - ImGui.TextUnformatted("Currently Selected Objects"); - ImGui.Separator(); - using var table = ImRaii.Table("mods", 3, ImGuiTableFlags.RowBg); - ImGui.TableSetupColumn("type", ImGuiTableColumnFlags.WidthFixed, sizeType); - ImGui.TableSetupColumn("mod", ImGuiTableColumnFlags.WidthFixed, sizeMods); - ImGui.TableSetupColumn("path", ImGuiTableColumnFlags.WidthFixed, sizeFolders); - - var i = 0; - foreach (var (fullName, path) in _selector.SelectedPaths.Select(p => (p.FullName(), p)) - .OrderBy(p => p.Item1, StringComparer.OrdinalIgnoreCase)) - { - using var id = ImRaii.PushId(i++); - ImGui.TableNextColumn(); - var icon = (path is ModFileSystem.Leaf ? FontAwesomeIcon.FileCircleMinus : FontAwesomeIcon.FolderMinus).ToIconString(); - if (ImGuiUtil.DrawDisabledButton(icon, new Vector2(sizeType), "Remove from selection.", false, true)) - _selector.RemovePathFromMultiselection(path); - - ImGui.TableNextColumn(); - ImGui.AlignTextToFramePadding(); - ImGui.TextUnformatted(path is ModFileSystem.Leaf l ? l.Value.Name : string.Empty); - - ImGui.TableNextColumn(); - ImGui.AlignTextToFramePadding(); - ImGui.TextUnformatted(fullName); - } - } - - private bool _valid; private Mod _mod = null!; diff --git a/Penumbra/UI/ModsTab/MultiModPanel.cs b/Penumbra/UI/ModsTab/MultiModPanel.cs new file mode 100644 index 00000000..1e4117ec --- /dev/null +++ b/Penumbra/UI/ModsTab/MultiModPanel.cs @@ -0,0 +1,125 @@ +using Dalamud.Interface; +using Dalamud.Interface.Utility; +using ImGuiNET; +using OtterGui; +using OtterGui.Raii; +using Penumbra.Mods; +using Penumbra.Mods.Manager; + +namespace Penumbra.UI.ModsTab; + +public class MultiModPanel(ModFileSystemSelector _selector, ModDataEditor _editor) +{ + public void Draw() + { + if (_selector.SelectedPaths.Count == 0) + return; + + ImGui.NewLine(); + DrawModList(); + DrawMultiTagger(); + } + + private void DrawModList() + { + using var tree = ImRaii.TreeNode("Currently Selected Objects", ImGuiTreeNodeFlags.DefaultOpen | ImGuiTreeNodeFlags.NoTreePushOnOpen); + ImGui.Separator(); + if (!tree) + return; + + var sizeType = ImGui.GetFrameHeight(); + var availableSizePercent = (ImGui.GetContentRegionAvail().X - sizeType - 4 * ImGui.GetStyle().CellPadding.X) / 100; + var sizeMods = availableSizePercent * 35; + var sizeFolders = availableSizePercent * 65; + + using (var table = ImRaii.Table("mods", 3, ImGuiTableFlags.RowBg)) + { + if (!table) + return; + + ImGui.TableSetupColumn("type", ImGuiTableColumnFlags.WidthFixed, sizeType); + ImGui.TableSetupColumn("mod", ImGuiTableColumnFlags.WidthFixed, sizeMods); + ImGui.TableSetupColumn("path", ImGuiTableColumnFlags.WidthFixed, sizeFolders); + + var i = 0; + foreach (var (fullName, path) in _selector.SelectedPaths.Select(p => (p.FullName(), p)) + .OrderBy(p => p.Item1, StringComparer.OrdinalIgnoreCase)) + { + using var id = ImRaii.PushId(i++); + ImGui.TableNextColumn(); + var icon = (path is ModFileSystem.Leaf ? FontAwesomeIcon.FileCircleMinus : FontAwesomeIcon.FolderMinus).ToIconString(); + if (ImGuiUtil.DrawDisabledButton(icon, new Vector2(sizeType), "Remove from selection.", false, true)) + _selector.RemovePathFromMultiselection(path); + + ImGui.TableNextColumn(); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted(path is ModFileSystem.Leaf l ? l.Value.Name : string.Empty); + + ImGui.TableNextColumn(); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted(fullName); + } + } + + ImGui.Separator(); + } + + private string _tag = string.Empty; + private readonly List _addMods = []; + private readonly List<(Mod, int)> _removeMods = []; + + private void DrawMultiTagger() + { + var width = ImGuiHelpers.ScaledVector2(150, 0); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted("Multi Tagger:"); + ImGui.SameLine(); + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - 2 * (width.X + ImGui.GetStyle().ItemSpacing.X)); + ImGui.InputTextWithHint("##tag", "Local Tag Name...", ref _tag, 128); + + UpdateTagCache(); + var label = _addMods.Count > 0 + ? $"Add to {_addMods.Count} Mods" + : "Add"; + var tooltip = _addMods.Count == 0 + ? _tag.Length == 0 + ? "No tag specified." + : $"All mods selected already contain the tag \"{_tag}\", either locally or as mod data." + : $"Add the tag \"{_tag}\" to {_addMods.Count} mods as a local tag:\n\n\t{string.Join("\n\t", _addMods.Select(m => m.Name.Text))}"; + ImGui.SameLine(); + if (ImGuiUtil.DrawDisabledButton(label, width, tooltip, _addMods.Count == 0)) + foreach (var mod in _addMods) + _editor.ChangeLocalTag(mod, mod.LocalTags.Count, _tag); + + label = _removeMods.Count > 0 + ? $"Remove from {_removeMods.Count} Mods" + : "Remove"; + tooltip = _removeMods.Count == 0 + ? _tag.Length == 0 + ? "No tag specified." + : $"No selected mod contains the tag \"{_tag}\" locally." + : $"Remove the local tag \"{_tag}\" from {_removeMods.Count} mods:\n\n\t{string.Join("\n\t", _removeMods.Select(m => m.Item1.Name.Text))}"; + ImGui.SameLine(); + if (ImGuiUtil.DrawDisabledButton(label, width, tooltip, _removeMods.Count == 0)) + foreach (var (mod, index) in _removeMods) + _editor.ChangeLocalTag(mod, index, string.Empty); + ImGui.Separator(); + } + + private void UpdateTagCache() + { + _addMods.Clear(); + _removeMods.Clear(); + if (_tag.Length == 0) + return; + + foreach (var leaf in _selector.SelectedPaths.OfType()) + { + var index = leaf.Value.LocalTags.IndexOf(_tag); + if (index >= 0) + _removeMods.Add((leaf.Value, index)); + else if (!leaf.Value.ModTags.Contains(_tag)) + _addMods.Add(leaf.Value); + } + } +}