mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-21 07:59:22 +01:00
Cleanup group drawing somewhat.
This commit is contained in:
parent
e85b84dafe
commit
2585de8b21
17 changed files with 841 additions and 782 deletions
|
|
@ -1,4 +1,3 @@
|
||||||
using System;
|
|
||||||
using Dalamud.Interface.Internal.Notifications;
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
using OtterGui.Classes;
|
using OtterGui.Classes;
|
||||||
|
|
@ -279,19 +278,8 @@ public class ModNormalizer(ModManager _modManager, Configuration _config)
|
||||||
private void ApplyRedirections()
|
private void ApplyRedirections()
|
||||||
{
|
{
|
||||||
foreach (var (group, groupIdx) in Mod.Groups.WithIndex())
|
foreach (var (group, groupIdx) in Mod.Groups.WithIndex())
|
||||||
{
|
foreach (var (container, containerIdx) in group.DataContainers.WithIndex())
|
||||||
switch (group)
|
_modManager.OptionEditor.SetFiles(container, _redirections[groupIdx + 1][containerIdx]);
|
||||||
{
|
|
||||||
case SingleModGroup single:
|
|
||||||
foreach (var (option, optionIdx) in single.OptionData.WithIndex())
|
|
||||||
_modManager.OptionEditor.SetFiles(option, _redirections[groupIdx + 1][optionIdx]);
|
|
||||||
break;
|
|
||||||
case MultiModGroup multi:
|
|
||||||
foreach (var (option, optionIdx) in multi.OptionData.WithIndex())
|
|
||||||
_modManager.OptionEditor.SetFiles(option, _redirections[groupIdx + 1][optionIdx]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
++Step;
|
++Step;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using Penumbra.Meta.Manipulations;
|
||||||
using Penumbra.Mods.Settings;
|
using Penumbra.Mods.Settings;
|
||||||
using Penumbra.Mods.SubMods;
|
using Penumbra.Mods.SubMods;
|
||||||
using Penumbra.String.Classes;
|
using Penumbra.String.Classes;
|
||||||
|
using Penumbra.UI.ModsTab.Groups;
|
||||||
|
|
||||||
namespace Penumbra.Mods.Groups;
|
namespace Penumbra.Mods.Groups;
|
||||||
|
|
||||||
|
|
@ -40,6 +41,8 @@ public interface IModGroup
|
||||||
|
|
||||||
public int GetIndex();
|
public int GetIndex();
|
||||||
|
|
||||||
|
public IModGroupEditDrawer EditDrawer(ModGroupEditDrawer editDrawer);
|
||||||
|
|
||||||
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations);
|
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations);
|
||||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, object?> changedItems);
|
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, object?> changedItems);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ using Penumbra.Meta.Manipulations;
|
||||||
using Penumbra.Mods.Settings;
|
using Penumbra.Mods.Settings;
|
||||||
using Penumbra.Mods.SubMods;
|
using Penumbra.Mods.SubMods;
|
||||||
using Penumbra.String.Classes;
|
using Penumbra.String.Classes;
|
||||||
|
using Penumbra.UI.ModsTab;
|
||||||
|
using Penumbra.UI.ModsTab.Groups;
|
||||||
using Penumbra.Util;
|
using Penumbra.Util;
|
||||||
|
|
||||||
namespace Penumbra.Mods.Groups;
|
namespace Penumbra.Mods.Groups;
|
||||||
|
|
@ -89,6 +91,9 @@ public class ImcModGroup(Mod mod) : IModGroup
|
||||||
public int GetIndex()
|
public int GetIndex()
|
||||||
=> ModGroup.GetIndex(this);
|
=> ModGroup.GetIndex(this);
|
||||||
|
|
||||||
|
public IModGroupEditDrawer EditDrawer(ModGroupEditDrawer editDrawer)
|
||||||
|
=> new ImcModGroupEditDrawer(editDrawer, this);
|
||||||
|
|
||||||
private ushort GetCurrentMask(Setting setting)
|
private ushort GetCurrentMask(Setting setting)
|
||||||
{
|
{
|
||||||
var mask = DefaultEntry.AttributeMask;
|
var mask = DefaultEntry.AttributeMask;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using Penumbra.Meta.Manipulations;
|
||||||
using Penumbra.Mods.Settings;
|
using Penumbra.Mods.Settings;
|
||||||
using Penumbra.Mods.SubMods;
|
using Penumbra.Mods.SubMods;
|
||||||
using Penumbra.String.Classes;
|
using Penumbra.String.Classes;
|
||||||
|
using Penumbra.UI.ModsTab.Groups;
|
||||||
using Penumbra.Util;
|
using Penumbra.Util;
|
||||||
|
|
||||||
namespace Penumbra.Mods.Groups;
|
namespace Penumbra.Mods.Groups;
|
||||||
|
|
@ -107,6 +108,9 @@ public sealed class MultiModGroup(Mod mod) : IModGroup, ITexToolsGroup
|
||||||
public int GetIndex()
|
public int GetIndex()
|
||||||
=> ModGroup.GetIndex(this);
|
=> ModGroup.GetIndex(this);
|
||||||
|
|
||||||
|
public IModGroupEditDrawer EditDrawer(ModGroupEditDrawer editDrawer)
|
||||||
|
=> new MultiModGroupEditDrawer(editDrawer, this);
|
||||||
|
|
||||||
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
|
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
|
||||||
{
|
{
|
||||||
foreach (var (option, index) in OptionData.WithIndex().OrderByDescending(o => o.Value.Priority))
|
foreach (var (option, index) in OptionData.WithIndex().OrderByDescending(o => o.Value.Priority))
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using Penumbra.Meta.Manipulations;
|
||||||
using Penumbra.Mods.Settings;
|
using Penumbra.Mods.Settings;
|
||||||
using Penumbra.Mods.SubMods;
|
using Penumbra.Mods.SubMods;
|
||||||
using Penumbra.String.Classes;
|
using Penumbra.String.Classes;
|
||||||
|
using Penumbra.UI.ModsTab.Groups;
|
||||||
using Penumbra.Util;
|
using Penumbra.Util;
|
||||||
|
|
||||||
namespace Penumbra.Mods.Groups;
|
namespace Penumbra.Mods.Groups;
|
||||||
|
|
@ -93,6 +94,9 @@ public sealed class SingleModGroup(Mod mod) : IModGroup, ITexToolsGroup
|
||||||
public int GetIndex()
|
public int GetIndex()
|
||||||
=> ModGroup.GetIndex(this);
|
=> ModGroup.GetIndex(this);
|
||||||
|
|
||||||
|
public IModGroupEditDrawer EditDrawer(ModGroupEditDrawer editDrawer)
|
||||||
|
=> new SingleModGroupEditDrawer(editDrawer, this);
|
||||||
|
|
||||||
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
|
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
|
||||||
{
|
{
|
||||||
if (OptionData.Count == 0)
|
if (OptionData.Count == 0)
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,8 @@ public class ModGroupEditor(
|
||||||
SingleModGroupEditor singleEditor,
|
SingleModGroupEditor singleEditor,
|
||||||
MultiModGroupEditor multiEditor,
|
MultiModGroupEditor multiEditor,
|
||||||
ImcModGroupEditor imcEditor,
|
ImcModGroupEditor imcEditor,
|
||||||
CommunicatorService Communicator,
|
CommunicatorService communicator,
|
||||||
SaveService SaveService,
|
SaveService saveService,
|
||||||
Configuration Config) : IService
|
Configuration Config) : IService
|
||||||
{
|
{
|
||||||
public SingleModGroupEditor SingleEditor
|
public SingleModGroupEditor SingleEditor
|
||||||
|
|
@ -57,8 +57,8 @@ public class ModGroupEditor(
|
||||||
return;
|
return;
|
||||||
|
|
||||||
group.DefaultSettings = defaultOption;
|
group.DefaultSettings = defaultOption;
|
||||||
SaveService.QueueSave(new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
saveService.QueueSave(new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
||||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.DefaultOptionChanged, group.Mod, group, null, null, -1);
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.DefaultOptionChanged, group.Mod, group, null, null, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Rename an option group if possible. </summary>
|
/// <summary> Rename an option group if possible. </summary>
|
||||||
|
|
@ -68,10 +68,10 @@ public class ModGroupEditor(
|
||||||
if (oldName == newName || !VerifyFileName(group.Mod, group, newName, true))
|
if (oldName == newName || !VerifyFileName(group.Mod, group, newName, true))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SaveService.ImmediateDelete(new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
saveService.ImmediateDelete(new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
||||||
group.Name = newName;
|
group.Name = newName;
|
||||||
SaveService.ImmediateSave(new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
saveService.ImmediateSave(new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
||||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupRenamed, group.Mod, group, null, null, -1);
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupRenamed, group.Mod, group, null, null, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Delete a given option group. Fires an event to prepare before actually deleting. </summary>
|
/// <summary> Delete a given option group. Fires an event to prepare before actually deleting. </summary>
|
||||||
|
|
@ -79,10 +79,10 @@ public class ModGroupEditor(
|
||||||
{
|
{
|
||||||
var mod = group.Mod;
|
var mod = group.Mod;
|
||||||
var idx = group.GetIndex();
|
var idx = group.GetIndex();
|
||||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, group, null, null, -1);
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, group, null, null, -1);
|
||||||
mod.Groups.RemoveAt(idx);
|
mod.Groups.RemoveAt(idx);
|
||||||
SaveService.SaveAllOptionGroups(mod, false, Config.ReplaceNonAsciiOnImport);
|
saveService.SaveAllOptionGroups(mod, false, Config.ReplaceNonAsciiOnImport);
|
||||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupDeleted, mod, null, null, null, idx);
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupDeleted, mod, null, null, null, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Move the index of a given option group. </summary>
|
/// <summary> Move the index of a given option group. </summary>
|
||||||
|
|
@ -93,8 +93,8 @@ public class ModGroupEditor(
|
||||||
if (!mod.Groups.Move(idxFrom, groupIdxTo))
|
if (!mod.Groups.Move(idxFrom, groupIdxTo))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SaveService.SaveAllOptionGroups(mod, false, Config.ReplaceNonAsciiOnImport);
|
saveService.SaveAllOptionGroups(mod, false, Config.ReplaceNonAsciiOnImport);
|
||||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupMoved, mod, group, null, null, idxFrom);
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupMoved, mod, group, null, null, idxFrom);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change the internal priority of the given option group. </summary>
|
/// <summary> Change the internal priority of the given option group. </summary>
|
||||||
|
|
@ -104,8 +104,8 @@ public class ModGroupEditor(
|
||||||
return;
|
return;
|
||||||
|
|
||||||
group.Priority = newPriority;
|
group.Priority = newPriority;
|
||||||
SaveService.QueueSave(new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
saveService.QueueSave(new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
||||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.PriorityChanged, group.Mod, group, null, null, -1);
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PriorityChanged, group.Mod, group, null, null, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change the description of the given option group. </summary>
|
/// <summary> Change the description of the given option group. </summary>
|
||||||
|
|
@ -115,8 +115,8 @@ public class ModGroupEditor(
|
||||||
return;
|
return;
|
||||||
|
|
||||||
group.Description = newDescription;
|
group.Description = newDescription;
|
||||||
SaveService.QueueSave(new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
saveService.QueueSave(new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
||||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, group.Mod, group, null, null, -1);
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, group.Mod, group, null, null, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Rename the given option. </summary>
|
/// <summary> Rename the given option. </summary>
|
||||||
|
|
@ -126,8 +126,8 @@ public class ModGroupEditor(
|
||||||
return;
|
return;
|
||||||
|
|
||||||
option.Name = newName;
|
option.Name = newName;
|
||||||
SaveService.QueueSave(new ModSaveGroup(option.Group, Config.ReplaceNonAsciiOnImport));
|
saveService.QueueSave(new ModSaveGroup(option.Group, Config.ReplaceNonAsciiOnImport));
|
||||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, option.Mod, option.Group, option, null, -1);
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, option.Mod, option.Group, option, null, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change the description of the given option. </summary>
|
/// <summary> Change the description of the given option. </summary>
|
||||||
|
|
@ -137,8 +137,8 @@ public class ModGroupEditor(
|
||||||
return;
|
return;
|
||||||
|
|
||||||
option.Description = newDescription;
|
option.Description = newDescription;
|
||||||
SaveService.QueueSave(new ModSaveGroup(option.Group, Config.ReplaceNonAsciiOnImport));
|
saveService.QueueSave(new ModSaveGroup(option.Group, Config.ReplaceNonAsciiOnImport));
|
||||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, option.Mod, option.Group, option, null, -1);
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, option.Mod, option.Group, option, null, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Set the meta manipulations for a given option. Replaces existing manipulations. </summary>
|
/// <summary> Set the meta manipulations for a given option. Replaces existing manipulations. </summary>
|
||||||
|
|
@ -148,10 +148,10 @@ public class ModGroupEditor(
|
||||||
&& subMod.Manipulations.All(m => manipulations.TryGetValue(m, out var old) && old.EntryEquals(m)))
|
&& subMod.Manipulations.All(m => manipulations.TryGetValue(m, out var old) && old.EntryEquals(m)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
||||||
subMod.Manipulations.SetTo(manipulations);
|
subMod.Manipulations.SetTo(manipulations);
|
||||||
SaveService.Save(saveType, new ModSaveGroup(subMod, Config.ReplaceNonAsciiOnImport));
|
saveService.Save(saveType, new ModSaveGroup(subMod, Config.ReplaceNonAsciiOnImport));
|
||||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Set the file redirections for a given option. Replaces existing redirections. </summary>
|
/// <summary> Set the file redirections for a given option. Replaces existing redirections. </summary>
|
||||||
|
|
@ -160,10 +160,10 @@ public class ModGroupEditor(
|
||||||
if (subMod.Files.SetEquals(replacements))
|
if (subMod.Files.SetEquals(replacements))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
||||||
subMod.Files.SetTo(replacements);
|
subMod.Files.SetTo(replacements);
|
||||||
SaveService.Save(saveType, new ModSaveGroup(subMod, Config.ReplaceNonAsciiOnImport));
|
saveService.Save(saveType, new ModSaveGroup(subMod, Config.ReplaceNonAsciiOnImport));
|
||||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionFilesChanged, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionFilesChanged, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Add additional file redirections to a given option, keeping already existing ones. Only fires an event if anything is actually added.</summary>
|
/// <summary> Add additional file redirections to a given option, keeping already existing ones. Only fires an event if anything is actually added.</summary>
|
||||||
|
|
@ -173,8 +173,8 @@ public class ModGroupEditor(
|
||||||
subMod.Files.AddFrom(additions);
|
subMod.Files.AddFrom(additions);
|
||||||
if (oldCount != subMod.Files.Count)
|
if (oldCount != subMod.Files.Count)
|
||||||
{
|
{
|
||||||
SaveService.QueueSave(new ModSaveGroup(subMod, Config.ReplaceNonAsciiOnImport));
|
saveService.QueueSave(new ModSaveGroup(subMod, Config.ReplaceNonAsciiOnImport));
|
||||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionFilesAdded, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionFilesAdded, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,10 +184,10 @@ public class ModGroupEditor(
|
||||||
if (subMod.FileSwaps.SetEquals(swaps))
|
if (subMod.FileSwaps.SetEquals(swaps))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
||||||
subMod.FileSwaps.SetTo(swaps);
|
subMod.FileSwaps.SetTo(swaps);
|
||||||
SaveService.Save(saveType, new ModSaveGroup(subMod, Config.ReplaceNonAsciiOnImport));
|
saveService.Save(saveType, new ModSaveGroup(subMod, Config.ReplaceNonAsciiOnImport));
|
||||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionSwapsChanged, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionSwapsChanged, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Verify that a new option group name is unique in this mod. </summary>
|
/// <summary> Verify that a new option group name is unique in this mod. </summary>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ using Penumbra.Mods.Manager;
|
||||||
using Penumbra.Mods.Manager.OptionEditor;
|
using Penumbra.Mods.Manager.OptionEditor;
|
||||||
using Penumbra.UI.Classes;
|
using Penumbra.UI.Classes;
|
||||||
|
|
||||||
namespace Penumbra.UI.ModsTab;
|
namespace Penumbra.UI.ModsTab.Groups;
|
||||||
|
|
||||||
public class AddGroupDrawer : IUiService
|
public class AddGroupDrawer : IUiService
|
||||||
{
|
{
|
||||||
6
Penumbra/UI/ModsTab/Groups/IModGroupEditDrawer.cs
Normal file
6
Penumbra/UI/ModsTab/Groups/IModGroupEditDrawer.cs
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
namespace Penumbra.UI.ModsTab.Groups;
|
||||||
|
|
||||||
|
public interface IModGroupEditDrawer
|
||||||
|
{
|
||||||
|
public void Draw();
|
||||||
|
}
|
||||||
138
Penumbra/UI/ModsTab/Groups/ImcModGroupEditDrawer.cs
Normal file
138
Penumbra/UI/ModsTab/Groups/ImcModGroupEditDrawer.cs
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
using Dalamud.Interface;
|
||||||
|
using ImGuiNET;
|
||||||
|
using OtterGui;
|
||||||
|
using OtterGui.Classes;
|
||||||
|
using OtterGui.Raii;
|
||||||
|
using OtterGui.Text;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Structs;
|
||||||
|
using Penumbra.Mods.Groups;
|
||||||
|
using Penumbra.Mods.Manager.OptionEditor;
|
||||||
|
using Penumbra.Mods.SubMods;
|
||||||
|
|
||||||
|
namespace Penumbra.UI.ModsTab.Groups;
|
||||||
|
|
||||||
|
public readonly struct ImcModGroupEditDrawer(ModGroupEditDrawer editor, ImcModGroup group) : IModGroupEditDrawer
|
||||||
|
{
|
||||||
|
public void Draw()
|
||||||
|
{
|
||||||
|
using (ImUtf8.Group())
|
||||||
|
{
|
||||||
|
ImUtf8.Text("Object Type"u8);
|
||||||
|
if (group.ObjectType is ObjectType.Equipment or ObjectType.Accessory or ObjectType.DemiHuman)
|
||||||
|
ImUtf8.Text("Slot"u8);
|
||||||
|
ImUtf8.Text("Primary ID");
|
||||||
|
if (group.ObjectType is not ObjectType.Equipment and not ObjectType.Accessory)
|
||||||
|
ImUtf8.Text("Secondary ID");
|
||||||
|
ImUtf8.Text("Variant"u8);
|
||||||
|
|
||||||
|
ImUtf8.TextFrameAligned("Material ID"u8);
|
||||||
|
ImUtf8.TextFrameAligned("Material Animation ID"u8);
|
||||||
|
ImUtf8.TextFrameAligned("Decal ID"u8);
|
||||||
|
ImUtf8.TextFrameAligned("VFX ID"u8);
|
||||||
|
ImUtf8.TextFrameAligned("Sound ID"u8);
|
||||||
|
ImUtf8.TextFrameAligned("Can Be Disabled"u8);
|
||||||
|
ImUtf8.TextFrameAligned("Default Attributes"u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
|
||||||
|
var attributeCache = new ImcAttributeCache(group);
|
||||||
|
|
||||||
|
using (ImUtf8.Group())
|
||||||
|
{
|
||||||
|
ImUtf8.Text(group.ObjectType.ToName());
|
||||||
|
if (group.ObjectType is ObjectType.Equipment or ObjectType.Accessory or ObjectType.DemiHuman)
|
||||||
|
ImUtf8.Text(group.EquipSlot.ToName());
|
||||||
|
ImUtf8.Text($"{group.PrimaryId.Id}");
|
||||||
|
if (group.ObjectType is not ObjectType.Equipment and not ObjectType.Accessory)
|
||||||
|
ImUtf8.Text($"{group.SecondaryId.Id}");
|
||||||
|
ImUtf8.Text($"{group.Variant.Id}");
|
||||||
|
|
||||||
|
ImUtf8.TextFrameAligned($"{group.DefaultEntry.MaterialId}");
|
||||||
|
ImUtf8.TextFrameAligned($"{group.DefaultEntry.MaterialAnimationId}");
|
||||||
|
ImUtf8.TextFrameAligned($"{group.DefaultEntry.DecalId}");
|
||||||
|
ImUtf8.TextFrameAligned($"{group.DefaultEntry.VfxId}");
|
||||||
|
ImUtf8.TextFrameAligned($"{group.DefaultEntry.SoundId}");
|
||||||
|
|
||||||
|
var canBeDisabled = group.CanBeDisabled;
|
||||||
|
if (ImUtf8.Checkbox("##disabled"u8, ref canBeDisabled))
|
||||||
|
editor.ModManager.OptionEditor.ImcEditor.ChangeCanBeDisabled(group, canBeDisabled, SaveType.Queue);
|
||||||
|
|
||||||
|
var defaultDisabled = group.DefaultDisabled;
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
if (ImUtf8.Checkbox("##defaultDisabled"u8, ref defaultDisabled))
|
||||||
|
editor.ModManager.OptionEditor.ChangeModGroupDefaultOption(group,
|
||||||
|
group.DefaultSettings.SetBit(ImcModGroup.DisabledIndex, defaultDisabled));
|
||||||
|
|
||||||
|
DrawAttributes(editor.ModManager.OptionEditor.ImcEditor, attributeCache, group.DefaultEntry.AttributeMask, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var (option, optionIdx) in group.OptionData.WithIndex())
|
||||||
|
{
|
||||||
|
using var id = ImRaii.PushId(optionIdx);
|
||||||
|
editor.DrawOptionPosition(group, option, optionIdx);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
editor.DrawOptionDefaultMultiBehaviour(group, option, optionIdx);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
editor.DrawOptionName(option);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
editor.DrawOptionDescription(option);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
editor.DrawOptionDelete(option);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
ImGui.Dummy(new Vector2(editor.PriorityWidth, 0));
|
||||||
|
|
||||||
|
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + editor.OptionIdxSelectable.X + ImUtf8.ItemInnerSpacing.X * 2 + ImUtf8.FrameHeight);
|
||||||
|
DrawAttributes(editor.ModManager.OptionEditor.ImcEditor, attributeCache, option.AttributeMask, option);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawNewOption(attributeCache);
|
||||||
|
return;
|
||||||
|
|
||||||
|
static void DrawAttributes(ImcModGroupEditor editor, in ImcAttributeCache cache, ushort mask, object data)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < ImcEntry.NumAttributes; ++i)
|
||||||
|
{
|
||||||
|
using var id = ImRaii.PushId(i);
|
||||||
|
var value = (mask & 1 << i) != 0;
|
||||||
|
using (ImRaii.Disabled(!cache.CanChange(i)))
|
||||||
|
{
|
||||||
|
if (ImUtf8.Checkbox(TerminatedByteString.Empty, ref value))
|
||||||
|
{
|
||||||
|
if (data is ImcModGroup g)
|
||||||
|
editor.ChangeDefaultAttribute(g, cache, i, value);
|
||||||
|
else
|
||||||
|
editor.ChangeOptionAttribute((ImcSubMod)data, cache, i, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImUtf8.HoverTooltip($"{(char)('A' + i)}");
|
||||||
|
if (i != 9)
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawNewOption(in ImcAttributeCache cache)
|
||||||
|
{
|
||||||
|
if (cache.LowestUnsetMask == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var name = editor.DrawNewOptionBase(group, group.Options.Count);
|
||||||
|
var validName = name.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))
|
||||||
|
{
|
||||||
|
editor.ModManager.OptionEditor.ImcEditor.AddOption(group, cache, name);
|
||||||
|
editor.NewOptionName = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,7 @@ using Penumbra.Mods.Groups;
|
||||||
using Penumbra.Mods.Settings;
|
using Penumbra.Mods.Settings;
|
||||||
using Penumbra.Mods.SubMods;
|
using Penumbra.Mods.SubMods;
|
||||||
|
|
||||||
namespace Penumbra.UI.ModsTab;
|
namespace Penumbra.UI.ModsTab.Groups;
|
||||||
|
|
||||||
public sealed class ModGroupDrawer(Configuration config, CollectionManager collectionManager) : IUiService
|
public sealed class ModGroupDrawer(Configuration config, CollectionManager collectionManager) : IUiService
|
||||||
{
|
{
|
||||||
360
Penumbra/UI/ModsTab/Groups/ModGroupEditDrawer.cs
Normal file
360
Penumbra/UI/ModsTab/Groups/ModGroupEditDrawer.cs
Normal file
|
|
@ -0,0 +1,360 @@
|
||||||
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
|
using ImGuiNET;
|
||||||
|
using OtterGui;
|
||||||
|
using OtterGui.Classes;
|
||||||
|
using OtterGui.Raii;
|
||||||
|
using OtterGui.Services;
|
||||||
|
using OtterGui.Text;
|
||||||
|
using OtterGui.Text.EndObjects;
|
||||||
|
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.Groups;
|
||||||
|
|
||||||
|
public sealed class ModGroupEditDrawer(
|
||||||
|
ModManager modManager,
|
||||||
|
Configuration config,
|
||||||
|
FilenameService filenames,
|
||||||
|
DescriptionEditPopup descriptionPopup) : IUiService
|
||||||
|
{
|
||||||
|
private static ReadOnlySpan<byte> DragDropLabel
|
||||||
|
=> "##DragOption"u8;
|
||||||
|
|
||||||
|
internal readonly ModManager ModManager = modManager;
|
||||||
|
internal readonly Queue<Action> ActionQueue = new();
|
||||||
|
|
||||||
|
internal Vector2 OptionIdxSelectable;
|
||||||
|
internal Vector2 AvailableWidth;
|
||||||
|
internal float PriorityWidth;
|
||||||
|
|
||||||
|
internal string? NewOptionName;
|
||||||
|
private IModGroup? _newOptionGroup;
|
||||||
|
|
||||||
|
private Vector2 _buttonSize;
|
||||||
|
private float _groupNameWidth;
|
||||||
|
private float _optionNameWidth;
|
||||||
|
private float _spacing;
|
||||||
|
private bool _deleteEnabled;
|
||||||
|
|
||||||
|
private string? _currentGroupName;
|
||||||
|
private ModPriority? _currentGroupPriority;
|
||||||
|
private IModGroup? _currentGroupEdited;
|
||||||
|
private bool _isGroupNameValid = true;
|
||||||
|
|
||||||
|
private IModGroup? _dragDropGroup;
|
||||||
|
private IModOption? _dragDropOption;
|
||||||
|
|
||||||
|
public void Draw(Mod mod)
|
||||||
|
{
|
||||||
|
PrepareStyle();
|
||||||
|
|
||||||
|
using var id = ImUtf8.PushId("##GroupEdit"u8);
|
||||||
|
foreach (var (group, groupIdx) in mod.Groups.WithIndex())
|
||||||
|
DrawGroup(group, groupIdx);
|
||||||
|
|
||||||
|
while (ActionQueue.TryDequeue(out var action))
|
||||||
|
action.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawGroup(IModGroup group, int idx)
|
||||||
|
{
|
||||||
|
using var id = ImUtf8.PushId(idx);
|
||||||
|
using var frame = ImRaii.FramedGroup($"Group #{idx + 1}");
|
||||||
|
DrawGroupNameRow(group, idx);
|
||||||
|
group.EditDrawer(this).Draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawGroupNameRow(IModGroup group, int idx)
|
||||||
|
{
|
||||||
|
DrawGroupName(group);
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
DrawGroupMoveButtons(group, idx);
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
DrawGroupOpenFile(group, idx);
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
DrawGroupDescription(group);
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
DrawGroupDelete(group);
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
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 (ImUtf8.InputText("##GroupName"u8, ref text))
|
||||||
|
{
|
||||||
|
_currentGroupEdited = group;
|
||||||
|
_currentGroupName = text;
|
||||||
|
_isGroupNameValid = text == group.Name || 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
|
||||||
|
? "Change the Group name."u8
|
||||||
|
: "Current name can not be used for this group."u8;
|
||||||
|
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, tt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawGroupDelete(IModGroup group)
|
||||||
|
{
|
||||||
|
if (ImUtf8.IconButton(FontAwesomeIcon.Trash, !_deleteEnabled))
|
||||||
|
ActionQueue.Enqueue(() => ModManager.OptionEditor.DeleteModGroup(group));
|
||||||
|
|
||||||
|
if (_deleteEnabled)
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
[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)
|
||||||
|
{
|
||||||
|
var isFirst = idx == 0;
|
||||||
|
if (ImUtf8.IconButton(FontAwesomeIcon.ArrowUp, isFirst))
|
||||||
|
ActionQueue.Enqueue(() => ModManager.OptionEditor.MoveModGroup(group, idx - 1));
|
||||||
|
|
||||||
|
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;
|
||||||
|
if (ImUtf8.IconButton(FontAwesomeIcon.ArrowDown, isLast))
|
||||||
|
ActionQueue.Enqueue(() => ModManager.OptionEditor.MoveModGroup(group, idx + 1));
|
||||||
|
|
||||||
|
if (isLast)
|
||||||
|
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, "Can not move this group further downwards."u8);
|
||||||
|
else
|
||||||
|
ImUtf8.HoverTooltip($"Move this group down to group {idx + 2}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawGroupOpenFile(IModGroup group, int idx)
|
||||||
|
{
|
||||||
|
var fileName = filenames.OptionGroupFile(group.Mod, idx, config.ReplaceNonAsciiOnImport);
|
||||||
|
var fileExists = File.Exists(fileName);
|
||||||
|
if (ImUtf8.IconButton(FontAwesomeIcon.FileExport, !fileExists))
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Process.Start(new ProcessStartInfo(fileName) { UseShellExecute = true });
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal 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)]
|
||||||
|
internal 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)]
|
||||||
|
internal 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)]
|
||||||
|
internal void DrawOptionDescription(IModOption option)
|
||||||
|
{
|
||||||
|
if (ImUtf8.IconButton(FontAwesomeIcon.Edit, "Edit option description."u8))
|
||||||
|
descriptionPopup.Open(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal 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)]
|
||||||
|
internal 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)]
|
||||||
|
internal 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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal string 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();
|
||||||
|
return newName;
|
||||||
|
}
|
||||||
|
|
||||||
|
[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();
|
||||||
|
}
|
||||||
|
}
|
||||||
63
Penumbra/UI/ModsTab/Groups/MultiModGroupEditDrawer.cs
Normal file
63
Penumbra/UI/ModsTab/Groups/MultiModGroupEditDrawer.cs
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
using Dalamud.Interface;
|
||||||
|
using OtterGui;
|
||||||
|
using OtterGui.Raii;
|
||||||
|
using OtterGui.Text;
|
||||||
|
using Penumbra.Mods.Groups;
|
||||||
|
|
||||||
|
namespace Penumbra.UI.ModsTab.Groups;
|
||||||
|
|
||||||
|
public readonly struct MultiModGroupEditDrawer(ModGroupEditDrawer editor, MultiModGroup group) : IModGroupEditDrawer
|
||||||
|
{
|
||||||
|
public void Draw()
|
||||||
|
{
|
||||||
|
foreach (var (option, optionIdx) in group.OptionData.WithIndex())
|
||||||
|
{
|
||||||
|
using var id = ImRaii.PushId(optionIdx);
|
||||||
|
editor.DrawOptionPosition(group, option, optionIdx);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
editor.DrawOptionDefaultMultiBehaviour(group, option, optionIdx);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
editor.DrawOptionName(option);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
editor.DrawOptionDescription(option);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
editor.DrawOptionDelete(option);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
editor.DrawOptionPriority(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawNewOption();
|
||||||
|
DrawConvertButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawConvertButton()
|
||||||
|
{
|
||||||
|
var g = group;
|
||||||
|
var e = editor.ModManager.OptionEditor.MultiEditor;
|
||||||
|
if (ImUtf8.Button("Convert to Single Group"u8, editor.AvailableWidth))
|
||||||
|
editor.ActionQueue.Enqueue(() => e.ChangeToSingle(g));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawNewOption()
|
||||||
|
{
|
||||||
|
var count = group.Options.Count;
|
||||||
|
if (count >= IModGroup.MaxMultiOptions)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var name = editor.DrawNewOptionBase(group, count);
|
||||||
|
|
||||||
|
var validName = name.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))
|
||||||
|
{
|
||||||
|
editor.ModManager.OptionEditor.MultiEditor.AddOption(group, name);
|
||||||
|
editor.NewOptionName = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
68
Penumbra/UI/ModsTab/Groups/SingleModGroupEditDrawer.cs
Normal file
68
Penumbra/UI/ModsTab/Groups/SingleModGroupEditDrawer.cs
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
using Dalamud.Interface;
|
||||||
|
using ImGuiNET;
|
||||||
|
using OtterGui;
|
||||||
|
using OtterGui.Raii;
|
||||||
|
using OtterGui.Text;
|
||||||
|
using Penumbra.Mods.Groups;
|
||||||
|
|
||||||
|
namespace Penumbra.UI.ModsTab.Groups;
|
||||||
|
|
||||||
|
public readonly struct SingleModGroupEditDrawer(ModGroupEditDrawer editor, SingleModGroup group) : IModGroupEditDrawer
|
||||||
|
{
|
||||||
|
public void Draw()
|
||||||
|
{
|
||||||
|
foreach (var (option, optionIdx) in group.OptionData.WithIndex())
|
||||||
|
{
|
||||||
|
using var id = ImRaii.PushId(optionIdx);
|
||||||
|
editor.DrawOptionPosition(group, option, optionIdx);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
editor.DrawOptionDefaultSingleBehaviour(group, option, optionIdx);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
editor.DrawOptionName(option);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
editor.DrawOptionDescription(option);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
editor.DrawOptionDelete(option);
|
||||||
|
|
||||||
|
ImUtf8.SameLineInner();
|
||||||
|
ImGui.Dummy(new Vector2(editor.PriorityWidth, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawNewOption();
|
||||||
|
DrawConvertButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawConvertButton()
|
||||||
|
{
|
||||||
|
var convertible = group.Options.Count <= IModGroup.MaxMultiOptions;
|
||||||
|
var g = group;
|
||||||
|
var e = editor.ModManager.OptionEditor.SingleEditor;
|
||||||
|
if (ImUtf8.ButtonEx("Convert to Multi Group", editor.AvailableWidth, !convertible))
|
||||||
|
editor.ActionQueue.Enqueue(() => e.ChangeToMulti(g));
|
||||||
|
if (!convertible)
|
||||||
|
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled,
|
||||||
|
"Can not convert to multi group since maximum number of options is exceeded."u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawNewOption()
|
||||||
|
{
|
||||||
|
var count = group.Options.Count;
|
||||||
|
if (count >= int.MaxValue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var name = editor.DrawNewOptionBase(group, count);
|
||||||
|
|
||||||
|
var validName = name.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))
|
||||||
|
{
|
||||||
|
editor.ModManager.OptionEditor.SingleEditor.AddOption(group, name);
|
||||||
|
editor.NewOptionName = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
105
Penumbra/UI/ModsTab/MetaManipulationDrawer.cs
Normal file
105
Penumbra/UI/ModsTab/MetaManipulationDrawer.cs
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
using ImGuiNET;
|
||||||
|
using OtterGui.Raii;
|
||||||
|
using OtterGui.Text;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.Meta.Manipulations;
|
||||||
|
using Penumbra.UI.Classes;
|
||||||
|
|
||||||
|
namespace Penumbra.UI.ModsTab;
|
||||||
|
|
||||||
|
public static class MetaManipulationDrawer
|
||||||
|
{
|
||||||
|
public static bool DrawObjectType(ref ImcManipulation manip, float width = 110)
|
||||||
|
{
|
||||||
|
var ret = Combos.ImcType("##imcType", manip.ObjectType, out var type, width);
|
||||||
|
ImUtf8.HoverTooltip("Object Type"u8);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
var equipSlot = type switch
|
||||||
|
{
|
||||||
|
ObjectType.Equipment => manip.EquipSlot.IsEquipment() ? manip.EquipSlot : EquipSlot.Head,
|
||||||
|
ObjectType.DemiHuman => manip.EquipSlot.IsEquipment() ? manip.EquipSlot : EquipSlot.Head,
|
||||||
|
ObjectType.Accessory => manip.EquipSlot.IsAccessory() ? manip.EquipSlot : EquipSlot.Ears,
|
||||||
|
_ => EquipSlot.Unknown,
|
||||||
|
};
|
||||||
|
manip = new ImcManipulation(type, manip.BodySlot, manip.PrimaryId, manip.SecondaryId == 0 ? 1 : manip.SecondaryId,
|
||||||
|
manip.Variant.Id, equipSlot, manip.Entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool DrawPrimaryId(ref ImcManipulation manip, float unscaledWidth = 80)
|
||||||
|
{
|
||||||
|
var ret = IdInput("##imcPrimaryId"u8, unscaledWidth, manip.PrimaryId.Id, out var newId, 0, ushort.MaxValue,
|
||||||
|
manip.PrimaryId.Id <= 1);
|
||||||
|
ImUtf8.HoverTooltip("Primary ID - You can usually find this as the 'x####' part of an item path.\n"u8
|
||||||
|
+ "This should generally not be left <= 1 unless you explicitly want that."u8);
|
||||||
|
if (ret)
|
||||||
|
manip = new ImcManipulation(manip.ObjectType, manip.BodySlot, newId, manip.SecondaryId, manip.Variant.Id, manip.EquipSlot,
|
||||||
|
manip.Entry);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool DrawSecondaryId(ref ImcManipulation manip, float unscaledWidth = 100)
|
||||||
|
{
|
||||||
|
var ret = IdInput("##imcSecondaryId"u8, unscaledWidth, manip.SecondaryId.Id, out var newId, 0, ushort.MaxValue, false);
|
||||||
|
ImUtf8.HoverTooltip("Secondary ID"u8);
|
||||||
|
if (ret)
|
||||||
|
manip = new ImcManipulation(manip.ObjectType, manip.BodySlot, manip.PrimaryId, newId, manip.Variant.Id, manip.EquipSlot,
|
||||||
|
manip.Entry);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool DrawVariant(ref ImcManipulation manip, float unscaledWidth = 45)
|
||||||
|
{
|
||||||
|
var ret = IdInput("##imcVariant"u8, unscaledWidth, manip.Variant.Id, out var newId, 0, byte.MaxValue, false);
|
||||||
|
ImUtf8.HoverTooltip("Variant ID"u8);
|
||||||
|
if (ret)
|
||||||
|
manip = new ImcManipulation(manip.ObjectType, manip.BodySlot, manip.PrimaryId, manip.SecondaryId, (byte)newId, manip.EquipSlot,
|
||||||
|
manip.Entry);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool DrawSlot(ref ImcManipulation manip, float unscaledWidth = 100)
|
||||||
|
{
|
||||||
|
bool ret;
|
||||||
|
EquipSlot slot;
|
||||||
|
switch (manip.ObjectType)
|
||||||
|
{
|
||||||
|
case ObjectType.Equipment:
|
||||||
|
case ObjectType.DemiHuman:
|
||||||
|
ret = Combos.EqpEquipSlot("##slot", manip.EquipSlot, out slot, unscaledWidth);
|
||||||
|
break;
|
||||||
|
case ObjectType.Accessory:
|
||||||
|
ret = Combos.AccessorySlot("##slot", manip.EquipSlot, out slot, unscaledWidth);
|
||||||
|
break;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImUtf8.HoverTooltip("Equip Slot"u8);
|
||||||
|
if (ret)
|
||||||
|
manip = new ImcManipulation(manip.ObjectType, manip.BodySlot, manip.PrimaryId, manip.SecondaryId, manip.Variant.Id, slot,
|
||||||
|
manip.Entry);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A number input for ids with an optional max id of given width.
|
||||||
|
/// Returns true if newId changed against currentId.
|
||||||
|
/// </summary>
|
||||||
|
private static bool IdInput(ReadOnlySpan<byte> label, float unscaledWidth, ushort currentId, out ushort newId, int minId, int maxId,
|
||||||
|
bool border)
|
||||||
|
{
|
||||||
|
int tmp = currentId;
|
||||||
|
ImGui.SetNextItemWidth(unscaledWidth * ImUtf8.GlobalScale);
|
||||||
|
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, UiHelpers.Scale, border);
|
||||||
|
using var color = ImRaii.PushColor(ImGuiCol.Border, Colors.RegexWarningBorder, border);
|
||||||
|
if (ImUtf8.InputScalar(label, ref tmp))
|
||||||
|
tmp = Math.Clamp(tmp, minId, maxId);
|
||||||
|
|
||||||
|
newId = (ushort)tmp;
|
||||||
|
return newId != currentId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,687 +0,0 @@
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.Internal.Notifications;
|
|
||||||
using ImGuiNET;
|
|
||||||
using OtterGui;
|
|
||||||
using OtterGui.Classes;
|
|
||||||
using OtterGui.Raii;
|
|
||||||
using OtterGui.Services;
|
|
||||||
using OtterGui.Text;
|
|
||||||
using OtterGui.Text.EndObjects;
|
|
||||||
using Penumbra.GameData.Enums;
|
|
||||||
using Penumbra.GameData.Structs;
|
|
||||||
using Penumbra.Meta;
|
|
||||||
using Penumbra.Meta.Manipulations;
|
|
||||||
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 static class MetaManipulationDrawer
|
|
||||||
{
|
|
||||||
public static bool DrawObjectType(ref ImcManipulation manip, float width = 110)
|
|
||||||
{
|
|
||||||
var ret = Combos.ImcType("##imcType", manip.ObjectType, out var type, width);
|
|
||||||
ImUtf8.HoverTooltip("Object Type"u8);
|
|
||||||
|
|
||||||
if (ret)
|
|
||||||
{
|
|
||||||
var equipSlot = type switch
|
|
||||||
{
|
|
||||||
ObjectType.Equipment => manip.EquipSlot.IsEquipment() ? manip.EquipSlot : EquipSlot.Head,
|
|
||||||
ObjectType.DemiHuman => manip.EquipSlot.IsEquipment() ? manip.EquipSlot : EquipSlot.Head,
|
|
||||||
ObjectType.Accessory => manip.EquipSlot.IsAccessory() ? manip.EquipSlot : EquipSlot.Ears,
|
|
||||||
_ => EquipSlot.Unknown,
|
|
||||||
};
|
|
||||||
manip = new ImcManipulation(type, manip.BodySlot, manip.PrimaryId, manip.SecondaryId == 0 ? 1 : manip.SecondaryId,
|
|
||||||
manip.Variant.Id, equipSlot, manip.Entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool DrawPrimaryId(ref ImcManipulation manip, float unscaledWidth = 80)
|
|
||||||
{
|
|
||||||
var ret = IdInput("##imcPrimaryId"u8, unscaledWidth, manip.PrimaryId.Id, out var newId, 0, ushort.MaxValue,
|
|
||||||
manip.PrimaryId.Id <= 1);
|
|
||||||
ImUtf8.HoverTooltip("Primary ID - You can usually find this as the 'x####' part of an item path.\n"u8
|
|
||||||
+ "This should generally not be left <= 1 unless you explicitly want that."u8);
|
|
||||||
if (ret)
|
|
||||||
manip = new ImcManipulation(manip.ObjectType, manip.BodySlot, newId, manip.SecondaryId, manip.Variant.Id, manip.EquipSlot,
|
|
||||||
manip.Entry);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool DrawSecondaryId(ref ImcManipulation manip, float unscaledWidth = 100)
|
|
||||||
{
|
|
||||||
var ret = IdInput("##imcSecondaryId"u8, unscaledWidth, manip.SecondaryId.Id, out var newId, 0, ushort.MaxValue, false);
|
|
||||||
ImUtf8.HoverTooltip("Secondary ID"u8);
|
|
||||||
if (ret)
|
|
||||||
manip = new ImcManipulation(manip.ObjectType, manip.BodySlot, manip.PrimaryId, newId, manip.Variant.Id, manip.EquipSlot,
|
|
||||||
manip.Entry);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool DrawVariant(ref ImcManipulation manip, float unscaledWidth = 45)
|
|
||||||
{
|
|
||||||
var ret = IdInput("##imcVariant"u8, unscaledWidth, manip.Variant.Id, out var newId, 0, byte.MaxValue, false);
|
|
||||||
ImUtf8.HoverTooltip("Variant ID"u8);
|
|
||||||
if (ret)
|
|
||||||
manip = new ImcManipulation(manip.ObjectType, manip.BodySlot, manip.PrimaryId, manip.SecondaryId, (byte)newId, manip.EquipSlot,
|
|
||||||
manip.Entry);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool DrawSlot(ref ImcManipulation manip, float unscaledWidth = 100)
|
|
||||||
{
|
|
||||||
bool ret;
|
|
||||||
EquipSlot slot;
|
|
||||||
switch (manip.ObjectType)
|
|
||||||
{
|
|
||||||
case ObjectType.Equipment:
|
|
||||||
case ObjectType.DemiHuman:
|
|
||||||
ret = Combos.EqpEquipSlot("##slot", manip.EquipSlot, out slot, unscaledWidth);
|
|
||||||
break;
|
|
||||||
case ObjectType.Accessory:
|
|
||||||
ret = Combos.AccessorySlot("##slot", manip.EquipSlot, out slot, unscaledWidth);
|
|
||||||
break;
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImUtf8.HoverTooltip("Equip Slot"u8);
|
|
||||||
if (ret)
|
|
||||||
manip = new ImcManipulation(manip.ObjectType, manip.BodySlot, manip.PrimaryId, manip.SecondaryId, manip.Variant.Id, slot,
|
|
||||||
manip.Entry);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A number input for ids with an optional max id of given width.
|
|
||||||
/// Returns true if newId changed against currentId.
|
|
||||||
/// </summary>
|
|
||||||
private static bool IdInput(ReadOnlySpan<byte> label, float unscaledWidth, ushort currentId, out ushort newId, int minId, int maxId,
|
|
||||||
bool border)
|
|
||||||
{
|
|
||||||
int tmp = currentId;
|
|
||||||
ImGui.SetNextItemWidth(unscaledWidth * ImUtf8.GlobalScale);
|
|
||||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, UiHelpers.Scale, border);
|
|
||||||
using var color = ImRaii.PushColor(ImGuiCol.Border, Colors.RegexWarningBorder, border);
|
|
||||||
if (ImUtf8.InputScalar(label, ref tmp))
|
|
||||||
tmp = Math.Clamp(tmp, minId, maxId);
|
|
||||||
|
|
||||||
newId = (ushort)tmp;
|
|
||||||
return newId != currentId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 _availableWidth;
|
|
||||||
private float _priorityWidth;
|
|
||||||
private float _groupNameWidth;
|
|
||||||
private float _optionNameWidth;
|
|
||||||
private float _spacing;
|
|
||||||
private Vector2 _optionIdxSelectable;
|
|
||||||
private bool _deleteEnabled;
|
|
||||||
|
|
||||||
private string? _currentGroupName;
|
|
||||||
private ModPriority? _currentGroupPriority;
|
|
||||||
private IModGroup? _currentGroupEdited;
|
|
||||||
private bool _isGroupNameValid = true;
|
|
||||||
|
|
||||||
private string? _newOptionName;
|
|
||||||
private IModGroup? _newOptionGroup;
|
|
||||||
private readonly Queue<Action> _actionQueue = new();
|
|
||||||
|
|
||||||
private IModGroup? _dragDropGroup;
|
|
||||||
private IModOption? _dragDropOption;
|
|
||||||
|
|
||||||
public void Draw(Mod mod)
|
|
||||||
{
|
|
||||||
PrepareStyle();
|
|
||||||
|
|
||||||
using var id = ImUtf8.PushId("##GroupEdit"u8);
|
|
||||||
foreach (var (group, groupIdx) in mod.Groups.WithIndex())
|
|
||||||
DrawGroup(group, groupIdx);
|
|
||||||
|
|
||||||
while (_actionQueue.TryDequeue(out var action))
|
|
||||||
action.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawGroup(IModGroup group, int idx)
|
|
||||||
{
|
|
||||||
using var id = ImUtf8.PushId(idx);
|
|
||||||
using var frame = ImRaii.FramedGroup($"Group #{idx + 1}");
|
|
||||||
DrawGroupNameRow(group, idx);
|
|
||||||
switch (group)
|
|
||||||
{
|
|
||||||
case SingleModGroup s:
|
|
||||||
DrawSingleGroup(s);
|
|
||||||
break;
|
|
||||||
case MultiModGroup m:
|
|
||||||
DrawMultiGroup(m);
|
|
||||||
break;
|
|
||||||
case ImcModGroup i:
|
|
||||||
DrawImcGroup(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawGroupNameRow(IModGroup group, int idx)
|
|
||||||
{
|
|
||||||
DrawGroupName(group);
|
|
||||||
ImUtf8.SameLineInner();
|
|
||||||
DrawGroupMoveButtons(group, idx);
|
|
||||||
ImUtf8.SameLineInner();
|
|
||||||
DrawGroupOpenFile(group, idx);
|
|
||||||
ImUtf8.SameLineInner();
|
|
||||||
DrawGroupDescription(group);
|
|
||||||
ImUtf8.SameLineInner();
|
|
||||||
DrawGroupDelete(group);
|
|
||||||
ImUtf8.SameLineInner();
|
|
||||||
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 (ImUtf8.InputText("##GroupName"u8, ref text))
|
|
||||||
{
|
|
||||||
_currentGroupEdited = group;
|
|
||||||
_currentGroupName = text;
|
|
||||||
_isGroupNameValid = text == group.Name || 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
|
|
||||||
? "Change the Group name."u8
|
|
||||||
: "Current name can not be used for this group."u8;
|
|
||||||
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, tt);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawGroupDelete(IModGroup group)
|
|
||||||
{
|
|
||||||
if (ImUtf8.IconButton(FontAwesomeIcon.Trash, !_deleteEnabled))
|
|
||||||
_actionQueue.Enqueue(() => modManager.OptionEditor.DeleteModGroup(group));
|
|
||||||
|
|
||||||
if (_deleteEnabled)
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
[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)
|
|
||||||
{
|
|
||||||
var isFirst = idx == 0;
|
|
||||||
if (ImUtf8.IconButton(FontAwesomeIcon.ArrowUp, isFirst))
|
|
||||||
_actionQueue.Enqueue(() => modManager.OptionEditor.MoveModGroup(group, idx - 1));
|
|
||||||
|
|
||||||
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;
|
|
||||||
if (ImUtf8.IconButton(FontAwesomeIcon.ArrowDown, isLast))
|
|
||||||
_actionQueue.Enqueue(() => modManager.OptionEditor.MoveModGroup(group, idx + 1));
|
|
||||||
|
|
||||||
if (isLast)
|
|
||||||
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, "Can not move this group further downwards."u8);
|
|
||||||
else
|
|
||||||
ImUtf8.HoverTooltip($"Move this group down to group {idx + 2}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawGroupOpenFile(IModGroup group, int idx)
|
|
||||||
{
|
|
||||||
var fileName = filenames.OptionGroupFile(group.Mod, idx, config.ReplaceNonAsciiOnImport);
|
|
||||||
var fileExists = File.Exists(fileName);
|
|
||||||
if (ImUtf8.IconButton(FontAwesomeIcon.FileExport, !fileExists))
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Process.Start(new ProcessStartInfo(fileName) { UseShellExecute = true });
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
ImUtf8.SameLineInner();
|
|
||||||
DrawOptionDefaultSingleBehaviour(group, option, optionIdx);
|
|
||||||
|
|
||||||
ImUtf8.SameLineInner();
|
|
||||||
DrawOptionName(option);
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
using (ImUtf8.Group())
|
|
||||||
{
|
|
||||||
ImUtf8.Text("Object Type"u8);
|
|
||||||
if (group.ObjectType is ObjectType.Equipment or ObjectType.Accessory or ObjectType.DemiHuman)
|
|
||||||
ImUtf8.Text("Slot"u8);
|
|
||||||
ImUtf8.Text("Primary ID");
|
|
||||||
if (group.ObjectType is not ObjectType.Equipment and not ObjectType.Accessory)
|
|
||||||
ImUtf8.Text("Secondary ID");
|
|
||||||
ImUtf8.Text("Variant"u8);
|
|
||||||
|
|
||||||
ImUtf8.TextFrameAligned("Material ID"u8);
|
|
||||||
ImUtf8.TextFrameAligned("Material Animation ID"u8);
|
|
||||||
ImUtf8.TextFrameAligned("Decal ID"u8);
|
|
||||||
ImUtf8.TextFrameAligned("VFX ID"u8);
|
|
||||||
ImUtf8.TextFrameAligned("Sound ID"u8);
|
|
||||||
ImUtf8.TextFrameAligned("Can Be Disabled"u8);
|
|
||||||
ImUtf8.TextFrameAligned("Default Attributes"u8);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
var attributeCache = new ImcAttributeCache(group);
|
|
||||||
|
|
||||||
using (ImUtf8.Group())
|
|
||||||
{
|
|
||||||
ImUtf8.Text(group.ObjectType.ToName());
|
|
||||||
if (group.ObjectType is ObjectType.Equipment or ObjectType.Accessory or ObjectType.DemiHuman)
|
|
||||||
ImUtf8.Text(group.EquipSlot.ToName());
|
|
||||||
ImUtf8.Text($"{group.PrimaryId.Id}");
|
|
||||||
if (group.ObjectType is not ObjectType.Equipment and not ObjectType.Accessory)
|
|
||||||
ImUtf8.Text($"{group.SecondaryId.Id}");
|
|
||||||
ImUtf8.Text($"{group.Variant.Id}");
|
|
||||||
|
|
||||||
ImUtf8.TextFrameAligned($"{group.DefaultEntry.MaterialId}");
|
|
||||||
ImUtf8.TextFrameAligned($"{group.DefaultEntry.MaterialAnimationId}");
|
|
||||||
ImUtf8.TextFrameAligned($"{group.DefaultEntry.DecalId}");
|
|
||||||
ImUtf8.TextFrameAligned($"{group.DefaultEntry.VfxId}");
|
|
||||||
ImUtf8.TextFrameAligned($"{group.DefaultEntry.SoundId}");
|
|
||||||
|
|
||||||
var canBeDisabled = group.CanBeDisabled;
|
|
||||||
if (ImUtf8.Checkbox("##disabled"u8, ref canBeDisabled))
|
|
||||||
modManager.OptionEditor.ImcEditor.ChangeCanBeDisabled(group, canBeDisabled, SaveType.Queue);
|
|
||||||
|
|
||||||
var defaultDisabled = group.DefaultDisabled;
|
|
||||||
ImUtf8.SameLineInner();
|
|
||||||
if (ImUtf8.Checkbox("##defaultDisabled"u8, ref defaultDisabled))
|
|
||||||
modManager.OptionEditor.ChangeModGroupDefaultOption(group,
|
|
||||||
group.DefaultSettings.SetBit(ImcModGroup.DisabledIndex, defaultDisabled));
|
|
||||||
|
|
||||||
DrawAttributes(modManager.OptionEditor.ImcEditor, attributeCache, group.DefaultEntry.AttributeMask, 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();
|
|
||||||
ImGui.Dummy(new Vector2(_priorityWidth, 0));
|
|
||||||
|
|
||||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + _optionIdxSelectable.X + ImUtf8.ItemInnerSpacing.X * 2 + ImUtf8.FrameHeight);
|
|
||||||
DrawAttributes(modManager.OptionEditor.ImcEditor, attributeCache, option.AttributeMask, option);
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawNewOption(group, attributeCache);
|
|
||||||
return;
|
|
||||||
|
|
||||||
static void DrawAttributes(ImcModGroupEditor editor, in ImcAttributeCache cache, ushort mask, object data)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < ImcEntry.NumAttributes; ++i)
|
|
||||||
{
|
|
||||||
using var id = ImRaii.PushId(i);
|
|
||||||
var value = (mask & (1 << i)) != 0;
|
|
||||||
using (ImRaii.Disabled(!cache.CanChange(i)))
|
|
||||||
{
|
|
||||||
if (ImUtf8.Checkbox(TerminatedByteString.Empty, ref value))
|
|
||||||
{
|
|
||||||
if (data is ImcModGroup g)
|
|
||||||
editor.ChangeDefaultAttribute(g, cache, i, value);
|
|
||||||
else
|
|
||||||
editor.ChangeOptionAttribute((ImcSubMod)data, cache, i, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImUtf8.HoverTooltip($"{(char)('A' + i)}");
|
|
||||||
if (i != 9)
|
|
||||||
ImUtf8.SameLineInner();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[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;
|
|
||||||
|
|
||||||
var name = DrawNewOptionBase(group, count);
|
|
||||||
|
|
||||||
var validName = name.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, name);
|
|
||||||
_newOptionName = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawNewOption(MultiModGroup group)
|
|
||||||
{
|
|
||||||
var count = group.Options.Count;
|
|
||||||
if (count >= IModGroup.MaxMultiOptions)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var name = DrawNewOptionBase(group, count);
|
|
||||||
|
|
||||||
var validName = name.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, name);
|
|
||||||
_newOptionName = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawNewOption(ImcModGroup group, in ImcAttributeCache cache)
|
|
||||||
{
|
|
||||||
if (cache.LowestUnsetMask == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var name = DrawNewOptionBase(group, group.Options.Count);
|
|
||||||
var validName = name.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.ImcEditor.AddOption(group, cache, name);
|
|
||||||
_newOptionName = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private string 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();
|
|
||||||
return newName;
|
|
||||||
}
|
|
||||||
|
|
||||||
[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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -13,6 +13,7 @@ using Penumbra.Services;
|
||||||
using Penumbra.UI.AdvancedWindow;
|
using Penumbra.UI.AdvancedWindow;
|
||||||
using Penumbra.Mods.Settings;
|
using Penumbra.Mods.Settings;
|
||||||
using Penumbra.Mods.Manager.OptionEditor;
|
using Penumbra.Mods.Manager.OptionEditor;
|
||||||
|
using Penumbra.UI.ModsTab.Groups;
|
||||||
|
|
||||||
namespace Penumbra.UI.ModsTab;
|
namespace Penumbra.UI.ModsTab;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using Penumbra.Collections.Manager;
|
||||||
using Penumbra.Mods.Manager;
|
using Penumbra.Mods.Manager;
|
||||||
using Penumbra.Services;
|
using Penumbra.Services;
|
||||||
using Penumbra.Mods.Settings;
|
using Penumbra.Mods.Settings;
|
||||||
|
using Penumbra.UI.ModsTab.Groups;
|
||||||
|
|
||||||
namespace Penumbra.UI.ModsTab;
|
namespace Penumbra.UI.ModsTab;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue