Merge branch 'master' into Exter-N/stockings-skin-slot

This commit is contained in:
Ottermandias 2025-08-18 15:31:52 +02:00
commit 83a36ed4cb
5 changed files with 137 additions and 18 deletions

View file

@ -30,7 +30,7 @@ public class ModPanelDescriptionTab(
ImGui.Dummy(ImGuiHelpers.ScaledVector2(2));
ImGui.Dummy(ImGuiHelpers.ScaledVector2(2));
var (predefinedTagsEnabled, predefinedTagButtonOffset) = predefinedTagsConfig.Count > 0
var (predefinedTagsEnabled, predefinedTagButtonOffset) = predefinedTagsConfig.Enabled
? (true, ImGui.GetFrameHeight() + ImGui.GetStyle().WindowPadding.X + (ImGui.GetScrollMaxY() > 0 ? ImGui.GetStyle().ScrollbarSize : 0))
: (false, 0);
var tagIdx = _localTags.Draw("Local Tags: ",

View file

@ -69,7 +69,7 @@ public class ModPanelEditTab(
FeatureChecker.DrawFeatureFlagInput(modManager.DataEditor, _mod, UiHelpers.InputTextWidth.X);
UiHelpers.DefaultLineSpace();
var sharedTagsEnabled = predefinedTagManager.Count > 0;
var sharedTagsEnabled = predefinedTagManager.Enabled;
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,
out var editedTag, rightEndOffset: sharedTagButtonOffset);

View file

@ -2,6 +2,7 @@ using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using OtterGui.Extensions;
using OtterGui.Filesystem;
using OtterGui.Raii;
using OtterGui.Services;
using OtterGui.Text;
@ -10,7 +11,7 @@ using Penumbra.Mods.Manager;
namespace Penumbra.UI.ModsTab;
public class MultiModPanel(ModFileSystemSelector selector, ModDataEditor editor) : IUiService
public class MultiModPanel(ModFileSystemSelector selector, ModDataEditor editor, PredefinedTagManager tagManager) : IUiService
{
public void Draw()
{
@ -97,7 +98,12 @@ public class MultiModPanel(ModFileSystemSelector selector, ModDataEditor editor)
var width = ImGuiHelpers.ScaledVector2(150, 0);
ImUtf8.TextFrameAligned("Multi Tagger:"u8);
ImGui.SameLine();
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - 2 * (width.X + ImGui.GetStyle().ItemSpacing.X));
var predefinedTagsEnabled = tagManager.Enabled;
var inputWidth = predefinedTagsEnabled
? ImGui.GetContentRegionAvail().X - 2 * width.X - 3 * ImGui.GetStyle().ItemInnerSpacing.X - ImGui.GetFrameHeight()
: ImGui.GetContentRegionAvail().X - 2 * (width.X + ImGui.GetStyle().ItemInnerSpacing.X);
ImGui.SetNextItemWidth(inputWidth);
ImUtf8.InputText("##tag"u8, ref _tag, "Local Tag Name..."u8);
UpdateTagCache();
@ -109,7 +115,7 @@ public class MultiModPanel(ModFileSystemSelector selector, ModDataEditor editor)
? "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();
ImUtf8.SameLineInner();
if (ImUtf8.ButtonEx(label, tooltip, width, _addMods.Count == 0))
foreach (var mod in _addMods)
editor.ChangeLocalTag(mod, mod.LocalTags.Count, _tag);
@ -122,10 +128,18 @@ public class MultiModPanel(ModFileSystemSelector selector, ModDataEditor editor)
? "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();
ImUtf8.SameLineInner();
if (ImUtf8.ButtonEx(label, tooltip, width, _removeMods.Count == 0))
foreach (var (mod, index) in _removeMods)
editor.ChangeLocalTag(mod, index, string.Empty);
if (predefinedTagsEnabled)
{
ImUtf8.SameLineInner();
tagManager.DrawToggleButton();
tagManager.DrawListMulti(selector.SelectedPaths.OfType<ModFileSystem.Leaf>().Select(l => l.Value));
}
ImGui.Separator();
}

View file

@ -8,6 +8,8 @@ using OtterGui.Classes;
using OtterGui.Extensions;
using OtterGui.Raii;
using OtterGui.Services;
using OtterGui.Text;
using Penumbra.Mods;
using Penumbra.Mods.Manager;
using Penumbra.Services;
using Penumbra.UI.Classes;
@ -52,6 +54,9 @@ public sealed class PredefinedTagManager : ISavable, IReadOnlyList<string>, ISer
jObj.WriteTo(jWriter);
}
public bool Enabled
=> Count > 0;
public void Save()
=> _saveService.DelaySave(this, TimeSpan.FromSeconds(5));
@ -98,9 +103,9 @@ public sealed class PredefinedTagManager : ISavable, IReadOnlyList<string>, ISer
}
public void DrawAddFromSharedTagsAndUpdateTags(IReadOnlyCollection<string> localTags, IReadOnlyCollection<string> modTags, bool editLocal,
Mods.Mod mod)
Mod mod)
{
DrawToggleButton();
DrawToggleButtonTopRight();
if (!DrawList(localTags, modTags, editLocal, out var changedTag, out var index))
return;
@ -110,17 +115,22 @@ public sealed class PredefinedTagManager : ISavable, IReadOnlyList<string>, ISer
_modManager.DataEditor.ChangeModTag(mod, index, changedTag);
}
private void DrawToggleButton()
public 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 void DrawToggleButtonTopRight()
{
ImGui.SameLine(ImGui.GetContentRegionMax().X
- ImGui.GetFrameHeight()
- (ImGui.GetScrollMaxY() > 0 ? ImGui.GetStyle().ItemInnerSpacing.X : 0));
DrawToggleButton();
}
private bool DrawList(IReadOnlyCollection<string> localTags, IReadOnlyCollection<string> modTags, bool editLocal, out string changedTag,
out int changedIndex)
{
@ -130,7 +140,7 @@ public sealed class PredefinedTagManager : ISavable, IReadOnlyList<string>, ISer
if (!_isListOpen)
return false;
ImGui.TextUnformatted("Predefined Tags");
ImUtf8.Text("Predefined Tags"u8);
ImGui.Separator();
var ret = false;
@ -155,6 +165,101 @@ public sealed class PredefinedTagManager : ISavable, IReadOnlyList<string>, ISer
return ret;
}
private readonly List<Mod> _selectedMods = [];
private readonly List<(int Index, int DataIndex)> _countedMods = [];
private void PrepareLists(IEnumerable<Mod> selection)
{
_selectedMods.Clear();
_selectedMods.AddRange(selection);
_countedMods.EnsureCapacity(_selectedMods.Count);
while (_countedMods.Count < _selectedMods.Count)
_countedMods.Add((-1, -1));
}
public void DrawListMulti(IEnumerable<Mod> selection)
{
if (!_isListOpen)
return;
ImUtf8.Text("Predefined Tags"u8);
PrepareLists(selection);
_enabledColor = ColorId.PredefinedTagAdd.Value();
_disabledColor = ColorId.PredefinedTagRemove.Value();
using var color = new ImRaii.Color();
foreach (var (tag, idx) in _predefinedTags.Keys.WithIndex())
{
var alreadyContained = 0;
var inModData = 0;
var missing = 0;
foreach (var (modIndex, mod) in _selectedMods.Index())
{
var tagIdx = mod.LocalTags.IndexOf(tag);
if (tagIdx >= 0)
{
++alreadyContained;
_countedMods[modIndex] = (tagIdx, -1);
}
else
{
var dataIdx = mod.ModTags.IndexOf(tag);
if (dataIdx >= 0)
{
++inModData;
_countedMods[modIndex] = (-1, dataIdx);
}
else
{
++missing;
_countedMods[modIndex] = (-1, -1);
}
}
}
using var id = ImRaii.PushId(idx);
var buttonWidth = CalcTextButtonWidth(tag);
// Prevent adding a new tag past the right edge of the popup
if (buttonWidth + ImGui.GetStyle().ItemSpacing.X >= ImGui.GetContentRegionAvail().X)
ImGui.NewLine();
var (usedColor, disabled, tt) = (missing, alreadyContained) switch
{
(> 0, _) => (_enabledColor, false,
$"Add this tag to {missing} mods.{(inModData > 0 ? $" {inModData} mods contain it in their mod tags and are untouched." : string.Empty)}"),
(_, > 0) => (_disabledColor, false,
$"Remove this tag from {alreadyContained} mods.{(inModData > 0 ? $" {inModData} mods contain it in their mod tags and are untouched." : string.Empty)}"),
_ => (_disabledColor, true, "This tag is already present in the mod tags of all selected mods."),
};
color.Push(ImGuiCol.Button, usedColor);
if (ImUtf8.ButtonEx(tag, tt, new Vector2(buttonWidth, 0), disabled))
{
if (missing > 0)
foreach (var (mod, (localIdx, _)) in _selectedMods.Zip(_countedMods))
{
if (localIdx >= 0)
continue;
_modManager.DataEditor.ChangeLocalTag(mod, mod.LocalTags.Count, tag);
}
else
foreach (var (mod, (localIdx, _)) in _selectedMods.Zip(_countedMods))
{
if (localIdx < 0)
continue;
_modManager.DataEditor.ChangeLocalTag(mod, localIdx, string.Empty);
}
}
ImGui.SameLine();
color.Pop();
}
ImGui.NewLine();
}
private bool DrawColoredButton(string buttonLabel, int index, int tagIdx, bool inOther)
{
using var id = ImRaii.PushId(index);

View file

@ -5,8 +5,8 @@
"Punchline": "Runtime mod loader and manager.",
"Description": "Runtime mod loader and manager.",
"InternalName": "Penumbra",
"AssemblyVersion": "1.5.0.5",
"TestingAssemblyVersion": "1.5.0.5",
"AssemblyVersion": "1.5.0.6",
"TestingAssemblyVersion": "1.5.0.6",
"RepoUrl": "https://github.com/xivdev/Penumbra",
"ApplicableVersion": "any",
"DalamudApiLevel": 13,
@ -18,9 +18,9 @@
"LoadPriority": 69420,
"LoadRequiredState": 2,
"LoadSync": true,
"DownloadLinkInstall": "https://github.com/xivdev/Penumbra/releases/download/1.5.0.5/Penumbra.zip",
"DownloadLinkTesting": "https://github.com/xivdev/Penumbra/releases/download/1.5.0.5/Penumbra.zip",
"DownloadLinkUpdate": "https://github.com/xivdev/Penumbra/releases/download/1.5.0.5/Penumbra.zip",
"DownloadLinkInstall": "https://github.com/xivdev/Penumbra/releases/download/1.5.0.6/Penumbra.zip",
"DownloadLinkTesting": "https://github.com/xivdev/Penumbra/releases/download/1.5.0.6/Penumbra.zip",
"DownloadLinkUpdate": "https://github.com/xivdev/Penumbra/releases/download/1.5.0.6/Penumbra.zip",
"IconUrl": "https://raw.githubusercontent.com/xivdev/Penumbra/master/images/icon.png"
}
]