mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-15 05:04:15 +01:00
Fix a bunch of issues.
This commit is contained in:
parent
9084b43e3e
commit
36fc251d5b
14 changed files with 513 additions and 266 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 1b0b5469f792999e5b412d4f0c3ed77d9994d7b7
|
||||
Subproject commit 595ac5722c9c400bea36110503ed2ae7b02d1489
|
||||
|
|
@ -12,16 +12,23 @@ public interface ITexToolsGroup
|
|||
public IReadOnlyList<OptionSubMod> OptionData { get; }
|
||||
}
|
||||
|
||||
public enum GroupDrawBehaviour
|
||||
{
|
||||
SingleSelection,
|
||||
MultiSelection,
|
||||
}
|
||||
|
||||
public interface IModGroup
|
||||
{
|
||||
public const int MaxMultiOptions = 63;
|
||||
|
||||
public Mod Mod { get; }
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public GroupType Type { get; }
|
||||
public ModPriority Priority { get; set; }
|
||||
public Setting DefaultSettings { get; set; }
|
||||
public Mod Mod { get; }
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public GroupType Type { get; }
|
||||
public GroupDrawBehaviour Behaviour { get; }
|
||||
public ModPriority Priority { get; set; }
|
||||
public Setting DefaultSettings { get; set; }
|
||||
|
||||
public FullPath? FindBestMatch(Utf8GamePath gamePath);
|
||||
public IModOption? AddOption(string name, string description = "");
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ public class ImcModGroup(Mod mod) : IModGroup
|
|||
public GroupType Type
|
||||
=> GroupType.Imc;
|
||||
|
||||
public GroupDrawBehaviour Behaviour
|
||||
=> GroupDrawBehaviour.MultiSelection;
|
||||
|
||||
public ModPriority Priority { get; set; } = ModPriority.Default;
|
||||
public Setting DefaultSettings { get; set; } = Setting.Zero;
|
||||
|
||||
|
|
@ -150,7 +153,6 @@ public class ImcModGroup(Mod mod) : IModGroup
|
|||
}
|
||||
|
||||
jWriter.WriteEndArray();
|
||||
jWriter.WriteEndObject();
|
||||
}
|
||||
|
||||
public (int Redirections, int Swaps, int Manips) GetCounts()
|
||||
|
|
|
|||
|
|
@ -56,10 +56,10 @@ public readonly struct ModSaveGroup : ISavable
|
|||
{
|
||||
_basePath = (container.Mod as Mod)?.ModPath
|
||||
?? throw new Exception("Invalid save group from default data container without base path."); // Should not happen.
|
||||
_defaultMod = null;
|
||||
_defaultMod = container as DefaultSubMod;
|
||||
_onlyAscii = onlyAscii;
|
||||
_group = container.Group!;
|
||||
_groupIdx = _group.GetIndex();
|
||||
_group = container.Group;
|
||||
_groupIdx = _group?.GetIndex() ?? -1;
|
||||
}
|
||||
|
||||
public string ToFilename(FilenameService fileNames)
|
||||
|
|
@ -80,7 +80,6 @@ public readonly struct ModSaveGroup : ISavable
|
|||
|
||||
public static void WriteJsonBase(JsonTextWriter jWriter, IModGroup group)
|
||||
{
|
||||
jWriter.WriteStartObject();
|
||||
jWriter.WritePropertyName(nameof(group.Name));
|
||||
jWriter.WriteValue(group.Name);
|
||||
jWriter.WritePropertyName(nameof(group.Description));
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ public sealed class MultiModGroup(Mod mod) : IModGroup, ITexToolsGroup
|
|||
public GroupType Type
|
||||
=> GroupType.Multi;
|
||||
|
||||
public GroupDrawBehaviour Behaviour
|
||||
=> GroupDrawBehaviour.MultiSelection;
|
||||
|
||||
public Mod Mod { get; } = mod;
|
||||
public string Name { get; set; } = "Group";
|
||||
public string Description { get; set; } = "A non-exclusive group of settings.";
|
||||
|
|
@ -127,7 +130,6 @@ public sealed class MultiModGroup(Mod mod) : IModGroup, ITexToolsGroup
|
|||
}
|
||||
|
||||
jWriter.WriteEndArray();
|
||||
jWriter.WriteEndObject();
|
||||
}
|
||||
|
||||
public (int Redirections, int Swaps, int Manips) GetCounts()
|
||||
|
|
|
|||
|
|
@ -15,7 +15,10 @@ public sealed class SingleModGroup(Mod mod) : IModGroup, ITexToolsGroup
|
|||
public GroupType Type
|
||||
=> GroupType.Single;
|
||||
|
||||
public Mod Mod { get; } = mod;
|
||||
public GroupDrawBehaviour Behaviour
|
||||
=> GroupDrawBehaviour.SingleSelection;
|
||||
|
||||
public Mod Mod { get; } = mod;
|
||||
public string Name { get; set; } = "Option";
|
||||
public string Description { get; set; } = "A mutually exclusive group of settings.";
|
||||
public ModPriority Priority { get; set; }
|
||||
|
|
@ -89,7 +92,12 @@ public sealed class SingleModGroup(Mod mod) : IModGroup, ITexToolsGroup
|
|||
=> ModGroup.GetIndex(this);
|
||||
|
||||
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
|
||||
=> OptionData[setting.AsIndex].AddDataTo(redirections, manipulations);
|
||||
{
|
||||
if (!IsOption)
|
||||
return;
|
||||
|
||||
OptionData[setting.AsIndex].AddDataTo(redirections, manipulations);
|
||||
}
|
||||
|
||||
public Setting FixSetting(Setting setting)
|
||||
=> OptionData.Count == 0 ? Setting.Zero : new Setting(Math.Min(setting.Value, (ulong)(OptionData.Count - 1)));
|
||||
|
|
@ -111,7 +119,6 @@ public sealed class SingleModGroup(Mod mod) : IModGroup, ITexToolsGroup
|
|||
}
|
||||
|
||||
jWriter.WriteEndArray();
|
||||
jWriter.WriteEndObject();
|
||||
}
|
||||
|
||||
/// <summary> Create a group without a mod only for saving it in the creator. </summary>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ public sealed class MultiModGroupEditor(CommunicatorService communicator, SaveSe
|
|||
var idx = group.GetIndex();
|
||||
var singleGroup = group.ConvertToSingle();
|
||||
group.Mod.Groups[idx] = singleGroup;
|
||||
SaveService.QueueSave(new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupTypeChanged, group.Mod, group, null, null, -1);
|
||||
SaveService.QueueSave(new ModSaveGroup(singleGroup, Config.ReplaceNonAsciiOnImport));
|
||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupTypeChanged, singleGroup.Mod, singleGroup, null, null, -1);
|
||||
}
|
||||
|
||||
/// <summary> Change the internal priority of the given option. </summary>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ public sealed class SingleModGroupEditor(CommunicatorService communicator, SaveS
|
|||
var idx = group.GetIndex();
|
||||
var multiGroup = group.ConvertToMulti();
|
||||
group.Mod.Groups[idx] = multiGroup;
|
||||
SaveService.QueueSave(new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupTypeChanged, group.Mod, multiGroup, null, null, -1);
|
||||
SaveService.QueueSave(new ModSaveGroup(multiGroup, Config.ReplaceNonAsciiOnImport));
|
||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupTypeChanged, multiGroup.Mod, multiGroup, null, null, -1);
|
||||
}
|
||||
|
||||
protected override SingleModGroup CreateGroup(Mod mod, string newName, ModPriority priority, SaveType saveType = SaveType.ImmediateSync)
|
||||
|
|
|
|||
|
|
@ -106,7 +106,6 @@ public static class SubMod
|
|||
j.WriteEndObject();
|
||||
j.WritePropertyName(nameof(data.Manipulations));
|
||||
serializer.Serialize(j, data.Manipulations);
|
||||
j.WriteEndObject();
|
||||
}
|
||||
|
||||
/// <summary> Write the data for a selectable mod option on a JsonWriter. </summary>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ public class BackupService : IAsyncService
|
|||
list.Add(new FileInfo(fileNames.ConfigFile));
|
||||
list.Add(new FileInfo(fileNames.FilesystemFile));
|
||||
list.Add(new FileInfo(fileNames.ActiveCollectionsFile));
|
||||
list.Add(new FileInfo(fileNames.PredefinedTagFile));
|
||||
return list;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -162,7 +162,6 @@ public static class StaticServiceManager
|
|||
.AddSingleton<CollectionSelectHeader>()
|
||||
.AddSingleton<ImportPopup>()
|
||||
.AddSingleton<ModPanelDescriptionTab>()
|
||||
.AddSingleton<ModPanelSettingsTab>()
|
||||
.AddSingleton<ModPanelEditTab>()
|
||||
.AddSingleton<ModPanelChangedItemsTab>()
|
||||
.AddSingleton<ModPanelConflictsTab>()
|
||||
|
|
|
|||
233
Penumbra/UI/ModsTab/ModGroupDrawer.cs
Normal file
233
Penumbra/UI/ModsTab/ModGroupDrawer.cs
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
using Dalamud.Interface.Components;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using OtterGui.Widgets;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Collections.Manager;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Mods.Groups;
|
||||
using Penumbra.Mods.Settings;
|
||||
using Penumbra.Mods.SubMods;
|
||||
|
||||
namespace Penumbra.UI.ModsTab;
|
||||
|
||||
public sealed class ModGroupDrawer(Configuration config, CollectionManager collectionManager) : IUiService
|
||||
{
|
||||
private readonly List<(IModGroup, int)> _blockGroupCache = [];
|
||||
|
||||
public void Draw(Mod mod, ModSettings settings)
|
||||
{
|
||||
if (mod.Groups.Count <= 0)
|
||||
return;
|
||||
|
||||
_blockGroupCache.Clear();
|
||||
var useDummy = true;
|
||||
foreach (var (group, idx) in mod.Groups.WithIndex())
|
||||
{
|
||||
if (!group.IsOption)
|
||||
continue;
|
||||
|
||||
switch (group.Behaviour)
|
||||
{
|
||||
case GroupDrawBehaviour.SingleSelection when group.Options.Count <= config.SingleGroupRadioMax:
|
||||
case GroupDrawBehaviour.MultiSelection:
|
||||
_blockGroupCache.Add((group, idx));
|
||||
break;
|
||||
|
||||
case GroupDrawBehaviour.SingleSelection:
|
||||
ImGuiUtil.Dummy(UiHelpers.DefaultSpace, useDummy);
|
||||
useDummy = false;
|
||||
DrawSingleGroupCombo(group, idx, settings == ModSettings.Empty ? group.DefaultSettings : settings.Settings[idx]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
useDummy = true;
|
||||
foreach (var (group, idx) in _blockGroupCache)
|
||||
{
|
||||
ImGuiUtil.Dummy(UiHelpers.DefaultSpace, useDummy);
|
||||
useDummy = false;
|
||||
var option = settings == ModSettings.Empty ? group.DefaultSettings : settings.Settings[idx];
|
||||
if (group.Behaviour is GroupDrawBehaviour.MultiSelection)
|
||||
DrawMultiGroup(group, idx, option);
|
||||
else
|
||||
DrawSingleGroupRadio(group, idx, option);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw a single group selector as a combo box.
|
||||
/// If a description is provided, add a help marker besides it.
|
||||
/// </summary>
|
||||
private void DrawSingleGroupCombo(IModGroup group, int groupIdx, Setting setting)
|
||||
{
|
||||
using var id = ImRaii.PushId(groupIdx);
|
||||
var selectedOption = setting.AsIndex;
|
||||
ImGui.SetNextItemWidth(UiHelpers.InputTextWidth.X * 3 / 4);
|
||||
var options = group.Options;
|
||||
using (var combo = ImRaii.Combo(string.Empty, options[selectedOption].Name))
|
||||
{
|
||||
if (combo)
|
||||
for (var idx2 = 0; idx2 < options.Count; ++idx2)
|
||||
{
|
||||
id.Push(idx2);
|
||||
var option = options[idx2];
|
||||
if (ImGui.Selectable(option.Name, idx2 == selectedOption))
|
||||
SetModSetting(group, groupIdx, Setting.Single(idx2));
|
||||
|
||||
if (option.Description.Length > 0)
|
||||
ImGuiUtil.SelectableHelpMarker(option.Description);
|
||||
|
||||
id.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if (group.Description.Length > 0)
|
||||
ImGuiUtil.LabeledHelpMarker(group.Name, group.Description);
|
||||
else
|
||||
ImGui.TextUnformatted(group.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw a single group selector as a set of radio buttons.
|
||||
/// If a description is provided, add a help marker besides it.
|
||||
/// </summary>
|
||||
private void DrawSingleGroupRadio(IModGroup group, int groupIdx, Setting setting)
|
||||
{
|
||||
using var id = ImRaii.PushId(groupIdx);
|
||||
var selectedOption = setting.AsIndex;
|
||||
var minWidth = Widget.BeginFramedGroup(group.Name, group.Description);
|
||||
var options = group.Options;
|
||||
DrawCollapseHandling(options, minWidth, DrawOptions);
|
||||
|
||||
Widget.EndFramedGroup();
|
||||
return;
|
||||
|
||||
void DrawOptions()
|
||||
{
|
||||
for (var idx = 0; idx < group.Options.Count; ++idx)
|
||||
{
|
||||
using var i = ImRaii.PushId(idx);
|
||||
var option = options[idx];
|
||||
if (ImGui.RadioButton(option.Name, selectedOption == idx))
|
||||
SetModSetting(group, groupIdx, Setting.Single(idx));
|
||||
|
||||
if (option.Description.Length <= 0)
|
||||
continue;
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGuiComponents.HelpMarker(option.Description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw a multi group selector as a bordered set of checkboxes.
|
||||
/// If a description is provided, add a help marker in the title.
|
||||
/// </summary>
|
||||
private void DrawMultiGroup(IModGroup group, int groupIdx, Setting setting)
|
||||
{
|
||||
using var id = ImRaii.PushId(groupIdx);
|
||||
var minWidth = Widget.BeginFramedGroup(group.Name, group.Description);
|
||||
var options = group.Options;
|
||||
DrawCollapseHandling(options, minWidth, DrawOptions);
|
||||
|
||||
Widget.EndFramedGroup();
|
||||
var label = $"##multi{groupIdx}";
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||
ImGui.OpenPopup($"##multi{groupIdx}");
|
||||
|
||||
DrawMultiPopup(group, groupIdx, label);
|
||||
return;
|
||||
|
||||
void DrawOptions()
|
||||
{
|
||||
for (var idx = 0; idx < options.Count; ++idx)
|
||||
{
|
||||
using var i = ImRaii.PushId(idx);
|
||||
var option = options[idx];
|
||||
var enabled = setting.HasFlag(idx);
|
||||
|
||||
if (ImGui.Checkbox(option.Name, ref enabled))
|
||||
SetModSetting(group, groupIdx, setting.SetBit(idx, enabled));
|
||||
|
||||
if (option.Description.Length > 0)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGuiComponents.HelpMarker(option.Description);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawMultiPopup(IModGroup group, int groupIdx, string label)
|
||||
{
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.PopupBorderSize, 1);
|
||||
using var popup = ImRaii.Popup(label);
|
||||
if (!popup)
|
||||
return;
|
||||
|
||||
ImGui.TextUnformatted(group.Name);
|
||||
ImGui.Separator();
|
||||
if (ImGui.Selectable("Enable All"))
|
||||
SetModSetting(group, groupIdx, Setting.AllBits(group.Options.Count));
|
||||
|
||||
if (ImGui.Selectable("Disable All"))
|
||||
SetModSetting(group, groupIdx, Setting.Zero);
|
||||
}
|
||||
|
||||
private void DrawCollapseHandling(IReadOnlyList<IModOption> options, float minWidth, Action draw)
|
||||
{
|
||||
if (options.Count <= config.OptionGroupCollapsibleMin)
|
||||
{
|
||||
draw();
|
||||
}
|
||||
else
|
||||
{
|
||||
var collapseId = ImGui.GetID("Collapse");
|
||||
var shown = ImGui.GetStateStorage().GetBool(collapseId, true);
|
||||
var buttonTextShow = $"Show {options.Count} Options";
|
||||
var buttonTextHide = $"Hide {options.Count} Options";
|
||||
var buttonWidth = Math.Max(ImGui.CalcTextSize(buttonTextShow).X, ImGui.CalcTextSize(buttonTextHide).X)
|
||||
+ 2 * ImGui.GetStyle().FramePadding.X;
|
||||
minWidth = Math.Max(buttonWidth, minWidth);
|
||||
if (shown)
|
||||
{
|
||||
var pos = ImGui.GetCursorPos();
|
||||
ImGui.Dummy(UiHelpers.IconButtonSize);
|
||||
using (var _ = ImRaii.Group())
|
||||
{
|
||||
draw();
|
||||
}
|
||||
|
||||
|
||||
var width = Math.Max(ImGui.GetItemRectSize().X, minWidth);
|
||||
var endPos = ImGui.GetCursorPos();
|
||||
ImGui.SetCursorPos(pos);
|
||||
if (ImGui.Button(buttonTextHide, new Vector2(width, 0)))
|
||||
ImGui.GetStateStorage().SetBool(collapseId, !shown);
|
||||
|
||||
ImGui.SetCursorPos(endPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
var optionWidth = options.Max(o => ImGui.CalcTextSize(o.Name).X)
|
||||
+ ImGui.GetStyle().ItemInnerSpacing.X
|
||||
+ ImGui.GetFrameHeight()
|
||||
+ ImGui.GetStyle().FramePadding.X;
|
||||
var width = Math.Max(optionWidth, minWidth);
|
||||
if (ImGui.Button(buttonTextShow, new Vector2(width, 0)))
|
||||
ImGui.GetStateStorage().SetBool(collapseId, !shown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ModCollection Current
|
||||
=> collectionManager.Active.Current;
|
||||
|
||||
private void SetModSetting(IModGroup group, int groupIdx, Setting setting)
|
||||
=> collectionManager.Editor.SetModSetting(Current, group.Mod, groupIdx, setting);
|
||||
}
|
||||
214
Penumbra/UI/ModsTab/ModGroupEditDrawer.cs
Normal file
214
Penumbra/UI/ModsTab/ModGroupEditDrawer.cs
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.Utility;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Mods.Groups;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Mods.Manager.OptionEditor;
|
||||
using Penumbra.Mods.Settings;
|
||||
using Penumbra.Mods.SubMods;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.UI.Classes;
|
||||
|
||||
namespace Penumbra.UI.ModsTab;
|
||||
|
||||
public sealed class ModGroupEditDrawer(ModManager modManager, Configuration config, FilenameService filenames) : IUiService
|
||||
{
|
||||
private Vector2 _buttonSize;
|
||||
private float _priorityWidth;
|
||||
private float _groupNameWidth;
|
||||
private float _spacing;
|
||||
|
||||
private string? _currentGroupName;
|
||||
private ModPriority? _currentGroupPriority;
|
||||
private IModGroup? _currentGroupEdited;
|
||||
private bool _isGroupNameValid;
|
||||
private IModGroup? _deleteGroup;
|
||||
private IModGroup? _moveGroup;
|
||||
private int _moveTo;
|
||||
|
||||
private string? _currentOptionName;
|
||||
private ModPriority? _currentOptionPriority;
|
||||
private IModOption? _currentOptionEdited;
|
||||
private IModOption? _deleteOption;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void SameLine()
|
||||
=> ImGui.SameLine(0, _spacing);
|
||||
|
||||
public void Draw(Mod mod)
|
||||
{
|
||||
_buttonSize = new Vector2(ImGui.GetFrameHeight());
|
||||
_priorityWidth = 50 * ImGuiHelpers.GlobalScale;
|
||||
_groupNameWidth = 350f * ImGuiHelpers.GlobalScale;
|
||||
_spacing = ImGui.GetStyle().ItemInnerSpacing.X;
|
||||
|
||||
|
||||
FinishGroupCleanup();
|
||||
}
|
||||
|
||||
private void FinishGroupCleanup()
|
||||
{
|
||||
if (_deleteGroup != null)
|
||||
{
|
||||
modManager.OptionEditor.DeleteModGroup(_deleteGroup);
|
||||
_deleteGroup = null;
|
||||
}
|
||||
|
||||
if (_deleteOption != null)
|
||||
{
|
||||
modManager.OptionEditor.DeleteOption(_deleteOption);
|
||||
_deleteOption = null;
|
||||
}
|
||||
|
||||
if (_moveGroup != null)
|
||||
{
|
||||
modManager.OptionEditor.MoveModGroup(_moveGroup, _moveTo);
|
||||
_moveGroup = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawGroup(IModGroup group, int idx)
|
||||
{
|
||||
using var id = ImRaii.PushId(idx);
|
||||
using var frame = ImRaii.FramedGroup($"Group #{idx + 1}");
|
||||
DrawGroupNameRow(group);
|
||||
switch (group)
|
||||
{
|
||||
case SingleModGroup s:
|
||||
DrawSingleGroup(s, idx);
|
||||
break;
|
||||
case MultiModGroup m:
|
||||
DrawMultiGroup(m, idx);
|
||||
break;
|
||||
case ImcModGroup i:
|
||||
DrawImcGroup(i, idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawGroupNameRow(IModGroup group)
|
||||
{
|
||||
DrawGroupName(group);
|
||||
SameLine();
|
||||
DrawGroupDelete(group);
|
||||
SameLine();
|
||||
DrawGroupPriority(group);
|
||||
}
|
||||
|
||||
private void DrawGroupName(IModGroup group)
|
||||
{
|
||||
var text = _currentGroupEdited == group ? _currentGroupName ?? group.Name : group.Name;
|
||||
ImGui.SetNextItemWidth(_groupNameWidth);
|
||||
using var border = ImRaii.PushFrameBorder(UiHelpers.ScaleX2, Colors.RegexWarningBorder, !_isGroupNameValid);
|
||||
if (ImGui.InputText("##GroupName", ref text, 256))
|
||||
{
|
||||
_currentGroupEdited = group;
|
||||
_currentGroupName = text;
|
||||
_isGroupNameValid = ModGroupEditor.VerifyFileName(group.Mod, group, text, false);
|
||||
}
|
||||
|
||||
if (ImGui.IsItemDeactivated())
|
||||
{
|
||||
if (_currentGroupName != null && _isGroupNameValid)
|
||||
modManager.OptionEditor.RenameModGroup(group, _currentGroupName);
|
||||
_currentGroupName = null;
|
||||
_currentGroupEdited = null;
|
||||
_isGroupNameValid = true;
|
||||
}
|
||||
|
||||
var tt = _isGroupNameValid
|
||||
? "Group Name"
|
||||
: "Current name can not be used for this group.";
|
||||
ImGuiUtil.HoverTooltip(tt);
|
||||
}
|
||||
|
||||
private void DrawGroupDelete(IModGroup group)
|
||||
{
|
||||
var enabled = config.DeleteModModifier.IsActive();
|
||||
var tt = enabled
|
||||
? "Delete this option group."
|
||||
: $"Delete this option group.\nHold {config.DeleteModModifier} while clicking to delete.";
|
||||
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), _buttonSize, tt, !enabled, true))
|
||||
_deleteGroup = group;
|
||||
}
|
||||
|
||||
private void DrawGroupPriority(IModGroup group)
|
||||
{
|
||||
var priority = _currentGroupEdited == group
|
||||
? (_currentGroupPriority ?? group.Priority).Value
|
||||
: group.Priority.Value;
|
||||
ImGui.SetNextItemWidth(_priorityWidth);
|
||||
if (ImGui.InputInt("##GroupPriority", ref priority, 0, 0))
|
||||
{
|
||||
_currentGroupEdited = group;
|
||||
_currentGroupPriority = new ModPriority(priority);
|
||||
}
|
||||
|
||||
if (ImGui.IsItemDeactivated())
|
||||
{
|
||||
if (_currentGroupPriority.HasValue)
|
||||
modManager.OptionEditor.ChangeGroupPriority(group, _currentGroupPriority.Value);
|
||||
_currentGroupEdited = null;
|
||||
_currentGroupPriority = null;
|
||||
}
|
||||
|
||||
ImGuiUtil.HoverTooltip("Group Priority");
|
||||
}
|
||||
|
||||
private void DrawGroupMoveButtons(IModGroup group, int idx)
|
||||
{
|
||||
var isFirst = idx == 0;
|
||||
var tt = isFirst ? "Can not move this group further upwards." : $"Move this group up to group {idx}.";
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.ArrowUp.ToIconString(), UiHelpers.IconButtonSize, tt, isFirst, true))
|
||||
{
|
||||
_moveGroup = group;
|
||||
_moveTo = idx - 1;
|
||||
}
|
||||
|
||||
SameLine();
|
||||
var isLast = idx == group.Mod.Groups.Count - 1;
|
||||
tt = isLast
|
||||
? "Can not move this group further downwards."
|
||||
: $"Move this group down to group {idx + 2}.";
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.ArrowDown.ToIconString(), UiHelpers.IconButtonSize, tt, isLast, true))
|
||||
{
|
||||
_moveGroup = group;
|
||||
_moveTo = idx + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawGroupOpenFile(IModGroup group, int idx)
|
||||
{
|
||||
var fileName = filenames.OptionGroupFile(group.Mod, idx, config.ReplaceNonAsciiOnImport);
|
||||
var fileExists = File.Exists(fileName);
|
||||
var tt = fileExists
|
||||
? $"Open the {group.Name} json file in the text editor of your choice."
|
||||
: $"The {group.Name} json file does not exist.";
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.FileExport.ToIconString(), UiHelpers.IconButtonSize, tt, !fileExists, true))
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(fileName) { UseShellExecute = true });
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage(e, "Could not open editor.", NotificationType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void DrawSingleGroup(SingleModGroup group, int idx)
|
||||
{ }
|
||||
|
||||
private void DrawMultiGroup(MultiModGroup group, int idx)
|
||||
{ }
|
||||
|
||||
private void DrawImcGroup(ImcModGroup group, int idx)
|
||||
{ }
|
||||
}
|
||||
|
|
@ -1,51 +1,37 @@
|
|||
using ImGuiNET;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui;
|
||||
using OtterGui.Services;
|
||||
using OtterGui.Text;
|
||||
using OtterGui.Widgets;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.UI.Classes;
|
||||
using Dalamud.Interface.Components;
|
||||
using Penumbra.Collections.Manager;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.Mods.SubMods;
|
||||
using Penumbra.Mods.Groups;
|
||||
using Penumbra.Mods.Settings;
|
||||
|
||||
namespace Penumbra.UI.ModsTab;
|
||||
|
||||
public class ModPanelSettingsTab : ITab
|
||||
public class ModPanelSettingsTab(
|
||||
CollectionManager collectionManager,
|
||||
ModManager modManager,
|
||||
ModFileSystemSelector selector,
|
||||
TutorialService tutorial,
|
||||
CommunicatorService communicator,
|
||||
ModGroupDrawer modGroupDrawer)
|
||||
: ITab, IUiService
|
||||
{
|
||||
private readonly Configuration _config;
|
||||
private readonly CommunicatorService _communicator;
|
||||
private readonly CollectionManager _collectionManager;
|
||||
private readonly ModFileSystemSelector _selector;
|
||||
private readonly TutorialService _tutorial;
|
||||
private readonly ModManager _modManager;
|
||||
|
||||
private bool _inherited;
|
||||
private ModSettings _settings = null!;
|
||||
private ModCollection _collection = null!;
|
||||
private bool _empty;
|
||||
private int? _currentPriority;
|
||||
|
||||
public ModPanelSettingsTab(CollectionManager collectionManager, ModManager modManager, ModFileSystemSelector selector,
|
||||
TutorialService tutorial, CommunicatorService communicator, Configuration config)
|
||||
{
|
||||
_collectionManager = collectionManager;
|
||||
_communicator = communicator;
|
||||
_modManager = modManager;
|
||||
_selector = selector;
|
||||
_tutorial = tutorial;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> Label
|
||||
=> "Settings"u8;
|
||||
|
||||
public void DrawHeader()
|
||||
=> _tutorial.OpenTutorial(BasicTutorialSteps.ModOptions);
|
||||
=> tutorial.OpenTutorial(BasicTutorialSteps.ModOptions);
|
||||
|
||||
public void Reset()
|
||||
=> _currentPriority = null;
|
||||
|
|
@ -56,53 +42,24 @@ public class ModPanelSettingsTab : ITab
|
|||
if (!child)
|
||||
return;
|
||||
|
||||
_settings = _selector.SelectedSettings;
|
||||
_collection = _selector.SelectedSettingCollection;
|
||||
_inherited = _collection != _collectionManager.Active.Current;
|
||||
_empty = _settings == ModSettings.Empty;
|
||||
|
||||
_settings = selector.SelectedSettings;
|
||||
_collection = selector.SelectedSettingCollection;
|
||||
_inherited = _collection != collectionManager.Active.Current;
|
||||
DrawInheritedWarning();
|
||||
UiHelpers.DefaultLineSpace();
|
||||
_communicator.PreSettingsPanelDraw.Invoke(_selector.Selected!.Identifier);
|
||||
communicator.PreSettingsPanelDraw.Invoke(selector.Selected!.Identifier);
|
||||
DrawEnabledInput();
|
||||
_tutorial.OpenTutorial(BasicTutorialSteps.EnablingMods);
|
||||
tutorial.OpenTutorial(BasicTutorialSteps.EnablingMods);
|
||||
ImGui.SameLine();
|
||||
DrawPriorityInput();
|
||||
_tutorial.OpenTutorial(BasicTutorialSteps.Priority);
|
||||
tutorial.OpenTutorial(BasicTutorialSteps.Priority);
|
||||
DrawRemoveSettings();
|
||||
|
||||
_communicator.PostEnabledDraw.Invoke(_selector.Selected!.Identifier);
|
||||
|
||||
if (_selector.Selected!.Groups.Count > 0)
|
||||
{
|
||||
var useDummy = true;
|
||||
foreach (var (group, idx) in _selector.Selected!.Groups.WithIndex()
|
||||
.Where(g => g.Value.Type == GroupType.Single && g.Value.Options.Count > _config.SingleGroupRadioMax))
|
||||
{
|
||||
ImGuiUtil.Dummy(UiHelpers.DefaultSpace, useDummy);
|
||||
useDummy = false;
|
||||
DrawSingleGroupCombo(group, idx);
|
||||
}
|
||||
|
||||
useDummy = true;
|
||||
foreach (var (group, idx) in _selector.Selected!.Groups.WithIndex().Where(g => g.Value.IsOption))
|
||||
{
|
||||
ImGuiUtil.Dummy(UiHelpers.DefaultSpace, useDummy);
|
||||
useDummy = false;
|
||||
switch (group.Type)
|
||||
{
|
||||
case GroupType.Multi:
|
||||
DrawMultiGroup(group, idx);
|
||||
break;
|
||||
case GroupType.Single when group.Options.Count <= _config.SingleGroupRadioMax:
|
||||
DrawSingleGroupRadio(group, idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
communicator.PostEnabledDraw.Invoke(selector.Selected!.Identifier);
|
||||
|
||||
modGroupDrawer.Draw(selector.Selected!, _settings);
|
||||
UiHelpers.DefaultLineSpace();
|
||||
_communicator.PostSettingsPanelDraw.Invoke(_selector.Selected!.Identifier);
|
||||
communicator.PostSettingsPanelDraw.Invoke(selector.Selected!.Identifier);
|
||||
}
|
||||
|
||||
/// <summary> Draw a big red bar if the current setting is inherited. </summary>
|
||||
|
|
@ -113,8 +70,8 @@ public class ModPanelSettingsTab : ITab
|
|||
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Button, Colors.PressEnterWarningBg);
|
||||
var width = new Vector2(ImGui.GetContentRegionAvail().X, 0);
|
||||
if (ImGui.Button($"These settings are inherited from {_collection.Name}.", width))
|
||||
_collectionManager.Editor.SetModInheritance(_collectionManager.Active.Current, _selector.Selected!, false);
|
||||
if (ImUtf8.Button($"These settings are inherited from {_collection.Name}.", width))
|
||||
collectionManager.Editor.SetModInheritance(collectionManager.Active.Current, selector.Selected!, false);
|
||||
|
||||
ImGuiUtil.HoverTooltip("You can click this button to copy the current settings to the current selection.\n"
|
||||
+ "You can also just change any setting, which will copy the settings with the single setting changed to the current selection.");
|
||||
|
|
@ -127,8 +84,8 @@ public class ModPanelSettingsTab : ITab
|
|||
if (!ImGui.Checkbox("Enabled", ref enabled))
|
||||
return;
|
||||
|
||||
_modManager.SetKnown(_selector.Selected!);
|
||||
_collectionManager.Editor.SetModState(_collectionManager.Active.Current, _selector.Selected!, enabled);
|
||||
modManager.SetKnown(selector.Selected!);
|
||||
collectionManager.Editor.SetModState(collectionManager.Active.Current, selector.Selected!, enabled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -146,7 +103,8 @@ public class ModPanelSettingsTab : ITab
|
|||
if (ImGui.IsItemDeactivatedAfterEdit() && _currentPriority.HasValue)
|
||||
{
|
||||
if (_currentPriority != _settings.Priority.Value)
|
||||
_collectionManager.Editor.SetModPriority(_collectionManager.Active.Current, _selector.Selected!, new ModPriority(_currentPriority.Value));
|
||||
collectionManager.Editor.SetModPriority(collectionManager.Active.Current, selector.Selected!,
|
||||
new ModPriority(_currentPriority.Value));
|
||||
|
||||
_currentPriority = null;
|
||||
}
|
||||
|
|
@ -162,189 +120,15 @@ public class ModPanelSettingsTab : ITab
|
|||
private void DrawRemoveSettings()
|
||||
{
|
||||
const string text = "Inherit Settings";
|
||||
if (_inherited || _empty)
|
||||
if (_inherited || _settings == ModSettings.Empty)
|
||||
return;
|
||||
|
||||
var scroll = ImGui.GetScrollMaxY() > 0 ? ImGui.GetStyle().ScrollbarSize : 0;
|
||||
ImGui.SameLine(ImGui.GetWindowWidth() - ImGui.CalcTextSize(text).X - ImGui.GetStyle().FramePadding.X * 2 - scroll);
|
||||
if (ImGui.Button(text))
|
||||
_collectionManager.Editor.SetModInheritance(_collectionManager.Active.Current, _selector.Selected!, true);
|
||||
collectionManager.Editor.SetModInheritance(collectionManager.Active.Current, selector.Selected!, true);
|
||||
|
||||
ImGuiUtil.HoverTooltip("Remove current settings from this collection so that it can inherit them.\n"
|
||||
+ "If no inherited collection has settings for this mod, it will be disabled.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw a single group selector as a combo box.
|
||||
/// If a description is provided, add a help marker besides it.
|
||||
/// </summary>
|
||||
private void DrawSingleGroupCombo(IModGroup group, int groupIdx)
|
||||
{
|
||||
using var id = ImRaii.PushId(groupIdx);
|
||||
var selectedOption = _empty ? group.DefaultSettings.AsIndex : _settings.Settings[groupIdx].AsIndex;
|
||||
ImGui.SetNextItemWidth(UiHelpers.InputTextWidth.X * 3 / 4);
|
||||
var options = group.Options;
|
||||
using (var combo = ImRaii.Combo(string.Empty, options[selectedOption].Name))
|
||||
{
|
||||
if (combo)
|
||||
for (var idx2 = 0; idx2 < options.Count; ++idx2)
|
||||
{
|
||||
id.Push(idx2);
|
||||
var option = options[idx2];
|
||||
if (ImGui.Selectable(option.Name, idx2 == selectedOption))
|
||||
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx,
|
||||
Setting.Single(idx2));
|
||||
|
||||
if (option.Description.Length > 0)
|
||||
ImGuiUtil.SelectableHelpMarker(option.Description);
|
||||
|
||||
id.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if (group.Description.Length > 0)
|
||||
ImGuiUtil.LabeledHelpMarker(group.Name, group.Description);
|
||||
else
|
||||
ImGui.TextUnformatted(group.Name);
|
||||
}
|
||||
|
||||
// Draw a single group selector as a set of radio buttons.
|
||||
// If a description is provided, add a help marker besides it.
|
||||
private void DrawSingleGroupRadio(IModGroup group, int groupIdx)
|
||||
{
|
||||
using var id = ImRaii.PushId(groupIdx);
|
||||
var selectedOption = _empty ? group.DefaultSettings.AsIndex : _settings.Settings[groupIdx].AsIndex;
|
||||
var minWidth = Widget.BeginFramedGroup(group.Name, group.Description);
|
||||
var options = group.Options;
|
||||
DrawCollapseHandling(options, minWidth, DrawOptions);
|
||||
|
||||
Widget.EndFramedGroup();
|
||||
return;
|
||||
|
||||
void DrawOptions()
|
||||
{
|
||||
for (var idx = 0; idx < group.Options.Count; ++idx)
|
||||
{
|
||||
using var i = ImRaii.PushId(idx);
|
||||
var option = options[idx];
|
||||
if (ImGui.RadioButton(option.Name, selectedOption == idx))
|
||||
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx,
|
||||
Setting.Single(idx));
|
||||
|
||||
if (option.Description.Length <= 0)
|
||||
continue;
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGuiComponents.HelpMarker(option.Description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void DrawCollapseHandling(IReadOnlyList<IModOption> options, float minWidth, Action draw)
|
||||
{
|
||||
if (options.Count <= _config.OptionGroupCollapsibleMin)
|
||||
{
|
||||
draw();
|
||||
}
|
||||
else
|
||||
{
|
||||
var collapseId = ImGui.GetID("Collapse");
|
||||
var shown = ImGui.GetStateStorage().GetBool(collapseId, true);
|
||||
var buttonTextShow = $"Show {options.Count} Options";
|
||||
var buttonTextHide = $"Hide {options.Count} Options";
|
||||
var buttonWidth = Math.Max(ImGui.CalcTextSize(buttonTextShow).X, ImGui.CalcTextSize(buttonTextHide).X)
|
||||
+ 2 * ImGui.GetStyle().FramePadding.X;
|
||||
minWidth = Math.Max(buttonWidth, minWidth);
|
||||
if (shown)
|
||||
{
|
||||
var pos = ImGui.GetCursorPos();
|
||||
ImGui.Dummy(UiHelpers.IconButtonSize);
|
||||
using (var _ = ImRaii.Group())
|
||||
{
|
||||
draw();
|
||||
}
|
||||
|
||||
|
||||
var width = Math.Max(ImGui.GetItemRectSize().X, minWidth);
|
||||
var endPos = ImGui.GetCursorPos();
|
||||
ImGui.SetCursorPos(pos);
|
||||
if (ImGui.Button(buttonTextHide, new Vector2(width, 0)))
|
||||
ImGui.GetStateStorage().SetBool(collapseId, !shown);
|
||||
|
||||
ImGui.SetCursorPos(endPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
var optionWidth = options.Max(o => ImGui.CalcTextSize(o.Name).X)
|
||||
+ ImGui.GetStyle().ItemInnerSpacing.X
|
||||
+ ImGui.GetFrameHeight()
|
||||
+ ImGui.GetStyle().FramePadding.X;
|
||||
var width = Math.Max(optionWidth, minWidth);
|
||||
if (ImGui.Button(buttonTextShow, new Vector2(width, 0)))
|
||||
ImGui.GetStateStorage().SetBool(collapseId, !shown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw a multi group selector as a bordered set of checkboxes.
|
||||
/// If a description is provided, add a help marker in the title.
|
||||
/// </summary>
|
||||
private void DrawMultiGroup(IModGroup group, int groupIdx)
|
||||
{
|
||||
using var id = ImRaii.PushId(groupIdx);
|
||||
var flags = _empty ? group.DefaultSettings : _settings.Settings[groupIdx];
|
||||
var minWidth = Widget.BeginFramedGroup(group.Name, group.Description);
|
||||
var options = group.Options;
|
||||
DrawCollapseHandling(options, minWidth, DrawOptions);
|
||||
|
||||
Widget.EndFramedGroup();
|
||||
var label = $"##multi{groupIdx}";
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||
ImGui.OpenPopup($"##multi{groupIdx}");
|
||||
|
||||
DrawMultiPopup(group, groupIdx, label);
|
||||
return;
|
||||
|
||||
void DrawOptions()
|
||||
{
|
||||
for (var idx = 0; idx < options.Count; ++idx)
|
||||
{
|
||||
using var i = ImRaii.PushId(idx);
|
||||
var option = options[idx];
|
||||
var setting = flags.HasFlag(idx);
|
||||
|
||||
if (ImGui.Checkbox(option.Name, ref setting))
|
||||
{
|
||||
flags = flags.SetBit(idx, setting);
|
||||
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx, flags);
|
||||
}
|
||||
|
||||
if (option.Description.Length > 0)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGuiComponents.HelpMarker(option.Description);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawMultiPopup(IModGroup group, int groupIdx, string label)
|
||||
{
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.PopupBorderSize, 1);
|
||||
using var popup = ImRaii.Popup(label);
|
||||
if (!popup)
|
||||
return;
|
||||
|
||||
ImGui.TextUnformatted(group.Name);
|
||||
ImGui.Separator();
|
||||
if (ImGui.Selectable("Enable All"))
|
||||
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx,
|
||||
Setting.AllBits(group.Options.Count));
|
||||
|
||||
if (ImGui.Selectable("Disable All"))
|
||||
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx, Setting.Zero);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue