mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-22 08:29:19 +01:00
achieve feature parity... I think.
This commit is contained in:
parent
32dbf419e2
commit
d47d31b665
5 changed files with 473 additions and 489 deletions
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
||||||
Subproject commit bc2afed8a873d1f9517eefe7a7296bc5b83e693b
|
Subproject commit 866389b3988d9c4926a786f6c78ac9d5265591ac
|
||||||
|
|
@ -20,7 +20,7 @@ public enum GroupDrawBehaviour
|
||||||
|
|
||||||
public interface IModGroup
|
public interface IModGroup
|
||||||
{
|
{
|
||||||
public const int MaxMultiOptions = 63;
|
public const int MaxMultiOptions = 32;
|
||||||
|
|
||||||
public Mod Mod { get; }
|
public Mod Mod { get; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
|
||||||
114
Penumbra/UI/ModsTab/DescriptionEditPopup.cs
Normal file
114
Penumbra/UI/ModsTab/DescriptionEditPopup.cs
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
using Dalamud.Interface.Utility;
|
||||||
|
using ImGuiNET;
|
||||||
|
using OtterGui.Services;
|
||||||
|
using OtterGui.Text;
|
||||||
|
using Penumbra.Mods;
|
||||||
|
using Penumbra.Mods.Groups;
|
||||||
|
using Penumbra.Mods.Manager;
|
||||||
|
using Penumbra.Mods.SubMods;
|
||||||
|
|
||||||
|
namespace Penumbra.UI.ModsTab;
|
||||||
|
|
||||||
|
public class DescriptionEditPopup(ModManager modManager) : IUiService
|
||||||
|
{
|
||||||
|
private static ReadOnlySpan<byte> PopupId
|
||||||
|
=> "PenumbraEditDescription"u8;
|
||||||
|
|
||||||
|
private bool _hasBeenEdited;
|
||||||
|
private string _description = string.Empty;
|
||||||
|
|
||||||
|
private object? _current;
|
||||||
|
private bool _opened;
|
||||||
|
|
||||||
|
public void Open(Mod mod)
|
||||||
|
{
|
||||||
|
_current = mod;
|
||||||
|
_opened = true;
|
||||||
|
_hasBeenEdited = false;
|
||||||
|
_description = mod.Description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Open(IModGroup group)
|
||||||
|
{
|
||||||
|
_current = group;
|
||||||
|
_opened = true;
|
||||||
|
_hasBeenEdited = false;
|
||||||
|
_description = group.Description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Open(IModOption option)
|
||||||
|
{
|
||||||
|
_current = option;
|
||||||
|
_opened = true;
|
||||||
|
_hasBeenEdited = false;
|
||||||
|
_description = option.Description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw()
|
||||||
|
{
|
||||||
|
if (_current == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_opened)
|
||||||
|
{
|
||||||
|
_opened = false;
|
||||||
|
ImUtf8.OpenPopup(PopupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
var inputSize = ImGuiHelpers.ScaledVector2(800);
|
||||||
|
using var popup = ImUtf8.Popup(PopupId);
|
||||||
|
if (!popup)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ImGui.IsWindowAppearing())
|
||||||
|
ImGui.SetKeyboardFocusHere();
|
||||||
|
|
||||||
|
ImUtf8.InputMultiLineOnDeactivated("##editDescription"u8, ref _description, inputSize);
|
||||||
|
_hasBeenEdited |= ImGui.IsItemEdited();
|
||||||
|
UiHelpers.DefaultLineSpace();
|
||||||
|
|
||||||
|
var buttonSize = new Vector2(ImUtf8.GlobalScale * 100, 0);
|
||||||
|
|
||||||
|
var width = 2 * buttonSize.X
|
||||||
|
+ 4 * ImUtf8.FramePadding.X
|
||||||
|
+ ImUtf8.ItemSpacing.X;
|
||||||
|
|
||||||
|
ImGui.SetCursorPosX((inputSize.X - width) / 2);
|
||||||
|
DrawSaveButton(buttonSize);
|
||||||
|
ImGui.SameLine();
|
||||||
|
DrawCancelButton(buttonSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSaveButton(Vector2 buttonSize)
|
||||||
|
{
|
||||||
|
if (!ImUtf8.ButtonEx("Save"u8, _hasBeenEdited ? [] : "No changes made yet."u8, buttonSize, !_hasBeenEdited))
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (_current)
|
||||||
|
{
|
||||||
|
case Mod mod:
|
||||||
|
modManager.DataEditor.ChangeModDescription(mod, _description);
|
||||||
|
break;
|
||||||
|
case IModGroup group:
|
||||||
|
modManager.OptionEditor.ChangeGroupDescription(group, _description);
|
||||||
|
break;
|
||||||
|
case IModOption option:
|
||||||
|
modManager.OptionEditor.ChangeOptionDescription(option, _description);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_description = string.Empty;
|
||||||
|
_hasBeenEdited = false;
|
||||||
|
ImGui.CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawCancelButton(Vector2 buttonSize)
|
||||||
|
{
|
||||||
|
if (!ImUtf8.Button("Cancel"u8, buttonSize) && !ImGui.IsKeyPressed(ImGuiKey.Escape))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_description = string.Empty;
|
||||||
|
_hasBeenEdited = false;
|
||||||
|
ImGui.CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Internal.Notifications;
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
using Dalamud.Interface.Utility;
|
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
using OtterGui.Classes;
|
using OtterGui.Classes;
|
||||||
using OtterGui.Raii;
|
using OtterGui.Raii;
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
|
using OtterGui.Text;
|
||||||
|
using OtterGui.Text.EndObjects;
|
||||||
using Penumbra.Mods;
|
using Penumbra.Mods;
|
||||||
using Penumbra.Mods.Groups;
|
using Penumbra.Mods.Groups;
|
||||||
using Penumbra.Mods.Manager;
|
using Penumbra.Mods.Manager;
|
||||||
|
|
@ -17,87 +18,79 @@ using Penumbra.UI.Classes;
|
||||||
|
|
||||||
namespace Penumbra.UI.ModsTab;
|
namespace Penumbra.UI.ModsTab;
|
||||||
|
|
||||||
public sealed class ModGroupEditDrawer(ModManager modManager, Configuration config, FilenameService filenames) : IUiService
|
public sealed class ModGroupEditDrawer(
|
||||||
|
ModManager modManager,
|
||||||
|
Configuration config,
|
||||||
|
FilenameService filenames,
|
||||||
|
DescriptionEditPopup descriptionPopup) : IUiService
|
||||||
{
|
{
|
||||||
|
private static ReadOnlySpan<byte> DragDropLabel
|
||||||
|
=> "##DragOption"u8;
|
||||||
|
|
||||||
private Vector2 _buttonSize;
|
private Vector2 _buttonSize;
|
||||||
|
private Vector2 _availableWidth;
|
||||||
private float _priorityWidth;
|
private float _priorityWidth;
|
||||||
private float _groupNameWidth;
|
private float _groupNameWidth;
|
||||||
|
private float _optionNameWidth;
|
||||||
private float _spacing;
|
private float _spacing;
|
||||||
|
private Vector2 _optionIdxSelectable;
|
||||||
|
private bool _deleteEnabled;
|
||||||
|
|
||||||
private string? _currentGroupName;
|
private string? _currentGroupName;
|
||||||
private ModPriority? _currentGroupPriority;
|
private ModPriority? _currentGroupPriority;
|
||||||
private IModGroup? _currentGroupEdited;
|
private IModGroup? _currentGroupEdited;
|
||||||
private bool _isGroupNameValid;
|
private bool _isGroupNameValid = true;
|
||||||
private IModGroup? _deleteGroup;
|
|
||||||
private IModGroup? _moveGroup;
|
|
||||||
private int _moveTo;
|
|
||||||
|
|
||||||
private string? _currentOptionName;
|
private string? _newOptionName;
|
||||||
private ModPriority? _currentOptionPriority;
|
private IModGroup? _newOptionGroup;
|
||||||
private IModOption? _currentOptionEdited;
|
private readonly Queue<Action> _actionQueue = new();
|
||||||
private IModOption? _deleteOption;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
private IModGroup? _dragDropGroup;
|
||||||
private void SameLine()
|
private IModOption? _dragDropOption;
|
||||||
=> ImGui.SameLine(0, _spacing);
|
|
||||||
|
|
||||||
public void Draw(Mod mod)
|
public void Draw(Mod mod)
|
||||||
{
|
{
|
||||||
_buttonSize = new Vector2(ImGui.GetFrameHeight());
|
PrepareStyle();
|
||||||
_priorityWidth = 50 * ImGuiHelpers.GlobalScale;
|
|
||||||
_groupNameWidth = 350f * ImGuiHelpers.GlobalScale;
|
|
||||||
_spacing = ImGui.GetStyle().ItemInnerSpacing.X;
|
|
||||||
|
|
||||||
|
using var id = ImUtf8.PushId("##GroupEdit"u8);
|
||||||
|
foreach (var (group, groupIdx) in mod.Groups.WithIndex())
|
||||||
|
DrawGroup(group, groupIdx);
|
||||||
|
|
||||||
FinishGroupCleanup();
|
while (_actionQueue.TryDequeue(out var action))
|
||||||
}
|
action.Invoke();
|
||||||
|
|
||||||
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)
|
private void DrawGroup(IModGroup group, int idx)
|
||||||
{
|
{
|
||||||
using var id = ImRaii.PushId(idx);
|
using var id = ImUtf8.PushId(idx);
|
||||||
using var frame = ImRaii.FramedGroup($"Group #{idx + 1}");
|
using var frame = ImRaii.FramedGroup($"Group #{idx + 1}");
|
||||||
DrawGroupNameRow(group);
|
DrawGroupNameRow(group, idx);
|
||||||
switch (group)
|
switch (group)
|
||||||
{
|
{
|
||||||
case SingleModGroup s:
|
case SingleModGroup s:
|
||||||
DrawSingleGroup(s, idx);
|
DrawSingleGroup(s);
|
||||||
break;
|
break;
|
||||||
case MultiModGroup m:
|
case MultiModGroup m:
|
||||||
DrawMultiGroup(m, idx);
|
DrawMultiGroup(m);
|
||||||
break;
|
break;
|
||||||
case ImcModGroup i:
|
case ImcModGroup i:
|
||||||
DrawImcGroup(i, idx);
|
DrawImcGroup(i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawGroupNameRow(IModGroup group)
|
private void DrawGroupNameRow(IModGroup group, int idx)
|
||||||
{
|
{
|
||||||
DrawGroupName(group);
|
DrawGroupName(group);
|
||||||
SameLine();
|
ImUtf8.SameLineInner();
|
||||||
|
DrawGroupMoveButtons(group, idx);
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
DrawGroupOpenFile(group, idx);
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
DrawGroupDescription(group);
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
DrawGroupDelete(group);
|
DrawGroupDelete(group);
|
||||||
SameLine();
|
ImUtf8.SameLineInner();
|
||||||
DrawGroupPriority(group);
|
DrawGroupPriority(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,11 +99,11 @@ public sealed class ModGroupEditDrawer(ModManager modManager, Configuration conf
|
||||||
var text = _currentGroupEdited == group ? _currentGroupName ?? group.Name : group.Name;
|
var text = _currentGroupEdited == group ? _currentGroupName ?? group.Name : group.Name;
|
||||||
ImGui.SetNextItemWidth(_groupNameWidth);
|
ImGui.SetNextItemWidth(_groupNameWidth);
|
||||||
using var border = ImRaii.PushFrameBorder(UiHelpers.ScaleX2, Colors.RegexWarningBorder, !_isGroupNameValid);
|
using var border = ImRaii.PushFrameBorder(UiHelpers.ScaleX2, Colors.RegexWarningBorder, !_isGroupNameValid);
|
||||||
if (ImGui.InputText("##GroupName", ref text, 256))
|
if (ImUtf8.InputText("##GroupName"u8, ref text))
|
||||||
{
|
{
|
||||||
_currentGroupEdited = group;
|
_currentGroupEdited = group;
|
||||||
_currentGroupName = text;
|
_currentGroupName = text;
|
||||||
_isGroupNameValid = ModGroupEditor.VerifyFileName(group.Mod, group, text, false);
|
_isGroupNameValid = text == group.Name || ModGroupEditor.VerifyFileName(group.Mod, group, text, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.IsItemDeactivated())
|
if (ImGui.IsItemDeactivated())
|
||||||
|
|
@ -123,20 +116,21 @@ public sealed class ModGroupEditDrawer(ModManager modManager, Configuration conf
|
||||||
}
|
}
|
||||||
|
|
||||||
var tt = _isGroupNameValid
|
var tt = _isGroupNameValid
|
||||||
? "Group Name"
|
? "Change the Group name."u8
|
||||||
: "Current name can not be used for this group.";
|
: "Current name can not be used for this group."u8;
|
||||||
ImGuiUtil.HoverTooltip(tt);
|
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, tt);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawGroupDelete(IModGroup group)
|
private void DrawGroupDelete(IModGroup group)
|
||||||
{
|
{
|
||||||
var enabled = config.DeleteModModifier.IsActive();
|
if (ImUtf8.IconButton(FontAwesomeIcon.Trash, !_deleteEnabled))
|
||||||
var tt = enabled
|
_actionQueue.Enqueue(() => modManager.OptionEditor.DeleteModGroup(group));
|
||||||
? "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))
|
if (_deleteEnabled)
|
||||||
_deleteGroup = group;
|
ImUtf8.HoverTooltip("Delete this option group."u8);
|
||||||
|
else
|
||||||
|
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled,
|
||||||
|
$"Delete this option group.\nHold {config.DeleteModModifier} while clicking to delete.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawGroupPriority(IModGroup group)
|
private void DrawGroupPriority(IModGroup group)
|
||||||
|
|
@ -162,36 +156,41 @@ public sealed class ModGroupEditDrawer(ModManager modManager, Configuration conf
|
||||||
ImGuiUtil.HoverTooltip("Group Priority");
|
ImGuiUtil.HoverTooltip("Group Priority");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void DrawGroupDescription(IModGroup group)
|
||||||
|
{
|
||||||
|
if (ImUtf8.IconButton(FontAwesomeIcon.Edit, "Edit group description."u8))
|
||||||
|
descriptionPopup.Open(group);
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawGroupMoveButtons(IModGroup group, int idx)
|
private void DrawGroupMoveButtons(IModGroup group, int idx)
|
||||||
{
|
{
|
||||||
var isFirst = idx == 0;
|
var isFirst = idx == 0;
|
||||||
var tt = isFirst ? "Can not move this group further upwards." : $"Move this group up to group {idx}.";
|
if (ImUtf8.IconButton(FontAwesomeIcon.ArrowUp, isFirst))
|
||||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.ArrowUp.ToIconString(), UiHelpers.IconButtonSize, tt, isFirst, true))
|
_actionQueue.Enqueue(() => modManager.OptionEditor.MoveModGroup(group, idx - 1));
|
||||||
{
|
|
||||||
_moveGroup = group;
|
|
||||||
_moveTo = idx - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SameLine();
|
if (isFirst)
|
||||||
|
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, "Can not move this group further upwards."u8);
|
||||||
|
else
|
||||||
|
ImUtf8.HoverTooltip($"Move this group up to group {idx}.");
|
||||||
|
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
var isLast = idx == group.Mod.Groups.Count - 1;
|
var isLast = idx == group.Mod.Groups.Count - 1;
|
||||||
tt = isLast
|
if (ImUtf8.IconButton(FontAwesomeIcon.ArrowDown, isLast))
|
||||||
? "Can not move this group further downwards."
|
_actionQueue.Enqueue(() => modManager.OptionEditor.MoveModGroup(group, idx + 1));
|
||||||
: $"Move this group down to group {idx + 2}.";
|
|
||||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.ArrowDown.ToIconString(), UiHelpers.IconButtonSize, tt, isLast, true))
|
if (isLast)
|
||||||
{
|
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, "Can not move this group further downwards."u8);
|
||||||
_moveGroup = group;
|
else
|
||||||
_moveTo = idx + 1;
|
ImUtf8.HoverTooltip($"Move this group down to group {idx + 2}.");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawGroupOpenFile(IModGroup group, int idx)
|
private void DrawGroupOpenFile(IModGroup group, int idx)
|
||||||
{
|
{
|
||||||
var fileName = filenames.OptionGroupFile(group.Mod, idx, config.ReplaceNonAsciiOnImport);
|
var fileName = filenames.OptionGroupFile(group.Mod, idx, config.ReplaceNonAsciiOnImport);
|
||||||
var fileExists = File.Exists(fileName);
|
var fileExists = File.Exists(fileName);
|
||||||
var tt = fileExists
|
if (ImUtf8.IconButton(FontAwesomeIcon.FileExport, !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
|
try
|
||||||
{
|
{
|
||||||
Process.Start(new ProcessStartInfo(fileName) { UseShellExecute = true });
|
Process.Start(new ProcessStartInfo(fileName) { UseShellExecute = true });
|
||||||
|
|
@ -200,15 +199,274 @@ public sealed class ModGroupEditDrawer(ModManager modManager, Configuration conf
|
||||||
{
|
{
|
||||||
Penumbra.Messager.NotificationMessage(e, "Could not open editor.", NotificationType.Error);
|
Penumbra.Messager.NotificationMessage(e, "Could not open editor.", NotificationType.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fileExists)
|
||||||
|
ImUtf8.HoverTooltip($"Open the {group.Name} json file in the text editor of your choice.");
|
||||||
|
else
|
||||||
|
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"The {group.Name} json file does not exist.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawSingleGroup(SingleModGroup group)
|
||||||
|
{
|
||||||
|
foreach (var (option, optionIdx) in group.OptionData.WithIndex())
|
||||||
|
{
|
||||||
|
using var id = ImRaii.PushId(optionIdx);
|
||||||
|
DrawOptionPosition(group, option, optionIdx);
|
||||||
|
|
||||||
private void DrawSingleGroup(SingleModGroup group, int idx)
|
ImUtf8.SameLineInner();
|
||||||
{ }
|
DrawOptionDefaultSingleBehaviour(group, option, optionIdx);
|
||||||
|
|
||||||
private void DrawMultiGroup(MultiModGroup group, int idx)
|
ImUtf8.SameLineInner();
|
||||||
{ }
|
DrawOptionName(option);
|
||||||
|
|
||||||
private void DrawImcGroup(ImcModGroup group, int idx)
|
ImUtf8.SameLineInner();
|
||||||
{ }
|
DrawOptionDescription(option);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
DrawOptionDelete(option);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
ImGui.Dummy(new Vector2(_priorityWidth, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawNewOption(group);
|
||||||
|
var convertible = group.Options.Count <= IModGroup.MaxMultiOptions;
|
||||||
|
if (ImUtf8.ButtonEx("Convert to Multi Group", _availableWidth, !convertible))
|
||||||
|
_actionQueue.Enqueue(() => modManager.OptionEditor.SingleEditor.ChangeToMulti(group));
|
||||||
|
if (!convertible)
|
||||||
|
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled,
|
||||||
|
"Can not convert to multi group since maximum number of options is exceeded."u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawMultiGroup(MultiModGroup group)
|
||||||
|
{
|
||||||
|
foreach (var (option, optionIdx) in group.OptionData.WithIndex())
|
||||||
|
{
|
||||||
|
using var id = ImRaii.PushId(optionIdx);
|
||||||
|
DrawOptionPosition(group, option, optionIdx);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
DrawOptionDefaultMultiBehaviour(group, option, optionIdx);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
DrawOptionName(option);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
DrawOptionDescription(option);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
DrawOptionDelete(option);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
DrawOptionPriority(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawNewOption(group);
|
||||||
|
if (ImUtf8.Button("Convert to Single Group"u8, _availableWidth))
|
||||||
|
_actionQueue.Enqueue(() => modManager.OptionEditor.MultiEditor.ChangeToSingle(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawImcGroup(ImcModGroup group)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void DrawOptionPosition(IModGroup group, IModOption option, int optionIdx)
|
||||||
|
{
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImUtf8.Selectable($"Option #{optionIdx + 1}", false, size: _optionIdxSelectable);
|
||||||
|
Target(group, optionIdx);
|
||||||
|
Source(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void DrawOptionDefaultSingleBehaviour(IModGroup group, IModOption option, int optionIdx)
|
||||||
|
{
|
||||||
|
var isDefaultOption = group.DefaultSettings.AsIndex == optionIdx;
|
||||||
|
if (ImUtf8.RadioButton("##default"u8, isDefaultOption))
|
||||||
|
modManager.OptionEditor.ChangeModGroupDefaultOption(group, Setting.Single(optionIdx));
|
||||||
|
ImUtf8.HoverTooltip($"Set {option.Name} as the default choice for this group.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void DrawOptionDefaultMultiBehaviour(IModGroup group, IModOption option, int optionIdx)
|
||||||
|
{
|
||||||
|
var isDefaultOption = group.DefaultSettings.HasFlag(optionIdx);
|
||||||
|
if (ImUtf8.Checkbox("##default"u8, ref isDefaultOption))
|
||||||
|
modManager.OptionEditor.ChangeModGroupDefaultOption(group, group.DefaultSettings.SetBit(optionIdx, isDefaultOption));
|
||||||
|
ImUtf8.HoverTooltip($"{(isDefaultOption ? "Disable"u8 : "Enable"u8)} {option.Name} per default in this group.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void DrawOptionDescription(IModOption option)
|
||||||
|
{
|
||||||
|
if (ImUtf8.IconButton(FontAwesomeIcon.Edit, "Edit option description."u8))
|
||||||
|
descriptionPopup.Open(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void DrawOptionPriority(MultiSubMod option)
|
||||||
|
{
|
||||||
|
var priority = option.Priority.Value;
|
||||||
|
ImGui.SetNextItemWidth(_priorityWidth);
|
||||||
|
if (ImUtf8.InputScalarOnDeactivated("##Priority"u8, ref priority))
|
||||||
|
modManager.OptionEditor.MultiEditor.ChangeOptionPriority(option, new ModPriority(priority));
|
||||||
|
ImUtf8.HoverTooltip("Option priority inside the mod."u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void DrawOptionName(IModOption option)
|
||||||
|
{
|
||||||
|
var name = option.Name;
|
||||||
|
ImGui.SetNextItemWidth(_optionNameWidth);
|
||||||
|
if (ImUtf8.InputTextOnDeactivated("##Name"u8, ref name))
|
||||||
|
modManager.OptionEditor.RenameOption(option, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void DrawOptionDelete(IModOption option)
|
||||||
|
{
|
||||||
|
if (ImUtf8.IconButton(FontAwesomeIcon.Trash, !_deleteEnabled))
|
||||||
|
_actionQueue.Enqueue(() => modManager.OptionEditor.DeleteOption(option));
|
||||||
|
|
||||||
|
if (_deleteEnabled)
|
||||||
|
ImUtf8.HoverTooltip("Delete this option."u8);
|
||||||
|
else
|
||||||
|
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled,
|
||||||
|
$"Delete this option.\nHold {config.DeleteModModifier} while clicking to delete.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawNewOption(SingleModGroup group)
|
||||||
|
{
|
||||||
|
var count = group.Options.Count;
|
||||||
|
if (count >= int.MaxValue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DrawNewOptionBase(group, count);
|
||||||
|
|
||||||
|
var validName = _newOptionName?.Length > 0;
|
||||||
|
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, validName
|
||||||
|
? "Add a new option to this group."u8
|
||||||
|
: "Please enter a name for the new option."u8, !validName))
|
||||||
|
{
|
||||||
|
modManager.OptionEditor.SingleEditor.AddOption(group, _newOptionName!);
|
||||||
|
_newOptionName = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawNewOption(MultiModGroup group)
|
||||||
|
{
|
||||||
|
var count = group.Options.Count;
|
||||||
|
if (count >= IModGroup.MaxMultiOptions)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DrawNewOptionBase(group, count);
|
||||||
|
|
||||||
|
var validName = _newOptionName?.Length > 0;
|
||||||
|
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, validName
|
||||||
|
? "Add a new option to this group."u8
|
||||||
|
: "Please enter a name for the new option."u8, !validName))
|
||||||
|
{
|
||||||
|
modManager.OptionEditor.MultiEditor.AddOption(group, _newOptionName!);
|
||||||
|
_newOptionName = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawNewOption(ImcModGroup group)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void DrawNewOptionBase(IModGroup group, int count)
|
||||||
|
{
|
||||||
|
ImUtf8.Selectable($"Option #{count + 1}", false, size: _optionIdxSelectable);
|
||||||
|
Target(group, count);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
ImUtf8.IconDummy();
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
ImGui.SetNextItemWidth(_optionNameWidth);
|
||||||
|
var newName = _newOptionGroup == group
|
||||||
|
? _newOptionName ?? string.Empty
|
||||||
|
: string.Empty;
|
||||||
|
if (ImUtf8.InputText("##newOption"u8, ref newName, "Add new option..."u8))
|
||||||
|
{
|
||||||
|
_newOptionName = newName;
|
||||||
|
_newOptionGroup = group;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void Source(IModOption option)
|
||||||
|
{
|
||||||
|
if (option.Group is not ITexToolsGroup)
|
||||||
|
return;
|
||||||
|
|
||||||
|
using var source = ImUtf8.DragDropSource();
|
||||||
|
if (!source)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!DragDropSource.SetPayload(DragDropLabel))
|
||||||
|
{
|
||||||
|
_dragDropGroup = option.Group;
|
||||||
|
_dragDropOption = option;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.TextUnformatted($"Dragging option {option.Name} from group {option.Group.Name}...");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Target(IModGroup group, int optionIdx)
|
||||||
|
{
|
||||||
|
if (group is not ITexToolsGroup)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_dragDropGroup != group && _dragDropGroup != null && group is MultiModGroup { Options.Count: >= IModGroup.MaxMultiOptions })
|
||||||
|
return;
|
||||||
|
|
||||||
|
using var target = ImRaii.DragDropTarget();
|
||||||
|
if (!target.Success || !DragDropTarget.CheckPayload(DragDropLabel))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_dragDropGroup != null && _dragDropOption != null)
|
||||||
|
{
|
||||||
|
if (_dragDropGroup == group)
|
||||||
|
{
|
||||||
|
var sourceOption = _dragDropOption;
|
||||||
|
_actionQueue.Enqueue(() => modManager.OptionEditor.MoveOption(sourceOption, optionIdx));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Move from one group to another by deleting, then adding, then moving the option.
|
||||||
|
var sourceOption = _dragDropOption;
|
||||||
|
_actionQueue.Enqueue(() =>
|
||||||
|
{
|
||||||
|
modManager.OptionEditor.DeleteOption(sourceOption);
|
||||||
|
if (modManager.OptionEditor.AddOption(group, sourceOption) is { } newOption)
|
||||||
|
modManager.OptionEditor.MoveOption(newOption, optionIdx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_dragDropGroup = null;
|
||||||
|
_dragDropOption = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void PrepareStyle()
|
||||||
|
{
|
||||||
|
var totalWidth = 400f * ImUtf8.GlobalScale;
|
||||||
|
_buttonSize = new Vector2(ImUtf8.FrameHeight);
|
||||||
|
_priorityWidth = 50 * ImUtf8.GlobalScale;
|
||||||
|
_availableWidth = new Vector2(totalWidth + 3 * _spacing + 2 * _buttonSize.X + _priorityWidth, 0);
|
||||||
|
_groupNameWidth = totalWidth - 3 * (_buttonSize.X + _spacing);
|
||||||
|
_spacing = ImGui.GetStyle().ItemInnerSpacing.X;
|
||||||
|
_optionIdxSelectable = ImUtf8.CalcTextSize("Option #88."u8);
|
||||||
|
_optionNameWidth = totalWidth - _optionIdxSelectable.X - _buttonSize.X - 2 * _spacing;
|
||||||
|
_deleteEnabled = config.DeleteModModifier.IsActive();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,17 @@
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Components;
|
using Dalamud.Interface.Components;
|
||||||
using Dalamud.Interface.Internal.Notifications;
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
using Dalamud.Interface.Utility;
|
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
using OtterGui.Raii;
|
using OtterGui.Raii;
|
||||||
using OtterGui.Widgets;
|
using OtterGui.Widgets;
|
||||||
using OtterGui.Classes;
|
using OtterGui.Classes;
|
||||||
using Penumbra.Api.Enums;
|
|
||||||
using Penumbra.Mods;
|
using Penumbra.Mods;
|
||||||
using Penumbra.Mods.Editor;
|
using Penumbra.Mods.Editor;
|
||||||
using Penumbra.Mods.Manager;
|
using Penumbra.Mods.Manager;
|
||||||
using Penumbra.Services;
|
using Penumbra.Services;
|
||||||
using Penumbra.UI.AdvancedWindow;
|
using Penumbra.UI.AdvancedWindow;
|
||||||
using Penumbra.Mods.Groups;
|
|
||||||
using Penumbra.Mods.Settings;
|
using Penumbra.Mods.Settings;
|
||||||
using Penumbra.Mods.SubMods;
|
|
||||||
using Penumbra.Mods.Manager.OptionEditor;
|
using Penumbra.Mods.Manager.OptionEditor;
|
||||||
|
|
||||||
namespace Penumbra.UI.ModsTab;
|
namespace Penumbra.UI.ModsTab;
|
||||||
|
|
@ -30,15 +26,13 @@ public class ModPanelEditTab(
|
||||||
FilenameService filenames,
|
FilenameService filenames,
|
||||||
ModExportManager modExportManager,
|
ModExportManager modExportManager,
|
||||||
Configuration config,
|
Configuration config,
|
||||||
PredefinedTagManager predefinedTagManager)
|
PredefinedTagManager predefinedTagManager,
|
||||||
|
ModGroupEditDrawer groupEditDrawer,
|
||||||
|
DescriptionEditPopup descriptionPopup)
|
||||||
: ITab
|
: ITab
|
||||||
{
|
{
|
||||||
private readonly ModManager _modManager = modManager;
|
|
||||||
|
|
||||||
private readonly TagButtons _modTags = new();
|
private readonly TagButtons _modTags = new();
|
||||||
|
|
||||||
private Vector2 _cellPadding = Vector2.Zero;
|
|
||||||
private Vector2 _itemSpacing = Vector2.Zero;
|
|
||||||
private ModFileSystem.Leaf _leaf = null!;
|
private ModFileSystem.Leaf _leaf = null!;
|
||||||
private Mod _mod = null!;
|
private Mod _mod = null!;
|
||||||
|
|
||||||
|
|
@ -54,9 +48,6 @@ public class ModPanelEditTab(
|
||||||
_leaf = selector.SelectedLeaf!;
|
_leaf = selector.SelectedLeaf!;
|
||||||
_mod = selector.Selected!;
|
_mod = selector.Selected!;
|
||||||
|
|
||||||
_cellPadding = ImGui.GetStyle().CellPadding with { X = 2 * UiHelpers.Scale };
|
|
||||||
_itemSpacing = ImGui.GetStyle().CellPadding with { X = 4 * UiHelpers.Scale };
|
|
||||||
|
|
||||||
EditButtons();
|
EditButtons();
|
||||||
EditRegularMeta();
|
EditRegularMeta();
|
||||||
UiHelpers.DefaultLineSpace();
|
UiHelpers.DefaultLineSpace();
|
||||||
|
|
@ -77,21 +68,18 @@ public class ModPanelEditTab(
|
||||||
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, rightEndOffset: sharedTagButtonOffset);
|
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)
|
if (sharedTagsEnabled)
|
||||||
predefinedTagManager.DrawAddFromSharedTagsAndUpdateTags(selector.Selected!.LocalTags, selector.Selected!.ModTags, false,
|
predefinedTagManager.DrawAddFromSharedTagsAndUpdateTags(selector.Selected!.LocalTags, selector.Selected!.ModTags, false,
|
||||||
selector.Selected!);
|
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)
|
groupEditDrawer.Draw(_mod);
|
||||||
EditGroup(groupIdx);
|
descriptionPopup.Draw();
|
||||||
|
|
||||||
EndActions();
|
|
||||||
DescriptionEdit.DrawPopup(_modManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
|
|
@ -99,7 +87,6 @@ public class ModPanelEditTab(
|
||||||
AddOptionGroup.Reset();
|
AddOptionGroup.Reset();
|
||||||
MoveDirectory.Reset();
|
MoveDirectory.Reset();
|
||||||
Input.Reset();
|
Input.Reset();
|
||||||
OptionTable.Reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> The general edit row for non-detailed mod edits. </summary>
|
/// <summary> The general edit row for non-detailed mod edits. </summary>
|
||||||
|
|
@ -117,10 +104,10 @@ public class ModPanelEditTab(
|
||||||
if (ImGuiUtil.DrawDisabledButton("Reload Mod", buttonSize, "Reload the current mod from its files.\n"
|
if (ImGuiUtil.DrawDisabledButton("Reload Mod", buttonSize, "Reload the current mod from its files.\n"
|
||||||
+ "If the mod directory or meta file do not exist anymore or if the new mod name is empty, the mod is deleted instead.",
|
+ "If the mod directory or meta file do not exist anymore or if the new mod name is empty, the mod is deleted instead.",
|
||||||
false))
|
false))
|
||||||
_modManager.ReloadMod(_mod);
|
modManager.ReloadMod(_mod);
|
||||||
|
|
||||||
BackupButtons(buttonSize);
|
BackupButtons(buttonSize);
|
||||||
MoveDirectory.Draw(_modManager, _mod, buttonSize);
|
MoveDirectory.Draw(modManager, _mod, buttonSize);
|
||||||
|
|
||||||
UiHelpers.DefaultLineSpace();
|
UiHelpers.DefaultLineSpace();
|
||||||
DrawUpdateBibo(buttonSize);
|
DrawUpdateBibo(buttonSize);
|
||||||
|
|
@ -169,7 +156,7 @@ public class ModPanelEditTab(
|
||||||
: $"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)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
@ -186,24 +173,24 @@ public class ModPanelEditTab(
|
||||||
private void EditRegularMeta()
|
private void EditRegularMeta()
|
||||||
{
|
{
|
||||||
if (Input.Text("Name", Input.Name, Input.None, _mod.Name, out var newName, 256, UiHelpers.InputTextWidth.X))
|
if (Input.Text("Name", Input.Name, Input.None, _mod.Name, out var newName, 256, UiHelpers.InputTextWidth.X))
|
||||||
_modManager.DataEditor.ChangeModName(_mod, newName);
|
modManager.DataEditor.ChangeModName(_mod, newName);
|
||||||
|
|
||||||
if (Input.Text("Author", Input.Author, Input.None, _mod.Author, out var newAuthor, 256, UiHelpers.InputTextWidth.X))
|
if (Input.Text("Author", Input.Author, Input.None, _mod.Author, out var newAuthor, 256, UiHelpers.InputTextWidth.X))
|
||||||
_modManager.DataEditor.ChangeModAuthor(_mod, newAuthor);
|
modManager.DataEditor.ChangeModAuthor(_mod, newAuthor);
|
||||||
|
|
||||||
if (Input.Text("Version", Input.Version, Input.None, _mod.Version, out var newVersion, 32,
|
if (Input.Text("Version", Input.Version, Input.None, _mod.Version, out var newVersion, 32,
|
||||||
UiHelpers.InputTextWidth.X))
|
UiHelpers.InputTextWidth.X))
|
||||||
_modManager.DataEditor.ChangeModVersion(_mod, newVersion);
|
modManager.DataEditor.ChangeModVersion(_mod, newVersion);
|
||||||
|
|
||||||
if (Input.Text("Website", Input.Website, Input.None, _mod.Website, out var newWebsite, 256,
|
if (Input.Text("Website", Input.Website, Input.None, _mod.Website, out var newWebsite, 256,
|
||||||
UiHelpers.InputTextWidth.X))
|
UiHelpers.InputTextWidth.X))
|
||||||
_modManager.DataEditor.ChangeModWebsite(_mod, newWebsite);
|
modManager.DataEditor.ChangeModWebsite(_mod, newWebsite);
|
||||||
|
|
||||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(UiHelpers.ScaleX3));
|
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(UiHelpers.ScaleX3));
|
||||||
|
|
||||||
var reducedSize = new Vector2(UiHelpers.InputTextMinusButton3, 0);
|
var reducedSize = new Vector2(UiHelpers.InputTextMinusButton3, 0);
|
||||||
if (ImGui.Button("Edit Description", reducedSize))
|
if (ImGui.Button("Edit Description", reducedSize))
|
||||||
_delayedActions.Enqueue(() => DescriptionEdit.OpenPopup(_mod, Input.Description));
|
descriptionPopup.Open(_mod);
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
var fileExists = File.Exists(filenames.ModMetaPath(_mod));
|
var fileExists = File.Exists(filenames.ModMetaPath(_mod));
|
||||||
|
|
@ -215,16 +202,6 @@ public class ModPanelEditTab(
|
||||||
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>
|
|
||||||
private readonly Queue<Action> _delayedActions = new();
|
|
||||||
|
|
||||||
/// <summary> Delete a marked group or option outside of iteration. </summary>
|
|
||||||
private void EndActions()
|
|
||||||
{
|
|
||||||
while (_delayedActions.TryDequeue(out var action))
|
|
||||||
action.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Text input to add a new option group at the end of the current groups. </summary>
|
/// <summary> Text input to add a new option group at the end of the current groups. </summary>
|
||||||
private static class AddOptionGroup
|
private static class AddOptionGroup
|
||||||
{
|
{
|
||||||
|
|
@ -309,372 +286,6 @@ public class ModPanelEditTab(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Open a popup to edit a multi-line mod or option description. </summary>
|
|
||||||
private static class DescriptionEdit
|
|
||||||
{
|
|
||||||
private const string PopupName = "Edit Description";
|
|
||||||
private static string _newDescription = string.Empty;
|
|
||||||
private static string _oldDescription = string.Empty;
|
|
||||||
private static int _newDescriptionIdx = -1;
|
|
||||||
private static int _newDescriptionOptionIdx = -1;
|
|
||||||
private static Mod? _mod;
|
|
||||||
|
|
||||||
public static void OpenPopup(Mod mod, int groupIdx, int optionIdx = -1)
|
|
||||||
{
|
|
||||||
_newDescriptionIdx = groupIdx;
|
|
||||||
_newDescriptionOptionIdx = optionIdx;
|
|
||||||
_newDescription = groupIdx < 0
|
|
||||||
? mod.Description
|
|
||||||
: optionIdx < 0
|
|
||||||
? mod.Groups[groupIdx].Description
|
|
||||||
: mod.Groups[groupIdx].Options[optionIdx].Description;
|
|
||||||
_oldDescription = _newDescription;
|
|
||||||
|
|
||||||
_mod = mod;
|
|
||||||
ImGui.OpenPopup(PopupName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DrawPopup(ModManager modManager)
|
|
||||||
{
|
|
||||||
if (_mod == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using var popup = ImRaii.Popup(PopupName);
|
|
||||||
if (!popup)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (ImGui.IsWindowAppearing())
|
|
||||||
ImGui.SetKeyboardFocusHere();
|
|
||||||
|
|
||||||
ImGui.InputTextMultiline("##editDescription", ref _newDescription, 4096, ImGuiHelpers.ScaledVector2(800, 800));
|
|
||||||
UiHelpers.DefaultLineSpace();
|
|
||||||
|
|
||||||
var buttonSize = ImGuiHelpers.ScaledVector2(100, 0);
|
|
||||||
var width = 2 * buttonSize.X
|
|
||||||
+ 4 * ImGui.GetStyle().FramePadding.X
|
|
||||||
+ ImGui.GetStyle().ItemSpacing.X;
|
|
||||||
ImGui.SetCursorPosX((800 * UiHelpers.Scale - width) / 2);
|
|
||||||
|
|
||||||
var tooltip = _newDescription != _oldDescription ? string.Empty : "No changes made yet.";
|
|
||||||
|
|
||||||
if (ImGuiUtil.DrawDisabledButton("Save", buttonSize, tooltip, tooltip.Length > 0))
|
|
||||||
{
|
|
||||||
switch (_newDescriptionIdx)
|
|
||||||
{
|
|
||||||
case Input.Description:
|
|
||||||
modManager.DataEditor.ChangeModDescription(_mod, _newDescription);
|
|
||||||
break;
|
|
||||||
case >= 0:
|
|
||||||
if (_newDescriptionOptionIdx < 0)
|
|
||||||
modManager.OptionEditor.ChangeGroupDescription(_mod.Groups[_newDescriptionIdx], _newDescription);
|
|
||||||
else
|
|
||||||
modManager.OptionEditor.ChangeOptionDescription(_mod.Groups[_newDescriptionIdx].Options[_newDescriptionOptionIdx],
|
|
||||||
_newDescription);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.CloseCurrentPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (!ImGui.Button("Cancel", buttonSize)
|
|
||||||
&& !ImGui.IsKeyPressed(ImGuiKey.Escape))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_newDescriptionIdx = Input.None;
|
|
||||||
_newDescription = string.Empty;
|
|
||||||
ImGui.CloseCurrentPopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EditGroup(int groupIdx)
|
|
||||||
{
|
|
||||||
var group = _mod.Groups[groupIdx];
|
|
||||||
using var id = ImRaii.PushId(groupIdx);
|
|
||||||
using var frame = ImRaii.FramedGroup($"Group #{groupIdx + 1}");
|
|
||||||
|
|
||||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.CellPadding, _cellPadding)
|
|
||||||
.Push(ImGuiStyleVar.ItemSpacing, _itemSpacing);
|
|
||||||
|
|
||||||
if (Input.Text("##Name", groupIdx, Input.None, group.Name, out var newGroupName, 256, UiHelpers.InputTextWidth.X))
|
|
||||||
_modManager.OptionEditor.RenameModGroup(group, newGroupName);
|
|
||||||
|
|
||||||
ImGuiUtil.HoverTooltip("Group Name");
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), UiHelpers.IconButtonSize,
|
|
||||||
"Delete this option group.\nHold Control while clicking to delete.", !ImGui.GetIO().KeyCtrl, true))
|
|
||||||
_delayedActions.Enqueue(() => _modManager.OptionEditor.DeleteModGroup(group));
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
if (Input.Priority("##Priority", groupIdx, Input.None, group.Priority, out var priority, 50 * UiHelpers.Scale))
|
|
||||||
_modManager.OptionEditor.ChangeGroupPriority(group, priority);
|
|
||||||
|
|
||||||
ImGuiUtil.HoverTooltip("Group Priority");
|
|
||||||
|
|
||||||
DrawGroupCombo(group, groupIdx);
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
var tt = groupIdx == 0 ? "Can not move this group further upwards." : $"Move this group up to group {groupIdx}.";
|
|
||||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.ArrowUp.ToIconString(), UiHelpers.IconButtonSize,
|
|
||||||
tt, groupIdx == 0, true))
|
|
||||||
_delayedActions.Enqueue(() => _modManager.OptionEditor.MoveModGroup(group, groupIdx - 1));
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
tt = groupIdx == _mod.Groups.Count - 1
|
|
||||||
? "Can not move this group further downwards."
|
|
||||||
: $"Move this group down to group {groupIdx + 2}.";
|
|
||||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.ArrowDown.ToIconString(), UiHelpers.IconButtonSize,
|
|
||||||
tt, groupIdx == _mod.Groups.Count - 1, true))
|
|
||||||
_delayedActions.Enqueue(() => _modManager.OptionEditor.MoveModGroup(group, groupIdx + 1));
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Edit.ToIconString(), UiHelpers.IconButtonSize,
|
|
||||||
"Edit group description.", false, true))
|
|
||||||
_delayedActions.Enqueue(() => DescriptionEdit.OpenPopup(_mod, groupIdx));
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
var fileName = filenames.OptionGroupFile(_mod, groupIdx, config.ReplaceNonAsciiOnImport);
|
|
||||||
var fileExists = File.Exists(fileName);
|
|
||||||
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))
|
|
||||||
Process.Start(new ProcessStartInfo(fileName) { UseShellExecute = true });
|
|
||||||
|
|
||||||
UiHelpers.DefaultLineSpace();
|
|
||||||
|
|
||||||
OptionTable.Draw(this, groupIdx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Draw the table displaying all options and the add new option line. </summary>
|
|
||||||
private static class OptionTable
|
|
||||||
{
|
|
||||||
private const string DragDropLabel = "##DragOption";
|
|
||||||
|
|
||||||
private static int _newOptionNameIdx = -1;
|
|
||||||
private static string _newOptionName = string.Empty;
|
|
||||||
private static IModGroup? _dragDropGroup;
|
|
||||||
private static IModOption? _dragDropOption;
|
|
||||||
|
|
||||||
public static void Reset()
|
|
||||||
{
|
|
||||||
_newOptionNameIdx = -1;
|
|
||||||
_newOptionName = string.Empty;
|
|
||||||
_dragDropGroup = null;
|
|
||||||
_dragDropOption = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Draw(ModPanelEditTab panel, int groupIdx)
|
|
||||||
{
|
|
||||||
using var table = ImRaii.Table(string.Empty, 6, ImGuiTableFlags.SizingFixedFit);
|
|
||||||
if (!table)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var maxWidth = ImGui.CalcTextSize("Option #88.").X;
|
|
||||||
ImGui.TableSetupColumn("idx", ImGuiTableColumnFlags.WidthFixed, maxWidth);
|
|
||||||
ImGui.TableSetupColumn("default", ImGuiTableColumnFlags.WidthFixed, ImGui.GetFrameHeight());
|
|
||||||
ImGui.TableSetupColumn("name", ImGuiTableColumnFlags.WidthFixed,
|
|
||||||
UiHelpers.InputTextWidth.X - maxWidth - 12 * UiHelpers.Scale - ImGui.GetFrameHeight() - UiHelpers.IconButtonSize.X);
|
|
||||||
ImGui.TableSetupColumn("description", ImGuiTableColumnFlags.WidthFixed, UiHelpers.IconButtonSize.X);
|
|
||||||
ImGui.TableSetupColumn("delete", ImGuiTableColumnFlags.WidthFixed, UiHelpers.IconButtonSize.X);
|
|
||||||
ImGui.TableSetupColumn("priority", ImGuiTableColumnFlags.WidthFixed, 50 * UiHelpers.Scale);
|
|
||||||
|
|
||||||
switch (panel._mod.Groups[groupIdx])
|
|
||||||
{
|
|
||||||
case SingleModGroup single:
|
|
||||||
for (var optionIdx = 0; optionIdx < single.OptionData.Count; ++optionIdx)
|
|
||||||
EditOption(panel, single, groupIdx, optionIdx);
|
|
||||||
break;
|
|
||||||
case MultiModGroup multi:
|
|
||||||
for (var optionIdx = 0; optionIdx < multi.OptionData.Count; ++optionIdx)
|
|
||||||
EditOption(panel, multi, groupIdx, optionIdx);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawNewOption(panel, groupIdx, UiHelpers.IconButtonSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Draw a line for a single option. </summary>
|
|
||||||
private static void EditOption(ModPanelEditTab panel, IModGroup group, int groupIdx, int optionIdx)
|
|
||||||
{
|
|
||||||
var option = group.Options[optionIdx];
|
|
||||||
using var id = ImRaii.PushId(optionIdx);
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
ImGui.AlignTextToFramePadding();
|
|
||||||
ImGui.Selectable($"Option #{optionIdx + 1}");
|
|
||||||
Source(option);
|
|
||||||
Target(panel, group, optionIdx);
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
|
|
||||||
|
|
||||||
if (group.Type == GroupType.Single)
|
|
||||||
{
|
|
||||||
if (ImGui.RadioButton("##default", group.DefaultSettings.AsIndex == optionIdx))
|
|
||||||
panel._modManager.OptionEditor.ChangeModGroupDefaultOption(group, Setting.Single(optionIdx));
|
|
||||||
|
|
||||||
ImGuiUtil.HoverTooltip($"Set {option.Name} as the default choice for this group.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var isDefaultOption = group.DefaultSettings.HasFlag(optionIdx);
|
|
||||||
if (ImGui.Checkbox("##default", ref isDefaultOption))
|
|
||||||
panel._modManager.OptionEditor.ChangeModGroupDefaultOption(group, group.DefaultSettings.SetBit(optionIdx, isDefaultOption));
|
|
||||||
|
|
||||||
ImGuiUtil.HoverTooltip($"{(isDefaultOption ? "Disable" : "Enable")} {option.Name} per default in this group.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
if (Input.Text("##Name", groupIdx, optionIdx, option.Name, out var newOptionName, 256, -1))
|
|
||||||
panel._modManager.OptionEditor.RenameOption(option, newOptionName);
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Edit.ToIconString(), UiHelpers.IconButtonSize, "Edit option description.",
|
|
||||||
false, true))
|
|
||||||
panel._delayedActions.Enqueue(() => DescriptionEdit.OpenPopup(panel._mod, groupIdx, optionIdx));
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), UiHelpers.IconButtonSize,
|
|
||||||
"Delete this option.\nHold Control while clicking to delete.", !ImGui.GetIO().KeyCtrl, true))
|
|
||||||
panel._delayedActions.Enqueue(() => panel._modManager.OptionEditor.DeleteOption(option));
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
if (option is not MultiSubMod multi)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (Input.Priority("##Priority", groupIdx, optionIdx, multi.Priority, out var priority,
|
|
||||||
50 * UiHelpers.Scale))
|
|
||||||
panel._modManager.OptionEditor.MultiEditor.ChangeOptionPriority(multi, priority);
|
|
||||||
|
|
||||||
ImGuiUtil.HoverTooltip("Option priority.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Draw the line to add a new option. </summary>
|
|
||||||
private static void DrawNewOption(ModPanelEditTab panel, int groupIdx, Vector2 iconButtonSize)
|
|
||||||
{
|
|
||||||
var mod = panel._mod;
|
|
||||||
var group = mod.Groups[groupIdx];
|
|
||||||
var count = group switch
|
|
||||||
{
|
|
||||||
SingleModGroup single => single.OptionData.Count,
|
|
||||||
MultiModGroup multi => multi.OptionData.Count,
|
|
||||||
_ => throw new Exception($"Dragging options to an option group of type {group.GetType()} is not supported."),
|
|
||||||
};
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
ImGui.AlignTextToFramePadding();
|
|
||||||
ImGui.Selectable($"Option #{count + 1}");
|
|
||||||
Target(panel, group, count);
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
ImGui.SetNextItemWidth(-1);
|
|
||||||
var tmp = _newOptionNameIdx == groupIdx ? _newOptionName : string.Empty;
|
|
||||||
if (ImGui.InputTextWithHint("##newOption", "Add new option...", ref tmp, 256))
|
|
||||||
{
|
|
||||||
_newOptionName = tmp;
|
|
||||||
_newOptionNameIdx = groupIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
var canAddGroup = mod.Groups[groupIdx].Type != GroupType.Multi || count < IModGroup.MaxMultiOptions;
|
|
||||||
var validName = _newOptionName.Length > 0 && _newOptionNameIdx == groupIdx;
|
|
||||||
var tt = canAddGroup
|
|
||||||
? validName ? "Add a new option to this group." : "Please enter a name for the new option."
|
|
||||||
: $"Can not add more than {IModGroup.MaxMultiOptions} options to a multi group.";
|
|
||||||
if (!ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), iconButtonSize,
|
|
||||||
tt, !(canAddGroup && validName), true))
|
|
||||||
return;
|
|
||||||
|
|
||||||
panel._modManager.OptionEditor.AddOption(group, _newOptionName);
|
|
||||||
_newOptionName = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle drag and drop to move options inside a group or into another group.
|
|
||||||
private static void Source(IModOption option)
|
|
||||||
{
|
|
||||||
if (option.Group is not ITexToolsGroup)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using var source = ImRaii.DragDropSource();
|
|
||||||
if (!source)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (ImGui.SetDragDropPayload(DragDropLabel, IntPtr.Zero, 0))
|
|
||||||
{
|
|
||||||
_dragDropGroup = option.Group;
|
|
||||||
_dragDropOption = option;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.TextUnformatted($"Dragging option {option.Name} from group {option.Group.Name}...");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Target(ModPanelEditTab panel, IModGroup group, int optionIdx)
|
|
||||||
{
|
|
||||||
if (group is not ITexToolsGroup)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using var target = ImRaii.DragDropTarget();
|
|
||||||
if (!target.Success || !ImGuiUtil.IsDropping(DragDropLabel))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_dragDropGroup != null && _dragDropOption != null)
|
|
||||||
{
|
|
||||||
if (_dragDropGroup == group)
|
|
||||||
{
|
|
||||||
var sourceOption = _dragDropOption;
|
|
||||||
panel._delayedActions.Enqueue(
|
|
||||||
() => panel._modManager.OptionEditor.MoveOption(sourceOption, optionIdx));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Move from one group to another by deleting, then adding, then moving the option.
|
|
||||||
var sourceOption = _dragDropOption;
|
|
||||||
panel._delayedActions.Enqueue(() =>
|
|
||||||
{
|
|
||||||
panel._modManager.OptionEditor.DeleteOption(sourceOption);
|
|
||||||
if (panel._modManager.OptionEditor.AddOption(group, sourceOption) is { } newOption)
|
|
||||||
panel._modManager.OptionEditor.MoveOption(newOption, optionIdx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_dragDropGroup = null;
|
|
||||||
_dragDropOption = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Draw a combo to select single or multi group and switch between them. </summary>
|
|
||||||
private void DrawGroupCombo(IModGroup group, int groupIdx)
|
|
||||||
{
|
|
||||||
ImGui.SetNextItemWidth(UiHelpers.InputTextWidth.X - 2 * UiHelpers.IconButtonSize.X - 2 * ImGui.GetStyle().ItemSpacing.X);
|
|
||||||
using var combo = ImRaii.Combo("##GroupType", GroupTypeName(group.Type));
|
|
||||||
if (!combo)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (ImGui.Selectable(GroupTypeName(GroupType.Single), group.Type == GroupType.Single) && group is MultiModGroup m)
|
|
||||||
_modManager.OptionEditor.MultiEditor.ChangeToSingle(m);
|
|
||||||
|
|
||||||
var canSwitchToMulti = group.Options.Count <= IModGroup.MaxMultiOptions;
|
|
||||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.Alpha, 0.5f, !canSwitchToMulti);
|
|
||||||
if (ImGui.Selectable(GroupTypeName(GroupType.Multi), group.Type == GroupType.Multi) && canSwitchToMulti && group is SingleModGroup s)
|
|
||||||
_modManager.OptionEditor.SingleEditor.ChangeToMulti(s);
|
|
||||||
|
|
||||||
style.Pop();
|
|
||||||
if (!canSwitchToMulti)
|
|
||||||
ImGuiUtil.HoverTooltip($"Can not convert group to multi group since it has more than {IModGroup.MaxMultiOptions} options.");
|
|
||||||
return;
|
|
||||||
|
|
||||||
static string GroupTypeName(GroupType type)
|
|
||||||
=> type switch
|
|
||||||
{
|
|
||||||
GroupType.Single => "Single Group",
|
|
||||||
GroupType.Multi => "Multi Group",
|
|
||||||
_ => "Unknown",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Handles input text and integers in separate fields without buffers for every single one. </summary>
|
/// <summary> Handles input text and integers in separate fields without buffers for every single one. </summary>
|
||||||
private static class Input
|
private static class Input
|
||||||
{
|
{
|
||||||
|
|
@ -705,6 +316,7 @@ public class ModPanelEditTab(
|
||||||
{
|
{
|
||||||
var tmp = field == _currentField && option == _optionIndex ? _currentEdit ?? oldValue : oldValue;
|
var tmp = field == _currentField && option == _optionIndex ? _currentEdit ?? oldValue : oldValue;
|
||||||
ImGui.SetNextItemWidth(width);
|
ImGui.SetNextItemWidth(width);
|
||||||
|
|
||||||
if (ImGui.InputText(label, ref tmp, maxLength))
|
if (ImGui.InputText(label, ref tmp, maxLength))
|
||||||
{
|
{
|
||||||
_currentEdit = tmp;
|
_currentEdit = tmp;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue