Fix a bunch of issues.

This commit is contained in:
Ottermandias 2024-05-03 18:52:52 +02:00
parent 9084b43e3e
commit 36fc251d5b
14 changed files with 513 additions and 266 deletions

@ -1 +1 @@
Subproject commit 1b0b5469f792999e5b412d4f0c3ed77d9994d7b7
Subproject commit 595ac5722c9c400bea36110503ed2ae7b02d1489

View file

@ -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 = "");

View file

@ -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()

View file

@ -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));

View file

@ -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()

View file

@ -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>

View file

@ -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>

View file

@ -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)

View file

@ -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>

View file

@ -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;
}

View file

@ -162,7 +162,6 @@ public static class StaticServiceManager
.AddSingleton<CollectionSelectHeader>()
.AddSingleton<ImportPopup>()
.AddSingleton<ModPanelDescriptionTab>()
.AddSingleton<ModPanelSettingsTab>()
.AddSingleton<ModPanelEditTab>()
.AddSingleton<ModPanelChangedItemsTab>()
.AddSingleton<ModPanelConflictsTab>()

View 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);
}

View 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)
{ }
}

View file

@ -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);
}
}