mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-23 17:09:22 +01:00
Merge branch 'refs/heads/AeAstralis/tagging'
This commit is contained in:
commit
5b9309a311
8 changed files with 317 additions and 102 deletions
|
|
@ -45,19 +45,19 @@ public sealed class Mod : IMod
|
||||||
public string Description { get; internal set; } = string.Empty;
|
public string Description { get; internal set; } = string.Empty;
|
||||||
public string Version { get; internal set; } = string.Empty;
|
public string Version { get; internal set; } = string.Empty;
|
||||||
public string Website { get; internal set; } = string.Empty;
|
public string Website { get; internal set; } = string.Empty;
|
||||||
public IReadOnlyList<string> ModTags { get; internal set; } = Array.Empty<string>();
|
public IReadOnlyList<string> ModTags { get; internal set; } = [];
|
||||||
|
|
||||||
|
|
||||||
// Local Data
|
// Local Data
|
||||||
public long ImportDate { get; internal set; } = DateTimeOffset.UnixEpoch.ToUnixTimeMilliseconds();
|
public long ImportDate { get; internal set; } = DateTimeOffset.UnixEpoch.ToUnixTimeMilliseconds();
|
||||||
public IReadOnlyList<string> LocalTags { get; internal set; } = Array.Empty<string>();
|
public IReadOnlyList<string> LocalTags { get; internal set; } = [];
|
||||||
public string Note { get; internal set; } = string.Empty;
|
public string Note { get; internal set; } = string.Empty;
|
||||||
public bool Favorite { get; internal set; } = false;
|
public bool Favorite { get; internal set; } = false;
|
||||||
|
|
||||||
|
|
||||||
// Options
|
// Options
|
||||||
public readonly SubMod Default;
|
public readonly SubMod Default;
|
||||||
public readonly List<IModGroup> Groups = new();
|
public readonly List<IModGroup> Groups = [];
|
||||||
|
|
||||||
ISubMod IMod.Default
|
ISubMod IMod.Default
|
||||||
=> Default;
|
=> Default;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ public class FilenameService(DalamudPluginInterface pi) : IService
|
||||||
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 FilesystemFile = Path.Combine(pi.ConfigDirectory.FullName, "sort_order.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");
|
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 CrashHandlerExe =
|
public readonly string CrashHandlerExe =
|
||||||
Path.Combine(pi.AssemblyLocation.DirectoryName!, "Penumbra.CrashHandler.exe");
|
Path.Combine(pi.AssemblyLocation.DirectoryName!, "Penumbra.CrashHandler.exe");
|
||||||
|
|
@ -43,7 +44,7 @@ public class FilenameService(DalamudPluginInterface pi) : IService
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var directory = new DirectoryInfo(CollectionDirectory);
|
var directory = new DirectoryInfo(CollectionDirectory);
|
||||||
return directory.Exists ? directory.EnumerateFiles("*.json") : Array.Empty<FileInfo>();
|
return directory.Exists ? directory.EnumerateFiles("*.json") : [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,7 +54,7 @@ public class FilenameService(DalamudPluginInterface pi) : IService
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var directory = new DirectoryInfo(LocalDataDirectory);
|
var directory = new DirectoryInfo(LocalDataDirectory);
|
||||||
return directory.Exists ? directory.EnumerateFiles("*.json") : Array.Empty<FileInfo>();
|
return directory.Exists ? directory.EnumerateFiles("*.json") : [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ public enum ColorId
|
||||||
ResTreePlayer,
|
ResTreePlayer,
|
||||||
ResTreeNetworked,
|
ResTreeNetworked,
|
||||||
ResTreeNonNetworked,
|
ResTreeNonNetworked,
|
||||||
|
PredefinedTagAdd,
|
||||||
|
PredefinedTagRemove,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Colors
|
public static class Colors
|
||||||
|
|
@ -73,6 +75,8 @@ public static class Colors
|
||||||
ColorId.ResTreePlayer => ( 0xFFC0FFC0, "On-Screen: Other Players", "Other players and what they own, in the On-Screen tab." ),
|
ColorId.ResTreePlayer => ( 0xFFC0FFC0, "On-Screen: Other Players", "Other players and what they own, in the On-Screen tab." ),
|
||||||
ColorId.ResTreeNetworked => ( 0xFFFFFFFF, "On-Screen: Non-Players (Networked)", "Non-player entities handled by the game server, in the On-Screen tab." ),
|
ColorId.ResTreeNetworked => ( 0xFFFFFFFF, "On-Screen: Non-Players (Networked)", "Non-player entities handled by the game server, in the On-Screen tab." ),
|
||||||
ColorId.ResTreeNonNetworked => ( 0xFFC0C0FF, "On-Screen: Non-Players (Local)", "Non-player entities handled locally, in the On-Screen tab." ),
|
ColorId.ResTreeNonNetworked => ( 0xFFC0C0FF, "On-Screen: Non-Players (Local)", "Non-player entities handled locally, in the On-Screen tab." ),
|
||||||
|
ColorId.PredefinedTagAdd => ( 0xFF44AA44, "Predefined Tags: Add Tag", "A predefined tag that is not present on the current mod and can be added." ),
|
||||||
|
ColorId.PredefinedTagRemove => ( 0xFF2222AA, "Predefined Tags: Remove Tag", "A predefined tag that is already present on the current mod and can be removed." ),
|
||||||
_ => throw new ArgumentOutOfRangeException( nameof( color ), color, null ),
|
_ => throw new ArgumentOutOfRangeException( nameof( color ), color, null ),
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -7,21 +7,16 @@ using Penumbra.Mods.Manager;
|
||||||
|
|
||||||
namespace Penumbra.UI.ModsTab;
|
namespace Penumbra.UI.ModsTab;
|
||||||
|
|
||||||
public class ModPanelDescriptionTab : ITab
|
public class ModPanelDescriptionTab(
|
||||||
|
ModFileSystemSelector selector,
|
||||||
|
TutorialService tutorial,
|
||||||
|
ModManager modManager,
|
||||||
|
PredefinedTagManager predefinedTagsConfig)
|
||||||
|
: ITab
|
||||||
{
|
{
|
||||||
private readonly ModFileSystemSelector _selector;
|
|
||||||
private readonly TutorialService _tutorial;
|
|
||||||
private readonly ModManager _modManager;
|
|
||||||
private readonly TagButtons _localTags = new();
|
private readonly TagButtons _localTags = new();
|
||||||
private readonly TagButtons _modTags = new();
|
private readonly TagButtons _modTags = new();
|
||||||
|
|
||||||
public ModPanelDescriptionTab(ModFileSystemSelector selector, TutorialService tutorial, ModManager modManager)
|
|
||||||
{
|
|
||||||
_selector = selector;
|
|
||||||
_tutorial = tutorial;
|
|
||||||
_modManager = modManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlySpan<byte> Label
|
public ReadOnlySpan<byte> Label
|
||||||
=> "Description"u8;
|
=> "Description"u8;
|
||||||
|
|
||||||
|
|
@ -34,22 +29,29 @@ public class ModPanelDescriptionTab : ITab
|
||||||
ImGui.Dummy(ImGuiHelpers.ScaledVector2(2));
|
ImGui.Dummy(ImGuiHelpers.ScaledVector2(2));
|
||||||
|
|
||||||
ImGui.Dummy(ImGuiHelpers.ScaledVector2(2));
|
ImGui.Dummy(ImGuiHelpers.ScaledVector2(2));
|
||||||
|
var (predefinedTagsEnabled, predefinedTagButtonOffset) = predefinedTagsConfig.Count > 0
|
||||||
|
? (true, ImGui.GetFrameHeight() + ImGui.GetStyle().WindowPadding.X + (ImGui.GetScrollMaxY() > 0 ? ImGui.GetStyle().ScrollbarSize : 0))
|
||||||
|
: (false, 0);
|
||||||
var tagIdx = _localTags.Draw("Local Tags: ",
|
var tagIdx = _localTags.Draw("Local Tags: ",
|
||||||
"Custom tags you can set personally that will not be exported to the mod data but only set for you.\n"
|
"Custom tags you can set personally that will not be exported to the mod data but only set for you.\n"
|
||||||
+ "If the mod already contains a local tag in its own tags, the local tag will be ignored.", _selector.Selected!.LocalTags,
|
+ "If the mod already contains a local tag in its own tags, the local tag will be ignored.", selector.Selected!.LocalTags,
|
||||||
out var editedTag);
|
out var editedTag, rightEndOffset: predefinedTagButtonOffset);
|
||||||
_tutorial.OpenTutorial(BasicTutorialSteps.Tags);
|
tutorial.OpenTutorial(BasicTutorialSteps.Tags);
|
||||||
if (tagIdx >= 0)
|
if (tagIdx >= 0)
|
||||||
_modManager.DataEditor.ChangeLocalTag(_selector.Selected!, tagIdx, editedTag);
|
modManager.DataEditor.ChangeLocalTag(selector.Selected!, tagIdx, editedTag);
|
||||||
|
|
||||||
if (_selector.Selected!.ModTags.Count > 0)
|
if (predefinedTagsEnabled)
|
||||||
|
predefinedTagsConfig.DrawAddFromSharedTagsAndUpdateTags(selector.Selected!.LocalTags, selector.Selected!.ModTags, true,
|
||||||
|
selector.Selected!);
|
||||||
|
|
||||||
|
if (selector.Selected!.ModTags.Count > 0)
|
||||||
_modTags.Draw("Mod Tags: ", "Tags assigned by the mod creator and saved with the mod data. To edit these, look at Edit Mod.",
|
_modTags.Draw("Mod Tags: ", "Tags assigned by the mod creator and saved with the mod data. To edit these, look at Edit Mod.",
|
||||||
_selector.Selected!.ModTags, out var _, false,
|
selector.Selected!.ModTags, out _, false,
|
||||||
ImGui.CalcTextSize("Local ").X - ImGui.CalcTextSize("Mod ").X);
|
ImGui.CalcTextSize("Local ").X - ImGui.CalcTextSize("Mod ").X);
|
||||||
|
|
||||||
ImGui.Dummy(ImGuiHelpers.ScaledVector2(2));
|
ImGui.Dummy(ImGuiHelpers.ScaledVector2(2));
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
ImGuiUtil.TextWrapped(_selector.Selected!.Description);
|
ImGuiUtil.TextWrapped(selector.Selected!.Description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,17 +17,20 @@ using Penumbra.UI.AdvancedWindow;
|
||||||
|
|
||||||
namespace Penumbra.UI.ModsTab;
|
namespace Penumbra.UI.ModsTab;
|
||||||
|
|
||||||
public class ModPanelEditTab : ITab
|
public class ModPanelEditTab(
|
||||||
|
ModManager modManager,
|
||||||
|
ModFileSystemSelector selector,
|
||||||
|
ModFileSystem fileSystem,
|
||||||
|
Services.MessageService messager,
|
||||||
|
ModEditWindow editWindow,
|
||||||
|
ModEditor editor,
|
||||||
|
FilenameService filenames,
|
||||||
|
ModExportManager modExportManager,
|
||||||
|
Configuration config,
|
||||||
|
PredefinedTagManager predefinedTagManager)
|
||||||
|
: ITab
|
||||||
{
|
{
|
||||||
private readonly Services.MessageService _messager;
|
private readonly ModManager _modManager = modManager;
|
||||||
private readonly FilenameService _filenames;
|
|
||||||
private readonly ModManager _modManager;
|
|
||||||
private readonly ModExportManager _modExportManager;
|
|
||||||
private readonly ModFileSystem _fileSystem;
|
|
||||||
private readonly ModFileSystemSelector _selector;
|
|
||||||
private readonly ModEditWindow _editWindow;
|
|
||||||
private readonly ModEditor _editor;
|
|
||||||
private readonly Configuration _config;
|
|
||||||
|
|
||||||
private readonly TagButtons _modTags = new();
|
private readonly TagButtons _modTags = new();
|
||||||
|
|
||||||
|
|
@ -36,20 +39,6 @@ public class ModPanelEditTab : ITab
|
||||||
private ModFileSystem.Leaf _leaf = null!;
|
private ModFileSystem.Leaf _leaf = null!;
|
||||||
private Mod _mod = null!;
|
private Mod _mod = null!;
|
||||||
|
|
||||||
public ModPanelEditTab(ModManager modManager, ModFileSystemSelector selector, ModFileSystem fileSystem, Services.MessageService messager,
|
|
||||||
ModEditWindow editWindow, ModEditor editor, FilenameService filenames, ModExportManager modExportManager, Configuration config)
|
|
||||||
{
|
|
||||||
_modManager = modManager;
|
|
||||||
_selector = selector;
|
|
||||||
_fileSystem = fileSystem;
|
|
||||||
_messager = messager;
|
|
||||||
_editWindow = editWindow;
|
|
||||||
_editor = editor;
|
|
||||||
_filenames = filenames;
|
|
||||||
_modExportManager = modExportManager;
|
|
||||||
_config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlySpan<byte> Label
|
public ReadOnlySpan<byte> Label
|
||||||
=> "Edit Mod"u8;
|
=> "Edit Mod"u8;
|
||||||
|
|
||||||
|
|
@ -59,8 +48,8 @@ public class ModPanelEditTab : ITab
|
||||||
if (!child)
|
if (!child)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_leaf = _selector.SelectedLeaf!;
|
_leaf = selector.SelectedLeaf!;
|
||||||
_mod = _selector.Selected!;
|
_mod = selector.Selected!;
|
||||||
|
|
||||||
_cellPadding = ImGui.GetStyle().CellPadding with { X = 2 * UiHelpers.Scale };
|
_cellPadding = ImGui.GetStyle().CellPadding with { X = 2 * UiHelpers.Scale };
|
||||||
_itemSpacing = ImGui.GetStyle().CellPadding with { X = 4 * UiHelpers.Scale };
|
_itemSpacing = ImGui.GetStyle().CellPadding with { X = 4 * UiHelpers.Scale };
|
||||||
|
|
@ -72,21 +61,27 @@ public class ModPanelEditTab : ITab
|
||||||
if (Input.Text("Mod Path", Input.Path, Input.None, _leaf.FullName(), out var newPath, 256, UiHelpers.InputTextWidth.X))
|
if (Input.Text("Mod Path", Input.Path, Input.None, _leaf.FullName(), out var newPath, 256, UiHelpers.InputTextWidth.X))
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_fileSystem.RenameAndMove(_leaf, newPath);
|
fileSystem.RenameAndMove(_leaf, newPath);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_messager.NotificationMessage(e.Message, NotificationType.Warning, false);
|
messager.NotificationMessage(e.Message, NotificationType.Warning, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
UiHelpers.DefaultLineSpace();
|
UiHelpers.DefaultLineSpace();
|
||||||
|
var sharedTagsEnabled = predefinedTagManager.Count > 0;
|
||||||
|
var sharedTagButtonOffset = sharedTagsEnabled ? ImGui.GetFrameHeight() + ImGui.GetStyle().FramePadding.X : 0;
|
||||||
var tagIdx = _modTags.Draw("Mod Tags: ", "Edit tags by clicking them, or add new tags. Empty tags are removed.", _mod.ModTags,
|
var tagIdx = _modTags.Draw("Mod Tags: ", "Edit tags by clicking them, or add new tags. Empty tags are removed.", _mod.ModTags,
|
||||||
out var editedTag);
|
out var editedTag, rightEndOffset: sharedTagButtonOffset);
|
||||||
if (tagIdx >= 0)
|
if (tagIdx >= 0)
|
||||||
_modManager.DataEditor.ChangeModTag(_mod, tagIdx, editedTag);
|
_modManager.DataEditor.ChangeModTag(_mod, tagIdx, editedTag);
|
||||||
|
|
||||||
|
if (sharedTagsEnabled)
|
||||||
|
predefinedTagManager.DrawAddFromSharedTagsAndUpdateTags(selector.Selected!.LocalTags, selector.Selected!.ModTags, false,
|
||||||
|
selector.Selected!);
|
||||||
|
|
||||||
UiHelpers.DefaultLineSpace();
|
UiHelpers.DefaultLineSpace();
|
||||||
AddOptionGroup.Draw(_filenames, _modManager, _mod, _config.ReplaceNonAsciiOnImport);
|
AddOptionGroup.Draw(filenames, _modManager, _mod, config.ReplaceNonAsciiOnImport);
|
||||||
UiHelpers.DefaultLineSpace();
|
UiHelpers.DefaultLineSpace();
|
||||||
|
|
||||||
for (var groupIdx = 0; groupIdx < _mod.Groups.Count; ++groupIdx)
|
for (var groupIdx = 0; groupIdx < _mod.Groups.Count; ++groupIdx)
|
||||||
|
|
@ -134,11 +129,11 @@ public class ModPanelEditTab : ITab
|
||||||
{
|
{
|
||||||
if (ImGui.Button("Update Bibo Material", buttonSize))
|
if (ImGui.Button("Update Bibo Material", buttonSize))
|
||||||
{
|
{
|
||||||
_editor.LoadMod(_mod);
|
editor.LoadMod(_mod);
|
||||||
_editor.MdlMaterialEditor.ReplaceAllMaterials("bibo", "b");
|
editor.MdlMaterialEditor.ReplaceAllMaterials("bibo", "b");
|
||||||
_editor.MdlMaterialEditor.ReplaceAllMaterials("bibopube", "c");
|
editor.MdlMaterialEditor.ReplaceAllMaterials("bibopube", "c");
|
||||||
_editor.MdlMaterialEditor.SaveAllModels(_editor.Compactor);
|
editor.MdlMaterialEditor.SaveAllModels(editor.Compactor);
|
||||||
_editWindow.UpdateModels();
|
editWindow.UpdateModels();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuiUtil.HoverTooltip(
|
ImGuiUtil.HoverTooltip(
|
||||||
|
|
@ -150,7 +145,7 @@ public class ModPanelEditTab : ITab
|
||||||
|
|
||||||
private void BackupButtons(Vector2 buttonSize)
|
private void BackupButtons(Vector2 buttonSize)
|
||||||
{
|
{
|
||||||
var backup = new ModBackup(_modExportManager, _mod);
|
var backup = new ModBackup(modExportManager, _mod);
|
||||||
var tt = ModBackup.CreatingBackup
|
var tt = ModBackup.CreatingBackup
|
||||||
? "Already exporting a mod."
|
? "Already exporting a mod."
|
||||||
: backup.Exists
|
: backup.Exists
|
||||||
|
|
@ -161,16 +156,16 @@ public class ModPanelEditTab : ITab
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
tt = backup.Exists
|
tt = backup.Exists
|
||||||
? $"Delete existing mod export \"{backup.Name}\" (hold {_config.DeleteModModifier} while clicking)."
|
? $"Delete existing mod export \"{backup.Name}\" (hold {config.DeleteModModifier} while clicking)."
|
||||||
: $"Exported mod \"{backup.Name}\" does not exist.";
|
: $"Exported mod \"{backup.Name}\" does not exist.";
|
||||||
if (ImGuiUtil.DrawDisabledButton("Delete Export", buttonSize, tt, !backup.Exists || !_config.DeleteModModifier.IsActive()))
|
if (ImGuiUtil.DrawDisabledButton("Delete Export", buttonSize, tt, !backup.Exists || !config.DeleteModModifier.IsActive()))
|
||||||
backup.Delete();
|
backup.Delete();
|
||||||
|
|
||||||
tt = backup.Exists
|
tt = backup.Exists
|
||||||
? $"Restore mod from exported file \"{backup.Name}\" (hold {_config.DeleteModModifier} while clicking)."
|
? $"Restore mod from exported file \"{backup.Name}\" (hold {config.DeleteModModifier} while clicking)."
|
||||||
: $"Exported mod \"{backup.Name}\" does not exist.";
|
: $"Exported mod \"{backup.Name}\" does not exist.";
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImGuiUtil.DrawDisabledButton("Restore From Export", buttonSize, tt, !backup.Exists || !_config.DeleteModModifier.IsActive()))
|
if (ImGuiUtil.DrawDisabledButton("Restore From Export", buttonSize, tt, !backup.Exists || !config.DeleteModModifier.IsActive()))
|
||||||
backup.Restore(_modManager);
|
backup.Restore(_modManager);
|
||||||
if (backup.Exists)
|
if (backup.Exists)
|
||||||
{
|
{
|
||||||
|
|
@ -208,13 +203,13 @@ public class ModPanelEditTab : ITab
|
||||||
_delayedActions.Enqueue(() => DescriptionEdit.OpenPopup(_mod, Input.Description));
|
_delayedActions.Enqueue(() => DescriptionEdit.OpenPopup(_mod, Input.Description));
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
var fileExists = File.Exists(_filenames.ModMetaPath(_mod));
|
var fileExists = File.Exists(filenames.ModMetaPath(_mod));
|
||||||
var tt = fileExists
|
var tt = fileExists
|
||||||
? "Open the metadata json file in the text editor of your choice."
|
? "Open the metadata json file in the text editor of your choice."
|
||||||
: "The metadata json file does not exist.";
|
: "The metadata json file does not exist.";
|
||||||
if (ImGuiUtil.DrawDisabledButton($"{FontAwesomeIcon.FileExport.ToIconString()}##metaFile", UiHelpers.IconButtonSize, tt,
|
if (ImGuiUtil.DrawDisabledButton($"{FontAwesomeIcon.FileExport.ToIconString()}##metaFile", UiHelpers.IconButtonSize, tt,
|
||||||
!fileExists, true))
|
!fileExists, true))
|
||||||
Process.Start(new ProcessStartInfo(_filenames.ModMetaPath(_mod)) { UseShellExecute = true });
|
Process.Start(new ProcessStartInfo(filenames.ModMetaPath(_mod)) { UseShellExecute = true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Do some edits outside of iterations. </summary>
|
/// <summary> Do some edits outside of iterations. </summary>
|
||||||
|
|
@ -438,7 +433,7 @@ public class ModPanelEditTab : ITab
|
||||||
_delayedActions.Enqueue(() => DescriptionEdit.OpenPopup(_mod, groupIdx));
|
_delayedActions.Enqueue(() => DescriptionEdit.OpenPopup(_mod, groupIdx));
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
var fileName = _filenames.OptionGroupFile(_mod, groupIdx, _config.ReplaceNonAsciiOnImport);
|
var fileName = filenames.OptionGroupFile(_mod, groupIdx, config.ReplaceNonAsciiOnImport);
|
||||||
var fileExists = File.Exists(fileName);
|
var fileExists = File.Exists(fileName);
|
||||||
tt = fileExists
|
tt = fileExists
|
||||||
? $"Open the {group.Name} json file in the text editor of your choice."
|
? $"Open the {group.Name} json file in the text editor of your choice."
|
||||||
|
|
|
||||||
194
Penumbra/UI/PredefinedTagManager.cs
Normal file
194
Penumbra/UI/PredefinedTagManager.cs
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
|
using ImGuiNET;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using OtterGui;
|
||||||
|
using OtterGui.Classes;
|
||||||
|
using OtterGui.Raii;
|
||||||
|
using Penumbra.Mods.Manager;
|
||||||
|
using Penumbra.Services;
|
||||||
|
using Penumbra.UI.Classes;
|
||||||
|
using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
|
||||||
|
|
||||||
|
namespace Penumbra.UI;
|
||||||
|
|
||||||
|
public sealed class PredefinedTagManager : ISavable, IReadOnlyList<string>
|
||||||
|
{
|
||||||
|
public const int Version = 1;
|
||||||
|
|
||||||
|
public record struct TagData
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private readonly ModManager _modManager;
|
||||||
|
private readonly SaveService _saveService;
|
||||||
|
|
||||||
|
private bool _isListOpen = false;
|
||||||
|
private uint _enabledColor;
|
||||||
|
private uint _disabledColor;
|
||||||
|
|
||||||
|
private readonly SortedList<string, TagData> _predefinedTags = [];
|
||||||
|
|
||||||
|
public PredefinedTagManager(ModManager modManager, SaveService saveService)
|
||||||
|
{
|
||||||
|
_modManager = modManager;
|
||||||
|
_saveService = saveService;
|
||||||
|
Load();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ToFilename(FilenameService fileNames)
|
||||||
|
=> fileNames.PredefinedTagFile;
|
||||||
|
|
||||||
|
public void Save(StreamWriter writer)
|
||||||
|
{
|
||||||
|
using var jWriter = new JsonTextWriter(writer);
|
||||||
|
jWriter.Formatting = Formatting.Indented;
|
||||||
|
var jObj = new JObject()
|
||||||
|
{
|
||||||
|
["Version"] = Version,
|
||||||
|
["Tags"] = JObject.FromObject(_predefinedTags),
|
||||||
|
};
|
||||||
|
jObj.WriteTo(jWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save()
|
||||||
|
=> _saveService.DelaySave(this, TimeSpan.FromSeconds(5));
|
||||||
|
|
||||||
|
private void Load()
|
||||||
|
{
|
||||||
|
if (!File.Exists(_saveService.FileNames.PredefinedTagFile))
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var text = File.ReadAllText(_saveService.FileNames.PredefinedTagFile);
|
||||||
|
var jObj = JObject.Parse(text);
|
||||||
|
var version = jObj["Version"]?.ToObject<int>() ?? 0;
|
||||||
|
switch (version)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
var tags = jObj["Tags"]?.ToObject<Dictionary<string, TagData>>() ?? [];
|
||||||
|
foreach (var (tag, data) in tags)
|
||||||
|
_predefinedTags.TryAdd(tag, data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception($"Invalid version {version}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Penumbra.Messager.NotificationMessage(ex,
|
||||||
|
"Error reading predefined tags Configuration, reverting to default.",
|
||||||
|
"Error reading predefined tags Configuration", NotificationType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ChangeSharedTag(int tagIdx, string tag)
|
||||||
|
{
|
||||||
|
if (tagIdx < 0 || tagIdx > _predefinedTags.Count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (tagIdx != _predefinedTags.Count)
|
||||||
|
_predefinedTags.RemoveAt(tagIdx);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(tag))
|
||||||
|
_predefinedTags.TryAdd(tag, default);
|
||||||
|
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawAddFromSharedTagsAndUpdateTags(IReadOnlyCollection<string> localTags, IReadOnlyCollection<string> modTags, bool editLocal,
|
||||||
|
Mods.Mod mod)
|
||||||
|
{
|
||||||
|
DrawToggleButton();
|
||||||
|
if (!DrawList(localTags, modTags, editLocal, out var changedTag, out var index))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (editLocal)
|
||||||
|
_modManager.DataEditor.ChangeLocalTag(mod, index, changedTag);
|
||||||
|
else
|
||||||
|
_modManager.DataEditor.ChangeModTag(mod, index, changedTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawToggleButton()
|
||||||
|
{
|
||||||
|
ImGui.SameLine(ImGui.GetContentRegionMax().X
|
||||||
|
- ImGui.GetFrameHeight()
|
||||||
|
- (ImGui.GetScrollMaxY() > 0 ? ImGui.GetStyle().ItemInnerSpacing.X : 0));
|
||||||
|
using var color = ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.ButtonActive), _isListOpen);
|
||||||
|
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Tags.ToIconString(), new Vector2(ImGui.GetFrameHeight()),
|
||||||
|
"Add Predefined Tags...", false, true))
|
||||||
|
_isListOpen = !_isListOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DrawList(IReadOnlyCollection<string> localTags, IReadOnlyCollection<string> modTags, bool editLocal, out string changedTag,
|
||||||
|
out int changedIndex)
|
||||||
|
{
|
||||||
|
changedTag = string.Empty;
|
||||||
|
changedIndex = -1;
|
||||||
|
|
||||||
|
if (!_isListOpen)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ImGui.TextUnformatted("Predefined Tags");
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
var ret = false;
|
||||||
|
_enabledColor = ColorId.PredefinedTagAdd.Value();
|
||||||
|
_disabledColor = ColorId.PredefinedTagRemove.Value();
|
||||||
|
var (edited, others) = editLocal ? (localTags, modTags) : (modTags, localTags);
|
||||||
|
foreach (var (tag, idx) in _predefinedTags.Keys.WithIndex())
|
||||||
|
{
|
||||||
|
var tagIdx = edited.IndexOf(tag);
|
||||||
|
var inOther = tagIdx < 0 && others.IndexOf(tag) >= 0;
|
||||||
|
if (DrawColoredButton(tag, idx, tagIdx, inOther))
|
||||||
|
{
|
||||||
|
(changedTag, changedIndex) = tagIdx >= 0 ? (string.Empty, tagIdx) : (tag, edited.Count);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.NewLine();
|
||||||
|
ImGui.Separator();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DrawColoredButton(string buttonLabel, int index, int tagIdx, bool inOther)
|
||||||
|
{
|
||||||
|
using var id = ImRaii.PushId(index);
|
||||||
|
var buttonWidth = CalcTextButtonWidth(buttonLabel);
|
||||||
|
// Prevent adding a new tag past the right edge of the popup
|
||||||
|
if (buttonWidth + ImGui.GetStyle().ItemSpacing.X >= ImGui.GetContentRegionAvail().X)
|
||||||
|
ImGui.NewLine();
|
||||||
|
|
||||||
|
bool ret;
|
||||||
|
using (ImRaii.Disabled(inOther))
|
||||||
|
{
|
||||||
|
using var color = ImRaii.PushColor(ImGuiCol.Button, tagIdx >= 0 || inOther ? _disabledColor : _enabledColor);
|
||||||
|
ret = ImGui.Button(buttonLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inOther && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
||||||
|
ImGui.SetTooltip("This tag is already present in the other set of tags.");
|
||||||
|
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float CalcTextButtonWidth(string text)
|
||||||
|
=> ImGui.CalcTextSize(text).X + 2 * ImGui.GetStyle().FramePadding.X;
|
||||||
|
|
||||||
|
public IEnumerator<string> GetEnumerator()
|
||||||
|
=> _predefinedTags.Keys.GetEnumerator();
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
=> GetEnumerator();
|
||||||
|
|
||||||
|
public int Count
|
||||||
|
=> _predefinedTags.Count;
|
||||||
|
|
||||||
|
public string this[int index]
|
||||||
|
=> _predefinedTags.Keys[index];
|
||||||
|
}
|
||||||
|
|
@ -110,7 +110,7 @@ public class ModsTab(
|
||||||
var frameColor = ImGui.GetColorU32(ImGuiCol.FrameBg);
|
var frameColor = ImGui.GetColorU32(ImGuiCol.FrameBg);
|
||||||
using (var _ = ImRaii.Group())
|
using (var _ = ImRaii.Group())
|
||||||
{
|
{
|
||||||
using (var font = ImRaii.PushFont(UiBuilder.IconFont))
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
||||||
{
|
{
|
||||||
ImGuiUtil.DrawTextButton(FontAwesomeIcon.InfoCircle.ToIconString(), frameHeight, frameColor);
|
ImGuiUtil.DrawTextButton(FontAwesomeIcon.InfoCircle.ToIconString(), frameHeight, frameColor);
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
|
||||||
|
|
@ -42,15 +42,18 @@ public class SettingsTab : ITab
|
||||||
private readonly DalamudConfigService _dalamudConfig;
|
private readonly DalamudConfigService _dalamudConfig;
|
||||||
private readonly DalamudPluginInterface _pluginInterface;
|
private readonly DalamudPluginInterface _pluginInterface;
|
||||||
private readonly IDataManager _gameData;
|
private readonly IDataManager _gameData;
|
||||||
|
private readonly PredefinedTagManager _predefinedTagManager;
|
||||||
|
|
||||||
private int _minimumX = int.MaxValue;
|
private int _minimumX = int.MaxValue;
|
||||||
private int _minimumY = int.MaxValue;
|
private int _minimumY = int.MaxValue;
|
||||||
|
|
||||||
|
private readonly TagButtons _sharedTags = new();
|
||||||
|
|
||||||
public SettingsTab(DalamudPluginInterface pluginInterface, Configuration config, FontReloader fontReloader, TutorialService tutorial,
|
public SettingsTab(DalamudPluginInterface pluginInterface, Configuration config, FontReloader fontReloader, TutorialService tutorial,
|
||||||
Penumbra penumbra, FileDialogService fileDialog, ModManager modManager, ModFileSystemSelector selector,
|
Penumbra penumbra, FileDialogService fileDialog, ModManager modManager, ModFileSystemSelector selector,
|
||||||
CharacterUtility characterUtility, ResidentResourceManager residentResources, ModExportManager modExportManager, HttpApi httpApi,
|
CharacterUtility characterUtility, ResidentResourceManager residentResources, ModExportManager modExportManager, HttpApi httpApi,
|
||||||
DalamudSubstitutionProvider dalamudSubstitutionProvider, FileCompactor compactor, DalamudConfigService dalamudConfig,
|
DalamudSubstitutionProvider dalamudSubstitutionProvider, FileCompactor compactor, DalamudConfigService dalamudConfig,
|
||||||
IDataManager gameData)
|
IDataManager gameData, PredefinedTagManager predefinedTagConfig)
|
||||||
{
|
{
|
||||||
_pluginInterface = pluginInterface;
|
_pluginInterface = pluginInterface;
|
||||||
_config = config;
|
_config = config;
|
||||||
|
|
@ -70,6 +73,7 @@ public class SettingsTab : ITab
|
||||||
_gameData = gameData;
|
_gameData = gameData;
|
||||||
if (_compactor.CanCompact)
|
if (_compactor.CanCompact)
|
||||||
_compactor.Enabled = _config.UseFileSystemCompression;
|
_compactor.Enabled = _config.UseFileSystemCompression;
|
||||||
|
_predefinedTagManager = predefinedTagConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawHeader()
|
public void DrawHeader()
|
||||||
|
|
@ -96,6 +100,7 @@ public class SettingsTab : ITab
|
||||||
|
|
||||||
DrawGeneralSettings();
|
DrawGeneralSettings();
|
||||||
DrawColorSettings();
|
DrawColorSettings();
|
||||||
|
DrawPredefinedTagsSection();
|
||||||
DrawAdvancedSettings();
|
DrawAdvancedSettings();
|
||||||
DrawSupportButtons();
|
DrawSupportButtons();
|
||||||
}
|
}
|
||||||
|
|
@ -222,9 +227,10 @@ public class SettingsTab : ITab
|
||||||
if (_newModDirectory.IsNullOrEmpty())
|
if (_newModDirectory.IsNullOrEmpty())
|
||||||
_newModDirectory = _config.ModDirectory;
|
_newModDirectory = _config.ModDirectory;
|
||||||
|
|
||||||
using var group = ImRaii.Group();
|
bool save, selected;
|
||||||
|
using (ImRaii.Group())
|
||||||
|
{
|
||||||
ImGui.SetNextItemWidth(UiHelpers.InputTextMinusButton3);
|
ImGui.SetNextItemWidth(UiHelpers.InputTextMinusButton3);
|
||||||
bool save;
|
|
||||||
using (ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale, !_modManager.Valid))
|
using (ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale, !_modManager.Valid))
|
||||||
{
|
{
|
||||||
using var color = ImRaii.PushColor(ImGuiCol.Border, Colors.RegexWarningBorder)
|
using var color = ImRaii.PushColor(ImGuiCol.Border, Colors.RegexWarningBorder)
|
||||||
|
|
@ -233,7 +239,7 @@ public class SettingsTab : ITab
|
||||||
RootDirectoryMaxLength, ImGuiInputTextFlags.EnterReturnsTrue);
|
RootDirectoryMaxLength, ImGuiInputTextFlags.EnterReturnsTrue);
|
||||||
}
|
}
|
||||||
|
|
||||||
var selected = ImGui.IsItemActive();
|
selected = ImGui.IsItemActive();
|
||||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(UiHelpers.ScaleX3, 0));
|
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(UiHelpers.ScaleX3, 0));
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
DrawDirectoryPickerButton();
|
DrawDirectoryPickerButton();
|
||||||
|
|
@ -251,8 +257,8 @@ public class SettingsTab : ITab
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.TextUnformatted("Root Directory");
|
ImGui.TextUnformatted("Root Directory");
|
||||||
ImGuiUtil.HoverTooltip(tt);
|
ImGuiUtil.HoverTooltip(tt);
|
||||||
|
}
|
||||||
|
|
||||||
group.Dispose();
|
|
||||||
_tutorial.OpenTutorial(BasicTutorialSteps.ModDirectory);
|
_tutorial.OpenTutorial(BasicTutorialSteps.ModDirectory);
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
var pos = ImGui.GetCursorPosX();
|
var pos = ImGui.GetCursorPosX();
|
||||||
|
|
@ -680,7 +686,7 @@ public class SettingsTab : ITab
|
||||||
foreach (var color in Enum.GetValues<ColorId>())
|
foreach (var color in Enum.GetValues<ColorId>())
|
||||||
{
|
{
|
||||||
var (defaultColor, name, description) = color.Data();
|
var (defaultColor, name, description) = color.Data();
|
||||||
var currentColor = _config.Colors.TryGetValue(color, out var current) ? current : defaultColor;
|
var currentColor = _config.Colors.GetValueOrDefault(color, defaultColor);
|
||||||
if (Widget.ColorPicker(name, description, currentColor, c => _config.Colors[color] = c, defaultColor))
|
if (Widget.ColorPicker(name, description, currentColor, c => _config.Colors[color] = c, defaultColor))
|
||||||
_config.Save();
|
_config.Save();
|
||||||
}
|
}
|
||||||
|
|
@ -866,7 +872,7 @@ public class SettingsTab : ITab
|
||||||
if (!_dalamudConfig.GetDalamudConfig(DalamudConfigService.WaitingForPluginsOption, out bool value))
|
if (!_dalamudConfig.GetDalamudConfig(DalamudConfigService.WaitingForPluginsOption, out bool value))
|
||||||
{
|
{
|
||||||
using var disabled = ImRaii.Disabled();
|
using var disabled = ImRaii.Disabled();
|
||||||
Checkbox("Wait for Plugins on Startup (Disabled, can not access Dalamud Configuration)", string.Empty, false, v => { });
|
Checkbox("Wait for Plugins on Startup (Disabled, can not access Dalamud Configuration)", string.Empty, false, _ => { });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -911,4 +917,17 @@ public class SettingsTab : ITab
|
||||||
if (ImGui.Button("Show Changelogs", new Vector2(width, 0)))
|
if (ImGui.Button("Show Changelogs", new Vector2(width, 0)))
|
||||||
_penumbra.ForceChangelogOpen();
|
_penumbra.ForceChangelogOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawPredefinedTagsSection()
|
||||||
|
{
|
||||||
|
if (!ImGui.CollapsingHeader("Tags"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var tagIdx = _sharedTags.Draw("Predefined Tags: ",
|
||||||
|
"Predefined tags that can be added or removed from mods with a single click.", _predefinedTagManager,
|
||||||
|
out var editedTag);
|
||||||
|
|
||||||
|
if (tagIdx >= 0)
|
||||||
|
_predefinedTagManager.ChangeSharedTag(tagIdx, editedTag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue