Current state.
Some checks failed
.NET Build / build (push) Has been cancelled

This commit is contained in:
Ottermandias 2025-12-09 17:18:58 +01:00
parent fec7819bf2
commit 5fc8209c85
69 changed files with 2455 additions and 2715 deletions

2
Luna

@ -1 +1 @@
Subproject commit 950ebea591f4ab7dc0900cce22415c5221df3685
Subproject commit 235825613cf1a2c95173d0a1ae43f2c6601a9df6

@ -1 +1 @@
Subproject commit 09cfde3dd43aa5afcfd147ccbe3ee61534556f12
Subproject commit d52071290b48a1f2292023675b4b72365aef4cc0

@ -1 +1 @@
Subproject commit 885e536267f814fc4e62e11a70a82cdde7b4778d
Subproject commit 67f313d42944e4d2c878b628cfc92b35d7d88436

@ -1 +1 @@
Subproject commit 4aac62e73b89a0c538a7a0a5c22822f15b13c0cc
Subproject commit 462afac558becebbe06b4e5be9b1b3c3f5a9b6d6

View file

@ -28,6 +28,11 @@ public class RedrawApi(RedrawService redrawService, IFramework framework) : IPen
framework.RunOnFrameworkThread(() => redrawService.RedrawAll(setting));
}
public void RedrawCollectionMembers(Guid collectionId, RedrawType setting)
{
throw new NotImplementedException();
}
public event GameObjectRedrawnDelegate? GameObjectRedrawn
{
add => redrawService.GameObjectRedrawn += value;

View file

@ -80,6 +80,12 @@ public class UiApi : IPenumbraApiUi, Luna.IApiService, IDisposable
public void CloseMainWindow()
=> _mainWindow.IsOpen = false;
public PenumbraApiEc RegisterSettingsSection(Action draw)
=> throw new NotImplementedException();
public PenumbraApiEc UnregisterSettingsSection(Action draw)
=> throw new NotImplementedException();
private void OnChangedItemClick(in ChangedItemClick.Arguments arguments)
{
if (ChangedItemClicked == null)

View file

@ -54,7 +54,7 @@ public class CollectionsIpcTester(IDalamudPluginInterface pi) : IUiService
Im.Text(!_oldCollection.HasValue ? "Created" : _oldCollection.ToString()!);
table.NextRow();
using (IpcTester.DrawIntro(GetCollectionsByIdentifier.Label, "Collection Identifier"u8))
using (IpcTester.DrawIntro(GetCollectionsByIdentifier.LabelU8, "Collection Identifier"u8))
{
var collectionList = new GetCollectionsByIdentifier(pi).Invoke(_collectionId.GetValueOrDefault().ToString());
if (collectionList.Count == 0)
@ -75,27 +75,27 @@ public class CollectionsIpcTester(IDalamudPluginInterface pi) : IUiService
}
}
using (IpcTester.DrawIntro(GetCollection.Label, "Current Collection"u8))
using (IpcTester.DrawIntro(GetCollection.LabelU8, "Current Collection"u8))
{
DrawCollection(table, new GetCollection(pi).Invoke(ApiCollectionType.Current));
}
using (IpcTester.DrawIntro(GetCollection.Label, "Default Collection"u8))
using (IpcTester.DrawIntro(GetCollection.LabelU8, "Default Collection"u8))
{
DrawCollection(table, new GetCollection(pi).Invoke(ApiCollectionType.Default));
}
using (IpcTester.DrawIntro(GetCollection.Label, "Interface Collection"u8))
using (IpcTester.DrawIntro(GetCollection.LabelU8, "Interface Collection"u8))
{
DrawCollection(table, new GetCollection(pi).Invoke(ApiCollectionType.Interface));
}
using (IpcTester.DrawIntro(GetCollection.Label, "Special Collection"u8))
using (IpcTester.DrawIntro(GetCollection.LabelU8, "Special Collection"u8))
{
DrawCollection(table, new GetCollection(pi).Invoke(_type));
}
using (IpcTester.DrawIntro(GetCollections.Label, "Collections"u8))
using (IpcTester.DrawIntro(GetCollections.LabelU8, "Collections"u8))
{
DrawCollectionPopup();
table.NextColumn();
@ -106,7 +106,7 @@ public class CollectionsIpcTester(IDalamudPluginInterface pi) : IUiService
}
}
using (IpcTester.DrawIntro(GetCollectionForObject.Label, "Get Object Collection"u8))
using (IpcTester.DrawIntro(GetCollectionForObject.LabelU8, "Get Object Collection"u8))
{
var (valid, individual, effectiveCollection) = new GetCollectionForObject(pi).Invoke(_objectIdx);
DrawCollection(table, effectiveCollection);
@ -114,7 +114,7 @@ public class CollectionsIpcTester(IDalamudPluginInterface pi) : IUiService
Im.Text($"({(valid ? "Valid" : "Invalid")} Object{(individual ? ", Individual Assignment)" : ")")}");
}
using (IpcTester.DrawIntro(SetCollection.Label, "Set Special Collection"u8))
using (IpcTester.DrawIntro(SetCollection.LabelU8, "Set Special Collection"u8))
{
table.NextColumn();
if (Im.SmallButton("Set##SpecialCollection"u8))
@ -125,7 +125,7 @@ public class CollectionsIpcTester(IDalamudPluginInterface pi) : IUiService
(_returnCode, _oldCollection) = new SetCollection(pi).Invoke(_type, null, _allowCreation, _allowDeletion);
}
using (IpcTester.DrawIntro(SetCollectionForObject.Label, "Set Object Collection"u8))
using (IpcTester.DrawIntro(SetCollectionForObject.LabelU8, "Set Object Collection"u8))
{
table.NextColumn();
if (Im.SmallButton("Set##ObjectCollection"u8))
@ -136,7 +136,7 @@ public class CollectionsIpcTester(IDalamudPluginInterface pi) : IUiService
(_returnCode, _oldCollection) = new SetCollectionForObject(pi).Invoke(_objectIdx, null, _allowCreation, _allowDeletion);
}
using (IpcTester.DrawIntro(GetChangedItemsForCollection.Label, "Changed Item List"u8))
using (IpcTester.DrawIntro(GetChangedItemsForCollection.LabelU8, "Changed Item List"u8))
{
DrawChangedItemPopup();
table.NextColumn();

View file

@ -35,7 +35,7 @@ public class EditingIpcTester(IDalamudPluginInterface pi) : Luna.IUiService
if (!table)
return;
using (IpcTester.DrawIntro(ConvertTextureFile.Label, "Convert Texture 1"u8))
using (IpcTester.DrawIntro(ConvertTextureFile.LabelU8, "Convert Texture 1"u8))
{
table.NextColumn();
if (ImEx.Button("Save 1"u8, Vector2.Zero, StringU8.Empty, _task1 is { IsCompleted: false }))
@ -46,7 +46,7 @@ public class EditingIpcTester(IDalamudPluginInterface pi) : Luna.IUiService
Im.Tooltip.Set($"{_task1.Exception}");
}
using (IpcTester.DrawIntro(ConvertTextureFile.Label, "Convert Texture 2"u8))
using (IpcTester.DrawIntro(ConvertTextureFile.LabelU8, "Convert Texture 2"u8))
{
table.NextColumn();
if (ImEx.Button("Save 2"u8, Vector2.Zero, StringU8.Empty, _task2 is { IsCompleted: false }))

View file

@ -70,7 +70,7 @@ public class GameStateIpcTester : IUiService, IDisposable
if (!table)
return;
using (IpcTester.DrawIntro(GetDrawObjectInfo.Label, "Draw Object Info"u8))
using (IpcTester.DrawIntro(GetDrawObjectInfo.LabelU8, "Draw Object Info"u8))
{
table.NextColumn();
if (_currentDrawObject == nint.Zero)
@ -86,12 +86,12 @@ public class GameStateIpcTester : IUiService, IDisposable
}
}
using (IpcTester.DrawIntro(GetCutsceneParentIndex.Label, "Cutscene Parent"u8))
using (IpcTester.DrawIntro(GetCutsceneParentIndex.LabelU8, "Cutscene Parent"u8))
{
table.DrawColumn($"{new GetCutsceneParentIndex(_pi).Invoke(_currentCutsceneActor)}");
}
using (IpcTester.DrawIntro(SetCutsceneParentIndex.Label, "Cutscene Parent"u8))
using (IpcTester.DrawIntro(SetCutsceneParentIndex.LabelU8, "Cutscene Parent"u8))
{
table.NextColumn();
if (Im.SmallButton("Set Parent"u8))
@ -99,7 +99,7 @@ public class GameStateIpcTester : IUiService, IDisposable
.Invoke(_currentCutsceneActor, _currentCutsceneParent);
}
using (IpcTester.DrawIntro(CreatingCharacterBase.Label, "Last Drawobject created"u8))
using (IpcTester.DrawIntro(CreatingCharacterBase.LabelU8, "Last Drawobject created"u8))
{
if (_lastCreatedGameObjectTime < DateTimeOffset.Now)
table.DrawColumn(_lastCreatedDrawObject != nint.Zero
@ -107,7 +107,7 @@ public class GameStateIpcTester : IUiService, IDisposable
: $"NULL for <{_lastCreatedGameObjectName}> at {_lastCreatedGameObjectTime}");
}
using (IpcTester.DrawIntro(IpcSubscribers.GameObjectResourcePathResolved.Label, "Last GamePath resolved"u8))
using (IpcTester.DrawIntro(IpcSubscribers.GameObjectResourcePathResolved.LabelU8, "Last GamePath resolved"u8))
{
if (_lastResolvedGamePathTime < DateTimeOffset.Now)
table.DrawColumn(

View file

@ -28,7 +28,7 @@ public class MetaIpcTester(IDalamudPluginInterface pi) : Luna.IUiService
if (!table)
return;
using (IpcTester.DrawIntro(GetPlayerMetaManipulations.Label, "Player Meta Manipulations"u8))
using (IpcTester.DrawIntro(GetPlayerMetaManipulations.LabelU8, "Player Meta Manipulations"u8))
{
table.NextColumn();
if (Im.SmallButton("Copy to Clipboard##Player"u8))
@ -38,7 +38,7 @@ public class MetaIpcTester(IDalamudPluginInterface pi) : Luna.IUiService
}
}
using (IpcTester.DrawIntro(GetMetaManipulations.Label, "Game Object Manipulations"u8))
using (IpcTester.DrawIntro(GetMetaManipulations.LabelU8, "Game Object Manipulations"u8))
{
table.NextColumn();
if (Im.SmallButton("Copy to Clipboard##GameObject"u8))

View file

@ -63,14 +63,14 @@ public class ModSettingsIpcTester : Luna.IUiService, IDisposable
IpcTester.DrawIntro("Last Error"u8, $"{_lastSettingsError}").Dispose();
using (IpcTester.DrawIntro(ModSettingChanged.Label, "Last Mod Setting Changed"u8))
using (IpcTester.DrawIntro(ModSettingChanged.LabelU8, "Last Mod Setting Changed"u8))
{
table.DrawColumn(_lastSettingChangeMod.Length > 0
? $"{_lastSettingChangeType} of {_lastSettingChangeMod} in {_lastSettingChangeCollection}{(_lastSettingChangeInherited ? " (Inherited)" : string.Empty)} at {_lastSettingChange}"
: "None"u8);
}
using (IpcTester.DrawIntro(GetAvailableModSettings.Label, "Get Available Settings"u8))
using (IpcTester.DrawIntro(GetAvailableModSettings.LabelU8, "Get Available Settings"u8))
{
table.NextColumn();
if (Im.SmallButton("Get##Available"u8))
@ -80,7 +80,7 @@ public class ModSettingsIpcTester : Luna.IUiService, IDisposable
}
}
using (IpcTester.DrawIntro(GetCurrentModSettings.Label, "Get Current Settings"u8))
using (IpcTester.DrawIntro(GetCurrentModSettings.LabelU8, "Get Current Settings"u8))
{
table.NextColumn();
if (Im.SmallButton("Get##Current"u8))
@ -103,7 +103,7 @@ public class ModSettingsIpcTester : Luna.IUiService, IDisposable
}
}
using (IpcTester.DrawIntro(GetCurrentModSettingsWithTemp.Label, "Get Current Settings With Temp"u8))
using (IpcTester.DrawIntro(GetCurrentModSettingsWithTemp.LabelU8, "Get Current Settings With Temp"u8))
{
table.NextColumn();
if (Im.SmallButton("Get##CurrentTemp"u8))
@ -127,7 +127,7 @@ public class ModSettingsIpcTester : Luna.IUiService, IDisposable
}
}
using (IpcTester.DrawIntro(GetAllModSettings.Label, "Get All Mod Settings"u8))
using (IpcTester.DrawIntro(GetAllModSettings.LabelU8, "Get All Mod Settings"u8))
{
table.NextColumn();
if (Im.SmallButton("Get##All"u8))
@ -144,7 +144,7 @@ public class ModSettingsIpcTester : Luna.IUiService, IDisposable
}
}
using (IpcTester.DrawIntro(TryInheritMod.Label, "Inherit Mod"u8))
using (IpcTester.DrawIntro(TryInheritMod.LabelU8, "Inherit Mod"u8))
{
table.NextColumn();
Im.Checkbox("##inherit"u8, ref _settingsInherit);
@ -154,7 +154,7 @@ public class ModSettingsIpcTester : Luna.IUiService, IDisposable
.Invoke(collection, _settingsModDirectory, _settingsInherit, _settingsModName);
}
using (IpcTester.DrawIntro(TrySetMod.Label, "Set Enabled"u8))
using (IpcTester.DrawIntro(TrySetMod.LabelU8, "Set Enabled"u8))
{
table.NextColumn();
Im.Checkbox("##enabled"u8, ref _settingsEnabled);
@ -164,7 +164,7 @@ public class ModSettingsIpcTester : Luna.IUiService, IDisposable
.Invoke(collection, _settingsModDirectory, _settingsEnabled, _settingsModName);
}
using (IpcTester.DrawIntro(TrySetModPriority.Label, "Set Priority"u8))
using (IpcTester.DrawIntro(TrySetModPriority.LabelU8, "Set Priority"u8))
{
table.NextColumn();
Im.Item.SetNextWidthScaled(200);
@ -175,7 +175,7 @@ public class ModSettingsIpcTester : Luna.IUiService, IDisposable
.Invoke(collection, _settingsModDirectory, _settingsPriority, _settingsModName);
}
using (IpcTester.DrawIntro(CopyModSettings.Label, "Copy Mod Settings"u8))
using (IpcTester.DrawIntro(CopyModSettings.LabelU8, "Copy Mod Settings"u8))
{
table.NextColumn();
if (Im.SmallButton("Copy Settings"u8))
@ -185,7 +185,7 @@ public class ModSettingsIpcTester : Luna.IUiService, IDisposable
}
using (IpcTester.DrawIntro(TrySetModSetting.Label, "Set Setting(s)"u8))
using (IpcTester.DrawIntro(TrySetModSetting.LabelU8, "Set Setting(s)"u8))
{
if (_availableSettings == null)
return;

View file

@ -82,7 +82,7 @@ public class ModsIpcTester : Luna.IUiService, IDisposable
if (!table)
return;
using (IpcTester.DrawIntro(GetModList.Label, "Mods"u8))
using (IpcTester.DrawIntro(GetModList.LabelU8, "Mods"u8))
{
DrawModsPopup();
table.NextColumn();
@ -93,7 +93,7 @@ public class ModsIpcTester : Luna.IUiService, IDisposable
}
}
using (IpcTester.DrawIntro(ReloadMod.Label, "Reload Mod"u8))
using (IpcTester.DrawIntro(ReloadMod.LabelU8, "Reload Mod"u8))
{
table.NextColumn();
if (Im.SmallButton("Reload"u8))
@ -104,7 +104,7 @@ public class ModsIpcTester : Luna.IUiService, IDisposable
}
using (IpcTester.DrawIntro(InstallMod.Label, "Install Mod"u8))
using (IpcTester.DrawIntro(InstallMod.LabelU8, "Install Mod"u8))
{
table.NextColumn();
if (Im.SmallButton("Install"u8))
@ -113,7 +113,7 @@ public class ModsIpcTester : Luna.IUiService, IDisposable
Im.Text($"{_lastInstallEc}");
}
using (IpcTester.DrawIntro(AddMod.Label, "Add Mod"u8))
using (IpcTester.DrawIntro(AddMod.LabelU8, "Add Mod"u8))
{
table.NextColumn();
if (Im.SmallButton("Add"u8))
@ -122,7 +122,7 @@ public class ModsIpcTester : Luna.IUiService, IDisposable
Im.Text($"{_lastAddEc}");
}
using (IpcTester.DrawIntro(DeleteMod.Label, "Delete Mod"u8))
using (IpcTester.DrawIntro(DeleteMod.LabelU8, "Delete Mod"u8))
{
table.NextColumn();
if (Im.SmallButton("Delete"u8))
@ -131,7 +131,7 @@ public class ModsIpcTester : Luna.IUiService, IDisposable
Im.Text(_lastDeleteEc.ToString());
}
using (IpcTester.DrawIntro(GetChangedItems.Label, "Get Changed Items"u8))
using (IpcTester.DrawIntro(GetChangedItems.LabelU8, "Get Changed Items"u8))
{
DrawChangedItemsPopup();
table.NextColumn();
@ -142,13 +142,13 @@ public class ModsIpcTester : Luna.IUiService, IDisposable
}
}
using (IpcTester.DrawIntro(GetModPath.Label, "Current Path"u8))
using (IpcTester.DrawIntro(GetModPath.LabelU8, "Current Path"u8))
{
var (ec, path, def, nameDef) = new GetModPath(_pi).Invoke(_modDirectory, _modName);
table.DrawColumn($"{path} ({(def ? "Custom" : "Default")} Path, {(nameDef ? "Custom" : "Default")} Name) [{ec}]");
}
using (IpcTester.DrawIntro(SetModPath.Label, "Set Path"u8))
using (IpcTester.DrawIntro(SetModPath.LabelU8, "Set Path"u8))
{
table.NextColumn();
if (Im.SmallButton("Set"u8))
@ -158,19 +158,19 @@ public class ModsIpcTester : Luna.IUiService, IDisposable
Im.Text($"{_lastSetPathEc}");
}
using (IpcTester.DrawIntro(ModDeleted.Label, "Last Mod Deleted"u8))
using (IpcTester.DrawIntro(ModDeleted.LabelU8, "Last Mod Deleted"u8))
{
if (_lastDeletedModTime > DateTimeOffset.UnixEpoch)
table.DrawColumn($"{_lastDeletedMod} at {_lastDeletedModTime}");
}
using (IpcTester.DrawIntro(ModAdded.Label, "Last Mod Added"u8))
using (IpcTester.DrawIntro(ModAdded.LabelU8, "Last Mod Added"u8))
{
if (_lastAddedModTime > DateTimeOffset.UnixEpoch)
table.DrawColumn($"{_lastAddedMod} at {_lastAddedModTime}");
}
using (IpcTester.DrawIntro(ModMoved.Label, "Last Mod Moved"))
using (IpcTester.DrawIntro(ModMoved.LabelU8, "Last Mod Moved"u8))
{
if (_lastMovedModTime > DateTimeOffset.UnixEpoch)
table.DrawColumn($"{_lastMovedModFrom} -> {_lastMovedModTo} at {_lastMovedModTime}");

View file

@ -64,31 +64,31 @@ public class PluginStateIpcTester : IUiService, IDisposable
DrawList(IpcSubscribers.Initialized.Label, "Last Initialized"u8, _initializedList);
DrawList(IpcSubscribers.Disposed.Label, "Last Disposed"u8, _disposedList);
using (IpcTester.DrawIntro(ApiVersion.Label, "Current Version"u8))
using (IpcTester.DrawIntro(ApiVersion.LabelU8, "Current Version"u8))
{
var (breaking, features) = new ApiVersion(_pi).Invoke();
table.DrawColumn($"{breaking}.{features:D4}");
}
using (IpcTester.DrawIntro(GetEnabledState.Label, "Current State"u8))
using (IpcTester.DrawIntro(GetEnabledState.LabelU8, "Current State"u8))
{
table.DrawColumn($"{new GetEnabledState(_pi).Invoke()}");
}
using (IpcTester.DrawIntro(IpcSubscribers.EnabledChange.Label, "Last Change"u8))
using (IpcTester.DrawIntro(IpcSubscribers.EnabledChange.LabelU8, "Last Change"u8))
{
table.DrawColumn(_lastEnabledValue is { } v ? $"{_lastEnabledChange} (to {v})" : "Never"u8);
}
using (IpcTester.DrawIntro(SupportedFeatures.Label, "Supported Features"u8))
using (IpcTester.DrawIntro(SupportedFeatures.LabelU8, "Supported Features"u8))
{
table.DrawColumn(StringU8.Join(", "u8, new SupportedFeatures(_pi).Invoke()));
}
using (IpcTester.DrawIntro(CheckSupportedFeatures.Label, "Missing Features"u8))
using (IpcTester.DrawIntro(CheckSupportedFeatures.LabelU8, "Missing Features"u8))
table.DrawColumn(StringU8.Join(", "u8, new CheckSupportedFeatures(_pi).Invoke(_requiredFeatures)));
using (IpcTester.DrawIntro(GetConfiguration.Label, "Configuration"u8))
using (IpcTester.DrawIntro(GetConfiguration.LabelU8, "Configuration"u8))
{
DrawConfigPopup();
table.NextColumn();
@ -99,12 +99,12 @@ public class PluginStateIpcTester : IUiService, IDisposable
}
}
using (IpcTester.DrawIntro(GetModDirectory.Label, "Current Mod Directory"u8))
using (IpcTester.DrawIntro(GetModDirectory.LabelU8, "Current Mod Directory"u8))
{
table.DrawColumn(new GetModDirectory(_pi).Invoke());
}
using (IpcTester.DrawIntro(IpcSubscribers.ModDirectoryChanged.Label, "Last Mod Directory Change"u8))
using (IpcTester.DrawIntro(IpcSubscribers.ModDirectoryChanged.LabelU8, "Last Mod Directory Change"u8))
{
table.DrawColumn(_lastModDirectoryTime > DateTimeOffset.MinValue
? $"{_lastModDirectory} ({(_lastModDirectoryValid ? "Valid" : "Invalid")}) at {_lastModDirectoryTime}"

View file

@ -38,7 +38,7 @@ public class RedrawingIpcTester : Luna.IUiService, IDisposable
if (!table)
return;
using (IpcTester.DrawIntro(RedrawObject.Label, "Redraw by Index"u8))
using (IpcTester.DrawIntro(RedrawObject.LabelU8, "Redraw by Index"u8))
{
var tmp = _redrawIndex;
table.NextColumn();
@ -50,14 +50,14 @@ public class RedrawingIpcTester : Luna.IUiService, IDisposable
new RedrawObject(_pi).Invoke(_redrawIndex);
}
using (IpcTester.DrawIntro(RedrawAll.Label, "Redraw All"u8))
using (IpcTester.DrawIntro(RedrawAll.LabelU8, "Redraw All"u8))
{
table.NextColumn();
if (Im.SmallButton("Redraw##All"u8))
new RedrawAll(_pi).Invoke();
}
using (IpcTester.DrawIntro(GameObjectRedrawn.Label, "Last Redrawn Object:"u8))
using (IpcTester.DrawIntro(GameObjectRedrawn.LabelU8, "Last Redrawn Object:"u8))
{
table.DrawColumn(_lastRedrawnString);
}

View file

@ -24,31 +24,31 @@ public class ResolveIpcTester(IDalamudPluginInterface pi) : Luna.IUiService
if (!table)
return;
using (IpcTester.DrawIntro(ResolveDefaultPath.Label, "Default Collection Resolve"u8))
using (IpcTester.DrawIntro(ResolveDefaultPath.LabelU8, "Default Collection Resolve"u8))
{
if (_currentResolvePath.Length is not 0)
table.DrawColumn(new ResolveDefaultPath(pi).Invoke(_currentResolvePath));
}
using (IpcTester.DrawIntro(ResolveInterfacePath.Label, "Interface Collection Resolve"u8))
using (IpcTester.DrawIntro(ResolveInterfacePath.LabelU8, "Interface Collection Resolve"u8))
{
if (_currentResolvePath.Length is not 0)
table.DrawColumn(new ResolveInterfacePath(pi).Invoke(_currentResolvePath));
}
using (IpcTester.DrawIntro(ResolvePlayerPath.Label, "Player Collection Resolve"u8))
using (IpcTester.DrawIntro(ResolvePlayerPath.LabelU8, "Player Collection Resolve"u8))
{
if (_currentResolvePath.Length is not 0)
table.DrawColumn(new ResolvePlayerPath(pi).Invoke(_currentResolvePath));
}
using (IpcTester.DrawIntro(ResolveGameObjectPath.Label, "Game Object Collection Resolve"u8))
using (IpcTester.DrawIntro(ResolveGameObjectPath.LabelU8, "Game Object Collection Resolve"u8))
{
if (_currentResolvePath.Length is not 0)
table.DrawColumn(new ResolveGameObjectPath(pi).Invoke(_currentResolvePath, _currentReverseIdx));
}
using (IpcTester.DrawIntro(ReverseResolvePlayerPath.Label, "Reversed Game Paths (Player)"u8))
using (IpcTester.DrawIntro(ReverseResolvePlayerPath.LabelU8, "Reversed Game Paths (Player)"u8))
{
if (_currentReversePath.Length is not 0)
{
@ -62,7 +62,7 @@ public class ResolveIpcTester(IDalamudPluginInterface pi) : Luna.IUiService
}
}
using (IpcTester.DrawIntro(ReverseResolveGameObjectPath.Label, "Reversed Game Paths (Game Object)"u8))
using (IpcTester.DrawIntro(ReverseResolveGameObjectPath.LabelU8, "Reversed Game Paths (Game Object)"u8))
{
if (_currentReversePath.Length is not 0)
{
@ -79,7 +79,7 @@ public class ResolveIpcTester(IDalamudPluginInterface pi) : Luna.IUiService
string[] forwardArray = _currentResolvePath.Length > 0 ? [_currentResolvePath] : [];
string[] reverseArray = _currentReversePath.Length > 0 ? [_currentReversePath] : [];
using (IpcTester.DrawIntro(ResolvePlayerPaths.Label, "Resolved Paths (Player)"u8))
using (IpcTester.DrawIntro(ResolvePlayerPaths.LabelU8, "Resolved Paths (Player)"u8))
{
if (forwardArray.Length > 0 || reverseArray.Length > 0)
{
@ -88,7 +88,7 @@ public class ResolveIpcTester(IDalamudPluginInterface pi) : Luna.IUiService
}
}
using (IpcTester.DrawIntro(ResolvePlayerPathsAsync.Label, "Resolved Paths Async (Player)"u8))
using (IpcTester.DrawIntro(ResolvePlayerPathsAsync.LabelU8, "Resolved Paths Async (Player)"u8))
{
table.NextColumn();
if (Im.SmallButton("Start"u8))

View file

@ -41,7 +41,7 @@ public class ResourceTreeIpcTester(IDalamudPluginInterface pi, ObjectManager obj
if (!table)
return;
using (IpcTester.DrawIntro(GetGameObjectResourcePaths.Label, "Get GameObject resource paths"u8))
using (IpcTester.DrawIntro(GetGameObjectResourcePaths.LabelU8, "Get GameObject resource paths"u8))
{
DrawPopup("GetGameObjectResourcePaths"u8, ref _lastGameObjectResourcePaths, DrawResourcePaths,
_lastCallDuration);
@ -63,7 +63,7 @@ public class ResourceTreeIpcTester(IDalamudPluginInterface pi, ObjectManager obj
}
}
using (IpcTester.DrawIntro(GetPlayerResourcePaths.Label, "Get local player resource paths"u8))
using (IpcTester.DrawIntro(GetPlayerResourcePaths.LabelU8, "Get local player resource paths"u8))
{
DrawPopup("GetPlayerResourcePaths"u8, ref _lastPlayerResourcePaths!, DrawResourcePaths, _lastCallDuration);
table.NextColumn();
@ -82,7 +82,7 @@ public class ResourceTreeIpcTester(IDalamudPluginInterface pi, ObjectManager obj
}
}
using (IpcTester.DrawIntro(GetGameObjectResourcesOfType.Label, "Get GameObject resources of type"u8))
using (IpcTester.DrawIntro(GetGameObjectResourcesOfType.LabelU8, "Get GameObject resources of type"u8))
{
DrawPopup("GetGameObjectResourcesOfType"u8, ref _lastGameObjectResourcesOfType, DrawResourcesOfType,
_lastCallDuration);
@ -104,7 +104,7 @@ public class ResourceTreeIpcTester(IDalamudPluginInterface pi, ObjectManager obj
}
}
using (IpcTester.DrawIntro(GetPlayerResourcesOfType.Label, "Get local player resources of type"u8))
using (IpcTester.DrawIntro(GetPlayerResourcesOfType.LabelU8, "Get local player resources of type"u8))
{
DrawPopup("GetPlayerResourcesOfType"u8, ref _lastPlayerResourcesOfType, DrawResourcesOfType,
_lastCallDuration);
@ -124,7 +124,7 @@ public class ResourceTreeIpcTester(IDalamudPluginInterface pi, ObjectManager obj
}
}
using (IpcTester.DrawIntro(GetGameObjectResourceTrees.Label, "Get GameObject resource trees"u8))
using (IpcTester.DrawIntro(GetGameObjectResourceTrees.LabelU8, "Get GameObject resource trees"u8))
{
DrawPopup("GetGameObjectResourceTrees"u8, ref _lastGameObjectResourceTrees, DrawResourceTrees,
_lastCallDuration);
@ -146,7 +146,7 @@ public class ResourceTreeIpcTester(IDalamudPluginInterface pi, ObjectManager obj
}
}
using (IpcTester.DrawIntro(GetPlayerResourceTrees.Label, "Get local player resource trees"u8))
using (IpcTester.DrawIntro(GetPlayerResourceTrees.LabelU8, "Get local player resource trees"u8))
{
DrawPopup("GetPlayerResourceTrees"u8, ref _lastPlayerResourceTrees, DrawResourceTrees!, _lastCallDuration);
table.NextColumn();
@ -200,13 +200,13 @@ public class ResourceTreeIpcTester(IDalamudPluginInterface pi, ObjectManager obj
{
if (item == null)
{
Im.Tree.Node($"{label}: null", TreeNodeFlags.Leaf).Dispose();
Im.Tree.Leaf($"{label}: null");
continue;
}
if (firstSeen.TryGetValue(item, out var firstLabel))
{
Im.Tree.Node($"{label}: same as {firstLabel}", TreeNodeFlags.Leaf).Dispose();
Im.Tree.Leaf($"{label}: same as {firstLabel}");
continue;
}

View file

@ -58,27 +58,26 @@ public class TemporaryIpcTester(
if (!table)
return;
using (IpcTester.DrawIntro("Last Error", $"{_lastTempError}"))
using (IpcTester.DrawIntro("Last Error"u8, $"{_lastTempError}"))
{
table.DrawColumn("Last Created Collection"u8);
table.NextColumn();
LunaStyle.DrawGuid(LastCreatedCollectionId);
}
using (IpcTester.DrawIntro(CreateTemporaryCollection.Label, "Create Temporary Collection"u8))
using (IpcTester.DrawIntro(CreateTemporaryCollection.LabelU8, "Create Temporary Collection"u8))
{
table.NextColumn();
if (Im.SmallButton("Create##Collection"u8))
{
_lastTempError = new CreateTemporaryCollection(pi).Invoke(_identity, _tempCollectionName, out LastCreatedCollectionId);
if (_tempGuid is null)
_tempGuid = LastCreatedCollectionId;
_lastTempError = new CreateTemporaryCollection(pi).Invoke(_identity, _tempCollectionName, out LastCreatedCollectionId);
_tempGuid ??= LastCreatedCollectionId;
}
}
var guid = _tempGuid.GetValueOrDefault(Guid.Empty);
using (IpcTester.DrawIntro(DeleteTemporaryCollection.Label, "Delete Temporary Collection"u8))
using (IpcTester.DrawIntro(DeleteTemporaryCollection.LabelU8, "Delete Temporary Collection"u8))
{
table.NextColumn();
if (Im.SmallButton("Delete##Collection"u8))
@ -88,14 +87,14 @@ public class TemporaryIpcTester(
_lastTempError = new DeleteTemporaryCollection(pi).Invoke(LastCreatedCollectionId);
}
using (IpcTester.DrawIntro(AssignTemporaryCollection.Label, "Assign Temporary Collection"u8))
using (IpcTester.DrawIntro(AssignTemporaryCollection.LabelU8, "Assign Temporary Collection"u8))
{
table.NextColumn();
if (Im.SmallButton("Assign##NamedCollection"u8))
_lastTempError = new AssignTemporaryCollection(pi).Invoke(guid, _tempActorIndex, _forceOverwrite);
}
using (IpcTester.DrawIntro(AddTemporaryMod.Label, "Add Temporary Mod to specific Collection"u8))
using (IpcTester.DrawIntro(AddTemporaryMod.LabelU8, "Add Temporary Mod to specific Collection"u8))
{
table.NextColumn();
if (Im.SmallButton("Add##Mod"u8))
@ -104,7 +103,7 @@ public class TemporaryIpcTester(
_tempManipulation.Length > 0 ? _tempManipulation : string.Empty, int.MaxValue);
}
using (IpcTester.DrawIntro(CreateTemporaryCollection.Label, "Copy Existing Collection"u8))
using (IpcTester.DrawIntro(CreateTemporaryCollection.LabelU8, "Copy Existing Collection"u8))
{
table.NextColumn();
if (ImEx.Button("Copy##Collection"u8, Vector2.Zero,
@ -118,7 +117,7 @@ public class TemporaryIpcTester(
}
}
using (IpcTester.DrawIntro(AddTemporaryModAll.Label, "Add Temporary Mod to all Collections"u8))
using (IpcTester.DrawIntro(AddTemporaryModAll.LabelU8, "Add Temporary Mod to all Collections"u8))
{
table.NextColumn();
if (Im.SmallButton("Add##All"u8))
@ -127,21 +126,21 @@ public class TemporaryIpcTester(
_tempManipulation.Length > 0 ? _tempManipulation : string.Empty, int.MaxValue);
}
using (IpcTester.DrawIntro(RemoveTemporaryMod.Label, "Remove Temporary Mod from specific Collection"u8))
using (IpcTester.DrawIntro(RemoveTemporaryMod.LabelU8, "Remove Temporary Mod from specific Collection"u8))
{
table.NextColumn();
if (Im.SmallButton("Remove##Mod"u8))
_lastTempError = new RemoveTemporaryMod(pi).Invoke(_tempModName, guid, int.MaxValue);
}
using (IpcTester.DrawIntro(RemoveTemporaryModAll.Label, "Remove Temporary Mod from all Collections"u8))
using (IpcTester.DrawIntro(RemoveTemporaryModAll.LabelU8, "Remove Temporary Mod from all Collections"u8))
{
table.NextColumn();
if (Im.SmallButton("Remove##ModAll"u8))
_lastTempError = new RemoveTemporaryModAll(pi).Invoke(_tempModName, int.MaxValue);
}
using (IpcTester.DrawIntro(SetTemporaryModSettings.Label, "Set Temporary Mod Settings (to default) in specific Collection"u8))
using (IpcTester.DrawIntro(SetTemporaryModSettings.LabelU8, "Set Temporary Mod Settings (to default) in specific Collection"u8))
{
table.NextColumn();
if (Im.SmallButton("Set##SetTemporary"u8))
@ -150,7 +149,8 @@ public class TemporaryIpcTester(
"IPC Tester", 1337);
}
using (IpcTester.DrawIntro(SetTemporaryModSettingsPlayer.Label, "Set Temporary Mod Settings (to default) in game object collection"u8))
using (IpcTester.DrawIntro(SetTemporaryModSettingsPlayer.LabelU8,
"Set Temporary Mod Settings (to default) in game object collection"u8))
{
table.NextColumn();
if (Im.SmallButton("Set##SetTemporaryPlayer"u8))
@ -159,7 +159,7 @@ public class TemporaryIpcTester(
"IPC Tester", 1337);
}
using (IpcTester.DrawIntro(RemoveTemporaryModSettings.Label, "Remove Temporary Mod Settings from specific Collection"u8))
using (IpcTester.DrawIntro(RemoveTemporaryModSettings.LabelU8, "Remove Temporary Mod Settings from specific Collection"u8))
{
table.NextColumn();
if (Im.SmallButton("Remove##RemoveTemporary"u8))
@ -169,7 +169,7 @@ public class TemporaryIpcTester(
_lastTempError = new RemoveTemporaryModSettings(pi).Invoke(guid, _modDirectory, 1338);
}
using (IpcTester.DrawIntro(RemoveTemporaryModSettingsPlayer.Label, "Remove Temporary Mod Settings from game object Collection"u8))
using (IpcTester.DrawIntro(RemoveTemporaryModSettingsPlayer.LabelU8, "Remove Temporary Mod Settings from game object Collection"u8))
{
table.NextColumn();
if (Im.SmallButton("Remove##RemoveTemporaryPlayer"u8))
@ -179,7 +179,7 @@ public class TemporaryIpcTester(
_lastTempError = new RemoveTemporaryModSettingsPlayer(pi).Invoke(_tempActorIndex, _modDirectory, 1338);
}
using (IpcTester.DrawIntro(RemoveAllTemporaryModSettings.Label, "Remove All Temporary Mod Settings from specific Collection"u8))
using (IpcTester.DrawIntro(RemoveAllTemporaryModSettings.LabelU8, "Remove All Temporary Mod Settings from specific Collection"u8))
{
table.NextColumn();
if (Im.SmallButton("Remove##RemoveAllTemporary"u8))
@ -189,7 +189,7 @@ public class TemporaryIpcTester(
_lastTempError = new RemoveAllTemporaryModSettings(pi).Invoke(guid, 1338);
}
using (IpcTester.DrawIntro(RemoveAllTemporaryModSettingsPlayer.Label,
using (IpcTester.DrawIntro(RemoveAllTemporaryModSettingsPlayer.LabelU8,
"Remove All Temporary Mod Settings from game object Collection"u8))
{
table.NextColumn();
@ -200,7 +200,7 @@ public class TemporaryIpcTester(
_lastTempError = new RemoveAllTemporaryModSettingsPlayer(pi).Invoke(_tempActorIndex, 1338);
}
using (IpcTester.DrawIntro(QueryTemporaryModSettings.Label, "Query Temporary Mod Settings from specific Collection"u8))
using (IpcTester.DrawIntro(QueryTemporaryModSettings.LabelU8, "Query Temporary Mod Settings from specific Collection"u8))
{
table.NextColumn();
Im.SmallButton("Query##QueryTemporaryModSettings"u8);
@ -219,7 +219,7 @@ public class TemporaryIpcTester(
}
}
using (IpcTester.DrawIntro(QueryTemporaryModSettingsPlayer.Label, "Query Temporary Mod Settings from game object Collection"u8))
using (IpcTester.DrawIntro(QueryTemporaryModSettingsPlayer.LabelU8, "Query Temporary Mod Settings from game object Collection"u8))
{
table.NextColumn();
Im.SmallButton("Query##QueryTemporaryModSettingsPlayer"u8);

View file

@ -66,12 +66,12 @@ public class UiIpcTester : Luna.IUiService, IDisposable
if (!table)
return;
using (IpcTester.DrawIntro(PostSettingsDraw.Label, "Last Drawn Mod"u8))
using (IpcTester.DrawIntro(PostSettingsDraw.LabelU8, "Last Drawn Mod"u8))
{
table.DrawColumn(_lastDrawnMod.Length > 0 ? $"{_lastDrawnMod} at {_lastDrawnModTime}" : "None"u8);
}
using (IpcTester.DrawIntro(IpcSubscribers.ChangedItemTooltip.Label, "Add Tooltip"u8))
using (IpcTester.DrawIntro(IpcSubscribers.ChangedItemTooltip.LabelU8, "Add Tooltip"u8))
{
table.NextColumn();
if (Im.Checkbox("##tooltip"u8, ref _subscribedToTooltip))
@ -86,7 +86,7 @@ public class UiIpcTester : Luna.IUiService, IDisposable
ImEx.TextFrameAligned(_lastHovered);
}
using (IpcTester.DrawIntro(IpcSubscribers.ChangedItemClicked.Label, "Subscribe Click"u8))
using (IpcTester.DrawIntro(IpcSubscribers.ChangedItemClicked.LabelU8, "Subscribe Click"u8))
{
table.NextColumn();
if (Im.Checkbox("##click"u8, ref _subscribedToClick))
@ -101,7 +101,7 @@ public class UiIpcTester : Luna.IUiService, IDisposable
ImEx.TextFrameAligned(_lastClicked);
}
using (IpcTester.DrawIntro(OpenMainWindow.Label, "Open Mod Window"u8))
using (IpcTester.DrawIntro(OpenMainWindow.LabelU8, "Open Mod Window"u8))
{
table.NextColumn();
if (Im.SmallButton("Open##window"u8))
@ -111,7 +111,7 @@ public class UiIpcTester : Luna.IUiService, IDisposable
Im.Text($"{_ec}");
}
using (IpcTester.DrawIntro(CloseMainWindow.Label, "Close Mod Window"u8))
using (IpcTester.DrawIntro(CloseMainWindow.LabelU8, "Close Mod Window"u8))
{
table.NextColumn();
if (Im.SmallButton("Close##window"u8))

View file

@ -108,7 +108,7 @@ public sealed class ModelManager(
if (targetId == EstEntry.Zero)
return [];
return [GamePaths.Sklb.Customization(info.GenderRace, type.ToName(), targetId.AsId)];
return [GamePaths.Sklb.Customization(info.GenderRace, type.ToSuffix(), targetId.AsId)];
}
/// <summary> Try to resolve the absolute path to a .mtrl from the potentially-partial path provided by a model. </summary>

View file

@ -9,7 +9,7 @@ namespace Penumbra.Import.Textures;
public abstract class PathSelectCombo(IDataManager dataManager) : FilterComboBase<PathSelectCombo.PathData>
{
public bool Draw(Utf8StringHandler<LabelStringHandlerBuffer> label, Utf8StringHandler<HintStringHandlerBuffer> tooltip, string current,
public bool Draw(Utf8StringHandler<LabelStringHandlerBuffer> label, Utf8StringHandler<TextStringHandlerBuffer> tooltip, string current,
int skipPrefix, out string newPath)
{
_skipPrefix = skipPrefix;

View file

@ -273,7 +273,7 @@ internal partial record ResolveContext
var metaCache = Global.Collection.MetaCache;
var skeletonSet = metaCache?.GetEstEntry(type, raceCode, primary)
?? EstFile.GetDefault(Global.MetaFileManager, type, raceCode, primary);
return (raceCode, type.ToName(), skeletonSet.AsId);
return (raceCode, type.ToSuffix(), skeletonSet.AsId);
}
private unsafe Utf8GamePath ResolveSkeletonPathNative(uint partialSkeletonIndex)

View file

@ -1,3 +1,4 @@
using Luna.Generators;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Penumbra.GameData.Data;
@ -7,6 +8,7 @@ using Penumbra.Interop.Structs;
namespace Penumbra.Meta.Manipulations;
[NamedEnum]
public enum EstType : byte
{
Hair = MetaIndex.HairEst,
@ -120,9 +122,9 @@ public readonly record struct EstEntry(ushort Value)
}
}
public static class EstTypeExtension
public static partial class EstTypeExtension
{
public static string ToName(this EstType type)
public static string ToSuffix(this EstType type)
=> type switch
{
EstType.Hair => "hair",

View file

@ -1,24 +1,58 @@
using Luna.Generators;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace Penumbra.Meta.Manipulations;
[NamedEnum(Utf16: false)]
[TooltipEnum]
[JsonConverter(typeof(StringEnumConverter))]
public enum GlobalEqpType
{
[Name("Always Show Earrings")]
[Tooltip("Prevents the game from hiding earrings through other models when a specific earring is worn.")]
DoNotHideEarrings,
[Name("Always Show Necklaces")]
[Tooltip("Prevents the game from hiding necklaces through other models when a specific necklace is worn.")]
DoNotHideNecklace,
[Name("Always Show Bracelets")]
[Tooltip("Prevents the game from hiding bracelets through other models when a specific bracelet is worn.")]
DoNotHideBracelets,
[Name("Always Show Rings (Right Finger)")]
[Tooltip(
"Prevents the game from hiding rings worn on the right finger through other models when a specific ring is worn on the right finger.")]
DoNotHideRingR,
[Name("Always Show Rings (Left Finger)")]
[Tooltip(
"Prevents the game from hiding rings worn on the left finger through other models when a specific ring is worn on the left finger.")]
DoNotHideRingL,
[Name("Always Show Hats for Hrothgar")]
[Tooltip("Prevents the game from hiding any hats for Hrothgar that are normally flagged to not display on them.")]
DoNotHideHrothgarHats,
[Name("Always Show Hats for Viera")]
[Tooltip("Prevents the game from hiding any hats for Viera that are normally flagged to not display on them.")]
DoNotHideVieraHats,
[Name("Always Hide Horns (Au Ra)")]
[Tooltip("Forces the game to hide Au Ra horns regardless of headwear.")]
HideHorns,
[Name("Always Hide Horns (Viera)")]
[Tooltip("Forces the game to hide Viera ears regardless of headwear.")]
HideVieraEars,
[Name("Always Hide Horns (Miqo'te)")]
[Tooltip("Forces the game to hide Miqo'te ears regardless of headwear.")]
HideMiqoteEars,
}
public static class GlobalEqpExtensions
public static partial class GlobalEqpExtensions
{
public static bool HasCondition(this GlobalEqpType type)
=> type switch
@ -35,43 +69,4 @@ public static class GlobalEqpExtensions
GlobalEqpType.HideMiqoteEars => false,
_ => false,
};
public static ReadOnlySpan<byte> ToName(this GlobalEqpType type)
=> type switch
{
GlobalEqpType.DoNotHideEarrings => "Always Show Earrings"u8,
GlobalEqpType.DoNotHideNecklace => "Always Show Necklaces"u8,
GlobalEqpType.DoNotHideBracelets => "Always Show Bracelets"u8,
GlobalEqpType.DoNotHideRingR => "Always Show Rings (Right Finger)"u8,
GlobalEqpType.DoNotHideRingL => "Always Show Rings (Left Finger)"u8,
GlobalEqpType.DoNotHideHrothgarHats => "Always Show Hats for Hrothgar"u8,
GlobalEqpType.DoNotHideVieraHats => "Always Show Hats for Viera"u8,
GlobalEqpType.HideHorns => "Always Hide Horns (Au Ra)"u8,
GlobalEqpType.HideVieraEars => "Always Hide Ears (Viera)"u8,
GlobalEqpType.HideMiqoteEars => "Always Hide Ears (Miqo'te)"u8,
_ => "\0"u8,
};
public static ReadOnlySpan<byte> ToDescription(this GlobalEqpType type)
=> type switch
{
GlobalEqpType.DoNotHideEarrings => "Prevents the game from hiding earrings through other models when a specific earring is worn."u8,
GlobalEqpType.DoNotHideNecklace =>
"Prevents the game from hiding necklaces through other models when a specific necklace is worn."u8,
GlobalEqpType.DoNotHideBracelets =>
"Prevents the game from hiding bracelets through other models when a specific bracelet is worn."u8,
GlobalEqpType.DoNotHideRingR =>
"Prevents the game from hiding rings worn on the right finger through other models when a specific ring is worn on the right finger."u8,
GlobalEqpType.DoNotHideRingL =>
"Prevents the game from hiding rings worn on the left finger through other models when a specific ring is worn on the left finger."u8,
GlobalEqpType.DoNotHideHrothgarHats =>
"Prevents the game from hiding any hats for Hrothgar that are normally flagged to not display on them."u8,
GlobalEqpType.DoNotHideVieraHats =>
"Prevents the game from hiding any hats for Viera that are normally flagged to not display on them."u8,
GlobalEqpType.HideHorns => "Forces the game to hide Au Ra horns regardless of headwear."u8,
GlobalEqpType.HideVieraEars => "Forces the game to hide Viera ears regardless of headwear."u8,
GlobalEqpType.HideMiqoteEars => "Forces the game to hide Miqo'te ears regardless of headwear."u8,
_ => "\0"u8,
};
}

View file

@ -9,7 +9,7 @@ namespace Penumbra.Meta.Manipulations;
public readonly record struct RspIdentifier(SubRace SubRace, RspAttribute Attribute) : IMetaIdentifier
{
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData> changedItems)
=> changedItems.UpdateCountOrSet($"{SubRace.ToName()} {Attribute.ToFullString()}", () => new IdentifiedName());
=> changedItems.UpdateCountOrSet($"{SubRace.ToName()} {Attribute.ToName()}", () => new IdentifiedName());
public MetaIndex FileIndex()
=> MetaIndex.HumanCmp;
@ -42,7 +42,7 @@ public readonly record struct RspIdentifier(SubRace SubRace, RspAttribute Attrib
=> MetaManipulationType.Rsp;
public override string ToString()
=> $"RSP - {SubRace.ToName()} - {Attribute.ToFullString()}";
=> $"RSP - {SubRace.ToName()} - {Attribute.ToName()}";
}
[JsonConverter(typeof(Converter))]

View file

@ -132,14 +132,14 @@ public static class ItemSwap
public static FileSwap CreatePhyb(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EstType type,
GenderRace race, EstEntry estEntry)
{
var phybPath = GamePaths.Phyb.Customization(race, type.ToName(), estEntry.AsId);
var phybPath = GamePaths.Phyb.Customization(race, type.ToSuffix(), estEntry.AsId);
return FileSwap.CreateSwap(manager, ResourceType.Phyb, redirections, phybPath, phybPath);
}
public static FileSwap CreateSklb(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EstType type,
GenderRace race, EstEntry estEntry)
{
var sklbPath = GamePaths.Sklb.Customization(race, type.ToName(), estEntry.AsId);
var sklbPath = GamePaths.Sklb.Customization(race, type.ToSuffix(), estEntry.AsId);
return FileSwap.CreateSwap(manager, ResourceType.Sklb, redirections, sklbPath, sklbPath);
}

View file

@ -1,14 +1,8 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Plugin.Services;
using ImSharp;
using Luna;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.Communication;
using Penumbra.GameData.Data;
@ -39,7 +33,7 @@ public class FileEditor<T>(
{
public void Draw()
{
using var tab = ImRaii.TabItem(tabName);
using var tab = Im.TabBar.BeginItem(tabName);
if (!tab)
{
_quickImport = null;
@ -55,7 +49,7 @@ public class FileEditor<T>(
RedrawOnSaveBox();
Im.Line.Same();
DefaultInput();
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
Im.Dummy(new Vector2(Im.Style.TextHeight / 2));
DrawFilePanel();
}
@ -63,13 +57,13 @@ public class FileEditor<T>(
private void RedrawOnSaveBox()
{
var redraw = config.Ephemeral.ForceRedrawOnFileChange;
if (ImGui.Checkbox("Redraw on Save", ref redraw))
if (Im.Checkbox("Redraw on Save"u8, ref redraw))
{
config.Ephemeral.ForceRedrawOnFileChange = redraw;
config.Ephemeral.Save();
}
ImGuiUtil.HoverTooltip("Force a redraw of your player character whenever you save a file here.");
Im.Tooltip.OnHover("Force a redraw of your player character whenever you save a file here."u8);
}
public void Dispose()
@ -92,7 +86,7 @@ public class FileEditor<T>(
private T? _defaultFile;
private Exception? _defaultException;
private readonly Combo _combo = new(config, getFiles);
private readonly Combo _combo = new(getFiles);
private ModEditWindow.QuickImportAction? _quickImport;
@ -100,9 +94,9 @@ public class FileEditor<T>(
{
using var spacing = ImStyleDouble.ItemSpacing.PushX(Im.Style.GlobalScale * 3);
Im.Item.SetNextWidth(Im.ContentRegion.Available.X - 2 * (Im.Style.GlobalScale * 3 + Im.Style.FrameHeight));
ImGui.InputTextWithHint("##defaultInput", "Input game path to compare...", ref _defaultPath, Utf8GamePath.MaxGamePathLength);
_inInput = ImGui.IsItemActive();
if (ImGui.IsItemDeactivatedAfterEdit() && _defaultPath.Length > 0)
Im.Input.Text("##defaultInput"u8, ref _defaultPath, "Input game path to compare..."u8, maxLength: Utf8GamePath.MaxGamePathLength);
_inInput = Im.Item.Active;
if (Im.Item.DeactivatedAfterEdit && _defaultPath.Length > 0)
{
_isDefaultPathUtf8Valid = Utf8GamePath.FromString(_defaultPath, out _defaultPathUtf8);
_quickImport = null;
@ -110,7 +104,7 @@ public class FileEditor<T>(
try
{
var file = gameData.GetFile(_defaultPath);
if (file != null)
if (file is not null)
{
_defaultException = null;
(_defaultFile as IDisposable)?.Dispose();
@ -131,8 +125,7 @@ public class FileEditor<T>(
}
Im.Line.Same();
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Save.ToIconString(), new Vector2(Im.Style.FrameHeight), "Export this file.",
_defaultFile == null, true))
if (ImEx.Icon.Button(LunaStyle.SaveIcon, "Export this file."u8, _defaultFile is null))
fileDialog.OpenSavePicker($"Export {_defaultPath} to...", fileType, Path.GetFileNameWithoutExtension(_defaultPath), fileType,
(success, name) =>
{
@ -152,8 +145,7 @@ public class FileEditor<T>(
_quickImport ??=
ModEditWindow.QuickImportAction.Prepare(owner, _isDefaultPathUtf8Valid ? _defaultPathUtf8 : Utf8GamePath.Empty, _defaultFile);
Im.Line.Same();
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.FileImport.ToIconString(), new Vector2(Im.Style.FrameHeight),
$"Add a copy of this file to {_quickImport.OptionName}.", !_quickImport.CanExecute, true))
if (ImEx.Icon.Button(LunaStyle.ImportIcon, $"Add a copy of this file to {_quickImport.OptionName}.", !_quickImport.CanExecute))
{
try
{
@ -211,7 +203,7 @@ public class FileEditor<T>(
private void SaveButton()
{
var canSave = _changed && _currentFile is { Valid: true };
if (ImGuiUtil.DrawDisabledButton("Save to File", Vector2.Zero,
if (ImEx.Button("Save to File"u8, Vector2.Zero,
$"Save the selected {fileType} file with all changes applied. This is not revertible.", !canSave))
SaveFile();
}
@ -219,14 +211,14 @@ public class FileEditor<T>(
public void SaveFile()
{
compactor.WriteAllBytes(_currentPath!.File.FullName, _currentFile!.Write());
if (owner.Mod != null)
if (owner.Mod is not null)
communicator.ModFileChanged.Invoke(new ModFileChanged.Arguments(owner.Mod, _currentPath));
_changed = false;
}
private void ResetButton()
{
if (ImGuiUtil.DrawDisabledButton("Reset Changes", Vector2.Zero,
if (ImEx.Button("Reset Changes"u8, Vector2.Zero,
$"Reset all changes made to the {fileType} file.", !_changed))
{
var tmp = _currentPath;
@ -237,31 +229,31 @@ public class FileEditor<T>(
private void DrawFilePanel()
{
using var child = ImRaii.Child("##filePanel", -Vector2.One, true);
using var child = Im.Child.Begin("##filePanel"u8, Im.ContentRegion.Available, true);
if (!child)
return;
if (_currentPath != null)
if (_currentPath is not null)
{
if (_currentFile == null)
if (_currentFile is null)
{
Im.Text($"Could not parse selected {fileType} file.");
if (_currentException != null)
if (_currentException is not null)
{
using var tab = ImRaii.PushIndent();
ImGuiUtil.TextWrapped(_currentException.ToString());
using var tab = Im.Indent();
Im.TextWrapped($"{_currentException}");
}
}
else
{
using var id = ImRaii.PushId(0);
using var id = Im.Id.Push(0);
_changed |= drawEdit(_currentFile, false);
}
}
if (!_inInput && _defaultPath.Length > 0)
{
if (_currentPath != null)
if (_currentPath is not null)
{
Im.Line.New();
Im.Line.New();
@ -272,50 +264,46 @@ public class FileEditor<T>(
if (_defaultFile == null)
{
Im.Text($"Could not parse provided {fileType} game file:\n");
if (_defaultException != null)
if (_defaultException is not null)
{
using var tab = ImRaii.PushIndent();
ImGuiUtil.TextWrapped(_defaultException.ToString());
using var tab = Im.Indent();
Im.TextWrapped($"{_defaultException}");
}
}
else
{
using var id = ImRaii.PushId(1);
using var id = Im.Id.Push(1);
drawEdit(_defaultFile, true);
}
}
}
private class Combo : FilterComboCache<FileRegistry>
private class Combo(Func<IReadOnlyList<FileRegistry>> generator)
: FilterComboCache<FileRegistry>(generator, MouseWheelType.None, Penumbra.Log)
{
private readonly Configuration _config;
public Combo(Configuration config, Func<IReadOnlyList<FileRegistry>> generator)
: base(generator, MouseWheelType.None, Penumbra.Log)
=> _config = config;
protected override bool DrawSelectable(int globalIdx, bool selected)
{
var file = Items[globalIdx];
bool ret;
using (var c = ImGuiColor.Text.Push(ColorId.HandledConflictMod.Value(), file.IsOnPlayer))
using (ImGuiColor.Text.Push(ColorId.HandledConflictMod.Value(), file.IsOnPlayer))
{
ret = ImGui.Selectable(file.RelPath.ToString(), selected);
ret = Im.Selectable(file.RelPath.ToString(), selected);
}
if (Im.Item.Hovered())
{
using var tt = ImRaii.Tooltip();
using var tt = Im.Tooltip.Begin();
Im.Text("All Game Paths"u8);
Im.Separator();
using var t = Im.Table.Begin("##Tooltip"u8, 2, TableFlags.SizingFixedFit);
foreach (var (option, gamePath) in file.SubModUsage)
if (t)
{
ImGui.TableNextColumn();
ImUtf8.Text(gamePath.Path.Span);
ImGui.TableNextColumn();
using var color = ImGuiColor.Text.Push(ColorId.ItemId.Value());
Im.Text(option.GetFullName());
foreach (var (option, gamePath) in file.SubModUsage)
{
t.DrawColumn(gamePath.Path.Span);
using var color = ImGuiColor.Text.Push(ColorId.ItemId.Value());
t.DrawColumn(option.GetFullName());
}
}
}
@ -323,7 +311,7 @@ public class FileEditor<T>(
{
Im.Line.Same();
using var color = ImGuiColor.Text.Push(ColorId.ItemId.Value());
ImGuiUtil.RightAlign(file.SubModUsage[0].Item2.Path.ToString());
ImEx.TextRightAligned($"{file.SubModUsage[0].Item2.Path}");
}
return ret;

View file

@ -1,9 +1,8 @@
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Luna;
using Luna.Generators;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Widgets;
using Penumbra.Api.Enums;
using Penumbra.Collections.Manager;
@ -29,6 +28,43 @@ using MouseWheelType = OtterGui.Widgets.MouseWheelType;
namespace Penumbra.UI.AdvancedWindow;
[NamedEnum(Utf16: false)]
public enum SwapType
{
Hat,
Top,
Gloves,
Pants,
Shoes,
Earrings,
Necklace,
Bracelet,
Ring,
[Name("Between Slots")]
BetweenSlots,
Hair,
Face,
Ears,
Tail,
Weapon,
Glasses,
}
[NamedEnum(Utf16: false)]
public enum BetweenSlotTypes
{
Hat,
Earrings,
Necklace,
Bracelets,
[Name("Right Ring")]
RightRing,
[Name("Left Ring")]
LeftRing,
Glasses,
}
public class ItemSwapTab : IDisposable, ITab
{
private readonly Configuration _config;
@ -49,19 +85,19 @@ public class ItemSwapTab : IDisposable, ITab
_swapData = new ItemSwapContainer(metaFileManager, identifier);
var a = collectionManager.Active;
_selectors = new Dictionary<SwapType, (ItemSelector Source, ItemSelector Target, string TextFrom, string TextTo)>
_selectors = new Dictionary<SwapType, (ItemSelector Source, ItemSelector Target, StringU8 TextFrom, StringU8 TextTo)>
{
// @formatter:off
[SwapType.Hat] = (new ItemSelector(a, itemService, selector, FullEquipType.Head), new ItemSelector(a, itemService, null, FullEquipType.Head), "Take this Hat", "and put it on this one" ),
[SwapType.Top] = (new ItemSelector(a, itemService, selector, FullEquipType.Body), new ItemSelector(a, itemService, null, FullEquipType.Body), "Take this Top", "and put it on this one" ),
[SwapType.Gloves] = (new ItemSelector(a, itemService, selector, FullEquipType.Hands), new ItemSelector(a, itemService, null, FullEquipType.Hands), "Take these Gloves", "and put them on these" ),
[SwapType.Pants] = (new ItemSelector(a, itemService, selector, FullEquipType.Legs), new ItemSelector(a, itemService, null, FullEquipType.Legs), "Take these Pants", "and put them on these" ),
[SwapType.Shoes] = (new ItemSelector(a, itemService, selector, FullEquipType.Feet), new ItemSelector(a, itemService, null, FullEquipType.Feet), "Take these Shoes", "and put them on these" ),
[SwapType.Earrings] = (new ItemSelector(a, itemService, selector, FullEquipType.Ears), new ItemSelector(a, itemService, null, FullEquipType.Ears), "Take these Earrings", "and put them on these" ),
[SwapType.Necklace] = (new ItemSelector(a, itemService, selector, FullEquipType.Neck), new ItemSelector(a, itemService, null, FullEquipType.Neck), "Take this Necklace", "and put it on this one" ),
[SwapType.Bracelet] = (new ItemSelector(a, itemService, selector, FullEquipType.Wrists), new ItemSelector(a, itemService, null, FullEquipType.Wrists), "Take these Bracelets", "and put them on these" ),
[SwapType.Ring] = (new ItemSelector(a, itemService, selector, FullEquipType.Finger), new ItemSelector(a, itemService, null, FullEquipType.Finger), "Take this Ring", "and put it on this one" ),
[SwapType.Glasses] = (new ItemSelector(a, itemService, selector, FullEquipType.Glasses), new ItemSelector(a, itemService, null, FullEquipType.Glasses), "Take these Glasses", "and put them on these" ),
[SwapType.Hat] = (new ItemSelector(a, itemService, selector, FullEquipType.Head), new ItemSelector(a, itemService, null, FullEquipType.Head), new StringU8("Take this Hat"u8), new StringU8("and put it on this one"u8) ),
[SwapType.Top] = (new ItemSelector(a, itemService, selector, FullEquipType.Body), new ItemSelector(a, itemService, null, FullEquipType.Body), new StringU8("Take this Top"u8), new StringU8("and put it on this one"u8) ),
[SwapType.Gloves] = (new ItemSelector(a, itemService, selector, FullEquipType.Hands), new ItemSelector(a, itemService, null, FullEquipType.Hands), new StringU8("Take these Gloves"u8), new StringU8("and put them on these"u8) ),
[SwapType.Pants] = (new ItemSelector(a, itemService, selector, FullEquipType.Legs), new ItemSelector(a, itemService, null, FullEquipType.Legs), new StringU8("Take these Pants"u8), new StringU8("and put them on these"u8) ),
[SwapType.Shoes] = (new ItemSelector(a, itemService, selector, FullEquipType.Feet), new ItemSelector(a, itemService, null, FullEquipType.Feet), new StringU8("Take these Shoes"u8), new StringU8("and put them on these"u8) ),
[SwapType.Earrings] = (new ItemSelector(a, itemService, selector, FullEquipType.Ears), new ItemSelector(a, itemService, null, FullEquipType.Ears), new StringU8("Take these Earrings"u8), new StringU8("and put them on these"u8) ),
[SwapType.Necklace] = (new ItemSelector(a, itemService, selector, FullEquipType.Neck), new ItemSelector(a, itemService, null, FullEquipType.Neck), new StringU8("Take this Necklace"u8), new StringU8("and put it on this one"u8) ),
[SwapType.Bracelet] = (new ItemSelector(a, itemService, selector, FullEquipType.Wrists), new ItemSelector(a, itemService, null, FullEquipType.Wrists), new StringU8("Take these Bracelets"u8), new StringU8("and put them on these"u8) ),
[SwapType.Ring] = (new ItemSelector(a, itemService, selector, FullEquipType.Finger), new ItemSelector(a, itemService, null, FullEquipType.Finger), new StringU8("Take this Ring"u8), new StringU8("and put it on this one"u8) ),
[SwapType.Glasses] = (new ItemSelector(a, itemService, selector, FullEquipType.Glasses), new ItemSelector(a, itemService, null, FullEquipType.Glasses), new StringU8("Take these Glasses"u8), new StringU8("and put them on these"u8) ),
// @formatter:on
};
@ -78,7 +114,7 @@ public class ItemSwapTab : IDisposable, ITab
return;
var oldDefaultName = $"{_mod?.Name ?? "Unknown"} (Swapped)";
if (_newModName.Length == 0 || oldDefaultName == _newModName)
if (_newModName.Length is 0 || oldDefaultName == _newModName)
_newModName = $"{mod.Name} (Swapped)";
_mod = mod;
@ -99,9 +135,9 @@ public class ItemSwapTab : IDisposable, ITab
DrawSwapBar();
using var table = ImRaii.ListBox("##swaps", -Vector2.One);
if (_loadException != null)
ImGuiUtil.TextWrapped($"Could not load Customization Swap:\n{_loadException}");
using var table = Im.ListBox.Begin("##swaps"u8, Im.ContentRegion.Available);
if (_loadException is not null)
Im.TextWrapped($"Could not load Customization Swap:\n{_loadException}");
else if (_swapData.Loaded)
foreach (var swap in _swapData.Swaps)
DrawSwap(swap);
@ -117,25 +153,7 @@ public class ItemSwapTab : IDisposable, ITab
_communicator.ModOptionChanged.Unsubscribe(OnModOptionChange);
}
private enum SwapType
{
Hat,
Top,
Gloves,
Pants,
Shoes,
Earrings,
Necklace,
Bracelet,
Ring,
BetweenSlots,
Hair,
Face,
Ears,
Tail,
Weapon,
Glasses,
}
private class ItemSelector(ActiveCollections collections, ItemData data, ModFileSystemSelector? selector, FullEquipType type)
: FilterComboCache<(EquipItem Item, bool InMod, SingleArray<IMod> InCollection)>(() =>
@ -173,8 +191,8 @@ public class ItemSwapTab : IDisposable, ITab
=> obj.Item.Name;
}
private readonly Dictionary<SwapType, (ItemSelector Source, ItemSelector Target, string TextFrom, string TextTo)> _selectors;
private readonly ItemSwapContainer _swapData;
private readonly Dictionary<SwapType, (ItemSelector Source, ItemSelector Target, StringU8 TextFrom, StringU8 TextTo)> _selectors;
private readonly ItemSwapContainer _swapData;
private Mod? _mod;
private ModSettings? _modSettings;
@ -315,11 +333,9 @@ public class ItemSwapTab : IDisposable, ITab
private string CreateAuthor()
{
if (_mod!.Author.Length is 0)
return _config.DefaultModAuthor;
if (_mod!.Author == _config.DefaultModAuthor)
return _config.DefaultModAuthor;
if (_mod!.Author is "TexTools User" or DefaultTexToolsData.Author)
if (_mod!.Author.Length is 0
|| _mod!.Author == _config.DefaultModAuthor
|| _mod!.Author is "TexTools User" or DefaultTexToolsData.Author)
return _config.DefaultModAuthor;
if (_config.DefaultModAuthor is DefaultTexToolsData.Author)
return _mod!.Author;
@ -330,7 +346,7 @@ public class ItemSwapTab : IDisposable, ITab
private void UpdateOption()
{
_selectedGroup = _mod?.Groups.FirstOrDefault(g => g.Name == _newGroupName);
_subModValid = _mod != null
_subModValid = _mod is not null
&& _newGroupName.Length > 0
&& _newOptionName.Length > 0
&& (_selectedGroup?.Options.All(o => o.Name != _newOptionName) ?? true);
@ -339,7 +355,7 @@ public class ItemSwapTab : IDisposable, ITab
private void CreateMod()
{
var newDir = _modManager.Creator.CreateEmptyMod(_modManager.BasePath, _newModName, CreateDescription(), CreateAuthor());
if (newDir == null)
if (newDir is null)
return;
_modManager.AddMod(newDir, false);
@ -351,7 +367,7 @@ public class ItemSwapTab : IDisposable, ITab
private void CreateOption()
{
if (_mod == null || !_subModValid)
if (_mod is null || !_subModValid)
return;
var groupCreated = false;
@ -366,9 +382,9 @@ public class ItemSwapTab : IDisposable, ITab
if (optionFolderName?.Exists == true)
throw new Exception($"The folder {optionFolderName.FullName} for the option already exists.");
if (optionFolderName != null)
if (optionFolderName is not null)
{
if (_selectedGroup == null)
if (_selectedGroup is null)
{
if (_modManager.OptionEditor.AddModGroup(_mod, GroupType.Multi, _newGroupName) is not { } group)
throw new Exception($"Failure creating option group.");
@ -383,7 +399,6 @@ public class ItemSwapTab : IDisposable, ITab
createdOption = option;
optionFolderName = Directory.CreateDirectory(optionFolderName.FullName);
dirCreated = true;
// #TODO ModOption <> DataContainer
if (!_swapData.WriteMod(_modManager, _mod, (IModDataContainer)option,
_useFileSwaps ? ItemSwapContainer.WriteType.UseSwaps : ItemSwapContainer.WriteType.NoSwaps, optionFolderName))
throw new Exception("Failure writing files for mod swap.");
@ -417,56 +432,55 @@ public class ItemSwapTab : IDisposable, ITab
private void DrawHeaderLine(float width)
{
var newModAvailable = _loadException == null && _swapData.Loaded;
var newModAvailable = _loadException is null && _swapData.Loaded;
Im.Item.SetNextWidth(width);
if (ImGui.InputTextWithHint("##newModName", "New Mod Name...", ref _newModName, 64))
if (Im.Input.Text("##newModName"u8, ref _newModName, "New Mod Name..."u8))
{ }
Im.Line.Same();
var tt = !newModAvailable
? "No swap is currently loaded."
: _newModName.Length == 0
? "Please enter a name for your mod."
: "Create a new mod of the given name containing only the swap.";
if (ImGuiUtil.DrawDisabledButton("Create New Mod", new Vector2(width / 2, 0), tt, !newModAvailable || _newModName.Length == 0))
? "No swap is currently loaded."u8
: _newModName.Length is 0
? "Please enter a name for your mod."u8
: "Create a new mod of the given name containing only the swap."u8;
if (ImEx.Button("Create New Mod"u8, new Vector2(width / 2, 0), tt, !newModAvailable || _newModName.Length is 0))
CreateMod();
Im.Line.Same();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 20 * Im.Style.GlobalScale);
ImGui.Checkbox("Use File Swaps", ref _useFileSwaps);
ImGuiUtil.HoverTooltip("Instead of writing every single non-default file to the newly created mod or option,\n"
+ "even those available from game files, use File Swaps to default game files where possible.");
Im.Cursor.X += 20 * Im.Style.GlobalScale;
Im.Checkbox("Use File Swaps"u8, ref _useFileSwaps);
Im.Tooltip.OnHover("Instead of writing every single non-default file to the newly created mod or option,\n"u8
+ "even those available from game files, use File Swaps to default game files where possible."u8);
Im.Item.SetNextWidth((width - Im.Style.ItemSpacing.X) / 2);
if (ImGui.InputTextWithHint("##groupName", "Group Name...", ref _newGroupName, 32))
if (Im.Input.Text("##groupName"u8, ref _newGroupName, "Group Name..."u8))
UpdateOption();
Im.Line.Same();
Im.Item.SetNextWidth((width - Im.Style.ItemSpacing.X) / 2);
if (ImGui.InputTextWithHint("##optionName", "New Option Name...", ref _newOptionName, 32))
if (Im.Input.Text("##optionName"u8, ref _newOptionName, "New Option Name..."u8))
UpdateOption();
Im.Line.Same();
tt = !_subModValid
? "An option with that name already exists in that group, or no name is specified."
? "An option with that name already exists in that group, or no name is specified."u8
: !newModAvailable
? "Create a new option inside this mod containing only the swap."
: "Create a new option (and possibly Multi-Group) inside the currently selected mod containing the swap.";
if (ImGuiUtil.DrawDisabledButton("Create New Option", new Vector2(width / 2, 0), tt, !newModAvailable || !_subModValid))
? "Create a new option inside this mod containing only the swap."u8
: "Create a new option (and possibly Multi-Group) inside the currently selected mod containing the swap."u8;
if (ImEx.Button("Create New Option"u8, new Vector2(width / 2, 0), tt, !newModAvailable || !_subModValid))
CreateOption();
Im.Line.Same();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 20 * Im.Style.GlobalScale);
_dirty |= ImGui.Checkbox("Use Entire Collection", ref _useCurrentCollection);
ImGuiUtil.HoverTooltip(
"Use all applied mods from the Selected Collection with their current settings and respecting the enabled state of mods and inheritance,\n"
+ "instead of using only the selected mod with its current settings in the Selected collection or the default settings, ignoring the enabled state and inheritance.");
Im.Cursor.X += 20 * Im.Style.GlobalScale;
_dirty |= Im.Checkbox("Use Entire Collection"u8, ref _useCurrentCollection);
Im.Tooltip.OnHover("Use all applied mods from the Selected Collection with their current settings and respecting the enabled state of mods and inheritance,\n"u8
+ "instead of using only the selected mod with its current settings in the Selected collection or the default settings, ignoring the enabled state and inheritance."u8);
}
private void DrawSwapBar()
{
using var bar = ImRaii.TabBar("##swapBar", ImGuiTabBarFlags.None);
using var bar = Im.TabBar.Begin("##swapBar"u8);
DrawEquipmentSwap(SwapType.Hat);
DrawEquipmentSwap(SwapType.Top);
@ -486,9 +500,9 @@ public class ItemSwapTab : IDisposable, ITab
//DrawWeaponSwap();
}
private ImRaii.IEndObject DrawTab(SwapType newTab)
private Im.TabItemDisposable DrawTab(SwapType newTab)
{
var tab = ImRaii.TabItem(newTab is SwapType.BetweenSlots ? "Between Slots" : newTab.ToString());
var tab = Im.TabBar.BeginItem(newTab.ToNameU8());
if (tab)
{
_dirty |= _lastTab != newTab;
@ -510,18 +524,16 @@ public class ItemSwapTab : IDisposable, ITab
table.SetupColumn("##text"u8, TableColumnFlags.WidthFixed, Im.Font.CalculateSize("and put them on these"u8).X);
var (article1, article2, selector) = GetAccessorySelector(_slotFrom, true);
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
Im.Text($"Take {article1}");
table.DrawFrameColumn($"Take {article1}");
ImGui.TableNextColumn();
Im.Item.SetNextWidth(100 * Im.Style.GlobalScale);
using (var combo = ImRaii.Combo("##fromType", ToName(_slotFrom)))
table.NextColumn();
Im.Item.SetNextWidthScaled(100);
using (var combo = Im.Combo.Begin("##fromType"u8, _slotFrom.ToNameU8()))
{
if (combo)
foreach (var slot in Enum.GetValues<BetweenSlotTypes>())
{
if (!ImGui.Selectable(ToName(slot), slot == _slotFrom) || slot == _slotFrom)
if (!Im.Selectable(slot.ToNameU8(), slot == _slotFrom) || slot == _slotFrom)
continue;
_dirty = true;
@ -531,24 +543,22 @@ public class ItemSwapTab : IDisposable, ITab
}
}
ImGui.TableNextColumn();
table.NextColumn();
_dirty |= selector.Draw("##itemSource", selector.CurrentSelection.Item.Name, string.Empty,
InputWidth * 2 * Im.Style.GlobalScale,
Im.Style.TextHeightWithSpacing);
(article1, _, selector) = GetAccessorySelector(_slotTo, false);
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
Im.Text($"and put {article2} on {article1}");
table.DrawFrameColumn($"and put {article2} on {article1}");
ImGui.TableNextColumn();
Im.Item.SetNextWidth(100 * Im.Style.GlobalScale);
using (var combo = ImRaii.Combo("##toType", ToName(_slotTo)))
table.NextColumn();
Im.Item.SetNextWidthScaled(100);
using (var combo = Im.Combo.Begin("##toType"u8, _slotTo.ToNameU8()))
{
if (combo)
foreach (var slot in AvailableToTypes.Where(t => t != _slotFrom))
{
if (!ImGui.Selectable(ToName(slot), slot == _slotTo) || slot == _slotTo)
if (!Im.Selectable(slot.ToNameU8(), slot == _slotTo) || slot == _slotTo)
continue;
_dirty = true;
@ -556,36 +566,37 @@ public class ItemSwapTab : IDisposable, ITab
}
}
ImGui.TableNextColumn();
table.NextColumn();
_dirty |= selector.Draw("##itemTarget", selector.CurrentSelection.Item.Name, string.Empty, InputWidth * 2 * Im.Style.GlobalScale,
Im.Style.TextHeightWithSpacing);
if (_affectedItems is not { Count: > 1 })
return;
Im.Line.Same();
ImGuiUtil.DrawTextButton($"which will also affect {_affectedItems.Count - 1} other Items.", Vector2.Zero,
new Rgba32(Colors.PressEnterWarningBg).Color);
ImEx.TextFramed($"which will also affect {_affectedItems.Count - 1} other Items.", Vector2.Zero, Colors.PressEnterWarningBg);
if (Im.Item.Hovered())
ImGui.SetTooltip(string.Join('\n', _affectedItems.Where(i => !ReferenceEquals(i.Name, selector.CurrentSelection.Item.Name))
.Select(i => i.Name)));
{
using var tt = Im.Tooltip.Begin();
foreach (var item in _affectedItems.Where(i => !ReferenceEquals(i.Name, selector.CurrentSelection.Item.Name)))
Im.Text(item.Name);
}
}
private (string, string, ItemSelector) GetAccessorySelector(BetweenSlotTypes slot, bool source)
private RefTuple<ReadOnlySpan<byte>, ReadOnlySpan<byte>, ItemSelector> GetAccessorySelector(BetweenSlotTypes slot, bool source)
{
var (type, article1, article2) = slot switch
{
BetweenSlotTypes.Hat => (SwapType.Hat, "this", "it"),
BetweenSlotTypes.Earrings => (SwapType.Earrings, "these", "them"),
BetweenSlotTypes.Necklace => (SwapType.Necklace, "this", "it"),
BetweenSlotTypes.Bracelets => (SwapType.Bracelet, "these", "them"),
BetweenSlotTypes.RightRing => (SwapType.Ring, "this", "it"),
BetweenSlotTypes.LeftRing => (SwapType.Ring, "this", "it"),
BetweenSlotTypes.Glasses => (SwapType.Glasses, "these", "them"),
_ => (SwapType.Ring, "this", "it"),
BetweenSlotTypes.Hat => RefTuple.Create(SwapType.Hat, "this"u8, "it"u8),
BetweenSlotTypes.Earrings => RefTuple.Create(SwapType.Earrings, "these"u8, "them"u8),
BetweenSlotTypes.Necklace => RefTuple.Create(SwapType.Necklace, "this"u8, "it"u8),
BetweenSlotTypes.Bracelets => RefTuple.Create(SwapType.Bracelet, "these"u8, "them"u8),
BetweenSlotTypes.RightRing => RefTuple.Create(SwapType.Ring, "this"u8, "it"u8),
BetweenSlotTypes.LeftRing => RefTuple.Create(SwapType.Ring, "this"u8, "it"u8),
BetweenSlotTypes.Glasses => RefTuple.Create(SwapType.Glasses, "these"u8, "them"u8),
_ => RefTuple.Create(SwapType.Ring, "this"u8, "it"u8),
};
var (itemSelector, target, _, _) = _selectors[type];
return (article1, article2, source ? itemSelector : target);
return RefTuple.Create(article1, article2, source ? itemSelector : target);
}
private void DrawEquipmentSwap(SwapType type)
@ -596,40 +607,41 @@ public class ItemSwapTab : IDisposable, ITab
var (sourceSelector, targetSelector, text1, text2) = _selectors[type];
using var table = Im.Table.Begin("##settings"u8, 2, TableFlags.SizingFixedFit);
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
Im.Text(text1);
ImGui.TableNextColumn();
_dirty |= sourceSelector.Draw("##itemSource", sourceSelector.CurrentSelection.Item.Name, string.Empty, InputWidth * 2 * Im.Style.GlobalScale,
Im.Style.TextHeightWithSpacing);
if (!table)
return;
table.DrawFrameColumn(text1);
table.NextColumn();
_dirty |= sourceSelector.Draw("##itemSource", sourceSelector.CurrentSelection.Item.Name, string.Empty,
InputWidth * 2 * Im.Style.GlobalScale, Im.Style.TextHeightWithSpacing);
if (type == SwapType.Ring)
if (type is SwapType.Ring)
{
Im.Line.Same();
_dirty |= ImGui.Checkbox("Swap Right Ring", ref _useRightRing);
_dirty |= Im.Checkbox("Swap Right Ring"u8, ref _useRightRing);
}
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
Im.Text(text2);
ImGui.TableNextColumn();
_dirty |= targetSelector.Draw("##itemTarget", targetSelector.CurrentSelection.Item.Name, string.Empty, InputWidth * 2 * Im.Style.GlobalScale,
table.DrawFrameColumn(text2);
table.NextColumn();
_dirty |= targetSelector.Draw("##itemTarget", targetSelector.CurrentSelection.Item.Name, string.Empty,
InputWidth * 2 * Im.Style.GlobalScale,
Im.Style.TextHeightWithSpacing);
if (type == SwapType.Ring)
if (type is SwapType.Ring)
{
Im.Line.Same();
_dirty |= ImGui.Checkbox("Swap Left Ring", ref _useLeftRing);
_dirty |= Im.Checkbox("Swap Left Ring"u8, ref _useLeftRing);
}
if (_affectedItems is not { Count: > 1 })
return;
Im.Line.Same();
ImGuiUtil.DrawTextButton($"which will also affect {_affectedItems.Count - 1} other Items.", Vector2.Zero,
new Rgba32(Colors.PressEnterWarningBg).Color);
ImEx.TextFramed($"which will also affect {_affectedItems.Count - 1} other Items.", Vector2.Zero, Colors.PressEnterWarningBg);
if (Im.Item.Hovered())
ImGui.SetTooltip(string.Join('\n', _affectedItems.Where(i => !ReferenceEquals(i.Name, targetSelector.CurrentSelection.Item.Name))
.Select(i => i.Name)));
{
using var tt = Im.Tooltip.Begin();
foreach (var item in _affectedItems.Where(i => !ReferenceEquals(i.Name, targetSelector.CurrentSelection.Item.Name)))
Im.Text(item.Name);
}
}
private void DrawHairSwap()
@ -639,9 +651,9 @@ public class ItemSwapTab : IDisposable, ITab
return;
using var table = Im.Table.Begin("##settings"u8, 2, TableFlags.SizingFixedFit);
DrawTargetIdInput("Take this Hairstyle");
DrawSourceIdInput();
DrawGenderInput();
DrawTargetIdInput(table, "Take this Hairstyle"u8);
DrawSourceIdInput(table, "and put it on this one"u8);
DrawGenderInput(table, "for all"u8);
}
private void DrawTailSwap()
@ -651,9 +663,9 @@ public class ItemSwapTab : IDisposable, ITab
return;
using var table = Im.Table.Begin("##settings"u8, 2, TableFlags.SizingFixedFit);
DrawTargetIdInput("Take this Tail Type");
DrawSourceIdInput();
DrawGenderInput("for all", 2);
DrawTargetIdInput(table, "Take this Tail Type"u8);
DrawSourceIdInput(table, "and put it on this one"u8);
DrawGenderInput(table, "for all"u8, 2);
}
@ -664,55 +676,48 @@ public class ItemSwapTab : IDisposable, ITab
return;
using var table = Im.Table.Begin("##settings"u8, 2, TableFlags.SizingFixedFit);
DrawTargetIdInput("Take this Ear Type");
DrawSourceIdInput();
DrawGenderInput("for all Viera", 0);
DrawTargetIdInput(table, "Take this Ear Type"u8);
DrawSourceIdInput(table, "and put it on this one"u8);
DrawGenderInput(table, "for all Viera"u8, 0);
}
private const float InputWidth = 120;
private void DrawTargetIdInput(string text = "Take this ID")
private void DrawTargetIdInput(in Im.TableDisposable table, ReadOnlySpan<byte> text)
{
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
Im.Text(text);
ImGui.TableNextColumn();
Im.Item.SetNextWidth(InputWidth * Im.Style.GlobalScale);
if (ImGui.InputInt("##targetId", ref _targetId))
table.DrawFrameColumn(text);
table.NextColumn();
Im.Item.SetNextWidthScaled(InputWidth);
if (Im.Input.Scalar("##targetId"u8, ref _targetId))
_targetId = Math.Clamp(_targetId, 0, byte.MaxValue);
_dirty |= ImGui.IsItemDeactivatedAfterEdit();
_dirty |= Im.Item.DeactivatedAfterEdit;
}
private void DrawSourceIdInput(string text = "and put it on this one")
private void DrawSourceIdInput(in Im.TableDisposable table, ReadOnlySpan<byte> text)
{
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
Im.Text(text);
table.DrawFrameColumn(text);
ImGui.TableNextColumn();
Im.Item.SetNextWidth(InputWidth * Im.Style.GlobalScale);
if (ImGui.InputInt("##sourceId", ref _sourceId))
table.NextColumn();
Im.Item.SetNextWidthScaled(InputWidth);
if (Im.Input.Scalar("##sourceId"u8, ref _sourceId))
_sourceId = Math.Clamp(_sourceId, 0, byte.MaxValue);
_dirty |= ImGui.IsItemDeactivatedAfterEdit();
_dirty |= Im.Item.DeactivatedAfterEdit;
}
private void DrawGenderInput(string text = "for all", int drawRace = 1)
private void DrawGenderInput(in Im.TableDisposable table, ReadOnlySpan<byte> text, int drawRace = 1)
{
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
Im.Text(text);
table.DrawFrameColumn(text);
ImGui.TableNextColumn();
_dirty |= Combos.Gender("##Gender", _currentGender, out _currentGender);
if (drawRace == 1)
table.NextColumn();
_dirty |= Combos.Combos.Gender("##Gender", _currentGender, out _currentGender);
if (drawRace is 1)
{
Im.Line.Same();
_dirty |= Combos.Race("##Race", _currentRace, out _currentRace, InputWidth);
_dirty |= Combos.Combos.Race("##Race", _currentRace, out _currentRace, InputWidth);
}
else if (drawRace == 2)
else if (drawRace is 2)
{
Im.Line.Same();
if (_currentRace is not ModelRace.Miqote and not ModelRace.AuRa and not ModelRace.Hrothgar)
@ -750,8 +755,8 @@ public class ItemSwapTab : IDisposable, ITab
private static void DrawSwap(Swap swap)
{
var flags = swap.ChildSwaps.Count is 0 ? ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.Leaf : ImGuiTreeNodeFlags.DefaultOpen;
using var tree = ImRaii.TreeNode(SwapToString(swap), flags);
var flags = swap.ChildSwaps.Count is 0 ? TreeNodeFlags.Bullet | TreeNodeFlags.Leaf : TreeNodeFlags.DefaultOpen;
using var tree = Im.Tree.Node(SwapToString(swap), flags);
if (!tree)
return;
@ -781,7 +786,7 @@ public class ItemSwapTab : IDisposable, ITab
private void OnInheritanceChange(in CollectionInheritanceChanged.Arguments arguments)
{
if (arguments.Collection != _collectionManager.Active.Current || _mod == null)
if (arguments.Collection != _collectionManager.Active.Current || _mod is null)
return;
UpdateMod(_mod, arguments.Collection.GetInheritedSettings(_mod.Index).Settings);
@ -800,17 +805,6 @@ public class ItemSwapTab : IDisposable, ITab
_dirty = true;
}
private enum BetweenSlotTypes
{
Hat,
Earrings,
Necklace,
Bracelets,
RightRing,
LeftRing,
Glasses,
}
private static EquipSlot ToEquipSlot(BetweenSlotTypes type)
=> type switch
{
@ -824,19 +818,6 @@ public class ItemSwapTab : IDisposable, ITab
_ => EquipSlot.Unknown,
};
private static string ToName(BetweenSlotTypes type)
=> type switch
{
BetweenSlotTypes.Hat => "Hat",
BetweenSlotTypes.Earrings => "Earrings",
BetweenSlotTypes.Necklace => "Necklace",
BetweenSlotTypes.Bracelets => "Bracelets",
BetweenSlotTypes.RightRing => "Right Ring",
BetweenSlotTypes.LeftRing => "Left Ring",
BetweenSlotTypes.Glasses => "Glasses",
_ => "Unknown",
};
private static readonly IReadOnlyList<BetweenSlotTypes> AvailableToTypes =
Enum.GetValues<BetweenSlotTypes>().Where(s => s is not BetweenSlotTypes.Hat).ToArray();
}

View file

@ -1,12 +1,9 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using ImSharp;
using Penumbra.GameData.Files.MaterialStructs;
using Luna;
using Penumbra.GameData.Files;
using OtterGui.Text;
using Penumbra.GameData.Files.MaterialStructs;
using Penumbra.GameData.Structs;
using OtterGui.Raii;
using OtterGui.Text.Widget;
namespace Penumbra.UI.AdvancedWindow.Materials;
@ -16,8 +13,6 @@ public partial class MtrlTab
private static readonly float HalfMaxValue = (float)Half.MaxValue;
private static readonly float HalfEpsilon = (float)Half.Epsilon;
private static readonly FontAwesomeCheckbox ApplyStainCheckbox = new(FontAwesomeIcon.FillDrip);
private static (Vector2 Scale, float Rotation, float Shear)? _pinnedTileTransform;
private bool DrawColorTableSection(bool disabled)
@ -35,15 +30,15 @@ public partial class MtrlTab
if (!disabled)
{
Im.Line.Same();
ImUtf8.IconDummy();
Im.FrameDummy();
Im.Line.Same();
ret |= ColorTableDyeableCheckbox();
}
if (Mtrl.DyeTable != null)
if (Mtrl.DyeTable is not null)
{
Im.Line.Same();
ImUtf8.IconDummy();
Im.FrameDummy();
Im.Line.Same();
ret |= DrawPreviewDye(disabled);
}
@ -65,7 +60,7 @@ public partial class MtrlTab
if (Mtrl.Table == null)
return;
if (!ImUtf8.Button("Export All Rows to Clipboard"u8, ImEx.ScaledVector(200, 0)))
if (!Im.Button("Export All Rows to Clipboard"u8, ImEx.ScaledVector(200, 0)))
return;
try
@ -78,7 +73,7 @@ public partial class MtrlTab
data2.TryCopyTo(array.AsSpan(data1.Length));
var text = Convert.ToBase64String(array);
ImGui.SetClipboardText(text);
Im.Clipboard.Set(text);
}
catch
{
@ -93,7 +88,7 @@ public partial class MtrlTab
var tt = dyeId1 is 0 && dyeId2 is 0
? "Select a preview dye first."u8
: "Apply all preview values corresponding to the dye template and chosen dye where dyeing is enabled."u8;
if (ImUtf8.ButtonEx("Apply Preview Dye"u8, tt, disabled: disabled || dyeId1 == 0 && dyeId2 == 0))
if (ImEx.Button("Apply Preview Dye"u8, default, tt, disabled || dyeId1 is 0 && dyeId2 is 0))
{
var ret = false;
if (Mtrl.DyeTable != null)
@ -108,11 +103,11 @@ public partial class MtrlTab
}
Im.Line.Same();
var label = dyeId1 == 0 ? "Preview Dye 1###previewDye1" : $"{name1} (Preview 1)###previewDye1";
var label = dyeId1 is 0 ? "Preview Dye 1###previewDye1" : $"{name1} (Preview 1)###previewDye1";
if (_stainService.StainCombo1.Draw(label, dyeColor1, string.Empty, true, gloss1))
UpdateColorTablePreview();
Im.Line.Same();
label = dyeId2 == 0 ? "Preview Dye 2###previewDye2" : $"{name2} (Preview 2)###previewDye2";
label = dyeId2 is 0 ? "Preview Dye 2###previewDye2" : $"{name2} (Preview 2)###previewDye2";
if (_stainService.StainCombo2.Draw(label, dyeColor2, string.Empty, true, gloss2))
UpdateColorTablePreview();
return false;
@ -120,15 +115,15 @@ public partial class MtrlTab
private bool ColorTablePasteAllClipboardButton(bool disabled)
{
if (Mtrl.Table == null)
if (Mtrl.Table is null)
return false;
if (!ImUtf8.ButtonEx("Import All Rows from Clipboard"u8, ImEx.ScaledVector(200, 0), disabled))
if (!ImEx.Button("Import All Rows from Clipboard"u8, ImEx.ScaledVector(200, 0), disabled))
return false;
try
{
var text = ImGui.GetClipboardText();
var text = Im.Clipboard.GetUtf16();
var data = Convert.FromBase64String(text);
var table = Mtrl.Table.AsBytes();
var dyeTable = Mtrl.DyeTable != null ? Mtrl.DyeTable.AsBytes() : [];
@ -154,21 +149,20 @@ public partial class MtrlTab
if (Mtrl.Table == null)
return;
if (!ImUtf8.IconButton(FontAwesomeIcon.Clipboard, "Export this row to your clipboard."u8,
Im.Style.FrameHeight * Vector2.One))
if (!ImEx.Icon.Button(LunaStyle.ToClipboardIcon, "Export this row to your clipboard."u8))
return;
try
{
var data1 = Mtrl.Table.RowAsBytes(rowIdx);
var data2 = Mtrl.DyeTable != null ? Mtrl.DyeTable.RowAsBytes(rowIdx) : [];
var data2 = Mtrl.DyeTable is not null ? Mtrl.DyeTable.RowAsBytes(rowIdx) : [];
var array = new byte[data1.Length + data2.Length];
data1.TryCopyTo(array);
data2.TryCopyTo(array.AsSpan(data1.Length));
var text = Convert.ToBase64String(array);
ImGui.SetClipboardText(text);
Im.Clipboard.Set(text);
}
catch
{
@ -178,8 +172,8 @@ public partial class MtrlTab
private bool ColorTableDyeableCheckbox()
{
var dyeable = Mtrl.DyeTable != null;
var ret = ImUtf8.Checkbox("Dyeable"u8, ref dyeable);
var dyeable = Mtrl.DyeTable is not null;
var ret = Im.Checkbox("Dyeable"u8, ref dyeable);
if (ret)
{
@ -199,18 +193,17 @@ public partial class MtrlTab
private bool ColorTablePasteFromClipboardButton(int rowIdx, bool disabled)
{
if (Mtrl.Table == null)
if (Mtrl.Table is null)
return false;
if (ImUtf8.IconButton(FontAwesomeIcon.Paste,
"Import an exported row from your clipboard onto this row.\n\nRight-Click for more options."u8,
Im.Style.FrameHeight * Vector2.One, disabled))
if (ImEx.Icon.Button(LunaStyle.FromClipboardIcon,
"Import an exported row from your clipboard onto this row.\n\nRight-Click for more options."u8, disabled))
try
{
var text = ImGui.GetClipboardText();
var text = Im.Clipboard.GetUtf16();
var data = Convert.FromBase64String(text);
var row = Mtrl.Table.RowAsBytes(rowIdx);
var dyeRow = Mtrl.DyeTable != null ? Mtrl.DyeTable.RowAsBytes(rowIdx) : [];
var dyeRow = Mtrl.DyeTable is not null ? Mtrl.DyeTable.RowAsBytes(rowIdx) : [];
if (data.Length != row.Length && data.Length != row.Length + dyeRow.Length)
return false;
@ -232,23 +225,23 @@ public partial class MtrlTab
private unsafe bool ColorTablePasteFromClipboardContext(int rowIdx, bool disabled)
{
if (!disabled && Im.Item.RightClicked())
ImUtf8.OpenPopup("context"u8);
Im.Popup.Open("context"u8);
using var context = ImUtf8.Popup("context"u8);
using var context = Im.Popup.Begin("context"u8);
if (!context)
return false;
using var _ = ImRaii.Disabled(disabled);
using var _ = Im.Disabled(disabled);
IColorTable.ValueTypes copy = 0;
IColorDyeTable.ValueTypes dyeCopy = 0;
if (ImUtf8.Selectable("Import Colors Only"u8))
if (Im.Selectable("Import Colors Only"u8))
{
copy = IColorTable.ValueTypes.Colors;
dyeCopy = IColorDyeTable.ValueTypes.Colors;
}
if (ImUtf8.Selectable("Import Other Values Only"u8))
if (Im.Selectable("Import Other Values Only"u8))
{
copy = ~IColorTable.ValueTypes.Colors;
dyeCopy = ~IColorDyeTable.ValueTypes.Colors;
@ -259,7 +252,7 @@ public partial class MtrlTab
try
{
var text = ImGui.GetClipboardText();
var text = Im.Clipboard.GetUtf16();
var data = Convert.FromBase64String(text);
var row = Mtrl.Table!.RowAsHalves(rowIdx);
var halves = new Span<Half>(Unsafe.AsPointer(ref data[0]), row.Length);
@ -280,9 +273,9 @@ public partial class MtrlTab
private void ColorTablePairHighlightButton(int pairIdx, bool disabled)
{
ImUtf8.IconButton(FontAwesomeIcon.Crosshairs,
ImEx.Icon.Button(LunaStyle.OnHoverIcon,
"Highlight this pair of rows on your character, if possible.\n\nHighlight colors can be configured in Penumbra's settings."u8,
Im.Style.FrameHeight * Vector2.One, disabled || _colorTablePreviewers.Count == 0);
disabled || _colorTablePreviewers.Count is 0);
if (Im.Item.Hovered())
HighlightColorTablePair(pairIdx);
@ -292,9 +285,9 @@ public partial class MtrlTab
private void ColorTableRowHighlightButton(int rowIdx, bool disabled)
{
ImUtf8.IconButton(FontAwesomeIcon.Crosshairs,
ImEx.Icon.Button(LunaStyle.OnHoverIcon,
"Highlight this row on your character, if possible.\n\nHighlight colors can be configured in Penumbra's settings."u8,
Im.Style.FrameHeight * Vector2.One, disabled || _colorTablePreviewers.Count == 0);
disabled || _colorTablePreviewers.Count is 0);
if (Im.Item.Hovered())
HighlightColorTableRow(rowIdx);
@ -335,24 +328,24 @@ public partial class MtrlTab
var ret = false;
var inputSqrt = PseudoSqrtRgb((Vector3)current);
var tmp = inputSqrt;
if (ImUtf8.ColorEdit(label, ref tmp,
ImGuiColorEditFlags.NoInputs
| ImGuiColorEditFlags.DisplayRgb
| ImGuiColorEditFlags.InputRgb
| ImGuiColorEditFlags.NoTooltip
| ImGuiColorEditFlags.Hdr)
if (Im.Color.Editor(label, ref tmp,
ColorEditorFlags.NoInputs
| ColorEditorFlags.DisplayRgb
| ColorEditorFlags.InputRgb
| ColorEditorFlags.NoTooltip
| ColorEditorFlags.Hdr)
&& tmp != inputSqrt)
{
setter((HalfColor)PseudoSquareRgb(tmp));
ret = true;
}
if (letter.Length > 0 && ImGui.IsItemVisible())
if (letter.Length > 0 && Im.Item.Visible)
{
var textSize = ImUtf8.CalcTextSize(letter);
var center = ImGui.GetItemRectMin() + (ImGui.GetItemRectSize() - textSize) / 2;
var textSize = Im.Font.CalculateSize(letter);
var center = Im.Item.UpperLeftCorner + (Im.Item.Size - textSize) / 2;
var textColor = inputSqrt.LengthSquared() < 0.25f ? 0x80FFFFFFu : 0x80000000u;
ImGui.GetWindowDrawList().AddText(letter, center, textColor);
Im.Window.DrawList.Text(center, textColor, letter);
}
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, description);
@ -370,19 +363,19 @@ public partial class MtrlTab
else
{
var tmp = Vector4.Zero;
ImUtf8.ColorEdit(label, ref tmp,
ImGuiColorEditFlags.NoInputs
| ImGuiColorEditFlags.DisplayRgb
| ImGuiColorEditFlags.InputRgb
| ImGuiColorEditFlags.NoTooltip
| ImGuiColorEditFlags.Hdr
| ImGuiColorEditFlags.AlphaPreview);
Im.Color.Editor(label, ref tmp,
ColorEditorFlags.NoInputs
| ColorEditorFlags.DisplayRgb
| ColorEditorFlags.InputRgb
| ColorEditorFlags.NoTooltip
| ColorEditorFlags.Hdr
| ColorEditorFlags.AlphaPreview);
if (letter.Length > 0 && ImGui.IsItemVisible())
if (letter.Length > 0 && Im.Item.Visible)
{
var textSize = ImUtf8.CalcTextSize(letter);
var center = ImGui.GetItemRectMin() + (ImGui.GetItemRectSize() - textSize) / 2;
ImGui.GetWindowDrawList().AddText(letter, center, 0x80000000u);
var textSize = Im.Font.CalculateSize(letter);
var center = Im.Item.UpperLeftCorner + (Im.Item.Size - textSize) / 2;
Im.Window.DrawList.Text(center, 0x80000000u, letter);
}
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, description);
@ -392,7 +385,7 @@ public partial class MtrlTab
private static bool CtApplyStainCheckbox(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, bool current, Action<bool> setter)
{
var tmp = current;
var result = ApplyStainCheckbox.Draw(label, ref tmp);
var result = ImEx.IconCheckbox(label, FontAwesomeIcon.FillDrip.Icon(), ref tmp);
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, description);
if (!result || tmp == current)
return false;
@ -405,7 +398,7 @@ public partial class MtrlTab
float max, float speed, Action<Half> setter)
{
var tmp = (float)value;
var result = ImUtf8.DragScalar(label, ref tmp, format, min, max, speed);
var result = Im.Drag(label, ref tmp, format, min, max, speed);
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, description);
if (!result)
return false;
@ -422,7 +415,7 @@ public partial class MtrlTab
float min, float max, float speed)
{
var tmp = (float)value;
var result = ImUtf8.DragScalar(label, ref tmp, format, min, max, speed);
var result = Im.Drag(label, ref tmp, format, min, max, speed);
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, description);
if (!result)
return false;
@ -437,7 +430,7 @@ public partial class MtrlTab
private static void CtDragHalf(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, Half? value, ReadOnlySpan<byte> format)
{
using var _ = ImRaii.Disabled();
using var _ = Im.Disabled();
var valueOrDefault = value ?? Half.Zero;
var floatValue = (float)valueOrDefault;
CtDragHalf(label, description, valueOrDefault, value.HasValue ? format : "-"u8, floatValue, floatValue, 0.0f, Nop);
@ -447,7 +440,7 @@ public partial class MtrlTab
T max, float speed, Action<T> setter) where T : unmanaged, INumber<T>
{
var tmp = value;
var result = ImUtf8.DragScalar(label, ref tmp, format, min, max, speed);
var result = Im.Drag(label, ref tmp, format, min, max, speed);
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, description);
if (!result || tmp == value)
return false;
@ -460,7 +453,7 @@ public partial class MtrlTab
T max, float speed) where T : unmanaged, INumber<T>
{
var tmp = value;
var result = ImUtf8.DragScalar(label, ref tmp, format, min, max, speed);
var result = Im.Drag(label, ref tmp, format, min, max, speed);
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, description);
if (!result || tmp == value)
return false;
@ -472,7 +465,7 @@ public partial class MtrlTab
private static void CtDragScalar<T>(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, T? value, ReadOnlySpan<byte> format)
where T : unmanaged, INumber<T>
{
using var _ = ImRaii.Disabled();
using var _ = Im.Disabled();
var valueOrDefault = value ?? T.Zero;
CtDragScalar(label, description, valueOrDefault, value.HasValue ? format : "-"u8, valueOrDefault, valueOrDefault, 0.0f, Nop);
}
@ -526,24 +519,24 @@ public partial class MtrlTab
shear *= 180.0f / MathF.PI;
Im.Item.SetNextWidth(floatSize);
var scaleXChanged = CtDragScalar("##TileScaleU"u8, "Tile Scale U"u8, ref scale.X, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f);
var activated = ImGui.IsItemActivated();
var deactivated = ImGui.IsItemDeactivated();
var activated = Im.Item.Activated;
var deactivated = Im.Item.Deactivated;
Im.Line.SameInner();
Im.Item.SetNextWidth(floatSize);
var scaleYChanged = CtDragScalar("##TileScaleV"u8, "Tile Scale V"u8, ref scale.Y, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f);
activated |= ImGui.IsItemActivated();
deactivated |= ImGui.IsItemDeactivated();
activated |= Im.Item.Activated;
deactivated |= Im.Item.Deactivated;
if (!twoRowLayout)
Im.Line.SameInner();
Im.Item.SetNextWidth(floatSize);
var rotationChanged = CtDragScalar("##TileRotation"u8, "Tile Rotation"u8, ref rotation, "%.0f°"u8, -180.0f, 180.0f, 1.0f);
activated |= ImGui.IsItemActivated();
deactivated |= ImGui.IsItemDeactivated();
activated |= Im.Item.Activated;
deactivated |= Im.Item.Deactivated;
Im.Line.SameInner();
Im.Item.SetNextWidth(floatSize);
var shearChanged = CtDragScalar("##TileShear"u8, "Tile Shear"u8, ref shear, "%.0f°"u8, -90.0f, 90.0f, 1.0f);
activated |= ImGui.IsItemActivated();
deactivated |= ImGui.IsItemDeactivated();
activated |= Im.Item.Activated;
deactivated |= Im.Item.Deactivated;
if (deactivated)
_pinnedTileTransform = null;
else if (activated)

View file

@ -1,10 +1,5 @@
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Luna;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Text.Widget.Editors;
using Penumbra.GameData.Files.ShaderStructs;
using static Penumbra.GameData.Files.ShpkFile;
@ -150,19 +145,18 @@ public partial class MtrlTab
private bool DrawConstantsSection(bool disabled)
{
if (Constants.Count == 0)
if (Constants.Count is 0)
return false;
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
if (!ImGui.CollapsingHeader("Material Constants"))
Im.Dummy(new Vector2(Im.Style.TextHeight / 2));
using var tree = Im.Tree.HeaderId("Material Constants"u8);
if (!tree)
return false;
using var _ = ImRaii.PushId("MaterialConstants");
var ret = false;
foreach (var (header, group) in Constants)
{
using var t = ImRaii.TreeNode(header, ImGuiTreeNodeFlags.DefaultOpen);
using var t = Im.Tree.Node(header, TreeNodeFlags.DefaultOpen);
if (!t)
continue;
@ -172,8 +166,8 @@ public partial class MtrlTab
var buffer = Mtrl.GetConstantValue<byte>(constant);
if (buffer.Length > 0)
{
using var id = ImRaii.PushId($"##{constant.Id:X8}:{slice.Start}");
Im.Item.SetNextWidth(MaterialConstantSize * Im.Style.GlobalScale);
using var id = Im.Id.Push($"##{constant.Id:X8}:{slice.Start}");
Im.Item.SetNextWidthScaled(MaterialConstantSize);
if (editor.Draw(buffer[slice], disabled))
{
ret = true;
@ -187,8 +181,7 @@ public partial class MtrlTab
? defaultValue.Length > 0 && !defaultValue.SequenceEqual(buffer[slice])
: buffer[slice].ContainsAnyExcept((byte)0);
Im.Line.SameInner();
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Backspace.ToIconString(), Im.Style.FrameHeight * Vector2.One,
"Reset this constant to its default value.\n\nHold Ctrl to unlock.", !ImGui.GetIO().KeyCtrl || !canReset, true))
if (ImEx.Icon.Button(LunaStyle.RefreshIcon, "Reset this constant to its default value.\n\nHold Ctrl to unlock."u8, !Im.Io.KeyControl || !canReset))
{
ret = true;
if (defaultValue.Length > 0)
@ -199,10 +192,10 @@ public partial class MtrlTab
SetMaterialParameter(constant.Id, slice.Start, buffer[slice]);
}
Im.Line.Same();
using var font = ImRaii.PushFont(UiBuilder.MonoFont, monoFont);
Im.Line.SameInner();
using var font = Im.Font.Mono.Push(monoFont);
if (description.Length > 0)
ImGuiUtil.LabeledHelpMarker(label, description);
LunaStyle.DrawHelpMarker(label, description);
else
Im.Text(label);
}

View file

@ -1,9 +1,6 @@
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Bindings.ImGui;
using ImSharp;
using OtterGui;
using OtterGui.Text;
using Luna;
using Penumbra.GameData.Files.MaterialStructs;
using Penumbra.GameData.Files.StainMapStructs;
using Penumbra.Services;
@ -25,7 +22,7 @@ public partial class MtrlTab
if (!imTable)
return false;
DrawLegacyColorTableHeader(dyeTable is not null);
DrawLegacyColorTableHeader(imTable, dyeTable is not null);
var ret = false;
for (var i = 0; i < LegacyColorTable.NumRows; ++i)
@ -36,7 +33,7 @@ public partial class MtrlTab
ret = true;
}
ImGui.TableNextRow();
imTable.NextRow();
}
return ret;
@ -49,7 +46,7 @@ public partial class MtrlTab
if (!imTable)
return false;
DrawLegacyColorTableHeader(dyeTable is not null);
DrawLegacyColorTableHeader(imTable, dyeTable is not null);
var ret = false;
for (var i = 0; i < ColorTable.NumRows; ++i)
@ -60,62 +57,62 @@ public partial class MtrlTab
ret = true;
}
ImGui.TableNextRow();
imTable.NextRow();
}
return ret;
}
private static void DrawLegacyColorTableHeader(bool hasDyeTable)
private static void DrawLegacyColorTableHeader(in Im.TableDisposable table, bool hasDyeTable)
{
ImGui.TableNextColumn();
ImUtf8.TableHeader(""u8);
ImGui.TableNextColumn();
ImUtf8.TableHeader("Row"u8);
ImGui.TableNextColumn();
ImUtf8.TableHeader("Diffuse"u8);
ImGui.TableNextColumn();
ImUtf8.TableHeader("Specular"u8);
ImGui.TableNextColumn();
ImUtf8.TableHeader("Emissive"u8);
ImGui.TableNextColumn();
ImUtf8.TableHeader("Gloss"u8);
ImGui.TableNextColumn();
ImUtf8.TableHeader("Tile"u8);
ImGui.TableNextColumn();
ImUtf8.TableHeader("Repeat / Skew"u8);
table.NextColumn();
table.Header(""u8);
table.NextColumn();
table.Header("Row"u8);
table.NextColumn();
table.Header("Diffuse"u8);
table.NextColumn();
table.Header("Specular"u8);
table.NextColumn();
table.Header("Emissive"u8);
table.NextColumn();
table.Header("Gloss"u8);
table.NextColumn();
table.Header("Tile"u8);
table.NextColumn();
table.Header("Repeat / Skew"u8);
if (hasDyeTable)
{
ImGui.TableNextColumn();
ImUtf8.TableHeader("Dye"u8);
ImGui.TableNextColumn();
ImUtf8.TableHeader("Dye Preview"u8);
table.NextColumn();
table.Header("Dye"u8);
table.NextColumn();
table.Header("Dye Preview"u8);
}
}
private bool DrawLegacyColorTableRow(LegacyColorTable table, LegacyColorDyeTable? dyeTable, int rowIdx, bool disabled)
{
using var id = ImRaii.PushId(rowIdx);
using var id = Im.Id.Push(rowIdx);
ref var row = ref table[rowIdx];
var dye = dyeTable != null ? dyeTable[rowIdx] : default;
var dye = dyeTable?[rowIdx] ?? default;
var floatSize = LegacyColorTableFloatSize * Im.Style.GlobalScale;
var pctSize = LegacyColorTablePercentageSize * Im.Style.GlobalScale;
var intSize = LegacyColorTableIntegerSize * Im.Style.GlobalScale;
ImGui.TableNextColumn();
Im.Table.NextColumn();
ColorTableCopyClipboardButton(rowIdx);
Im.Line.SameInner();
var ret = ColorTablePasteFromClipboardButton(rowIdx, disabled);
Im.Line.SameInner();
ColorTableRowHighlightButton(rowIdx, disabled);
ImGui.TableNextColumn();
using (ImRaii.PushFont(UiBuilder.MonoFont))
Im.Table.NextColumn();
using (Im.Font.PushMono())
{
ImUtf8.Text($"{(rowIdx >> 1) + 1,2:D}{"AB"[rowIdx & 1]}");
Im.Text($"{(rowIdx >> 1) + 1,2:D}{"AB"[rowIdx & 1]}");
}
ImGui.TableNextColumn();
using var dis = ImRaii.Disabled(disabled);
Im.Table.NextColumn();
using var dis = Im.Disabled(disabled);
ret |= CtColorPicker("##Diffuse"u8, "Diffuse Color"u8, row.DiffuseColor,
c => table[rowIdx].DiffuseColor = c);
if (dyeTable != null)
@ -125,10 +122,10 @@ public partial class MtrlTab
b => dyeTable[rowIdx].DiffuseColor = b);
}
ImGui.TableNextColumn();
Im.Table.NextColumn();
ret |= CtColorPicker("##Specular"u8, "Specular Color"u8, row.SpecularColor,
c => table[rowIdx].SpecularColor = c);
if (dyeTable != null)
if (dyeTable is not null)
{
Im.Line.SameInner();
ret |= CtApplyStainCheckbox("##dyeSpecular"u8, "Apply Specular Color on Dye"u8, dye.SpecularColor,
@ -147,7 +144,7 @@ public partial class MtrlTab
b => dyeTable[rowIdx].SpecularMask = b);
}
ImGui.TableNextColumn();
Im.Table.NextColumn();
ret |= CtColorPicker("##Emissive"u8, "Emissive Color"u8, row.EmissiveColor,
c => table[rowIdx].EmissiveColor = c);
if (dyeTable != null)
@ -157,32 +154,32 @@ public partial class MtrlTab
b => dyeTable[rowIdx].EmissiveColor = b);
}
ImGui.TableNextColumn();
Im.Table.NextColumn();
Im.Item.SetNextWidth(floatSize);
var glossStrengthMin = ImGui.GetIO().KeyCtrl ? 0.0f : HalfEpsilon;
var glossStrengthMin = Im.Io.KeyControl ? 0.0f : HalfEpsilon;
ret |= CtDragHalf("##Shininess"u8, "Gloss Strength"u8, row.Shininess, "%.1f"u8, glossStrengthMin, HalfMaxValue,
Math.Max(0.1f, (float)row.Shininess * 0.025f),
v => table[rowIdx].Shininess = v);
if (dyeTable != null)
if (dyeTable is not null)
{
Im.Line.SameInner();
ret |= CtApplyStainCheckbox("##dyeShininess"u8, "Apply Gloss Strength on Dye"u8, dye.Shininess,
b => dyeTable[rowIdx].Shininess = b);
}
ImGui.TableNextColumn();
Im.Table.NextColumn();
Im.Item.SetNextWidth(intSize);
ret |= CtTileIndexPicker("##TileIndex"u8, "Tile Index"u8, row.TileIndex, true,
value => table[rowIdx].TileIndex = value);
ImGui.TableNextColumn();
Im.Table.NextColumn();
ret |= CtTileTransformMatrix(row.TileTransform, floatSize, false,
m => table[rowIdx].TileTransform = m);
if (dyeTable != null)
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
if (_stainService.LegacyTemplateCombo.Draw("##dyeTemplate", dye.Template.ToString(), string.Empty, intSize
+ Im.Style.ScrollbarSize / 2, Im.Style.TextHeightWithSpacing, ImGuiComboFlags.NoArrowButton))
{
@ -192,7 +189,7 @@ public partial class MtrlTab
Im.Tooltip.OnHover("Dye Template"u8, HoveredFlags.AllowWhenDisabled);
ImGui.TableNextColumn();
Im.Table.NextColumn();
ret |= DrawLegacyDyePreview(rowIdx, disabled, dye, floatSize);
}
@ -201,41 +198,41 @@ public partial class MtrlTab
private bool DrawLegacyColorTableRow(ColorTable table, ColorDyeTable? dyeTable, int rowIdx, bool disabled)
{
using var id = ImRaii.PushId(rowIdx);
using var id = Im.Id.Push(rowIdx);
ref var row = ref table[rowIdx];
var dye = dyeTable?[rowIdx] ?? default;
var floatSize = LegacyColorTableFloatSize * Im.Style.GlobalScale;
var pctSize = LegacyColorTablePercentageSize * Im.Style.GlobalScale;
var intSize = LegacyColorTableIntegerSize * Im.Style.GlobalScale;
var byteSize = LegacyColorTableByteSize * Im.Style.GlobalScale;
ImGui.TableNextColumn();
Im.Table.NextColumn();
ColorTableCopyClipboardButton(rowIdx);
Im.Line.SameInner();
var ret = ColorTablePasteFromClipboardButton(rowIdx, disabled);
Im.Line.SameInner();
ColorTableRowHighlightButton(rowIdx, disabled);
ImGui.TableNextColumn();
using (ImRaii.PushFont(UiBuilder.MonoFont))
Im.Table.NextColumn();
using (Im.Font.PushMono())
{
ImUtf8.Text($"{(rowIdx >> 1) + 1,2:D}{"AB"[rowIdx & 1]}");
Im.Text($"{(rowIdx >> 1) + 1,2:D}{"AB"[rowIdx & 1]}");
}
ImGui.TableNextColumn();
using var dis = ImRaii.Disabled(disabled);
Im.Table.NextColumn();
using var dis = Im.Disabled(disabled);
ret |= CtColorPicker("##Diffuse"u8, "Diffuse Color"u8, row.DiffuseColor,
c => table[rowIdx].DiffuseColor = c);
if (dyeTable != null)
if (dyeTable is not null)
{
Im.Line.SameInner();
ret |= CtApplyStainCheckbox("##dyeDiffuse"u8, "Apply Diffuse Color on Dye"u8, dye.DiffuseColor,
b => dyeTable[rowIdx].DiffuseColor = b);
}
ImGui.TableNextColumn();
Im.Table.NextColumn();
ret |= CtColorPicker("##Specular"u8, "Specular Color"u8, row.SpecularColor,
c => table[rowIdx].SpecularColor = c);
if (dyeTable != null)
if (dyeTable is not null)
{
Im.Line.SameInner();
ret |= CtApplyStainCheckbox("##dyeSpecular"u8, "Apply Specular Color on Dye"u8, dye.SpecularColor,
@ -246,38 +243,38 @@ public partial class MtrlTab
Im.Item.SetNextWidth(pctSize);
ret |= CtDragScalar("##SpecularMask"u8, "Specular Strength"u8, (float)row.Scalar7 * 100.0f, "%.0f%%"u8, 0f, HalfMaxValue * 100.0f, 1.0f,
v => table[rowIdx].Scalar7 = (Half)(v * 0.01f));
if (dyeTable != null)
if (dyeTable is not null)
{
Im.Line.SameInner();
ret |= CtApplyStainCheckbox("##dyeSpecularMask"u8, "Apply Specular Strength on Dye"u8, dye.Metalness,
b => dyeTable[rowIdx].Metalness = b);
}
ImGui.TableNextColumn();
Im.Table.NextColumn();
ret |= CtColorPicker("##Emissive"u8, "Emissive Color"u8, row.EmissiveColor,
c => table[rowIdx].EmissiveColor = c);
if (dyeTable != null)
if (dyeTable is not null)
{
Im.Line.SameInner();
ret |= CtApplyStainCheckbox("##dyeEmissive"u8, "Apply Emissive Color on Dye"u8, dye.EmissiveColor,
b => dyeTable[rowIdx].EmissiveColor = b);
}
ImGui.TableNextColumn();
Im.Table.NextColumn();
Im.Item.SetNextWidth(floatSize);
var glossStrengthMin = ImGui.GetIO().KeyCtrl ? 0.0f : HalfEpsilon;
var glossStrengthMin = Im.Io.KeyControl ? 0.0f : HalfEpsilon;
ret |= CtDragHalf("##Shininess"u8, "Gloss Strength"u8, row.Scalar3, "%.1f"u8, glossStrengthMin, HalfMaxValue,
Math.Max(0.1f, (float)row.Scalar3 * 0.025f),
v => table[rowIdx].Scalar3 = v);
if (dyeTable != null)
if (dyeTable is not null)
{
Im.Line.SameInner();
ret |= CtApplyStainCheckbox("##dyeShininess"u8, "Apply Gloss Strength on Dye"u8, dye.Scalar3,
b => dyeTable[rowIdx].Scalar3 = b);
}
ImGui.TableNextColumn();
Im.Table.NextColumn();
Im.Item.SetNextWidth(intSize);
ret |= CtTileIndexPicker("##TileIndex"u8, "Tile Index"u8, row.TileIndex, true,
value => table[rowIdx].TileIndex = value);
@ -286,13 +283,13 @@ public partial class MtrlTab
ret |= CtDragScalar("##TileAlpha"u8, "Tile Opacity"u8, (float)row.TileAlpha * 100.0f, "%.0f%%"u8, 0f, HalfMaxValue * 100.0f, 1.0f,
v => table[rowIdx].TileAlpha = (Half)(v * 0.01f));
ImGui.TableNextColumn();
Im.Table.NextColumn();
ret |= CtTileTransformMatrix(row.TileTransform, floatSize, false,
m => table[rowIdx].TileTransform = m);
if (dyeTable != null)
if (dyeTable is not null)
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
Im.Item.SetNextWidth(byteSize);
ret |= CtDragScalar("##DyeChannel"u8, "Dye Channel"u8, dye.Channel + 1, "%hhd"u8, 1, StainService.ChannelCount, 0.25f,
value => dyeTable[rowIdx].Channel = (byte)(Math.Clamp(value, 1, StainService.ChannelCount) - 1));
@ -307,7 +304,7 @@ public partial class MtrlTab
Im.Tooltip.OnHover("Dye Template"u8, HoveredFlags.AllowWhenDisabled);
ImGui.TableNextColumn();
Im.Table.NextColumn();
ret |= DrawLegacyDyePreview(rowIdx, disabled, dye, floatSize);
}
@ -322,8 +319,7 @@ public partial class MtrlTab
using var style = ImStyleDouble.ItemSpacing.Push(Im.Style.ItemSpacing / 2);
var ret = ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.PaintBrush.ToIconString(), new Vector2(Im.Style.FrameHeight),
"Apply the selected dye to this row.", disabled, true);
var ret = ImEx.Icon.Button(LunaStyle.DyeIcon, "Apply the selected dye to this row."u8, disabled);
ret = ret && Mtrl.ApplyDyeToRow(_stainService.LegacyStmFile, [stain], rowIdx);
@ -341,8 +337,7 @@ public partial class MtrlTab
using var style = ImStyleDouble.ItemSpacing.Push(Im.Style.ItemSpacing / 2);
var ret = ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.PaintBrush.ToIconString(), new Vector2(Im.Style.FrameHeight),
"Apply the selected dye to this row.", disabled, true);
var ret = ImEx.Icon.Button(LunaStyle.DyeIcon, "Apply the selected dye to this row."u8, disabled);
ret = ret
&& Mtrl.ApplyDyeToRow(_stainService.LegacyStmFile, [
@ -364,13 +359,13 @@ public partial class MtrlTab
Im.Line.SameInner();
CtColorPicker("##emissivePreview"u8, default, values.EmissiveColor, "E"u8);
Im.Line.SameInner();
using var dis = ImRaii.Disabled();
using var dis = Im.Disabled();
Im.Item.SetNextWidth(floatSize);
var shininess = (float)values.Shininess;
ImGui.DragFloat("##shininessPreview", ref shininess, 0, shininess, shininess, "%.1f G");
Im.Drag("##shininessPreview"u8, ref shininess, "%.1f G"u8, shininess, shininess, 0f);
Im.Line.SameInner();
Im.Item.SetNextWidth(floatSize);
var specularMask = (float)values.SpecularMask * 100.0f;
ImGui.DragFloat("##specularMaskPreview", ref specularMask, 0, specularMask, specularMask, "%.0f%% S");
Im.Drag("##specularMaskPreview"u8, ref specularMask, "%.0f%% S"u8, specularMask, specularMask, 0f);
}
}

View file

@ -1,7 +1,4 @@
using Dalamud.Bindings.ImGui;
using ImSharp;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.GameData.Files.MaterialStructs;
using Penumbra.GameData.Structs;
using Penumbra.Interop.Hooks.Objects;
@ -27,7 +24,7 @@ public partial class MtrlTab
if (Im.Button("Reload live preview"u8))
BindToMaterialInstances();
if (_materialPreviewers.Count != 0 || _colorTablePreviewers.Count != 0)
if (_materialPreviewers.Count is not 0 || _colorTablePreviewers.Count is not 0)
return;
Im.Line.Same();
@ -62,7 +59,7 @@ public partial class MtrlTab
UpdateMaterialPreview();
if (Mtrl.Table == null)
if (Mtrl.Table is null)
return;
foreach (var materialInfo in instances)
@ -207,10 +204,10 @@ public partial class MtrlTab
private void UpdateColorTableRowPreview(int rowIdx)
{
if (_colorTablePreviewers.Count == 0)
if (_colorTablePreviewers.Count is 0)
return;
if (Mtrl.Table == null)
if (Mtrl.Table is null)
return;
var row = Mtrl.Table switch
@ -219,7 +216,7 @@ public partial class MtrlTab
ColorTable table => table[rowIdx],
_ => throw new InvalidOperationException($"Unsupported color table type {Mtrl.Table.GetType()}"),
};
if (Mtrl.DyeTable != null)
if (Mtrl.DyeTable is not null)
{
var dyeRow = Mtrl.DyeTable switch
{
@ -251,15 +248,15 @@ public partial class MtrlTab
private void UpdateColorTablePreview()
{
if (_colorTablePreviewers.Count == 0)
if (_colorTablePreviewers.Count is 0)
return;
if (Mtrl.Table == null)
if (Mtrl.Table is null)
return;
var rows = new ColorTable(Mtrl.Table);
var dyeRows = Mtrl.DyeTable != null ? ColorDyeTable.CastOrConvert(Mtrl.DyeTable) : null;
if (dyeRows != null)
var dyeRows = Mtrl.DyeTable is not null ? ColorDyeTable.CastOrConvert(Mtrl.DyeTable) : null;
if (dyeRows is not null)
{
ReadOnlySpan<StainId> stainIds =
[

View file

@ -1,12 +1,7 @@
using Dalamud.Interface;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Luna;
using Newtonsoft.Json.Linq;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.GameData;
using Penumbra.GameData.Data;
using Penumbra.GameData.Files;
@ -326,7 +321,7 @@ public partial class MtrlTab
private bool DrawShaderSection(bool disabled)
{
var ret = false;
if (ImGui.CollapsingHeader(_shaderHeader))
if (Im.Tree.Header(_shaderHeader))
{
ret |= DrawPackageNameInput(disabled);
ret |= DrawShaderFlagsInput(disabled);
@ -335,17 +330,17 @@ public partial class MtrlTab
DrawMaterialShaders();
}
if (!_shpkLoading && (_associatedShpk == null || _associatedShpkDevkit == null))
if (!_shpkLoading && (_associatedShpk is null || _associatedShpkDevkit is null))
{
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
Im.Dummy(new Vector2(Im.Style.TextHeight / 2));
if (_associatedShpk == null)
ImUtf8.Text("Unable to find a suitable shader (.shpk) file for cross-references. Some functionality will be missing."u8,
ImGuiUtil.HalfBlendText(0x80u)); // Half red
if (_associatedShpk is null)
Im.Text("Unable to find a suitable shader (.shpk) file for cross-references. Some functionality will be missing."u8,
ImGuiColor.Text.Get().HalfBlend(0x80)); // Half red
else
ImUtf8.Text(
Im.Text(
"No dev-kit file found for this material's shaders. Please install one for optimal editing experience, such as actual constant names instead of hexadecimal identifiers."u8,
ImGuiUtil.HalfBlendText(0x8080u)); // Half yellow
ImGuiColor.Text.Get().HalfBlend(0x8080u)); // Half yellow
}
return ret;
@ -360,12 +355,12 @@ public partial class MtrlTab
}
var ret = false;
Im.Item.SetNextWidth(Im.Style.GlobalScale * 250.0f);
using var c = ImRaii.Combo("Shader Package", Mtrl.ShaderPackage.Name);
Im.Item.SetNextWidthScaled(250.0f);
using var c = Im.Combo.Begin("Shader Package"u8, Mtrl.ShaderPackage.Name);
if (c)
foreach (var value in GetShpkNames())
{
if (!ImGui.Selectable(value, value == Mtrl.ShaderPackage.Name))
if (!Im.Selectable(value, value == Mtrl.ShaderPackage.Name))
continue;
Mtrl.ShaderPackage.Name = value;
@ -382,9 +377,9 @@ public partial class MtrlTab
private bool DrawShaderFlagsInput(bool disabled)
{
var shpkFlags = (int)Mtrl.ShaderPackage.Flags;
Im.Item.SetNextWidth(Im.Style.GlobalScale * 250.0f);
if (!ImGui.InputInt("Shader Flags", ref shpkFlags, 0, 0,
flags: ImGuiInputTextFlags.CharsHexadecimal | (disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None)))
Im.Item.SetNextWidthScaled(250.0f);
if (!Im.Input.Scalar("Shader Flags"u8, ref shpkFlags, 0, 0,
InputTextFlags.CharsHexadecimal | (disabled ? InputTextFlags.ReadOnly : InputTextFlags.None)))
return false;
Mtrl.ShaderPackage.Flags = (uint)shpkFlags;
@ -398,24 +393,19 @@ public partial class MtrlTab
/// </summary>
private void DrawCustomAssociations()
{
const string tooltip = "Click to copy file path to clipboard.";
var text = _associatedShpk == null
? "Associated .shpk file: None"
: $"Associated .shpk file: {_loadedShpkPathName}";
var devkitText = _associatedShpkDevkit == null
? "Associated dev-kit file: None"
: $"Associated dev-kit file: {_loadedShpkDevkitPathName}";
var baseDevkitText = _associatedBaseDevkit == null
? "Base dev-kit file: None"
: $"Base dev-kit file: {_loadedBaseDevkitPathName}";
var tooltip = "Click to copy file path to clipboard."u8;
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
Im.Dummy(new Vector2(Im.Style.TextHeight / 2));
ImEx.CopyOnClickSelectable(_associatedShpk is null ? "Associated .shpk file: None" : $"Associated .shpk file: {_loadedShpkPathName}",
_loadedShpkPathName, tooltip);
ImEx.CopyOnClickSelectable(
_associatedShpkDevkit is null ? "Associated dev-kit file: None" : $"Associated dev-kit file: {_loadedShpkDevkitPathName}",
_loadedShpkDevkitPathName, tooltip);
ImEx.CopyOnClickSelectable(
_associatedBaseDevkit is null ? "Base dev-kit file: None" : $"Base dev-kit file: {_loadedBaseDevkitPathName}",
_loadedBaseDevkitPathName, tooltip);
ImUtf8.CopyOnClickSelectable(text, _loadedShpkPathName, tooltip);
ImUtf8.CopyOnClickSelectable(devkitText, _loadedShpkDevkitPathName, tooltip);
ImUtf8.CopyOnClickSelectable(baseDevkitText, _loadedBaseDevkitPathName, tooltip);
if (ImUtf8.Button("Associate Custom .shpk File"u8))
if (Im.Button("Associate Custom .shpk File"u8))
_fileDialog.OpenFilePicker("Associate Custom .shpk File...", ".shpk", (success, name) =>
{
if (success)
@ -424,19 +414,17 @@ public partial class MtrlTab
var moddedPath = FindAssociatedShpk(out var defaultPath, out var gamePath);
Im.Line.Same();
if (ImUtf8.ButtonEx("Associate Default .shpk File"u8, moddedPath.ToPath(), Vector2.Zero,
moddedPath.Equals(_loadedShpkPath)))
if (ImEx.Button("Associate Default .shpk File"u8, Vector2.Zero, moddedPath.ToPath(), moddedPath.Equals(_loadedShpkPath)))
LoadShpk(moddedPath);
if (!gamePath.Path.Equals(moddedPath.InternalName))
{
Im.Line.Same();
if (ImUtf8.ButtonEx("Associate Unmodded .shpk File", defaultPath, Vector2.Zero,
gamePath.Path.Equals(_loadedShpkPath.InternalName)))
if (ImEx.Button("Associate Unmodded .shpk File"u8, Vector2.Zero, defaultPath, gamePath.Path.Equals(_loadedShpkPath.InternalName)))
LoadShpk(new FullPath(gamePath));
}
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
Im.Dummy(new Vector2(Im.Style.TextHeight / 2));
}
private bool DrawMaterialShaderKeys(bool disabled)
@ -447,22 +435,22 @@ public partial class MtrlTab
var ret = false;
foreach (var (label, index, description, monoFont, values) in _shaderKeys)
{
using var font = ImRaii.PushFont(UiBuilder.MonoFont, monoFont);
using var font = Im.Font.Mono.Push(monoFont);
ref var key = ref Mtrl.ShaderPackage.ShaderKeys[index];
using var id = ImUtf8.PushId((int)key.Key);
using var id = Im.Id.Push((int)key.Key);
var shpkKey = _associatedShpk?.GetMaterialKeyById(key.Key);
var currentValue = key.Value;
var (currentLabel, _, currentDescription) =
values.FirstOrNull(v => v.Value == currentValue) ?? ($"0x{currentValue:X8}", currentValue, string.Empty);
if (!disabled && shpkKey.HasValue)
{
Im.Item.SetNextWidth(Im.Style.GlobalScale * 250.0f);
using (var c = ImUtf8.Combo(""u8, currentLabel))
Im.Item.SetNextWidthScaled(250.0f);
using (var c = Im.Combo.Begin(StringU8.Empty, currentLabel))
{
if (c)
foreach (var (valueLabel, value, valueDescription) in values)
{
if (ImGui.Selectable(valueLabel, value == currentValue))
if (Im.Selectable(valueLabel, value == currentValue))
{
key.Value = value;
ret = true;
@ -471,24 +459,24 @@ public partial class MtrlTab
}
if (valueDescription.Length > 0)
ImGuiUtil.SelectableHelpMarker(valueDescription);
LunaStyle.DrawRightAlignedHelpMarker(valueDescription);
}
}
Im.Line.Same();
Im.Line.SameInner();
if (description.Length > 0)
ImGuiUtil.LabeledHelpMarker(label, description);
LunaStyle.DrawHelpMarkerLabel(label, description);
else
ImUtf8.Text(label);
Im.Text(label);
}
else if (description.Length > 0 || currentDescription.Length > 0)
{
ImUtf8.LabeledHelpMarker($"{label}: {currentLabel}",
LunaStyle.DrawHelpMarkerLabel($"{label}: {currentLabel}",
description + (description.Length > 0 && currentDescription.Length > 0 ? "\n\n" : string.Empty) + currentDescription);
}
else
{
ImUtf8.Text($"{label}: {currentLabel}");
Im.Text($"{label}: {currentLabel}");
}
}
@ -497,19 +485,19 @@ public partial class MtrlTab
private void DrawMaterialShaders()
{
if (_associatedShpk == null)
if (_associatedShpk is null)
return;
using (var node = ImUtf8.TreeNode("Candidate Shaders"u8))
using (var node = Im.Tree.Node("Candidate Shaders"u8))
{
if (node)
ImUtf8.Text(_shadersString.Span);
Im.Text(_shadersString.Span);
}
if (_shaderComment.Length > 0)
{
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
ImUtf8.Text(_shaderComment);
Im.Dummy(new Vector2(Im.Style.TextHeight / 2));
Im.Text(_shaderComment);
}
}
}

View file

@ -1,9 +1,6 @@
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using ImSharp;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using Luna;
using Penumbra.GameData.Files.MaterialStructs;
using Penumbra.String.Classes;
using static Penumbra.GameData.Files.MaterialStructs.SamplerFlags;
@ -79,25 +76,20 @@ public partial class MtrlTab
TextureLabelWidth = 50f * Im.Style.GlobalScale;
float helpWidth;
using (var _ = ImRaii.PushFont(UiBuilder.IconFont))
{
helpWidth = Im.Style.ItemSpacing.X + ImGui.CalcTextSize(FontAwesomeIcon.InfoCircle.ToIconString()).X;
}
var helpWidth = Im.Style.ItemSpacing.X + ImEx.Icon.CalculateSize(FontAwesomeIcon.InfoCircle.Icon()).X;
foreach (var (label, _, _, description, monoFont) in Textures)
{
if (!monoFont)
TextureLabelWidth = Math.Max(TextureLabelWidth, ImGui.CalcTextSize(label).X + (description.Length > 0 ? helpWidth : 0.0f));
TextureLabelWidth = Math.Max(TextureLabelWidth, Im.Font.CalculateSize(label).X + (description.Length > 0 ? helpWidth : 0.0f));
}
using (var _ = ImRaii.PushFont(UiBuilder.MonoFont))
using (Im.Font.PushMono())
{
foreach (var (label, _, _, description, monoFont) in Textures)
{
if (monoFont)
TextureLabelWidth = Math.Max(TextureLabelWidth,
ImGui.CalcTextSize(label).X + (description.Length > 0 ? helpWidth : 0.0f));
TextureLabelWidth = Math.Max(TextureLabelWidth, Im.Font.CalculateSize(label).X + (description.Length > 0 ? helpWidth : 0.0f));
}
}
@ -122,8 +114,8 @@ public partial class MtrlTab
if (Textures.Count == 0)
return false;
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
if (!ImGui.CollapsingHeader("Textures and Samplers", ImGuiTreeNodeFlags.DefaultOpen))
Im.Dummy(new Vector2(Im.Style.TextHeight / 2));
if (!Im.Tree.Header("Textures and Samplers"u8, TreeNodeFlags.DefaultOpen))
return false;
var frameHeight = Im.Style.FrameHeight;
@ -135,13 +127,11 @@ public partial class MtrlTab
table.SetupColumn("Name"u8, TableColumnFlags.WidthFixed, TextureLabelWidth * Im.Style.GlobalScale);
foreach (var (label, textureI, samplerI, description, monoFont) in Textures)
{
using var _ = ImRaii.PushId(samplerI);
using var _ = Im.Id.Push(samplerI);
var tmp = Mtrl.Textures[textureI].Path;
var unfolded = UnfoldedTextures.Contains(samplerI);
ImGui.TableNextColumn();
if (ImGuiUtil.DrawDisabledButton((unfolded ? FontAwesomeIcon.CaretDown : FontAwesomeIcon.CaretRight).ToIconString(),
new Vector2(frameHeight),
"Settings for this texture and the associated sampler", false, true))
table.NextColumn();
if (ImEx.Icon.Button(unfolded ? LunaStyle.ExpandDownIcon : LunaStyle.CollapseUpIcon, "Settings for this texture and the associated sampler"u8))
{
unfolded = !unfolded;
if (unfolded)
@ -150,10 +140,9 @@ public partial class MtrlTab
UnfoldedTextures.Remove(samplerI);
}
ImGui.TableNextColumn();
table.NextColumn();
Im.Item.SetNextWidth(Im.ContentRegion.Available.X);
if (ImGui.InputText(string.Empty, ref tmp, Utf8GamePath.MaxGamePathLength,
disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None)
if (Im.Input.Text(StringU8.Empty, ref tmp, default, disabled ? InputTextFlags.ReadOnly : InputTextFlags.None, Utf8GamePath.MaxGamePathLength)
&& tmp.Length > 0
&& tmp != Mtrl.Textures[textureI].Path)
{
@ -161,22 +150,21 @@ public partial class MtrlTab
Mtrl.Textures[textureI].Path = tmp;
}
ImGui.TableNextColumn();
using (ImRaii.PushFont(UiBuilder.MonoFont, monoFont))
table.NextColumn();
using (Im.Font.Mono.Push(monoFont))
{
ImGui.AlignTextToFramePadding();
if (description.Length > 0)
ImGuiUtil.LabeledHelpMarker(label, description);
LunaStyle.DrawHelpMarkerLabel(label, description);
else
Im.Text(label);
ImEx.TextFrameAligned(label);
}
if (unfolded)
{
ImGui.TableNextColumn();
ImGui.TableNextColumn();
table.NextColumn();
table.NextColumn();
ret |= DrawMaterialSampler(disabled, textureI, samplerI);
ImGui.TableNextColumn();
table.NextColumn();
}
}
@ -185,20 +173,20 @@ public partial class MtrlTab
private static bool ComboTextureAddressMode(ReadOnlySpan<byte> label, ref TextureAddressMode value)
{
using var c = ImUtf8.Combo(label, value.ToString());
using var c = Im.Combo.Begin(label, value.ToNameU8());
if (!c)
return false;
var ret = false;
foreach (var mode in Enum.GetValues<TextureAddressMode>())
{
if (ImGui.Selectable(mode.ToString(), mode == value))
if (Im.Selectable(mode.ToNameU8(), mode == value))
{
value = mode;
ret = true;
}
ImUtf8.SelectableHelpMarker(TextureAddressModeTooltip(mode));
LunaStyle.DrawRightAlignedHelpMarker(TextureAddressModeTooltip(mode));
}
return ret;
@ -211,7 +199,7 @@ public partial class MtrlTab
ref var sampler = ref Mtrl.ShaderPackage.Samplers[samplerIdx];
var dx11 = texture.DX11;
if (ImUtf8.Checkbox("Prepend -- to the file name on DirectX 11"u8, ref dx11))
if (Im.Checkbox("Prepend -- to the file name on DirectX 11"u8, ref dx11))
{
texture.DX11 = dx11;
ret = true;
@ -221,7 +209,7 @@ public partial class MtrlTab
{
ref var samplerFlags = ref Wrap(ref sampler.Flags);
Im.Item.SetNextWidth(Im.Style.GlobalScale * 100.0f);
Im.Item.SetNextWidthScaled(100.0f);
var addressMode = samplerFlags.UAddressMode;
if (ComboTextureAddressMode("##UAddressMode"u8, ref addressMode))
{
@ -230,11 +218,11 @@ public partial class MtrlTab
SetSamplerFlags(sampler.SamplerId, sampler.Flags);
}
Im.Line.Same();
ImUtf8.LabeledHelpMarker("U Address Mode"u8,
"Method to use for resolving a U texture coordinate that is outside the 0 to 1 range.");
Im.Line.SameInner();
LunaStyle.DrawHelpMarkerLabel("U Address Mode"u8,
"Method to use for resolving a U texture coordinate that is outside the 0 to 1 range."u8);
Im.Item.SetNextWidth(Im.Style.GlobalScale * 100.0f);
Im.Item.SetNextWidthScaled(100.0f);
addressMode = samplerFlags.VAddressMode;
if (ComboTextureAddressMode("##VAddressMode"u8, ref addressMode))
{
@ -244,12 +232,12 @@ public partial class MtrlTab
}
Im.Line.Same();
ImUtf8.LabeledHelpMarker("V Address Mode"u8,
"Method to use for resolving a V texture coordinate that is outside the 0 to 1 range.");
LunaStyle.DrawHelpMarkerLabel("V Address Mode"u8,
"Method to use for resolving a V texture coordinate that is outside the 0 to 1 range."u8);
var lodBias = samplerFlags.LodBias;
Im.Item.SetNextWidth(Im.Style.GlobalScale * 100.0f);
if (ImUtf8.DragScalar("##LoDBias"u8, ref lodBias, -8.0f, 7.984375f, 0.1f))
Im.Item.SetNextWidthScaled(100.0f);
if (Im.Drag("##LoDBias"u8, ref lodBias, -8.0f, 7.984375f, 0.1f))
{
samplerFlags.LodBias = lodBias;
ret = true;
@ -257,12 +245,12 @@ public partial class MtrlTab
}
Im.Line.Same();
ImUtf8.LabeledHelpMarker("Level of Detail Bias"u8,
"Offset from the calculated mipmap level.\n\nHigher means that the texture will start to lose detail nearer.\nLower means that the texture will keep its detail until farther.");
LunaStyle.DrawHelpMarkerLabel("Level of Detail Bias"u8,
"Offset from the calculated mipmap level.\n\nHigher means that the texture will start to lose detail nearer.\nLower means that the texture will keep its detail until farther."u8);
var minLod = samplerFlags.MinLod;
Im.Item.SetNextWidth(Im.Style.GlobalScale * 100.0f);
if (ImUtf8.DragScalar("##MinLoD"u8, ref minLod, 0, 15, 0.1f))
Im.Item.SetNextWidthScaled(100.0f);
if (Im.Drag("##MinLoD"u8, ref minLod, 0, 15, 0.1f))
{
samplerFlags.MinLod = minLod;
ret = true;
@ -270,26 +258,25 @@ public partial class MtrlTab
}
Im.Line.Same();
ImUtf8.LabeledHelpMarker("Minimum Level of Detail"u8,
"Most detailed mipmap level to use.\n\n0 is the full-sized texture, 1 is the half-sized texture, 2 is the quarter-sized texture, and so on.\n15 will forcibly reduce the texture to its smallest mipmap.");
LunaStyle.DrawHelpMarkerLabel("Minimum Level of Detail"u8,
"Most detailed mipmap level to use.\n\n0 is the full-sized texture, 1 is the half-sized texture, 2 is the quarter-sized texture, and so on.\n15 will forcibly reduce the texture to its smallest mipmap."u8);
}
else
{
ImUtf8.Text("This texture does not have a dedicated sampler."u8);
Im.Text("This texture does not have a dedicated sampler."u8);
}
using var t = ImUtf8.TreeNode("Advanced Settings"u8);
using var t = Im.Tree.Node("Advanced Settings"u8);
if (!t)
return ret;
Im.Item.SetNextWidth(Im.Style.GlobalScale * 100.0f);
if (ImUtf8.InputScalar("Texture Flags"u8, ref texture.Flags, "%04X"u8,
flags: disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None))
Im.Item.SetNextWidthScaled(100.0f);
if (Im.Input.Scalar("Texture Flags"u8, ref texture.Flags, "%04X"u8,
flags: disabled ? InputTextFlags.ReadOnly : InputTextFlags.None))
ret = true;
Im.Item.SetNextWidth(Im.Style.GlobalScale * 100.0f);
if (ImUtf8.InputScalar("Sampler Flags"u8, ref sampler.Flags, "%08X"u8,
flags: ImGuiInputTextFlags.CharsHexadecimal | (disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None)))
Im.Item.SetNextWidthScaled(100.0f);
if (Im.Input.Scalar("Sampler Flags"u8, ref sampler.Flags, "%08X"u8, flags: InputTextFlags.CharsHexadecimal | (disabled ? InputTextFlags.ReadOnly : InputTextFlags.None)))
{
ret = true;
SetSamplerFlags(sampler.SamplerId, sampler.Flags);

View file

@ -1,11 +1,6 @@
using Dalamud.Interface.Components;
using Dalamud.Plugin.Services;
using Dalamud.Bindings.ImGui;
using ImSharp;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets;
using Luna;
using Penumbra.GameData.Files;
using Penumbra.GameData.Files.MaterialStructs;
using Penumbra.GameData.Interop;
@ -75,9 +70,8 @@ public sealed partial class MtrlTab : IWritable, IDisposable
if (disabled || Mtrl.IsDawntrail)
return false;
if (!ImUtf8.ButtonEx("Update MTRL Version to Dawntrail"u8,
"Try using this if the material can not be loaded or should use legacy shaders.\n\nThis is not revertible."u8,
new Vector2(-0.1f, 0), false, 0, new Rgba32(Colors.PressEnterWarningBg).Color))
if (!ImEx.Button("Update MTRL Version to Dawntrail"u8, Colors.PressEnterWarningBg, default, Im.ContentRegion.Available with { Y = 0 },
"Try using this if the material can not be loaded or should use legacy shaders.\n\nThis is not revertible."u8))
return false;
Mtrl.MigrateToDawntrail();
@ -96,17 +90,17 @@ public sealed partial class MtrlTab : IWritable, IDisposable
DrawMaterialLivePreviewRebind(disabled);
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
Im.Dummy(new Vector2(Im.Style.TextHeight / 2));
var ret = DrawBackFaceAndTransparency(disabled);
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
Im.Dummy(new Vector2(Im.Style.TextHeight / 2));
ret |= DrawShaderSection(disabled);
ret |= DrawTextureSection(disabled);
ret |= DrawColorTableSection(disabled);
ret |= DrawConstantsSection(disabled);
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
Im.Dummy(new Vector2(Im.Style.TextHeight / 2));
DrawOtherMaterialDetails(disabled);
return !disabled && ret;
@ -118,15 +112,15 @@ public sealed partial class MtrlTab : IWritable, IDisposable
var ret = false;
using var dis = ImRaii.Disabled(disabled);
using var dis = Im.Disabled(disabled);
var tmp = shaderFlags.EnableTransparency;
// guardrail: the game crashes if transparency is enabled on characterstockings.shpk
var disallowTransparency = Mtrl.ShaderPackage.Name == "characterstockings.shpk";
using (ImRaii.Disabled(disallowTransparency))
using (Im.Disabled(disallowTransparency))
{
if (ImUtf8.Checkbox("Enable Transparency"u8, ref tmp))
if (Im.Checkbox("Enable Transparency"u8, ref tmp))
{
shaderFlags.EnableTransparency = tmp;
ret = true;
@ -135,13 +129,11 @@ public sealed partial class MtrlTab : IWritable, IDisposable
}
if (disallowTransparency)
{
ImGuiComponents.HelpMarker("Enabling transparency for shader package characterstockings.shpk will crash the game.");
}
LunaStyle.DrawHelpMarker("Enabling transparency for shader package characterstockings.shpk will crash the game."u8);
Im.Line.Same(200 * Im.Style.GlobalScale + Im.Style.ItemSpacing.X + Im.Style.WindowPadding.X);
tmp = shaderFlags.HideBackfaces;
if (ImUtf8.Checkbox("Hide Backfaces"u8, ref tmp))
if (Im.Checkbox("Hide Backfaces"u8, ref tmp))
{
shaderFlags.HideBackfaces = tmp;
ret = true;
@ -152,8 +144,8 @@ public sealed partial class MtrlTab : IWritable, IDisposable
{
Im.Line.Same(400 * Im.Style.GlobalScale + 2 * Im.Style.ItemSpacing.X + Im.Style.WindowPadding.X);
ImUtf8.Text("Loading shader (.shpk) file. Some functionality will only be available after this is done."u8,
ImGuiUtil.HalfBlendText(0x808000u)); // Half cyan
Im.Text("Loading shader (.shpk) file. Some functionality will only be available after this is done."u8,
ImGuiColor.Text.Get().HalfBlend(0x808000));
}
return ret;
@ -161,29 +153,29 @@ public sealed partial class MtrlTab : IWritable, IDisposable
private void DrawOtherMaterialDetails(bool _)
{
if (!ImUtf8.CollapsingHeader("Further Content"u8))
if (!Im.Tree.Header("Further Content"u8))
return;
using (var sets = ImUtf8.TreeNode("UV Sets"u8, ImGuiTreeNodeFlags.DefaultOpen))
using (var sets = Im.Tree.Node("UV Sets"u8, TreeNodeFlags.DefaultOpen))
{
if (sets)
foreach (var set in Mtrl.UvSets)
ImUtf8.TreeNode($"#{set.Index:D2} - {set.Name}", ImGuiTreeNodeFlags.Leaf).Dispose();
Im.Tree.Leaf($"#{set.Index:D2} - {set.Name}");
}
using (var sets = ImUtf8.TreeNode("Color Sets"u8, ImGuiTreeNodeFlags.DefaultOpen))
using (var sets = Im.Tree.Node("Color Sets"u8, TreeNodeFlags.DefaultOpen))
{
if (sets)
foreach (var set in Mtrl.ColorSets)
ImUtf8.TreeNode($"#{set.Index:D2} - {set.Name}", ImGuiTreeNodeFlags.Leaf).Dispose();
Im.Tree.Leaf($"#{set.Index:D2} - {set.Name}");
}
if (Mtrl.AdditionalData.Length <= 0)
return;
using var t = ImUtf8.TreeNode($"Additional Data (Size: {Mtrl.AdditionalData.Length})###AdditionalData");
using var t = Im.Tree.Node($"Additional Data (Size: {Mtrl.AdditionalData.Length})###AdditionalData");
if (t)
Widget.DrawHexViewer(Mtrl.AdditionalData);
ImEx.HexViewer(Mtrl.AdditionalData);
}
private void UnpinResources(bool all)
@ -209,7 +201,7 @@ public sealed partial class MtrlTab : IWritable, IDisposable
UpdateConstants();
}
public unsafe void Dispose()
public void Dispose()
{
UnbindFromMaterialInstances();
if (Writable)

View file

@ -1,11 +1,8 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.ImGuiNotification;
using ImSharp;
using Luna;
using Newtonsoft.Json.Linq;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.Collections.Cache;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
@ -14,8 +11,7 @@ using Penumbra.GameData.Files.AtchStructs;
using Penumbra.Meta;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
using MouseWheelType = OtterGui.Widgets.MouseWheelType;
using Penumbra.UI.Combos;
using Notification = Luna.Notification;
namespace Penumbra.UI.AdvancedWindow.Meta;
@ -29,12 +25,11 @@ public sealed class AtchMetaDrawer : MetaDrawer<AtchIdentifier, AtchEntry>
=> 10;
public override float ColumnHeight
=> 2 * ImUtf8.FrameHeightSpacing;
=> 2 * Im.Style.FrameHeightWithSpacing;
private AtchFile? _currentBaseAtchFile;
private AtchPoint? _currentBaseAtchPoint;
private readonly AtchPointCombo _combo;
private string _fileImport = string.Empty;
public AtchMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: base(editor, metaFiles)
@ -42,13 +37,6 @@ public sealed class AtchMetaDrawer : MetaDrawer<AtchIdentifier, AtchEntry>
_combo = new AtchPointCombo(() => _currentBaseAtchFile?.Points.Select(p => p.Type).ToList() ?? []);
}
private sealed class AtchPointCombo(Func<IReadOnlyList<AtchType>> generator)
: FilterComboCache<AtchType>(generator, MouseWheelType.Control, Penumbra.Log)
{
protected override string ToString(AtchType obj)
=> obj.ToName();
}
private sealed class RaceCodeException(string filePath) : Exception($"Could not identify race code from path {filePath}.");
public void ImportFile(string filePath)
@ -70,7 +58,7 @@ public sealed class AtchMetaDrawer : MetaDrawer<AtchIdentifier, AtchEntry>
{
var identifier = new AtchIdentifier(point.Type, gr, (ushort)index);
var defaultValue = AtchCache.GetDefault(MetaFiles, identifier);
if (defaultValue == null)
if (defaultValue is null)
continue;
if (defaultValue.Value.Equals(entry))
@ -83,27 +71,25 @@ public sealed class AtchMetaDrawer : MetaDrawer<AtchIdentifier, AtchEntry>
catch (RaceCodeException ex)
{
Penumbra.Messager.AddMessage(new Notification(ex, "The imported .atch file does not contain a race code (cXXXX) in its name.",
"Could not import .atch file:",
NotificationType.Warning));
"Could not import .atch file:", NotificationType.Warning));
}
catch (Exception ex)
{
Penumbra.Messager.AddMessage(new Notification(ex, "Unable to import .atch file.", "Could not import .atch file:",
NotificationType.Warning));
Penumbra.Messager.AddMessage(new Notification(ex, "Unable to import .atch file.", "Could not import .atch file:", NotificationType.Warning));
}
}
protected override void DrawNew()
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
CopyToClipboardButton("Copy all current ATCH manipulations to clipboard."u8,
new Lazy<JToken?>(() => MetaDictionary.SerializeTo([], Editor.Atch)));
ImGui.TableNextColumn();
Im.Table.NextColumn();
var canAdd = !Editor.Contains(Identifier);
var tt = canAdd ? "Stage this edit."u8 : "This entry is already edited."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
if (ImEx.Icon.Button(LunaStyle.AddObjectIcon, tt, disabled: !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier, Entry);
if (DrawIdentifierInput(ref Identifier))
@ -146,20 +132,20 @@ public sealed class AtchMetaDrawer : MetaDrawer<AtchIdentifier, AtchEntry>
private bool DrawIdentifierInput(ref AtchIdentifier identifier)
{
var changes = false;
ImGui.TableNextColumn();
Im.Table.NextColumn();
changes |= DrawRace(ref identifier);
ImGui.TableNextColumn();
Im.Table.NextColumn();
changes |= DrawGender(ref identifier, false);
if (changes)
UpdateFile();
ImGui.TableNextColumn();
Im.Table.NextColumn();
if (DrawPointInput(ref identifier, _combo))
{
_currentBaseAtchPoint = _currentBaseAtchFile?.GetPoint(identifier.Type);
changes = true;
}
ImGui.TableNextColumn();
Im.Table.NextColumn();
changes |= DrawEntryIndexInput(ref identifier, _currentBaseAtchPoint!);
return changes;
@ -169,7 +155,7 @@ public sealed class AtchMetaDrawer : MetaDrawer<AtchIdentifier, AtchEntry>
{
_currentBaseAtchFile = MetaFiles.AtchManager.AtchFileBase[Identifier.GenderRace];
_currentBaseAtchPoint = _currentBaseAtchFile.GetPoint(Identifier.Type);
if (_currentBaseAtchPoint == null)
if (_currentBaseAtchPoint is null)
{
_currentBaseAtchPoint = _currentBaseAtchFile.Points.First();
Identifier = Identifier with { Type = _currentBaseAtchPoint.Type };
@ -181,32 +167,32 @@ public sealed class AtchMetaDrawer : MetaDrawer<AtchIdentifier, AtchEntry>
private static void DrawIdentifier(AtchIdentifier identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Race.ToName(), FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed(identifier.Race.ToNameU8(), default, FrameColor);
Im.Tooltip.OnHover("Model Race"u8);
ImGui.TableNextColumn();
Im.Table.NextColumn();
DrawGender(ref identifier, true);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Type.ToName(), FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed(identifier.Type.ToName(), default, FrameColor);
Im.Tooltip.OnHover("Attachment Point Type"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.EntryIndex.ToString(), FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed($"{identifier.EntryIndex}", default, FrameColor);
Im.Tooltip.OnHover("State Entry Index"u8);
}
private static bool DrawEntry(in AtchEntry defaultEntry, ref AtchEntry entry, bool disabled)
{
var changes = false;
using var dis = ImRaii.Disabled(disabled);
using var dis = Im.Disabled(disabled);
if (defaultEntry.Bone.Length == 0)
return false;
ImGui.TableNextColumn();
Im.Item.SetNextWidth(200 * Im.Style.GlobalScale);
if (ImUtf8.InputText("##BoneName"u8, entry.FullSpan, out TerminatedByteString newBone))
Im.Table.NextColumn();
Im.Item.SetNextWidthScaled(200);
if (Im.Input.Text("##BoneName"u8, entry.FullSpan, out StringU8 newBone))
{
entry.SetBoneName(newBone);
changes = true;
@ -214,32 +200,32 @@ public sealed class AtchMetaDrawer : MetaDrawer<AtchIdentifier, AtchEntry>
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, "Bone Name"u8);
Im.Item.SetNextWidth(200 * Im.Style.GlobalScale);
changes |= ImUtf8.InputScalar("##AtchScale"u8, ref entry.Scale);
Im.Item.SetNextWidthScaled(200);
changes |= Im.Input.Scalar("##AtchScale"u8, ref entry.Scale);
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, "Scale"u8);
ImGui.TableNextColumn();
Im.Item.SetNextWidth(120 * Im.Style.GlobalScale);
changes |= ImUtf8.InputScalar("##AtchOffsetX"u8, ref entry.OffsetX);
Im.Table.NextColumn();
Im.Item.SetNextWidthScaled(120);
changes |= Im.Input.Scalar("##AtchOffsetX"u8, ref entry.OffsetX);
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, "Offset X-Coordinate"u8);
Im.Item.SetNextWidth(120 * Im.Style.GlobalScale);
changes |= ImUtf8.InputScalar("##AtchRotationX"u8, ref entry.RotationX);
Im.Item.SetNextWidthScaled(120);
changes |= Im.Input.Scalar("##AtchRotationX"u8, ref entry.RotationX);
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, "Rotation X-Axis"u8);
ImGui.TableNextColumn();
Im.Item.SetNextWidth(120 * Im.Style.GlobalScale);
changes |= ImUtf8.InputScalar("##AtchOffsetY"u8, ref entry.OffsetY);
Im.Table.NextColumn();
Im.Item.SetNextWidthScaled(120);
changes |= Im.Input.Scalar("##AtchOffsetY"u8, ref entry.OffsetY);
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, "Offset Y-Coordinate"u8);
Im.Item.SetNextWidth(120 * Im.Style.GlobalScale);
changes |= ImUtf8.InputScalar("##AtchRotationY"u8, ref entry.RotationY);
Im.Item.SetNextWidthScaled(120);
changes |= Im.Input.Scalar("##AtchRotationY"u8, ref entry.RotationY);
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, "Rotation Y-Axis"u8);
ImGui.TableNextColumn();
Im.Item.SetNextWidth(120 * Im.Style.GlobalScale);
changes |= ImUtf8.InputScalar("##AtchOffsetZ"u8, ref entry.OffsetZ);
Im.Table.NextColumn();
Im.Item.SetNextWidthScaled(120);
changes |= Im.Input.Scalar("##AtchOffsetZ"u8, ref entry.OffsetZ);
Im.Tooltip.OnHover("Offset Z-Coordinate"u8);
Im.Item.SetNextWidth(120 * Im.Style.GlobalScale);
changes |= ImUtf8.InputScalar("##AtchRotationZ"u8, ref entry.RotationZ);
Im.Item.SetNextWidthScaled(120);
changes |= Im.Input.Scalar("##AtchRotationZ"u8, ref entry.RotationZ);
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, "Rotation Z-Axis"u8);
return changes;
@ -247,7 +233,7 @@ public sealed class AtchMetaDrawer : MetaDrawer<AtchIdentifier, AtchEntry>
private static bool DrawRace(ref AtchIdentifier identifier, float unscaledWidth = 100)
{
var ret = Combos.Race("##atchRace", identifier.Race, out var race, unscaledWidth);
var ret = Combos.Combos.Race("##atchRace", identifier.Race, out var race, unscaledWidth);
Im.Tooltip.OnHover("Model Race"u8);
if (ret)
identifier = identifier with { GenderRace = Names.CombinedRace(identifier.Gender, race) };
@ -259,7 +245,7 @@ public sealed class AtchMetaDrawer : MetaDrawer<AtchIdentifier, AtchEntry>
{
var isMale = identifier.Gender is Gender.Male;
if (!ImUtf8.IconButton(isMale ? FontAwesomeIcon.Mars : FontAwesomeIcon.Venus, "Gender"u8, buttonColor: disabled ? 0x000F0000u : 0)
if (!ImEx.Icon.Button(isMale ? FontAwesomeIcon.Mars.Icon() : FontAwesomeIcon.Venus.Icon(), "Gender"u8, buttonColor: disabled ? 0x000F0000u : 0)
|| disabled)
return false;
@ -281,8 +267,8 @@ public sealed class AtchMetaDrawer : MetaDrawer<AtchIdentifier, AtchEntry>
{
var index = identifier.EntryIndex;
Im.Item.SetNextWidth(40 * Im.Style.GlobalScale);
var ret = ImUtf8.DragScalar("##AtchEntry"u8, ref index, 0, (ushort)(currentAtchPoint.Entries.Length - 1), 0.05f,
ImGuiSliderFlags.AlwaysClamp);
var ret = Im.Drag("##AtchEntry"u8, ref index, 0, (ushort)(currentAtchPoint.Entries.Length - 1), 0.05f,
SliderFlags.AlwaysClamp);
Im.Tooltip.OnHover("State Entry Index"u8);
if (!ret)
return false;
@ -291,4 +277,4 @@ public sealed class AtchMetaDrawer : MetaDrawer<AtchIdentifier, AtchEntry>
identifier = identifier with { EntryIndex = index };
return true;
}
}
}

View file

@ -1,9 +1,6 @@
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Luna;
using Newtonsoft.Json.Linq;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.Collections.Cache;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -28,7 +25,7 @@ public sealed class AtrMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
=> 7;
public override float ColumnHeight
=> ImUtf8.FrameHeightSpacing;
=> Im.Style.FrameHeightWithSpacing;
protected override void Initialize()
{
@ -38,18 +35,18 @@ public sealed class AtrMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
protected override void DrawNew()
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
CopyToClipboardButton("Copy all current ATR manipulations to clipboard."u8,
new Lazy<JToken?>(() => MetaDictionary.SerializeTo([], Editor.Atr)));
ImGui.TableNextColumn();
Im.Table.NextColumn();
var canAdd = !Editor.Contains(Identifier) && _identifierValid;
var tt = canAdd
? "Stage this edit."u8
: _identifierValid
? "This entry does not contain a valid attribute."u8
: "This entry is already edited."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
if (ImEx.Icon.Button(LunaStyle.AddObjectIcon, tt, !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier, AtrEntry.False);
DrawIdentifierInput(ref Identifier);
@ -77,55 +74,55 @@ public sealed class AtrMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
private bool DrawIdentifierInput(ref AtrIdentifier identifier)
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
var changes = DrawHumanSlot(ref identifier);
ImGui.TableNextColumn();
Im.Table.NextColumn();
changes |= DrawGenderRaceConditionInput(ref identifier);
ImGui.TableNextColumn();
Im.Table.NextColumn();
changes |= DrawPrimaryId(ref identifier);
ImGui.TableNextColumn();
Im.Table.NextColumn();
changes |= DrawAttributeKeyInput(ref identifier, ref _buffer, ref _identifierValid);
return changes;
}
private static void DrawIdentifier(AtrIdentifier identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed(ShpMetaDrawer.SlotName(identifier.Slot), FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed(ShpMetaDrawer.SlotName(identifier.Slot), default, FrameColor);
Im.Tooltip.OnHover("Model Slot"u8);
ImGui.TableNextColumn();
Im.Table.NextColumn();
if (identifier.GenderRaceCondition is not GenderRace.Unknown)
{
ImUtf8.TextFramed($"{identifier.GenderRaceCondition.ToName()} ({identifier.GenderRaceCondition.ToRaceCode()})", FrameColor);
ImEx.TextFramed($"{identifier.GenderRaceCondition.ToNameU8()} ({identifier.GenderRaceCondition.ToRaceCode()})", default,
FrameColor);
Im.Tooltip.OnHover("Gender & Race Code for this attribute to be set.");
}
else
{
ImUtf8.TextFramed("Any Gender & Race"u8, FrameColor);
ImEx.TextFramed("Any Gender & Race"u8, default, FrameColor);
}
ImGui.TableNextColumn();
Im.Table.NextColumn();
if (identifier.Id.HasValue)
ImUtf8.TextFramed($"{identifier.Id.Value.Id}", FrameColor);
ImEx.TextFramed($"{identifier.Id.Value.Id}", default, FrameColor);
else
ImUtf8.TextFramed("All IDs"u8, FrameColor);
ImEx.TextFramed("All IDs"u8, default, FrameColor);
Im.Tooltip.OnHover("Primary ID"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Attribute.AsSpan, FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed(identifier.Attribute.AsSpan, default, FrameColor);
}
private static bool DrawEntry(ref AtrEntry entry, bool disabled)
{
using var dis = ImRaii.Disabled(disabled);
ImGui.TableNextColumn();
using var dis = Im.Disabled(disabled);
Im.Table.NextColumn();
var value = entry.Value;
var changes = ImUtf8.Checkbox("##atrEntry"u8, ref value);
var changes = Im.Checkbox("##atrEntry"u8, ref value);
if (changes)
entry = new AtrEntry(value);
Im.Tooltip.OnHover("Whether to enable or disable this attribute for the selected items.");
@ -137,9 +134,9 @@ public sealed class AtrMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
var allSlots = identifier.Slot is HumanSlot.Unknown;
var all = !identifier.Id.HasValue;
var ret = false;
using (ImRaii.Disabled(allSlots))
using (Im.Disabled(allSlots))
{
if (ImUtf8.Checkbox("##atrAll"u8, ref all))
if (Im.Checkbox("##atrAll"u8, ref all))
{
identifier = identifier with { Id = all ? null : 0 };
ret = true;
@ -150,12 +147,12 @@ public sealed class AtrMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
? "When using all slots, you also need to use all IDs."u8
: "Enable this attribute for all model IDs."u8);
Im.Line.Same(0, Im.Style.ItemInnerSpacing.X);
Im.Line.SameInner();
if (all)
{
using var style = ImStyleDouble.ButtonTextAlign.Push(new Vector2(0.05f, 0.5f));
ImUtf8.TextFramed("All IDs"u8, ImGuiColor.FrameBackground.Get(all || allSlots ? Im.Style.DisabledAlpha : 1f).Color,
new Vector2(unscaledWidth, 0), ImGuiColor.TextDisabled.Get().Color);
ImEx.TextFramed("All IDs"u8, new Vector2(unscaledWidth, 0),
ImGuiColor.FrameBackground.Get(all || allSlots ? Im.Style.DisabledAlpha : 1f).Color, ImGuiColor.TextDisabled.Get().Color);
}
else
{
@ -175,13 +172,13 @@ public sealed class AtrMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
public bool DrawHumanSlot(ref AtrIdentifier identifier, float unscaledWidth = 150)
{
var ret = false;
Im.Item.SetNextWidth(unscaledWidth * Im.Style.GlobalScale);
using (var combo = ImUtf8.Combo("##atrSlot"u8, ShpMetaDrawer.SlotName(identifier.Slot)))
Im.Item.SetNextWidthScaled(unscaledWidth);
using (var combo = Im.Combo.Begin("##atrSlot"u8, ShpMetaDrawer.SlotName(identifier.Slot)))
{
if (combo)
foreach (var slot in ShpMetaDrawer.AvailableSlots)
{
if (!ImUtf8.Selectable(ShpMetaDrawer.SlotName(slot), slot == identifier.Slot) || slot == identifier.Slot)
if (!Im.Selectable(ShpMetaDrawer.SlotName(slot), slot == identifier.Slot) || slot == identifier.Slot)
continue;
ret = true;
@ -215,16 +212,16 @@ public sealed class AtrMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
private static bool DrawGenderRaceConditionInput(ref AtrIdentifier identifier, float unscaledWidth = 250)
{
var ret = false;
Im.Item.SetNextWidth(unscaledWidth * Im.Style.GlobalScale);
Im.Item.SetNextWidthScaled(unscaledWidth);
using (var combo = ImUtf8.Combo("##shpGenderRace"u8,
using (var combo = Im.Combo.Begin("##shpGenderRace"u8,
identifier.GenderRaceCondition is GenderRace.Unknown
? "Any Gender & Race"
: $"{identifier.GenderRaceCondition.ToName()} ({identifier.GenderRaceCondition.ToRaceCode()})"))
? "Any Gender & Race"u8
: $"{identifier.GenderRaceCondition.ToNameU8()} ({identifier.GenderRaceCondition.ToRaceCode()})"))
{
if (combo)
{
if (ImUtf8.Selectable("Any Gender & Race"u8, identifier.GenderRaceCondition is GenderRace.Unknown)
if (Im.Selectable("Any Gender & Race"u8, identifier.GenderRaceCondition is GenderRace.Unknown)
&& identifier.GenderRaceCondition is not GenderRace.Unknown)
{
identifier = identifier with { GenderRaceCondition = GenderRace.Unknown };
@ -233,7 +230,7 @@ public sealed class AtrMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
foreach (var gr in ShapeAttributeHashSet.GenderRaceValues.Skip(1))
{
if (ImUtf8.Selectable($"{gr.ToName()} ({gr.ToRaceCode()})", identifier.GenderRaceCondition == gr)
if (Im.Selectable($"{gr.ToNameU8()} ({gr.ToRaceCode()})", identifier.GenderRaceCondition == gr)
&& identifier.GenderRaceCondition != gr)
{
identifier = identifier with { GenderRaceCondition = gr };
@ -243,8 +240,7 @@ public sealed class AtrMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
}
}
Im.Tooltip.OnHover(
"Only activate this attribute for this gender & race code."u8);
Im.Tooltip.OnHover("Only activate this attribute for this gender & race code."u8);
return ret;
}
@ -257,8 +253,8 @@ public sealed class AtrMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
var span = new Span<byte>(ptr, ShapeAttributeString.MaxLength + 1);
using (ImStyleBorder.Frame.Push(Colors.RegexWarningBorder, Im.Style.GlobalScale, !valid))
{
Im.Item.SetNextWidth(unscaledWidth * Im.Style.GlobalScale);
if (ImUtf8.InputText("##atrAttribute"u8, span, out int newLength, "Attribute..."u8))
Im.Item.SetNextWidthScaled(unscaledWidth);
if (Im.Input.Text("##atrAttribute"u8, span, out ulong newLength, "Attribute..."u8))
{
buffer.ForceLength((byte)newLength);
valid = buffer.ValidateCustomAttributeString();

View file

@ -1,16 +1,12 @@
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Luna;
using Newtonsoft.Json.Linq;
using OtterGui.Text;
using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs;
using Penumbra.Meta;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta;
@ -34,15 +30,15 @@ public sealed class EqdpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFil
protected override void DrawNew()
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
CopyToClipboardButton("Copy all current EQDP manipulations to clipboard."u8, new Lazy<JToken?>(() => MetaDictionary.SerializeTo([], Editor.Eqdp)));
ImGui.TableNextColumn();
Im.Table.NextColumn();
var validRaceCode = CharacterUtilityData.EqdpIdx(Identifier.GenderRace, false) >= 0;
var canAdd = validRaceCode && !Editor.Contains(Identifier);
var tt = canAdd ? "Stage this edit."u8 :
validRaceCode ? "This entry is already edited."u8 : "This combination of race and gender can not be used."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
if (ImEx.Icon.Button(LunaStyle.AddObjectIcon, tt, !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier, Entry);
if (DrawIdentifierInput(ref Identifier))
@ -72,44 +68,44 @@ public sealed class EqdpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFil
private static bool DrawIdentifierInput(ref EqdpIdentifier identifier)
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
var changes = DrawPrimaryId(ref identifier);
ImGui.TableNextColumn();
Im.Table.NextColumn();
changes |= DrawRace(ref identifier);
ImGui.TableNextColumn();
Im.Table.NextColumn();
changes |= DrawGender(ref identifier);
ImGui.TableNextColumn();
Im.Table.NextColumn();
changes |= DrawEquipSlot(ref identifier);
return changes;
}
private static void DrawIdentifier(EqdpIdentifier identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed($"{identifier.SetId.Id}", FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed($"{identifier.SetId.Id}", default, FrameColor);
Im.Tooltip.OnHover("Model Set ID"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Race.ToName(), FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed(identifier.Race.ToNameU8(), default, FrameColor);
Im.Tooltip.OnHover("Model Race"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Gender.ToName(), FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed(identifier.Gender.ToNameU8(), default, FrameColor);
Im.Tooltip.OnHover("Gender"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Slot.ToName(), FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed(identifier.Slot.ToNameU8(), default, FrameColor);
Im.Tooltip.OnHover("Equip Slot"u8);
}
private static bool DrawEntry(EqdpEntryInternal defaultEntry, ref EqdpEntryInternal entry, bool disabled)
{
var changes = false;
using var dis = ImRaii.Disabled(disabled);
ImGui.TableNextColumn();
using var dis = Im.Disabled(disabled);
Im.Table.NextColumn();
if (Checkmark("Material##eqdp"u8, "\0"u8, entry.Material, defaultEntry.Material, out var newMaterial))
{
entry = entry with { Material = newMaterial };
@ -139,7 +135,7 @@ public sealed class EqdpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFil
public static bool DrawRace(ref EqdpIdentifier identifier, float unscaledWidth = 100)
{
var ret = Combos.Race("##eqdpRace", identifier.Race, out var race, unscaledWidth);
var ret = Combos.Combos.Race("##eqdpRace", identifier.Race, out var race, unscaledWidth);
Im.Tooltip.OnHover("Model Race"u8);
if (ret)
identifier = identifier with { GenderRace = Names.CombinedRace(identifier.Gender, race) };
@ -148,7 +144,7 @@ public sealed class EqdpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFil
public static bool DrawGender(ref EqdpIdentifier identifier, float unscaledWidth = 120)
{
var ret = Combos.Gender("##eqdpGender", identifier.Gender, out var gender, unscaledWidth);
var ret = Combos.Combos.Gender("##eqdpGender", identifier.Gender, out var gender, unscaledWidth);
Im.Tooltip.OnHover("Gender"u8);
if (ret)
identifier = identifier with { GenderRace = Names.CombinedRace(gender, identifier.Race) };
@ -157,7 +153,7 @@ public sealed class EqdpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFil
public static bool DrawEquipSlot(ref EqdpIdentifier identifier, float unscaledWidth = 100)
{
var ret = Combos.EqdpEquipSlot("##eqdpSlot", identifier.Slot, out var slot, unscaledWidth);
var ret = Combos.Combos.EqdpEquipSlot("##eqdpSlot", identifier.Slot, out var slot, unscaledWidth);
Im.Tooltip.OnHover("Equip Slot"u8);
if (ret)
identifier = identifier with { Slot = slot };

View file

@ -1,16 +1,12 @@
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Luna;
using Newtonsoft.Json.Linq;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Meta;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta;
@ -34,13 +30,13 @@ public sealed class EqpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
protected override void DrawNew()
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
CopyToClipboardButton("Copy all current EQP manipulations to clipboard."u8, new Lazy<JToken?>(() => MetaDictionary.SerializeTo([], Editor.Eqp)));
ImGui.TableNextColumn();
Im.Table.NextColumn();
var canAdd = !Editor.Contains(Identifier);
var tt = canAdd ? "Stage this edit."u8 : "This entry is already edited."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
if (ImEx.Icon.Button(LunaStyle.AddObjectIcon, tt, !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier, Entry);
if (DrawIdentifierInput(ref Identifier))
@ -70,30 +66,30 @@ public sealed class EqpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
private static bool DrawIdentifierInput(ref EqpIdentifier identifier)
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
var changes = DrawPrimaryId(ref identifier);
ImGui.TableNextColumn();
Im.Table.NextColumn();
changes |= DrawEquipSlot(ref identifier);
return changes;
}
private static void DrawIdentifier(EqpIdentifier identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed($"{identifier.SetId.Id}", FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed($"{identifier.SetId.Id}", default, FrameColor);
Im.Tooltip.OnHover("Model Set ID"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Slot.ToName(), FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed(identifier.Slot.ToNameU8(), default, FrameColor);
Im.Tooltip.OnHover("Equip Slot"u8);
}
private static bool DrawEntry(EquipSlot slot, EqpEntryInternal defaultEntry, ref EqpEntryInternal entry, bool disabled)
{
var changes = false;
using var dis = ImRaii.Disabled(disabled);
ImGui.TableNextColumn();
using var dis = Im.Disabled(disabled);
Im.Table.NextColumn();
var offset = Eqp.OffsetAndMask(slot).Item1;
DrawBox(ref entry, 0);
for (var i = 1; i < Eqp.EqpAttributes[slot].Count; ++i)
@ -106,7 +102,7 @@ public sealed class EqpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
void DrawBox(ref EqpEntryInternal entry, int i)
{
using var id = ImUtf8.PushId(i);
using var id = Im.Id.Push(i);
var flag = 1u << i;
var eqpFlag = (EqpEntry)((ulong)flag << offset);
var defaultValue = (flag & defaultEntry.Value) != 0;
@ -132,7 +128,7 @@ public sealed class EqpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
public static bool DrawEquipSlot(ref EqpIdentifier identifier, float unscaledWidth = 100)
{
var ret = Combos.EqpEquipSlot("##eqpSlot", identifier.Slot, out var slot, unscaledWidth);
var ret = Combos.Combos.EqpEquipSlot("##eqpSlot", identifier.Slot, out var slot, unscaledWidth);
Im.Tooltip.OnHover("Equip Slot"u8);
if (ret)
identifier = identifier with { Slot = slot };

View file

@ -1,15 +1,11 @@
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Luna;
using Newtonsoft.Json.Linq;
using OtterGui.Text;
using Penumbra.GameData.Enums;
using Penumbra.Meta;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta;
@ -33,14 +29,14 @@ public sealed class EstMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
protected override void DrawNew()
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
CopyToClipboardButton("Copy all current EST manipulations to clipboard."u8,
new Lazy<JToken?>(() => MetaDictionary.SerializeTo([], Editor.Est)));
ImGui.TableNextColumn();
Im.Table.NextColumn();
var canAdd = !Editor.Contains(Identifier);
var tt = canAdd ? "Stage this edit."u8 : "This entry is already edited."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
if (ImEx.Icon.Button(LunaStyle.AddObjectIcon, tt, !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier, Entry);
if (DrawIdentifierInput(ref Identifier))
@ -71,16 +67,16 @@ public sealed class EstMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
private static bool DrawIdentifierInput(ref EstIdentifier identifier)
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
var changes = DrawPrimaryId(ref identifier);
ImGui.TableNextColumn();
Im.Table.NextColumn();
changes |= DrawRace(ref identifier);
ImGui.TableNextColumn();
Im.Table.NextColumn();
changes |= DrawGender(ref identifier);
ImGui.TableNextColumn();
Im.Table.NextColumn();
changes |= DrawSlot(ref identifier);
return changes;
@ -88,27 +84,27 @@ public sealed class EstMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
private static void DrawIdentifier(EstIdentifier identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed($"{identifier.SetId.Id}", FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed($"{identifier.SetId.Id}", default, FrameColor);
Im.Tooltip.OnHover("Model Set ID"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Race.ToName(), FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed(identifier.Race.ToNameU8(), default, FrameColor);
Im.Tooltip.OnHover("Model Race"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Gender.ToName(), FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed(identifier.Gender.ToNameU8(), default, FrameColor);
Im.Tooltip.OnHover("Gender"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Slot.ToString(), FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed(identifier.Slot.ToNameU8(), default, FrameColor);
Im.Tooltip.OnHover("Extra Skeleton Type"u8);
}
private static bool DrawEntry(EstEntry defaultEntry, ref EstEntry entry, bool disabled)
{
using var dis = ImRaii.Disabled(disabled);
ImGui.TableNextColumn();
using var dis = Im.Disabled(disabled);
Im.Table.NextColumn();
var ret = DragInput("##estValue"u8, [], 100f * Im.Style.GlobalScale, entry.Value, defaultEntry.Value, out var newValue, (ushort)0,
ushort.MaxValue, 0.05f, !disabled);
if (ret)
@ -129,7 +125,7 @@ public sealed class EstMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
public static bool DrawRace(ref EstIdentifier identifier, float unscaledWidth = 100)
{
var ret = Combos.Race("##estRace", identifier.Race, out var race, unscaledWidth);
var ret = Combos.Combos.Race("##estRace", identifier.Race, out var race, unscaledWidth);
Im.Tooltip.OnHover("Model Race"u8);
if (ret)
identifier = identifier with { GenderRace = Names.CombinedRace(identifier.Gender, race) };
@ -138,7 +134,7 @@ public sealed class EstMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
public static bool DrawGender(ref EstIdentifier identifier, float unscaledWidth = 120)
{
var ret = Combos.Gender("##estGender", identifier.Gender, out var gender, unscaledWidth);
var ret = Combos.Combos.Gender("##estGender", identifier.Gender, out var gender, unscaledWidth);
Im.Tooltip.OnHover("Gender"u8);
if (ret)
identifier = identifier with { GenderRace = Names.CombinedRace(gender, identifier.Race) };
@ -147,7 +143,7 @@ public sealed class EstMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
public static bool DrawSlot(ref EstIdentifier identifier, float unscaledWidth = 200)
{
var ret = Combos.EstSlot("##estSlot", identifier.Slot, out var slot, unscaledWidth);
var ret = Combos.Combos.EstSlot("##estSlot", identifier.Slot, out var slot, unscaledWidth);
Im.Tooltip.OnHover("Extra Skeleton Type"u8);
if (ret)
identifier = identifier with { Slot = slot };

View file

@ -1,8 +1,6 @@
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Luna;
using Newtonsoft.Json.Linq;
using OtterGui.Text;
using Penumbra.GameData.Structs;
using Penumbra.Meta;
using Penumbra.Meta.Manipulations;
@ -30,14 +28,14 @@ public sealed class GlobalEqpMetaDrawer(ModMetaEditor editor, MetaFileManager me
protected override void DrawNew()
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
CopyToClipboardButton("Copy all current global EQP manipulations to clipboard."u8,
new Lazy<JToken?>(() => MetaDictionary.SerializeTo([], Editor.GlobalEqp)));
ImGui.TableNextColumn();
Im.Table.NextColumn();
var canAdd = !Editor.Contains(Identifier);
var tt = canAdd ? "Stage this edit."u8 : "This entry is already edited."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
if (ImEx.Icon.Button(LunaStyle.AddObjectIcon, tt, !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier);
DrawIdentifierInput(ref Identifier);
@ -60,41 +58,41 @@ public sealed class GlobalEqpMetaDrawer(ModMetaEditor editor, MetaFileManager me
private static void DrawIdentifierInput(ref GlobalEqpManipulation identifier)
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
DrawType(ref identifier);
ImGui.TableNextColumn();
Im.Table.NextColumn();
if (identifier.Type.HasCondition())
DrawCondition(ref identifier);
else
ImUtf8.ScaledDummy(100);
Im.ScaledDummy(100);
}
private static void DrawIdentifier(GlobalEqpManipulation identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Type.ToName(), FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed(identifier.Type.ToNameU8(), default, FrameColor);
Im.Tooltip.OnHover("Global EQP Type"u8);
ImGui.TableNextColumn();
Im.Table.NextColumn();
if (identifier.Type.HasCondition())
{
ImUtf8.TextFramed($"{identifier.Condition.Id}", FrameColor);
ImEx.TextFramed($"{identifier.Condition.Id}", default, FrameColor);
Im.Tooltip.OnHover("Conditional Model ID"u8);
}
}
public static bool DrawType(ref GlobalEqpManipulation identifier, float unscaledWidth = 250)
{
Im.Item.SetNextWidth(unscaledWidth * Im.Style.GlobalScale);
using var combo = ImUtf8.Combo("##geqpType"u8, identifier.Type.ToName());
Im.Item.SetNextWidthScaled(unscaledWidth);
using var combo = Im.Combo.Begin("##geqpType"u8, identifier.Type.ToNameU8());
if (!combo)
return false;
var ret = false;
foreach (var type in Enum.GetValues<GlobalEqpType>())
{
if (ImUtf8.Selectable(type.ToName(), type == identifier.Type))
if (Im.Selectable(type.ToNameU8(), type == identifier.Type))
{
identifier = new GlobalEqpManipulation
{
@ -104,7 +102,7 @@ public sealed class GlobalEqpMetaDrawer(ModMetaEditor editor, MetaFileManager me
ret = true;
}
Im.Tooltip.OnHover(type.ToDescription());
Im.Tooltip.OnHover(type.Tooltip());
}
return ret;

View file

@ -1,8 +1,5 @@
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Bindings.ImGui;
using ImSharp;
using OtterGui.Text;
using Luna;
using Penumbra.GameData.Structs;
using Penumbra.Meta.Files;
using Penumbra.Meta;
@ -32,14 +29,14 @@ public sealed class GmpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
protected override void DrawNew()
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
CopyToClipboardButton("Copy all current Gmp manipulations to clipboard."u8,
new Lazy<JToken?>(() => MetaDictionary.SerializeTo([], Editor.Gmp)));
ImGui.TableNextColumn();
Im.Table.NextColumn();
var canAdd = !Editor.Contains(Identifier);
var tt = canAdd ? "Stage this edit."u8 : "This entry is already edited."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
if (ImEx.Icon.Button(LunaStyle.AddObjectIcon, tt, !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier, Entry);
if (DrawIdentifierInput(ref Identifier))
@ -68,21 +65,21 @@ public sealed class GmpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
private static bool DrawIdentifierInput(ref GmpIdentifier identifier)
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
return DrawPrimaryId(ref identifier);
}
private static void DrawIdentifier(GmpIdentifier identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed($"{identifier.SetId.Id}", FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed($"{identifier.SetId.Id}", default, FrameColor);
Im.Tooltip.OnHover("Model Set ID"u8);
}
private static bool DrawEntry(GmpEntry defaultEntry, ref GmpEntry entry, bool disabled)
{
using var dis = ImRaii.Disabled(disabled);
ImGui.TableNextColumn();
using var dis = Im.Disabled(disabled);
Im.Table.NextColumn();
var changes = false;
if (Checkmark("##gmpEnabled"u8, "Gimmick Enabled", entry.Enabled, defaultEntry.Enabled, out var enabled))
{
@ -90,7 +87,7 @@ public sealed class GmpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
changes = true;
}
ImGui.TableNextColumn();
Im.Table.NextColumn();
if (Checkmark("##gmpAnimated"u8, "Gimmick Animated", entry.Animated, defaultEntry.Animated, out var animated))
{
entry = entry with { Animated = animated };
@ -98,7 +95,7 @@ public sealed class GmpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
}
var rotationWidth = 75 * Im.Style.GlobalScale;
ImGui.TableNextColumn();
Im.Table.NextColumn();
if (DragInput("##gmpRotationA"u8, "Rotation A in Degrees"u8, rotationWidth, entry.RotationA, defaultEntry.RotationA, out var rotationA,
(ushort)0, (ushort)360, 0.05f, !disabled))
{
@ -123,7 +120,7 @@ public sealed class GmpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
}
var unkWidth = 50 * Im.Style.GlobalScale;
ImGui.TableNextColumn();
Im.Table.NextColumn();
if (DragInput("##gmpUnkA"u8, "Animation Type A?"u8, unkWidth, entry.UnknownA, defaultEntry.UnknownA, out var unknownA,
(byte)0, (byte)15, 0.01f, !disabled))
{

View file

@ -1,15 +1,11 @@
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Luna;
using Newtonsoft.Json.Linq;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Meta;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta;
@ -35,19 +31,19 @@ public sealed class ImcMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
protected override void DrawNew()
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
CopyToClipboardButton("Copy all current IMC manipulations to clipboard."u8,
new Lazy<JToken?>(() => MetaDictionary.SerializeTo([], Editor.Imc)));
ImGui.TableNextColumn();
Im.Table.NextColumn();
var canAdd = _fileExists && !Editor.Contains(Identifier);
var tt = canAdd ? "Stage this edit."u8 : !_fileExists ? "This IMC file does not exist."u8 : "This entry is already edited."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
if (ImEx.Icon.Button(LunaStyle.AddObjectIcon, tt, !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier, Entry);
if (DrawIdentifierInput(ref Identifier))
UpdateEntry();
using var disabled = ImRaii.Disabled();
using var disabled = Im.Disabled();
DrawEntry(Entry, ref Entry, false);
}
@ -63,83 +59,82 @@ public sealed class ImcMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
private static bool DrawIdentifierInput(ref ImcIdentifier identifier)
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
var change = DrawObjectType(ref identifier);
ImGui.TableNextColumn();
Im.Table.NextColumn();
change |= DrawPrimaryId(ref identifier);
ImGui.TableNextColumn();
Im.Table.NextColumn();
if (identifier.ObjectType is ObjectType.Equipment or ObjectType.Accessory)
change |= DrawSlot(ref identifier);
else
change |= DrawSecondaryId(ref identifier);
ImGui.TableNextColumn();
Im.Table.NextColumn();
change |= DrawVariant(ref identifier);
ImGui.TableNextColumn();
Im.Table.NextColumn();
if (identifier.ObjectType is ObjectType.DemiHuman)
change |= DrawSlot(ref identifier, 70f);
else
ImUtf8.ScaledDummy(70f);
Im.ScaledDummy(70f);
return change;
}
private static void DrawIdentifier(ImcIdentifier identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.ObjectType.ToName(), FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed(identifier.ObjectType.ToName(), default, FrameColor);
Im.Tooltip.OnHover("Object Type"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed($"{identifier.PrimaryId.Id}", FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed($"{identifier.PrimaryId.Id}", default, FrameColor);
Im.Tooltip.OnHover("Primary ID");
ImGui.TableNextColumn();
Im.Table.NextColumn();
if (identifier.ObjectType is ObjectType.Equipment or ObjectType.Accessory)
{
ImUtf8.TextFramed(identifier.EquipSlot.ToName(), FrameColor);
ImEx.TextFramed(identifier.EquipSlot.ToNameU8(), default, FrameColor);
Im.Tooltip.OnHover("Equip Slot"u8);
}
else
{
ImUtf8.TextFramed($"{identifier.SecondaryId.Id}", FrameColor);
ImEx.TextFramed($"{identifier.SecondaryId.Id}", default, FrameColor);
Im.Tooltip.OnHover("Secondary ID"u8);
}
ImGui.TableNextColumn();
ImUtf8.TextFramed($"{identifier.Variant.Id}", FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed($"{identifier.Variant.Id}", default, FrameColor);
Im.Tooltip.OnHover("Variant"u8);
ImGui.TableNextColumn();
Im.Table.NextColumn();
if (identifier.ObjectType is ObjectType.DemiHuman)
{
ImUtf8.TextFramed(identifier.EquipSlot.ToName(), FrameColor);
ImEx.TextFramed(identifier.EquipSlot.ToNameU8(), default, FrameColor);
Im.Tooltip.OnHover("Equip Slot"u8);
}
}
private static bool DrawEntry(ImcEntry defaultEntry, ref ImcEntry entry, bool addDefault)
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
var change = DrawMaterialId(defaultEntry, ref entry, addDefault);
Im.Line.SameInner();
change |= DrawMaterialAnimationId(defaultEntry, ref entry, addDefault);
ImGui.TableNextColumn();
Im.Table.NextColumn();
change |= DrawDecalId(defaultEntry, ref entry, addDefault);
Im.Line.SameInner();
change |= DrawVfxId(defaultEntry, ref entry, addDefault);
Im.Line.SameInner();
change |= DrawSoundId(defaultEntry, ref entry, addDefault);
ImGui.TableNextColumn();
Im.Table.NextColumn();
change |= DrawAttributes(defaultEntry, ref entry);
return change;
}
protected override IEnumerable<(ImcIdentifier, ImcEntry)> Enumerate()
=> Editor.Imc
.OrderBy(kvp => kvp.Key.ObjectType)
@ -155,7 +150,7 @@ public sealed class ImcMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
public static bool DrawObjectType(ref ImcIdentifier identifier, float width = 110)
{
var ret = Combos.ImcType("##imcType", identifier.ObjectType, out var type, width);
var ret = Combos.Combos.ImcType("##imcType", identifier.ObjectType, out var type, width);
Im.Tooltip.OnHover("Object Type"u8);
if (ret)
@ -216,9 +211,9 @@ public sealed class ImcMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
{
case ObjectType.Equipment:
case ObjectType.DemiHuman:
ret = Combos.EqpEquipSlot("##slot", identifier.EquipSlot, out slot, unscaledWidth);
ret = Combos.Combos.EqpEquipSlot("##slot", identifier.EquipSlot, out slot, unscaledWidth);
break;
case ObjectType.Accessory: ret = Combos.AccessorySlot("##slot", identifier.EquipSlot, out slot, unscaledWidth); break;
case ObjectType.Accessory: ret = Combos.Combos.AccessorySlot("##slot", identifier.EquipSlot, out slot, unscaledWidth); break;
default: return false;
}
@ -284,7 +279,7 @@ public sealed class ImcMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
var changes = false;
for (var i = 0; i < ImcEntry.NumAttributes; ++i)
{
using var id = ImRaii.PushId(i);
using var id = Im.Id.Push(i);
var flag = 1 << i;
var value = (entry.AttributeMask & flag) != 0;
var def = (defaultEntry.AttributeMask & flag) != 0;

View file

@ -1,11 +1,8 @@
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Luna;
using Newtonsoft.Json.Linq;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.Api.Api;
using Penumbra.Meta;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
@ -41,11 +38,11 @@ public abstract class MetaDrawer<TIdentifier, TEntry>(ModMetaEditor editor, Meta
_initialized = true;
}
using var id = ImUtf8.PushId((int)Identifier.Type);
using var id = Im.Id.Push((int)Identifier.Type);
DrawNew();
var height = ColumnHeight;
var skips = ImGuiClip.GetNecessarySkipsAtPos(height, ImGui.GetCursorPosY(), Count);
var skips = ImGuiClip.GetNecessarySkipsAtPos(height, Im.Cursor.Y, Count);
if (skips < Count)
{
var remainder = ImGuiClip.ClippedTableDraw(Enumerate(), skips, DrawLine, Count);
@ -99,7 +96,7 @@ public abstract class MetaDrawer<TIdentifier, TEntry>(ModMetaEditor editor, Meta
var c = defaultValue > currentValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value();
using var color = ImGuiColor.FrameBackground.Push(c, defaultValue != currentValue);
Im.Item.SetNextWidth(width);
if (ImUtf8.DragScalar(label, ref newValue, minValue, maxValue, speed))
if (Im.Drag(label, ref newValue, minValue, maxValue, speed))
newValue = newValue <= minValue ? minValue : newValue >= maxValue ? maxValue : newValue;
if (addDefault)
@ -120,7 +117,7 @@ public abstract class MetaDrawer<TIdentifier, TEntry>(ModMetaEditor editor, Meta
var c = defaultValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value();
using var color = ImGuiColor.FrameBackground.Push(c, defaultValue != currentValue);
newValue = currentValue;
ImUtf8.Checkbox(label, ref newValue);
Im.Checkbox(label, ref newValue);
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, tooltip);
return newValue != currentValue;
}
@ -135,29 +132,29 @@ public abstract class MetaDrawer<TIdentifier, TEntry>(ModMetaEditor editor, Meta
var c = defaultValue != currentValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value();
using var color = ImGuiColor.FrameBackground.Push(c, defaultValue != currentValue);
newValue = currentValue;
ImUtf8.Checkbox(label, ref newValue);
Im.Checkbox(label, ref newValue);
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, tooltip);
return newValue != currentValue;
}
protected void DrawMetaButtons(TIdentifier identifier, TEntry entry)
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
CopyToClipboardButton("Copy this manipulation to clipboard."u8,
new Lazy<JToken?>(() => new JArray { MetaDictionary.Serialize(identifier, entry)! }));
ImGui.TableNextColumn();
if (ImUtf8.IconButton(FontAwesomeIcon.Trash, "Delete this meta manipulation."u8))
Im.Table.NextColumn();
if (ImEx.Icon.Button(LunaStyle.DeleteIcon, "Delete this meta manipulation."u8))
Editor.Changes |= Editor.Remove(identifier);
}
protected void CopyToClipboardButton(ReadOnlySpan<byte> tooltip, Lazy<JToken?> manipulations)
{
if (!ImUtf8.IconButton(FontAwesomeIcon.Clipboard, tooltip))
if (!ImEx.Icon.Button(LunaStyle.ToClipboardIcon, tooltip))
return;
var text = Functions.ToCompressedBase64(manipulations.Value, 0);
if (text.Length > 0)
ImGui.SetClipboardText(text);
Im.Clipboard.Set(text);
}
}

View file

@ -1,15 +1,11 @@
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Luna;
using Newtonsoft.Json.Linq;
using OtterGui.Text;
using Penumbra.GameData.Enums;
using Penumbra.Meta;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta;
@ -33,14 +29,14 @@ public sealed class RspMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
protected override void DrawNew()
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
CopyToClipboardButton("Copy all current RSP manipulations to clipboard."u8,
new Lazy<JToken?>(() => MetaDictionary.SerializeTo([], Editor.Rsp)));
ImGui.TableNextColumn();
Im.Table.NextColumn();
var canAdd = !Editor.Contains(Identifier);
var tt = canAdd ? "Stage this edit."u8 : "This entry is already edited."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
if (ImEx.Icon.Button(LunaStyle.AddObjectIcon, tt, !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier, Entry);
if (DrawIdentifierInput(ref Identifier))
@ -70,29 +66,29 @@ public sealed class RspMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
private static bool DrawIdentifierInput(ref RspIdentifier identifier)
{
ImGui.TableNextColumn();
Im.Table.NextColumn();
var changes = DrawSubRace(ref identifier);
ImGui.TableNextColumn();
Im.Table.NextColumn();
changes |= DrawAttribute(ref identifier);
return changes;
}
private static void DrawIdentifier(RspIdentifier identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.SubRace.ToName(), FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed(identifier.SubRace.ToNameU8(), default, FrameColor);
Im.Tooltip.OnHover("Model Set ID"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Attribute.ToFullString(), FrameColor);
Im.Table.NextColumn();
ImEx.TextFramed(identifier.Attribute.ToNameU8(), default, FrameColor);
Im.Tooltip.OnHover("Equip Slot"u8);
}
private static bool DrawEntry(RspEntry defaultEntry, ref RspEntry entry, bool disabled)
{
using var dis = ImRaii.Disabled(disabled);
ImGui.TableNextColumn();
using var dis = Im.Disabled(disabled);
Im.Table.NextColumn();
var ret = DragInput("##rspValue"u8, [], Im.Style.GlobalScale * 150, entry.Value, defaultEntry.Value, out var newValue,
RspEntry.MinValue, RspEntry.MaxValue, 0.001f, !disabled);
if (ret)
@ -102,7 +98,7 @@ public sealed class RspMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
public static bool DrawSubRace(ref RspIdentifier identifier, float unscaledWidth = 150)
{
var ret = Combos.SubRace("##rspSubRace", identifier.SubRace, out var subRace, unscaledWidth);
var ret = Combos.Combos.SubRace("##rspSubRace", identifier.SubRace, out var subRace, unscaledWidth);
Im.Tooltip.OnHover("Racial Clan"u8);
if (ret)
identifier = identifier with { SubRace = subRace };
@ -111,7 +107,7 @@ public sealed class RspMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
public static bool DrawAttribute(ref RspIdentifier identifier, float unscaledWidth = 200)
{
var ret = Combos.RspAttribute("##rspAttribute", identifier.Attribute, out var attribute, unscaledWidth);
var ret = Combos.Combos.RspAttribute("##rspAttribute", identifier.Attribute, out var attribute, unscaledWidth);
Im.Tooltip.OnHover("Scaling Attribute"u8);
if (ret)
identifier = identifier with { Attribute = attribute };

View file

@ -1,366 +1,363 @@
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Newtonsoft.Json.Linq;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.Collections.Cache;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Meta;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class ShpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<ShpIdentifier, ShpEntry>(editor, metaFiles)
{
public override ReadOnlySpan<byte> Label
=> "Shape Keys (SHP)###SHP"u8;
private ShapeAttributeString _buffer = ShapeAttributeString.TryRead("shpx_"u8, out var s) ? s : ShapeAttributeString.Empty;
private bool _identifierValid;
public override int NumColumns
=> 8;
public override float ColumnHeight
=> ImUtf8.FrameHeightSpacing;
protected override void Initialize()
{
Identifier = new ShpIdentifier(HumanSlot.Unknown, null, ShapeAttributeString.Empty, ShapeConnectorCondition.None, GenderRace.Unknown);
}
protected override void DrawNew()
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy all current SHP manipulations to clipboard."u8,
new Lazy<JToken?>(() => MetaDictionary.SerializeTo([], Editor.Shp)));
ImGui.TableNextColumn();
var canAdd = !Editor.Contains(Identifier) && _identifierValid;
var tt = canAdd
? "Stage this edit."u8
: _identifierValid
? "This entry does not contain a valid shape key."u8
: "This entry is already edited."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier, ShpEntry.True);
DrawIdentifierInput(ref Identifier);
DrawEntry(ref Entry, true);
}
protected override void DrawEntry(ShpIdentifier identifier, ShpEntry entry)
{
DrawMetaButtons(identifier, entry);
DrawIdentifier(identifier);
if (DrawEntry(ref entry, false))
Editor.Changes |= Editor.Update(identifier, entry);
}
protected override IEnumerable<(ShpIdentifier, ShpEntry)> Enumerate()
=> Editor.Shp
.OrderBy(kvp => kvp.Key.Shape)
.ThenBy(kvp => kvp.Key.Slot)
.ThenBy(kvp => kvp.Key.Id)
.ThenBy(kvp => kvp.Key.ConnectorCondition)
.Select(kvp => (kvp.Key, kvp.Value));
protected override int Count
=> Editor.Shp.Count;
private bool DrawIdentifierInput(ref ShpIdentifier identifier)
{
ImGui.TableNextColumn();
var changes = DrawHumanSlot(ref identifier);
ImGui.TableNextColumn();
changes |= DrawGenderRaceConditionInput(ref identifier);
ImGui.TableNextColumn();
changes |= DrawPrimaryId(ref identifier);
ImGui.TableNextColumn();
changes |= DrawShapeKeyInput(ref identifier, ref _buffer, ref _identifierValid);
ImGui.TableNextColumn();
changes |= DrawConnectorConditionInput(ref identifier);
return changes;
}
private static void DrawIdentifier(ShpIdentifier identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed(SlotName(identifier.Slot), FrameColor);
Im.Tooltip.OnHover("Model Slot"u8);
ImGui.TableNextColumn();
if (identifier.GenderRaceCondition is not GenderRace.Unknown)
{
ImUtf8.TextFramed($"{identifier.GenderRaceCondition.ToName()} ({identifier.GenderRaceCondition.ToRaceCode()})", FrameColor);
Im.Tooltip.OnHover("Gender & Race Code for this shape key to be set.");
}
else
{
ImUtf8.TextFramed("Any Gender & Race"u8, FrameColor);
}
ImGui.TableNextColumn();
if (identifier.Id.HasValue)
ImUtf8.TextFramed($"{identifier.Id.Value.Id}", FrameColor);
else
ImUtf8.TextFramed("All IDs"u8, FrameColor);
Im.Tooltip.OnHover("Primary ID"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Shape.AsSpan, FrameColor);
ImGui.TableNextColumn();
if (identifier.ConnectorCondition is not ShapeConnectorCondition.None)
{
ImUtf8.TextFramed($"{identifier.ConnectorCondition}", FrameColor);
Im.Tooltip.OnHover("Connector condition for this shape to be activated.");
}
}
private static bool DrawEntry(ref ShpEntry entry, bool disabled)
{
using var dis = ImRaii.Disabled(disabled);
ImGui.TableNextColumn();
var value = entry.Value;
var changes = ImUtf8.Checkbox("##shpEntry"u8, ref value);
if (changes)
entry = new ShpEntry(value);
Im.Tooltip.OnHover("Whether to enable or disable this shape key for the selected items.");
return changes;
}
public static bool DrawPrimaryId(ref ShpIdentifier identifier, float unscaledWidth = 100)
{
var allSlots = identifier.Slot is HumanSlot.Unknown;
var all = !identifier.Id.HasValue;
var ret = false;
using (ImRaii.Disabled(allSlots))
{
if (ImUtf8.Checkbox("##shpAll"u8, ref all))
{
identifier = identifier with { Id = all ? null : 0 };
ret = true;
}
}
Im.Tooltip.OnHover(allSlots ? "When using all slots, you also need to use all IDs."u8 : "Enable this shape key for all model IDs."u8);
Im.Line.Same(0, Im.Style.ItemInnerSpacing.X);
if (all)
{
using var style = ImStyleDouble.ButtonTextAlign.Push(new Vector2(0.05f, 0.5f));
ImUtf8.TextFramed("All IDs"u8, ImGuiColor.FrameBackground.Get(all || allSlots ? Im.Style.DisabledAlpha : 1f).Color,
new Vector2(unscaledWidth, 0), ImGuiColor.TextDisabled.Get().Color);
}
else
{
var max = identifier.Slot.ToSpecificEnum() is BodySlot ? byte.MaxValue : ExpandedEqpGmpBase.Count - 1;
if (IdInput("##shpPrimaryId"u8, unscaledWidth, identifier.Id.GetValueOrDefault(0).Id, out var setId, 0, max, false))
{
identifier = identifier with { Id = setId };
ret = true;
}
}
Im.Tooltip.OnHover("Primary ID - You can usually find this as the 'e####' part of an item path or similar for customizations."u8);
return ret;
}
public bool DrawHumanSlot(ref ShpIdentifier identifier, float unscaledWidth = 170)
{
var ret = false;
Im.Item.SetNextWidth(unscaledWidth * Im.Style.GlobalScale);
using (var combo = ImUtf8.Combo("##shpSlot"u8, SlotName(identifier.Slot)))
{
if (combo)
foreach (var slot in AvailableSlots)
{
if (!ImUtf8.Selectable(SlotName(slot), slot == identifier.Slot) || slot == identifier.Slot)
continue;
ret = true;
if (slot is HumanSlot.Unknown)
{
identifier = identifier with
{
Id = null,
Slot = slot,
};
}
else
{
identifier = identifier with
{
Id = identifier.Id.HasValue
? (PrimaryId)Math.Clamp(identifier.Id.Value.Id, 0,
slot.ToSpecificEnum() is BodySlot ? byte.MaxValue : ExpandedEqpGmpBase.Count - 1)
: null,
Slot = slot,
ConnectorCondition = Identifier.ConnectorCondition switch
{
ShapeConnectorCondition.Wrists when slot is HumanSlot.Body or HumanSlot.Hands => ShapeConnectorCondition.Wrists,
ShapeConnectorCondition.Waist when slot is HumanSlot.Body or HumanSlot.Legs => ShapeConnectorCondition.Waist,
ShapeConnectorCondition.Ankles when slot is HumanSlot.Legs or HumanSlot.Feet => ShapeConnectorCondition.Ankles,
_ => ShapeConnectorCondition.None,
},
};
ret = true;
}
}
}
Im.Tooltip.OnHover("Model Slot"u8);
return ret;
}
public static unsafe bool DrawShapeKeyInput(ref ShpIdentifier identifier, ref ShapeAttributeString buffer, ref bool valid,
float unscaledWidth = 200)
{
var ret = false;
var ptr = Unsafe.AsPointer(ref buffer);
var span = new Span<byte>(ptr, ShapeAttributeString.MaxLength + 1);
using (ImStyleBorder.Frame.Push(Colors.RegexWarningBorder, Im.Style.GlobalScale, !valid))
{
Im.Item.SetNextWidth(unscaledWidth * Im.Style.GlobalScale);
if (ImUtf8.InputText("##shpShape"u8, span, out int newLength, "Shape Key..."u8))
{
buffer.ForceLength((byte)newLength);
valid = buffer.ValidateCustomShapeString();
if (valid)
identifier = identifier with { Shape = buffer };
ret = true;
}
}
Im.Tooltip.OnHover("Supported shape keys need to have the format `shpx_*` and a maximum length of 30 characters."u8);
return ret;
}
private static bool DrawConnectorConditionInput(ref ShpIdentifier identifier, float unscaledWidth = 80)
{
var ret = false;
Im.Item.SetNextWidth(unscaledWidth * Im.Style.GlobalScale);
var (showWrists, showWaist, showAnkles, disable) = identifier.Slot switch
{
HumanSlot.Unknown => (true, true, true, false),
HumanSlot.Body => (true, true, false, false),
HumanSlot.Legs => (false, true, true, false),
HumanSlot.Hands => (true, false, false, false),
HumanSlot.Feet => (false, false, true, false),
_ => (false, false, false, true),
};
using var disabled = ImRaii.Disabled(disable);
using (var combo = ImUtf8.Combo("##shpCondition"u8, $"{identifier.ConnectorCondition}"))
{
if (combo)
{
if (ImUtf8.Selectable("None"u8, identifier.ConnectorCondition is ShapeConnectorCondition.None))
identifier = identifier with { ConnectorCondition = ShapeConnectorCondition.None };
if (showWrists && ImUtf8.Selectable("Wrists"u8, identifier.ConnectorCondition is ShapeConnectorCondition.Wrists))
identifier = identifier with { ConnectorCondition = ShapeConnectorCondition.Wrists };
if (showWaist && ImUtf8.Selectable("Waist"u8, identifier.ConnectorCondition is ShapeConnectorCondition.Waist))
identifier = identifier with { ConnectorCondition = ShapeConnectorCondition.Waist };
if (showAnkles && ImUtf8.Selectable("Ankles"u8, identifier.ConnectorCondition is ShapeConnectorCondition.Ankles))
identifier = identifier with { ConnectorCondition = ShapeConnectorCondition.Ankles };
}
}
Im.Tooltip.OnHover(
"Only activate this shape key if any custom connector shape keys (shpx_[wr|wa|an]_*) are also enabled through matching attributes."u8);
return ret;
}
private static bool DrawGenderRaceConditionInput(ref ShpIdentifier identifier, float unscaledWidth = 250)
{
var ret = false;
Im.Item.SetNextWidth(unscaledWidth * Im.Style.GlobalScale);
using (var combo = ImUtf8.Combo("##shpGenderRace"u8, identifier.GenderRaceCondition is GenderRace.Unknown
? "Any Gender & Race"
: $"{identifier.GenderRaceCondition.ToName()} ({identifier.GenderRaceCondition.ToRaceCode()})"))
{
if (combo)
{
if (ImUtf8.Selectable("Any Gender & Race"u8, identifier.GenderRaceCondition is GenderRace.Unknown)
&& identifier.GenderRaceCondition is not GenderRace.Unknown)
{
identifier = identifier with { GenderRaceCondition = GenderRace.Unknown };
ret = true;
}
foreach (var gr in ShapeAttributeHashSet.GenderRaceValues.Skip(1))
{
if (ImUtf8.Selectable($"{gr.ToName()} ({gr.ToRaceCode()})", identifier.GenderRaceCondition == gr)
&& identifier.GenderRaceCondition != gr)
{
identifier = identifier with { GenderRaceCondition = gr };
ret = true;
}
}
}
}
Im.Tooltip.OnHover(
"Only activate this shape key for this gender & race code."u8);
return ret;
}
public static ReadOnlySpan<HumanSlot> AvailableSlots
=>
[
HumanSlot.Unknown,
HumanSlot.Head,
HumanSlot.Body,
HumanSlot.Hands,
HumanSlot.Legs,
HumanSlot.Feet,
HumanSlot.Ears,
HumanSlot.Neck,
HumanSlot.Wrists,
HumanSlot.RFinger,
HumanSlot.LFinger,
HumanSlot.Glasses,
HumanSlot.Hair,
HumanSlot.Face,
HumanSlot.Ear,
];
public static ReadOnlySpan<byte> SlotName(HumanSlot slot)
=> slot switch
{
HumanSlot.Unknown => "All Slots"u8,
HumanSlot.Head => "Equipment: Head"u8,
HumanSlot.Body => "Equipment: Body"u8,
HumanSlot.Hands => "Equipment: Hands"u8,
HumanSlot.Legs => "Equipment: Legs"u8,
HumanSlot.Feet => "Equipment: Feet"u8,
HumanSlot.Ears => "Equipment: Ears"u8,
HumanSlot.Neck => "Equipment: Neck"u8,
HumanSlot.Wrists => "Equipment: Wrists"u8,
HumanSlot.RFinger => "Equipment: Right Finger"u8,
HumanSlot.LFinger => "Equipment: Left Finger"u8,
HumanSlot.Glasses => "Equipment: Glasses"u8,
HumanSlot.Hair => "Customization: Hair"u8,
HumanSlot.Face => "Customization: Face"u8,
HumanSlot.Ear => "Customization: Ears"u8,
_ => "Unknown"u8,
};
}
using ImSharp;
using Luna;
using Newtonsoft.Json.Linq;
using Penumbra.Collections.Cache;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Meta;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class ShpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<ShpIdentifier, ShpEntry>(editor, metaFiles)
{
public override ReadOnlySpan<byte> Label
=> "Shape Keys (SHP)###SHP"u8;
private ShapeAttributeString _buffer = ShapeAttributeString.TryRead("shpx_"u8, out var s) ? s : ShapeAttributeString.Empty;
private bool _identifierValid;
public override int NumColumns
=> 8;
public override float ColumnHeight
=> Im.Style.FrameHeightWithSpacing;
protected override void Initialize()
=> Identifier = new ShpIdentifier(HumanSlot.Unknown, null, ShapeAttributeString.Empty, ShapeConnectorCondition.None,
GenderRace.Unknown);
protected override void DrawNew()
{
Im.Table.NextColumn();
CopyToClipboardButton("Copy all current SHP manipulations to clipboard."u8,
new Lazy<JToken?>(() => MetaDictionary.SerializeTo([], Editor.Shp)));
Im.Table.NextColumn();
var canAdd = !Editor.Contains(Identifier) && _identifierValid;
var tt = canAdd
? "Stage this edit."u8
: _identifierValid
? "This entry does not contain a valid shape key."u8
: "This entry is already edited."u8;
if (ImEx.Icon.Button(LunaStyle.AddObjectIcon, tt, !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier, ShpEntry.True);
DrawIdentifierInput(ref Identifier);
DrawEntry(ref Entry, true);
}
protected override void DrawEntry(ShpIdentifier identifier, ShpEntry entry)
{
DrawMetaButtons(identifier, entry);
DrawIdentifier(identifier);
if (DrawEntry(ref entry, false))
Editor.Changes |= Editor.Update(identifier, entry);
}
protected override IEnumerable<(ShpIdentifier, ShpEntry)> Enumerate()
=> Editor.Shp
.OrderBy(kvp => kvp.Key.Shape)
.ThenBy(kvp => kvp.Key.Slot)
.ThenBy(kvp => kvp.Key.Id)
.ThenBy(kvp => kvp.Key.ConnectorCondition)
.Select(kvp => (kvp.Key, kvp.Value));
protected override int Count
=> Editor.Shp.Count;
private bool DrawIdentifierInput(ref ShpIdentifier identifier)
{
Im.Table.NextColumn();
var changes = DrawHumanSlot(ref identifier);
Im.Table.NextColumn();
changes |= DrawGenderRaceConditionInput(ref identifier);
Im.Table.NextColumn();
changes |= DrawPrimaryId(ref identifier);
Im.Table.NextColumn();
changes |= DrawShapeKeyInput(ref identifier, ref _buffer, ref _identifierValid);
Im.Table.NextColumn();
changes |= DrawConnectorConditionInput(ref identifier);
return changes;
}
private static void DrawIdentifier(ShpIdentifier identifier)
{
Im.Table.NextColumn();
ImEx.TextFramed(SlotName(identifier.Slot), default, FrameColor);
Im.Tooltip.OnHover("Model Slot"u8);
Im.Table.NextColumn();
if (identifier.GenderRaceCondition is not GenderRace.Unknown)
{
ImEx.TextFramed($"{identifier.GenderRaceCondition.ToNameU8()} ({identifier.GenderRaceCondition.ToRaceCode()})", default,
FrameColor);
Im.Tooltip.OnHover("Gender & Race Code for this shape key to be set.");
}
else
{
ImEx.TextFramed("Any Gender & Race"u8, default, FrameColor);
}
Im.Table.NextColumn();
if (identifier.Id.HasValue)
ImEx.TextFramed($"{identifier.Id.Value.Id}", default, FrameColor);
else
ImEx.TextFramed("All IDs"u8, default, FrameColor);
Im.Tooltip.OnHover("Primary ID"u8);
Im.Table.NextColumn();
ImEx.TextFramed(identifier.Shape.AsSpan, default, FrameColor);
Im.Table.NextColumn();
if (identifier.ConnectorCondition is not ShapeConnectorCondition.None)
{
ImEx.TextFramed($"{identifier.ConnectorCondition}", default, FrameColor);
Im.Tooltip.OnHover("Connector condition for this shape to be activated."u8);
}
}
private static bool DrawEntry(ref ShpEntry entry, bool disabled)
{
using var dis = Im.Disabled(disabled);
Im.Table.NextColumn();
var value = entry.Value;
var changes = Im.Checkbox("##shpEntry"u8, ref value);
if (changes)
entry = new ShpEntry(value);
Im.Tooltip.OnHover("Whether to enable or disable this shape key for the selected items."u8);
return changes;
}
public static bool DrawPrimaryId(ref ShpIdentifier identifier, float unscaledWidth = 100)
{
var allSlots = identifier.Slot is HumanSlot.Unknown;
var all = !identifier.Id.HasValue;
var ret = false;
using (Im.Disabled(allSlots))
{
if (Im.Checkbox("##shpAll"u8, ref all))
{
identifier = identifier with { Id = all ? null : 0 };
ret = true;
}
}
Im.Tooltip.OnHover(allSlots ? "When using all slots, you also need to use all IDs."u8 : "Enable this shape key for all model IDs."u8);
Im.Line.SameInner();
if (all)
{
using var style = ImStyleDouble.ButtonTextAlign.Push(new Vector2(0.05f, 0.5f));
ImEx.TextFramed("All IDs"u8, new Vector2(unscaledWidth, 0),
ImGuiColor.FrameBackground.Get(all || allSlots ? Im.Style.DisabledAlpha : 1f).Color,
ImGuiColor.TextDisabled.Get().Color);
}
else
{
var max = identifier.Slot.ToSpecificEnum() is BodySlot ? byte.MaxValue : ExpandedEqpGmpBase.Count - 1;
if (IdInput("##shpPrimaryId"u8, unscaledWidth, identifier.Id.GetValueOrDefault(0).Id, out var setId, 0, max, false))
{
identifier = identifier with { Id = setId };
ret = true;
}
}
Im.Tooltip.OnHover("Primary ID - You can usually find this as the 'e####' part of an item path or similar for customizations."u8);
return ret;
}
public bool DrawHumanSlot(ref ShpIdentifier identifier, float unscaledWidth = 170)
{
var ret = false;
Im.Item.SetNextWidthScaled(unscaledWidth);
using (var combo = Im.Combo.Begin("##shpSlot"u8, SlotName(identifier.Slot)))
{
if (combo)
foreach (var slot in AvailableSlots)
{
if (!Im.Selectable(SlotName(slot), slot == identifier.Slot) || slot == identifier.Slot)
continue;
ret = true;
if (slot is HumanSlot.Unknown)
{
identifier = identifier with
{
Id = null,
Slot = slot,
};
}
else
{
identifier = identifier with
{
Id = identifier.Id.HasValue
? (PrimaryId)Math.Clamp(identifier.Id.Value.Id, 0,
slot.ToSpecificEnum() is BodySlot ? byte.MaxValue : ExpandedEqpGmpBase.Count - 1)
: null,
Slot = slot,
ConnectorCondition = Identifier.ConnectorCondition switch
{
ShapeConnectorCondition.Wrists when slot is HumanSlot.Body or HumanSlot.Hands => ShapeConnectorCondition.Wrists,
ShapeConnectorCondition.Waist when slot is HumanSlot.Body or HumanSlot.Legs => ShapeConnectorCondition.Waist,
ShapeConnectorCondition.Ankles when slot is HumanSlot.Legs or HumanSlot.Feet => ShapeConnectorCondition.Ankles,
_ => ShapeConnectorCondition.None,
},
};
ret = true;
}
}
}
Im.Tooltip.OnHover("Model Slot"u8);
return ret;
}
public static unsafe bool DrawShapeKeyInput(ref ShpIdentifier identifier, ref ShapeAttributeString buffer, ref bool valid,
float unscaledWidth = 200)
{
var ret = false;
var ptr = Unsafe.AsPointer(ref buffer);
var span = new Span<byte>(ptr, ShapeAttributeString.MaxLength + 1);
using (ImStyleBorder.Frame.Push(Colors.RegexWarningBorder, Im.Style.GlobalScale, !valid))
{
Im.Item.SetNextWidthScaled(unscaledWidth);
if (Im.Input.Text("##shpShape"u8, span, out ulong newLength, "Shape Key..."u8))
{
buffer.ForceLength((byte)newLength);
valid = buffer.ValidateCustomShapeString();
if (valid)
identifier = identifier with { Shape = buffer };
ret = true;
}
}
Im.Tooltip.OnHover("Supported shape keys need to have the format `shpx_*` and a maximum length of 30 characters."u8);
return ret;
}
private static bool DrawConnectorConditionInput(ref ShpIdentifier identifier, float unscaledWidth = 80)
{
var ret = false;
Im.Item.SetNextWidth(unscaledWidth * Im.Style.GlobalScale);
var (showWrists, showWaist, showAnkles, disable) = identifier.Slot switch
{
HumanSlot.Unknown => (true, true, true, false),
HumanSlot.Body => (true, true, false, false),
HumanSlot.Legs => (false, true, true, false),
HumanSlot.Hands => (true, false, false, false),
HumanSlot.Feet => (false, false, true, false),
_ => (false, false, false, true),
};
using var disabled = Im.Disabled(disable);
using (var combo = Im.Combo.Begin("##shpCondition"u8, $"{identifier.ConnectorCondition}"))
{
if (combo)
{
if (Im.Selectable("None"u8, identifier.ConnectorCondition is ShapeConnectorCondition.None))
identifier = identifier with { ConnectorCondition = ShapeConnectorCondition.None };
if (showWrists && Im.Selectable("Wrists"u8, identifier.ConnectorCondition is ShapeConnectorCondition.Wrists))
identifier = identifier with { ConnectorCondition = ShapeConnectorCondition.Wrists };
if (showWaist && Im.Selectable("Waist"u8, identifier.ConnectorCondition is ShapeConnectorCondition.Waist))
identifier = identifier with { ConnectorCondition = ShapeConnectorCondition.Waist };
if (showAnkles && Im.Selectable("Ankles"u8, identifier.ConnectorCondition is ShapeConnectorCondition.Ankles))
identifier = identifier with { ConnectorCondition = ShapeConnectorCondition.Ankles };
}
}
Im.Tooltip.OnHover(
"Only activate this shape key if any custom connector shape keys (shpx_[wr|wa|an]_*) are also enabled through matching attributes."u8);
return ret;
}
private static bool DrawGenderRaceConditionInput(ref ShpIdentifier identifier, float unscaledWidth = 250)
{
var ret = false;
Im.Item.SetNextWidth(unscaledWidth * Im.Style.GlobalScale);
using (var combo = Im.Combo.Begin("##shpGenderRace"u8, identifier.GenderRaceCondition is GenderRace.Unknown
? "Any Gender & Race"
: $"{identifier.GenderRaceCondition.ToName()} ({identifier.GenderRaceCondition.ToRaceCode()})"))
{
if (combo)
{
if (Im.Selectable("Any Gender & Race"u8, identifier.GenderRaceCondition is GenderRace.Unknown)
&& identifier.GenderRaceCondition is not GenderRace.Unknown)
{
identifier = identifier with { GenderRaceCondition = GenderRace.Unknown };
ret = true;
}
foreach (var gr in ShapeAttributeHashSet.GenderRaceValues.Skip(1))
{
if (Im.Selectable($"{gr.ToNameU8()} ({gr.ToRaceCode()})", identifier.GenderRaceCondition == gr)
&& identifier.GenderRaceCondition != gr)
{
identifier = identifier with { GenderRaceCondition = gr };
ret = true;
}
}
}
}
Im.Tooltip.OnHover("Only activate this shape key for this gender & race code."u8);
return ret;
}
public static ReadOnlySpan<HumanSlot> AvailableSlots
=>
[
HumanSlot.Unknown,
HumanSlot.Head,
HumanSlot.Body,
HumanSlot.Hands,
HumanSlot.Legs,
HumanSlot.Feet,
HumanSlot.Ears,
HumanSlot.Neck,
HumanSlot.Wrists,
HumanSlot.RFinger,
HumanSlot.LFinger,
HumanSlot.Glasses,
HumanSlot.Hair,
HumanSlot.Face,
HumanSlot.Ear,
];
public static ReadOnlySpan<byte> SlotName(HumanSlot slot)
=> slot switch
{
HumanSlot.Unknown => "All Slots"u8,
HumanSlot.Head => "Equipment: Head"u8,
HumanSlot.Body => "Equipment: Body"u8,
HumanSlot.Hands => "Equipment: Hands"u8,
HumanSlot.Legs => "Equipment: Legs"u8,
HumanSlot.Feet => "Equipment: Feet"u8,
HumanSlot.Ears => "Equipment: Ears"u8,
HumanSlot.Neck => "Equipment: Neck"u8,
HumanSlot.Wrists => "Equipment: Wrists"u8,
HumanSlot.RFinger => "Equipment: Right Finger"u8,
HumanSlot.LFinger => "Equipment: Left Finger"u8,
HumanSlot.Glasses => "Equipment: Glasses"u8,
HumanSlot.Hair => "Customization: Hair"u8,
HumanSlot.Face => "Customization: Face"u8,
HumanSlot.Ear => "Customization: Ears"u8,
_ => "Unknown"u8,
};
}

View file

@ -1,10 +1,5 @@
using Dalamud.Interface;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Bindings.ImGui;
using ImSharp;
using OtterGui;
using OtterGui.Text;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Files;
@ -29,17 +24,17 @@ public partial class ModEditWindow
private void DrawGenderRaceSelector(PbdTab tab)
{
using var group = ImUtf8.Group();
var width = ImUtf8.CalcTextSize("Hellsguard - Female (Child)____0000"u8).X + 2 * Im.Style.WindowPadding.X;
using var group = Im.Group();
var width = Im.Font.CalculateSize("Hellsguard - Female (Child)____0000"u8).X + 2 * Im.Style.WindowPadding.X;
using (ImStyleSingle.FrameRounding.Push(0)
.Push(ImStyleDouble.ItemSpacing, Vector2.Zero))
{
Im.Item.SetNextWidth(width);
ImUtf8.InputText("##grFilter"u8, ref _pbdData.RaceCodeFilter, "Filter..."u8);
Im.Input.Text("##grFilter"u8, ref _pbdData.RaceCodeFilter, "Filter..."u8);
}
using var child = ImUtf8.Child("GenderRace"u8,
new Vector2(width, ImGui.GetContentRegionMax().Y - Im.Style.FrameHeight - Im.Style.WindowPadding.Y), true);
using var child = Im.Child.Begin("GenderRace"u8,
new Vector2(width, Im.ContentRegion.Maximum.Y - Im.Style.FrameHeight - Im.Style.WindowPadding.Y), true);
if (!child)
return;
@ -52,9 +47,9 @@ public partial class ModEditWindow
if (!name.Contains(_pbdData.RaceCodeFilter) && !raceCode.Contains(_pbdData.RaceCodeFilter))
continue;
using var id = ImUtf8.PushId(index);
using var id = Im.Id.Push(index);
using var color = ImGuiColor.Text.Push(Im.Style[ImGuiColor.TextDisabled], deformer.RacialDeformer.IsEmpty);
if (ImUtf8.Selectable(name, deformer.GenderRace == _pbdData.SelectedRaceCode))
if (Im.Selectable(name, deformer.GenderRace == _pbdData.SelectedRaceCode))
{
_pbdData.SelectedRaceCode = deformer.GenderRace;
_pbdData.SelectedDeformer = deformer.RacialDeformer;
@ -62,32 +57,32 @@ public partial class ModEditWindow
Im.Line.Same();
color.Push(ImGuiColor.Text, metaColor);
ImUtf8.TextRightAligned(raceCode);
ImEx.TextRightAligned(raceCode);
}
}
private void DrawBoneSelector()
{
using var group = ImUtf8.Group();
using var group = Im.Group();
var width = 200 * Im.Style.GlobalScale;
using (ImStyleSingle.FrameRounding.Push(0)
.Push(ImStyleDouble.ItemSpacing, Vector2.Zero))
{
Im.Item.SetNextWidth(width);
ImUtf8.InputText("##boneFilter"u8, ref _pbdData.BoneFilter, "Filter..."u8);
Im.Input.Text("##boneFilter"u8, ref _pbdData.BoneFilter, "Filter..."u8);
}
using var child = ImUtf8.Child("Bone"u8,
new Vector2(width, ImGui.GetContentRegionMax().Y - Im.Style.FrameHeight - Im.Style.WindowPadding.Y), true);
using var child = Im.Child.Begin("Bone"u8,
new Vector2(width, Im.ContentRegion.Maximum.Y - Im.Style.FrameHeight - Im.Style.WindowPadding.Y), true);
if (!child)
return;
if (_pbdData.SelectedDeformer == null)
if (_pbdData.SelectedDeformer is null)
return;
if (_pbdData.SelectedDeformer.IsEmpty)
{
ImUtf8.Text("<Empty>"u8);
Im.Text("<Empty>"u8);
}
else
{
@ -97,7 +92,7 @@ public partial class ModEditWindow
b => b.Contains(_pbdData.BoneFilter), bone
=>
{
if (ImUtf8.Selectable(bone, bone == _pbdData.SelectedBone))
if (Im.Selectable(bone, bone == _pbdData.SelectedBone))
_pbdData.SelectedBone = bone;
});
ImGuiClip.DrawEndDummy(remainder, height);
@ -106,32 +101,32 @@ public partial class ModEditWindow
private bool DrawBoneData(PbdTab tab, bool disabled)
{
using var child = ImUtf8.Child("Data"u8,
Im.ContentRegion.Available with { Y = ImGui.GetContentRegionMax().Y - Im.Style.WindowPadding.Y }, true);
using var child = Im.Child.Begin("Data"u8,
Im.ContentRegion.Available with { Y = Im.ContentRegion.Maximum.Y - Im.Style.WindowPadding.Y }, true);
if (!child)
return false;
if (_pbdData.SelectedBone == null)
if (_pbdData.SelectedBone is null)
return false;
if (!_pbdData.SelectedDeformer!.DeformMatrices.TryGetValue(_pbdData.SelectedBone, out var matrix))
return false;
var width = UiBuilder.MonoFont.GetCharAdvance('0') * 12 + Im.Style.FramePadding.X * 2;
var width = Im.Font.Mono.GetCharacterAdvance('0') * 12 + Im.Style.FramePadding.X * 2;
var dummyHeight = Im.Style.TextHeight / 2;
var ret = DrawAddNewBone(tab, disabled, matrix, width);
ImUtf8.Dummy(0, dummyHeight);
Im.Dummy(0, dummyHeight);
Im.Separator();
ImUtf8.Dummy(0, dummyHeight);
Im.Dummy(0, dummyHeight);
ret |= DrawDeformerMatrix(disabled, matrix, width);
ImUtf8.Dummy(0, dummyHeight);
Im.Dummy(0, dummyHeight);
ret |= DrawCopyPasteButtons(disabled, matrix, width);
ImUtf8.Dummy(0, dummyHeight);
Im.Dummy(0, dummyHeight);
Im.Separator();
ImUtf8.Dummy(0, dummyHeight);
Im.Dummy(0, dummyHeight);
ret |= DrawDecomposedData(disabled, matrix, width);
return ret;
@ -140,23 +135,23 @@ public partial class ModEditWindow
private bool DrawAddNewBone(PbdTab tab, bool disabled, in TransformMatrix matrix, float width)
{
var ret = false;
ImUtf8.TextFrameAligned("Copy the values of the bone "u8);
Im.Line.Same(0, 0);
ImEx.TextFrameAligned("Copy the values of the bone "u8);
Im.Line.NoSpacing();
using (ImGuiColor.Text.Push(ColorId.NewMod.Value()))
{
ImUtf8.TextFrameAligned(_pbdData.SelectedBone);
ImEx.TextFrameAligned(_pbdData.SelectedBone!);
}
Im.Line.Same(0, 0);
ImUtf8.TextFrameAligned(" to a new bone of name"u8);
Im.Line.NoSpacing();
ImEx.TextFrameAligned(" to a new bone of name"u8);
var fullWidth = width * 4 + Im.Style.ItemSpacing.X * 3;
Im.Item.SetNextWidth(fullWidth);
ImUtf8.InputText("##newBone"u8, ref _pbdData.NewBoneName, "New Bone Name..."u8);
ImUtf8.TextFrameAligned("for all races that have a corresponding bone."u8);
Im.Line.Same(0, fullWidth - width - ImGui.GetItemRectSize().X);
if (ImUtf8.ButtonEx("Apply"u8, ""u8, new Vector2(width, 0),
disabled || _pbdData.NewBoneName.Length == 0 || _pbdData.SelectedBone == null))
Im.Input.Text("##newBone"u8, ref _pbdData.NewBoneName, "New Bone Name..."u8);
ImEx.TextFrameAligned("for all races that have a corresponding bone."u8);
Im.Line.Same(0, fullWidth - width - Im.Item.Size.X);
if (ImEx.Button("Apply"u8, new Vector2(width, 0), StringU8.Empty,
disabled || _pbdData.NewBoneName.Length is 0 || _pbdData.SelectedBone is null))
{
foreach (var deformer in tab.File.Deformers)
{
@ -167,8 +162,7 @@ public partial class ModEditWindow
&& deformer.RacialDeformer.DeformMatrices.TryGetValue(_pbdData.NewBoneName, out var newBoneMatrix)
&& !newBoneMatrix.Equals(existingMatrix))
Penumbra.Messager.AddMessage(new Luna.Notification(
$"Could not add deformer matrix to {deformer.GenderRace.ToName()}, Bone {_pbdData.NewBoneName} because it already has a deformer that differs from the intended one.",
NotificationType.Warning));
$"Could not add deformer matrix to {deformer.GenderRace.ToName()}, Bone {_pbdData.NewBoneName} because it already has a deformer that differs from the intended one."));
else
ret = true;
}
@ -176,31 +170,30 @@ public partial class ModEditWindow
_pbdData.NewBoneName = string.Empty;
}
if (ImUtf8.ButtonEx("Copy Values to Single New Bone Entry"u8, ""u8, new Vector2(fullWidth, 0),
disabled || _pbdData.NewBoneName.Length == 0 || _pbdData.SelectedDeformer!.DeformMatrices.ContainsKey(_pbdData.NewBoneName)))
if (ImEx.Button("Copy Values to Single New Bone Entry"u8, new Vector2(fullWidth, 0), StringU8.Empty,
disabled || _pbdData.NewBoneName.Length is 0 || _pbdData.SelectedDeformer!.DeformMatrices.ContainsKey(_pbdData.NewBoneName)))
{
_pbdData.SelectedDeformer!.DeformMatrices[_pbdData.NewBoneName] = matrix;
ret = true;
_pbdData.NewBoneName = string.Empty;
}
return ret;
}
private bool DrawDeformerMatrix(bool disabled, in TransformMatrix matrix, float width)
{
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
using var _ = ImRaii.Disabled(disabled);
using var font = Im.Font.PushMono();
using var _ = Im.Disabled(disabled);
var ret = false;
for (var i = 0; i < 3; ++i)
{
for (var j = 0; j < 4; ++j)
{
using var id = ImUtf8.PushId(i * 4 + j);
using var id = Im.Id.Push(i * 4 + j);
Im.Item.SetNextWidth(width);
var tmp = matrix[i, j];
if (ImUtf8.InputScalar(""u8, ref tmp, "% 12.8f"u8))
if (Im.Input.Scalar(StringU8.Empty, ref tmp, "% 12.8f"u8))
{
ret = true;
_pbdData.SelectedDeformer!.DeformMatrices[_pbdData.SelectedBone!] = matrix.ChangeValue(i, j, tmp);
@ -218,13 +211,13 @@ public partial class ModEditWindow
private bool DrawCopyPasteButtons(bool disabled, in TransformMatrix matrix, float width)
{
var size = new Vector2(width, 0);
if (ImUtf8.Button("Copy Values"u8, size))
if (Im.Button("Copy Values"u8, size))
_pbdData.CopiedMatrix = matrix;
Im.Line.Same();
var ret = false;
if (ImUtf8.ButtonEx("Paste Values"u8, ""u8, size, disabled || !_pbdData.CopiedMatrix.HasValue))
if (ImEx.Button("Paste Values"u8, size, StringU8.Empty, disabled || !_pbdData.CopiedMatrix.HasValue))
{
_pbdData.SelectedDeformer!.DeformMatrices[_pbdData.SelectedBone!] = _pbdData.CopiedMatrix!.Value;
ret = true;
@ -234,7 +227,7 @@ public partial class ModEditWindow
Im.Line.Same();
if (modifier)
{
if (ImUtf8.ButtonEx("Delete"u8, "Delete this bone entry."u8, size, disabled))
if (ImEx.Button("Delete"u8, size, "Delete this bone entry."u8, disabled))
{
ret |= _pbdData.SelectedDeformer!.DeformMatrices.Remove(_pbdData.SelectedBone!);
_pbdData.SelectedBone = null;
@ -242,7 +235,7 @@ public partial class ModEditWindow
}
else
{
ImUtf8.ButtonEx("Delete"u8, $"Delete this bone entry. Hold {_config.DeleteModModifier} to delete.", size, true);
ImEx.Button("Delete"u8, size, $"Delete this bone entry. Hold {_config.DeleteModModifier} to delete.", true);
}
return ret;
@ -256,56 +249,56 @@ public partial class ModEditWindow
if (!matrix.TryDecompose(out var scale, out var rotation, out var translation))
return false;
using (ImUtf8.Group())
using (Im.Group())
{
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
using var _ = ImRaii.Disabled(disabled);
using var font = Im.Font.PushMono();
using var _ = Im.Disabled(disabled);
Im.Item.SetNextWidth(width);
ret |= ImUtf8.InputScalar("##ScaleX"u8, ref scale.X, "% 12.8f"u8);
ret |= Im.Input.Scalar("##ScaleX"u8, ref scale.X, "% 12.8f"u8);
Im.Line.Same();
Im.Item.SetNextWidth(width);
ret |= ImUtf8.InputScalar("##ScaleY"u8, ref scale.Y, "% 12.8f"u8);
ret |= Im.Input.Scalar("##ScaleY"u8, ref scale.Y, "% 12.8f"u8);
Im.Line.Same();
Im.Item.SetNextWidth(width);
ret |= ImUtf8.InputScalar("##ScaleZ"u8, ref scale.Z, "% 12.8f"u8);
ret |= Im.Input.Scalar("##ScaleZ"u8, ref scale.Z, "% 12.8f"u8);
Im.Item.SetNextWidth(width);
ret |= ImUtf8.InputScalar("##TranslationX"u8, ref translation.X, "% 12.8f"u8);
ret |= Im.Input.Scalar("##TranslationX"u8, ref translation.X, "% 12.8f"u8);
Im.Line.Same();
Im.Item.SetNextWidth(width);
ret |= ImUtf8.InputScalar("##TranslationY"u8, ref translation.Y, "% 12.8f"u8);
ret |= Im.Input.Scalar("##TranslationY"u8, ref translation.Y, "% 12.8f"u8);
Im.Line.Same();
Im.Item.SetNextWidth(width);
ret |= ImUtf8.InputScalar("##TranslationZ"u8, ref translation.Z, "% 12.8f"u8);
ret |= Im.Input.Scalar("##TranslationZ"u8, ref translation.Z, "% 12.8f"u8);
Im.Item.SetNextWidth(width);
ret |= ImUtf8.InputScalar("##RotationR"u8, ref rotation.W, "% 12.8f"u8);
ret |= Im.Input.Scalar("##RotationR"u8, ref rotation.W, "% 12.8f"u8);
Im.Line.Same();
Im.Item.SetNextWidth(width);
ret |= ImUtf8.InputScalar("##RotationI"u8, ref rotation.X, "% 12.8f"u8);
ret |= Im.Input.Scalar("##RotationI"u8, ref rotation.X, "% 12.8f"u8);
Im.Line.Same();
Im.Item.SetNextWidth(width);
ret |= ImUtf8.InputScalar("##RotationJ"u8, ref rotation.Y, "% 12.8f"u8);
ret |= Im.Input.Scalar("##RotationJ"u8, ref rotation.Y, "% 12.8f"u8);
Im.Line.Same();
Im.Item.SetNextWidth(width);
ret |= ImUtf8.InputScalar("##RotationK"u8, ref rotation.Z, "% 12.8f"u8);
ret |= Im.Input.Scalar("##RotationK"u8, ref rotation.Z, "% 12.8f"u8);
}
Im.Line.Same();
using (ImUtf8.Group())
using (Im.Group())
{
ImUtf8.TextFrameAligned("Scale"u8);
ImUtf8.TextFrameAligned("Translation"u8);
ImUtf8.TextFrameAligned("Rotation (Quaternion, rijk)"u8);
ImEx.TextFrameAligned("Scale"u8);
ImEx.TextFrameAligned("Translation"u8);
ImEx.TextFrameAligned("Rotation (Quaternion, rijk)"u8);
}
if (ret)

View file

@ -1,9 +1,7 @@
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Luna;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.Mods.Editor;
using Penumbra.Mods.SubMods;
using Penumbra.String.Classes;
@ -35,7 +33,7 @@ public partial class ModEditWindow
private void DrawFileTab()
{
using var tab = ImRaii.TabItem("File Redirections");
using var tab = Im.TabBar.BeginItem("File Redirections"u8);
if (!tab)
return;
@ -47,7 +45,7 @@ public partial class ModEditWindow
else
DrawFileManagementNormal();
using var child = ImRaii.Child("##files", -Vector2.One, true);
using var child = Im.Child.Begin("##files"u8, Im.ContentRegion.Available, true);
if (!child)
return;
@ -62,7 +60,7 @@ public partial class ModEditWindow
var height = Im.Style.TextHeightWithSpacing + 2 * Im.Style.CellPadding.Y;
var skips = ImGuiClip.GetNecessarySkips(height);
using var table = Im.Table.Begin("##table"u8, 3, TableFlags.RowBackground | TableFlags.BordersInnerVertical, -Vector2.One);
using var table = Im.Table.Begin("##table"u8, 3, TableFlags.RowBackground | TableFlags.BordersInnerVertical, Im.ContentRegion.Available);
if (!table)
return;
@ -86,22 +84,21 @@ public partial class ModEditWindow
void DrawLine((string, string, string, uint) data)
{
using var id = ImRaii.PushId(idx++);
ImGui.TableNextColumn();
if (data.Item4 != 0)
ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, data.Item4);
using var id = Im.Id.Push(idx++);
if (data.Item4 is not 0)
Im.Table.SetBackgroundColor(TableBackgroundTarget.Cell, data.Item4);
ImGuiUtil.CopyOnClickSelectable(data.Item1);
ImGui.TableNextColumn();
if (data.Item4 != 0)
ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, data.Item4);
ImEx.CopyOnClickSelectable(data.Item1);
Im.Table.NextColumn();
if (data.Item4 is not 0)
Im.Table.SetBackgroundColor(TableBackgroundTarget.Cell, data.Item4);
ImGuiUtil.CopyOnClickSelectable(data.Item2);
ImGui.TableNextColumn();
if (data.Item4 != 0)
ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, data.Item4);
ImEx.CopyOnClickSelectable(data.Item2);
Im.Table.NextColumn();
if (data.Item4 is not 0)
Im.Table.SetBackgroundColor(TableBackgroundTarget.Cell, data.Item4);
ImGuiUtil.CopyOnClickSelectable(data.Item3);
ImEx.CopyOnClickSelectable(data.Item3);
}
bool Filter((string, string, string, uint) data)
@ -115,22 +112,21 @@ public partial class ModEditWindow
private void DrawFilesNormalMode()
{
using var list = Im.Table.Begin("##table"u8, 1);
if (!list)
using var table = Im.Table.Begin("##table"u8, 1);
if (!table)
return;
foreach (var (i, registry) in _editor.Files.Available.Index().Where(CheckFilter))
{
using var id = ImRaii.PushId(i);
ImGui.TableNextColumn();
using var id = Im.Id.Push(i);
table.NextColumn();
DrawSelectable(registry, i);
if (!_showGamePaths)
continue;
using var indent = ImRaii.PushIndent(50f);
using var indent = Im.Indent(50f);
for (var j = 0; j < registry.SubModUsage.Count; ++j)
{
var (subMod, gamePath) = registry.SubModUsage[j];
@ -154,11 +150,11 @@ public partial class ModEditWindow
_ => (null, 0),
};
if (text != null && Im.Item.Hovered())
if (text is not null && Im.Item.Hovered())
{
using var tt = ImUtf8.Tooltip();
using var c = ImRaii.DefaultColors();
ImUtf8.Text(string.Join('\n', text));
using var tt = Im.Tooltip.Begin();
using var c = ImGuiColor.Text.PushDefault();
Im.Text(StringU8.Join((byte) '\n', text));
}
@ -193,12 +189,12 @@ public partial class ModEditWindow
}
if (Im.Item.RightClicked())
ImUtf8.OpenPopup("context"u8);
Im.Popup.Open("context"u8);
var rightText = DrawFileTooltip(registry, color);
Im.Line.Same();
ImGuiUtil.RightAlign(rightText);
ImEx.TextRightAligned(rightText);
}
DrawContextMenu(registry, i);
@ -206,16 +202,16 @@ public partial class ModEditWindow
private void DrawContextMenu(FileRegistry registry, int i)
{
using var context = ImUtf8.Popup("context"u8);
using var context = Im.Popup.Begin("context"u8);
if (!context)
return;
if (ImUtf8.Selectable("Copy Full File Path"))
ImUtf8.SetClipboardText(registry.File.FullName);
if (Im.Selectable("Copy Full File Path"u8))
Im.Clipboard.Set(registry.File.FullName);
using (ImRaii.Disabled(registry.CurrentUsage == 0))
using (Im.Disabled(registry.CurrentUsage is 0))
{
if (ImUtf8.Selectable("Copy Game Paths"u8))
if (Im.Selectable("Copy Game Paths"u8))
{
_cutPaths.Clear();
for (var j = 0; j < registry.SubModUsage.Count; ++j)
@ -228,9 +224,9 @@ public partial class ModEditWindow
}
}
using (ImRaii.Disabled(registry.CurrentUsage == 0))
using (Im.Disabled(registry.CurrentUsage is 0))
{
if (ImUtf8.Selectable("Cut Game Paths"u8))
if (Im.Selectable("Cut Game Paths"u8))
{
_cutPaths.Clear();
for (var j = 0; j < registry.SubModUsage.Count; ++j)
@ -244,9 +240,9 @@ public partial class ModEditWindow
}
}
using (ImRaii.Disabled(_cutPaths.Count == 0))
using (Im.Disabled(_cutPaths.Count is 0))
{
if (ImUtf8.Selectable("Paste Game Paths"u8))
if (Im.Selectable("Paste Game Paths"u8))
foreach (var path in _cutPaths)
_editor.FileEditor.SetGamePath(_editor.Option!, i, -1, path);
}
@ -254,21 +250,21 @@ public partial class ModEditWindow
private void PrintGamePath(int i, int j, FileRegistry registry, IModDataContainer _, Utf8GamePath gamePath)
{
using var id = ImRaii.PushId(j);
ImGui.TableNextColumn();
using var id = Im.Id.Push(j);
Im.Table.NextColumn();
var tmp = _fileIdx == i && _pathIdx == j ? _gamePathEdit : gamePath.ToString();
var pos = ImGui.GetCursorPosX() - Im.Style.FrameHeight;
var pos = Im.Cursor.X - Im.Style.FrameHeight;
Im.Item.SetNextWidth(-1);
if (ImGui.InputText(string.Empty, ref tmp, Utf8GamePath.MaxGamePathLength))
if (Im.Input.Text(StringU8.Empty, ref tmp, maxLength:Utf8GamePath.MaxGamePathLength))
{
_fileIdx = i;
_pathIdx = j;
_gamePathEdit = tmp;
}
ImGuiUtil.HoverTooltip("Clear completely to remove the path from this mod.");
Im.Tooltip.OnHover("Clear completely to remove the path from this mod."u8);
if (ImGui.IsItemDeactivatedAfterEdit())
if (Im.Item.DeactivatedAfterEdit)
{
if (Utf8GamePath.FromString(_gamePathEdit, out var path))
_editor.FileEditor.SetGamePath(_editor.Option!, _fileIdx, _pathIdx, path);
@ -282,19 +278,14 @@ public partial class ModEditWindow
|| !path.IsEmpty && !path.Equals(gamePath) && !_editor.FileEditor.CanAddGamePath(path)))
{
Im.Line.Same();
ImGui.SetCursorPosX(pos);
using var font = ImRaii.PushFont(UiBuilder.IconFont);
ImGuiUtil.TextColored(0xFF0000FF, FontAwesomeIcon.TimesCircle.ToIconString());
Im.Cursor.X = pos;
ImEx.Icon.Draw(FontAwesomeIcon.TimesCircle.Icon(), Rgba32.Red);
}
else if (tmp.Length > 0 && Path.GetExtension(tmp) != registry.File.Extension)
{
Im.Line.Same();
ImGui.SetCursorPosX(pos);
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGuiUtil.TextColored(0xFF00B0B0, FontAwesomeIcon.ExclamationCircle.ToIconString());
}
Im.Cursor.X = pos;
ImEx.Icon.Draw(FontAwesomeIcon.ExclamationCircle.Icon(), new Rgba32(0xFF00B0B0));
Im.Tooltip.OnHover("The game path and the file do not have the same extension."u8);
}
}
@ -302,16 +293,16 @@ public partial class ModEditWindow
private void PrintNewGamePath(int i, FileRegistry registry, IModDataContainer _)
{
var tmp = _fileIdx == i && _pathIdx == -1 ? _gamePathEdit : string.Empty;
var pos = ImGui.GetCursorPosX() - Im.Style.FrameHeight;
var pos = Im.Cursor.X - Im.Style.FrameHeight;
Im.Item.SetNextWidth(-1);
if (ImGui.InputTextWithHint("##new", "Add New Path...", ref tmp, Utf8GamePath.MaxGamePathLength))
if (Im.Input.Text("##new"u8, ref tmp, "Add New Path..."u8, maxLength: Utf8GamePath.MaxGamePathLength))
{
_fileIdx = i;
_pathIdx = -1;
_gamePathEdit = tmp;
}
if (ImGui.IsItemDeactivatedAfterEdit())
if (Im.Item.DeactivatedAfterEdit)
{
if (Utf8GamePath.FromString(_gamePathEdit, out var path) && !path.IsEmpty)
_editor.FileEditor.SetGamePath(_editor.Option!, _fileIdx, _pathIdx, path);
@ -325,19 +316,14 @@ public partial class ModEditWindow
|| !path.IsEmpty && !_editor.FileEditor.CanAddGamePath(path)))
{
Im.Line.Same();
ImGui.SetCursorPosX(pos);
using var font = ImRaii.PushFont(UiBuilder.IconFont);
ImGuiUtil.TextColored(0xFF0000FF, FontAwesomeIcon.TimesCircle.ToIconString());
Im.Cursor.X = pos;
ImEx.Icon.Draw(FontAwesomeIcon.TimesCircle.Icon(), Rgba32.Red);
}
else if (tmp.Length > 0 && Path.GetExtension(tmp) != registry.File.Extension)
{
Im.Line.Same();
ImGui.SetCursorPosX(pos);
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGuiUtil.TextColored(0xFF00B0B0, FontAwesomeIcon.ExclamationCircle.ToIconString());
}
Im.Cursor.X = pos;
ImEx.Icon.Draw(FontAwesomeIcon.ExclamationCircle.Icon(), new Rgba32(0xFF00B0B0));
Im.Tooltip.OnHover("The game path and the file do not have the same extension."u8);
}
}
@ -347,41 +333,40 @@ public partial class ModEditWindow
Im.Line.New();
using var spacing = ImStyleDouble.ItemSpacing.Push(new Vector2(3 * Im.Style.GlobalScale, 0));
Im.Item.SetNextWidth(30 * Im.Style.GlobalScale);
ImGui.DragInt("##skippedFolders", ref _folderSkip, 0.01f, 0, 10);
ImGuiUtil.HoverTooltip("Skip the first N folders when automatically constructing the game path from the file path.");
Im.Item.SetNextWidthScaled(30);
Im.Drag("##skippedFolders"u8, ref _folderSkip, 0, 10, 0.01f);
Im.Tooltip.OnHover("Skip the first N folders when automatically constructing the game path from the file path."u8);
Im.Line.Same();
spacing.Pop();
if (ImGui.Button("Add Paths"))
if (Im.Button("Add Paths"u8))
_editor.FileEditor.AddPathsToSelected(_editor.Option!, _editor.Files.Available.Where(_selectedFiles.Contains), _folderSkip);
ImGuiUtil.HoverTooltip(
"Add the file path converted to a game path to all selected files for the current option, optionally skipping the first N folders.");
Im.Tooltip.OnHover("Add the file path converted to a game path to all selected files for the current option, optionally skipping the first N folders."u8);
Im.Line.Same();
if (ImGui.Button("Remove Paths"))
if (Im.Button("Remove Paths"u8))
_editor.FileEditor.RemovePathsFromSelected(_editor.Option!, _editor.Files.Available.Where(_selectedFiles.Contains));
ImGuiUtil.HoverTooltip("Remove all game paths associated with the selected files in the current option.");
Im.Tooltip.OnHover("Remove all game paths associated with the selected files in the current option."u8);
Im.Line.Same();
var active = _config.DeleteModModifier.IsActive();
var tt =
"Delete all selected files entirely from your filesystem, but not their file associations in the mod.\n!!!This can not be reverted!!!";
if (_selectedFiles.Count == 0)
if (_selectedFiles.Count is 0)
tt += "\n\nNo files selected.";
else if (!active)
tt += $"\n\nHold {_config.DeleteModModifier} to delete.";
if (ImGuiUtil.DrawDisabledButton("Delete Selected Files", Vector2.Zero, tt, _selectedFiles.Count == 0 || !active))
if (ImEx.Button("Delete Selected Files"u8, Vector2.Zero, tt, _selectedFiles.Count is 0 || !active))
_editor.FileEditor.DeleteFiles(_editor.Mod!, _editor.Option!, _editor.Files.Available.Where(_selectedFiles.Contains));
Im.Line.Same();
var changes = _editor.FileEditor.Changes;
tt = changes ? "Apply the current file setup to the currently selected option." : "No changes made.";
if (ImGuiUtil.DrawDisabledButton("Apply Changes", Vector2.Zero, tt, !changes))
var tt2 = changes ? "Apply the current file setup to the currently selected option."u8 : "No changes made."u8;
if (ImEx.Button("Apply Changes"u8, Vector2.Zero, tt2, !changes))
{
var failedFiles = _editor.FileEditor.Apply(_editor.Mod!, _editor.Option!);
if (failedFiles > 0)
@ -390,42 +375,42 @@ public partial class ModEditWindow
Im.Line.Same();
var label = changes ? "Revert Changes" : "Reload Files";
var length = new Vector2(ImGui.CalcTextSize("Revert Changes").X, 0);
if (ImGui.Button(label, length))
var label = changes ? "Revert Changes"u8 : "Reload Files"u8;
var length = new Vector2(Im.Font.CalculateSize("Revert Changes"u8).X, 0);
if (Im.Button(label, length))
_editor.FileEditor.Revert(_editor.Mod!, _editor.Option!);
ImGuiUtil.HoverTooltip("Revert all revertible changes since the last file or option reload or data refresh.");
Im.Tooltip.OnHover("Revert all revertible changes since the last file or option reload or data refresh."u8);
Im.Line.Same();
ImGui.Checkbox("Overview Mode", ref _overviewMode);
Im.Checkbox("Overview Mode"u8, ref _overviewMode);
}
private void DrawFileManagementNormal()
{
Im.Item.SetNextWidth(250 * Im.Style.GlobalScale);
Im.Item.SetNextWidthScaled(250);
Im.Input.Text("##filter"u8, ref _fileFilter, "Filter paths..."u8);
Im.Line.Same();
ImGui.Checkbox("Show Game Paths", ref _showGamePaths);
Im.Checkbox("Show Game Paths"u8, ref _showGamePaths);
Im.Line.Same();
if (ImGui.Button("Unselect All"))
if (Im.Button("Unselect All"u8))
_selectedFiles.Clear();
Im.Line.Same();
if (ImGui.Button("Select Visible"))
if (Im.Button("Select Visible"u8))
_selectedFiles.UnionWith(_editor.Files.Available.Where(CheckFilter));
Im.Line.Same();
if (ImGui.Button("Select Unused"))
if (Im.Button("Select Unused"u8))
_selectedFiles.UnionWith(_editor.Files.Available.Where(f => f.SubModUsage.Count == 0));
Im.Line.Same();
if (ImGui.Button("Select Used Here"))
if (Im.Button("Select Used Here"u8))
_selectedFiles.UnionWith(_editor.Files.Available.Where(f => f.CurrentUsage > 0));
Im.Line.Same();
ImGuiUtil.RightAlign($"{_selectedFiles.Count} / {_editor.Files.Available.Count} Files Selected");
ImEx.TextRightAligned($"{_selectedFiles.Count} / {_editor.Files.Available.Count} Files Selected");
}
private void DrawFileManagementOverview()

View file

@ -1,8 +1,5 @@
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using ImSharp;
using OtterGui.Raii;
using OtterGui.Text;
using Luna;
using Penumbra.UI.AdvancedWindow.Materials;
namespace Penumbra.UI.AdvancedWindow;
@ -21,10 +18,10 @@ public partial class ModEditWindow
private void DrawMaterialReassignmentTab()
{
if (_editor.Files.Mdl.Count == 0)
if (_editor.Files.Mdl.Count is 0)
return;
using var tab = ImUtf8.TabItem("Material Reassignment"u8);
using var tab = Im.TabBar.BeginItem("Material Reassignment"u8);
if (!tab)
return;
@ -32,43 +29,43 @@ public partial class ModEditWindow
MaterialSuffix.Draw(_editor, ImEx.ScaledVector(175, 0));
Im.Line.New();
using var child = ImUtf8.Child("##mdlFiles"u8, -Vector2.One, true);
using var child = Im.Child.Begin("##mdlFiles"u8, Im.ContentRegion.Available, true);
if (!child)
return;
using var table = Im.Table.Begin("##files"u8, 4, TableFlags.RowBackground | TableFlags.SizingFixedFit, -Vector2.One);
using var table = Im.Table.Begin("##files"u8, 4, TableFlags.RowBackground | TableFlags.SizingFixedFit, Im.ContentRegion.Available);
if (!table)
return;
foreach (var (idx, info) in _editor.MdlMaterialEditor.ModelFiles.Index())
{
using var id = ImRaii.PushId(idx);
ImGui.TableNextColumn();
if (ImUtf8.IconButton(FontAwesomeIcon.Save, "Save the changed mdl file.\nUse at own risk!"u8, disabled: !info.Changed))
using var id = Im.Id.Push(idx);
table.NextColumn();
if (ImEx.Icon.Button(LunaStyle.SaveIcon, "Save the changed mdl file.\nUse at own risk!"u8, !info.Changed))
info.Save(_editor.Compactor);
ImGui.TableNextColumn();
if (ImUtf8.IconButton(FontAwesomeIcon.Recycle, "Restore current changes to default."u8, disabled: !info.Changed))
table.NextColumn();
if (ImEx.Icon.Button(LunaStyle.RefreshIcon, "Restore current changes to default."u8, !info.Changed))
info.Restore();
ImGui.TableNextColumn();
ImUtf8.Text(info.Path.InternalName.Span[(Mod!.ModPath.FullName.Length + 1)..]);
ImGui.TableNextColumn();
Im.Item.SetNextWidth(400 * Im.Style.GlobalScale);
table.NextColumn();
Im.Text(info.Path.InternalName.Span[(Mod!.ModPath.FullName.Length + 1)..]);
table.NextColumn();
Im.Item.SetNextWidthScaled(400);
var tmp = info.CurrentMaterials[0];
if (ImUtf8.InputText("##0"u8, ref tmp))
if (Im.Input.Text("##0"u8, ref tmp))
info.SetMaterial(tmp, 0);
for (var i = 1; i < info.Count; ++i)
{
using var id2 = ImUtf8.PushId(i);
ImGui.TableNextColumn();
ImGui.TableNextColumn();
ImGui.TableNextColumn();
ImGui.TableNextColumn();
Im.Item.SetNextWidth(400 * Im.Style.GlobalScale);
using var id2 = Im.Id.Push(i);
table.NextColumn();
table.NextColumn();
table.NextColumn();
table.NextColumn();
Im.Item.SetNextWidthScaled(400);
tmp = info.CurrentMaterials[i];
if (ImUtf8.InputText(""u8, ref tmp))
if (Im.Input.Text(""u8, ref tmp))
info.SetMaterial(tmp, i);
}
}

View file

@ -1,9 +1,5 @@
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using ImSharp;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using Luna;
using Penumbra.Api.Api;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
@ -19,7 +15,7 @@ public partial class ModEditWindow
private void DrawMetaTab()
{
using var tab = ImUtf8.TabItem("Meta Manipulations"u8);
using var tab = Im.TabBar.BeginItem("Meta Manipulations"u8);
if (!tab)
return;
@ -28,12 +24,12 @@ public partial class ModEditWindow
var setsEqual = !_editor.MetaEditor.Changes;
var tt = setsEqual ? "No changes staged."u8 : "Apply the currently staged changes to the option."u8;
Im.Line.New();
if (ImUtf8.ButtonEx("Apply Changes"u8, tt, Vector2.Zero, setsEqual))
if (ImEx.Button("Apply Changes"u8, Vector2.Zero, tt, setsEqual))
_editor.MetaEditor.Apply(_editor.Option!);
Im.Line.Same();
tt = setsEqual ? "No changes staged."u8 : "Revert all currently staged changes."u8;
if (ImUtf8.ButtonEx("Revert Changes"u8, tt, Vector2.Zero, setsEqual))
if (ImEx.Button("Revert Changes"u8, Vector2.Zero, tt, setsEqual))
_editor.MetaEditor.Load(_editor.Mod!, _editor.Option!);
Im.Line.Same();
@ -41,17 +37,17 @@ public partial class ModEditWindow
Im.Line.Same();
SetFromClipboardButton();
Im.Line.Same();
CopyToClipboardButton("Copy all current manipulations to clipboard.", _iconSize, _editor.MetaEditor);
CopyToClipboardButton("Copy all current manipulations to clipboard."u8, _iconSize, _editor.MetaEditor);
Im.Line.Same();
if (ImUtf8.Button("Write as TexTools Files"u8))
if (Im.Button("Write as TexTools Files"u8))
_metaFileManager.WriteAllTexToolsMeta(Mod!);
Im.Line.Same();
if (ImUtf8.ButtonEx("Remove All Default-Values"u8, "Delete any entries from all lists that set the value to its default value."u8))
if (ImEx.Button("Remove All Default-Values"u8, "Delete any entries from all lists that set the value to its default value."u8))
_editor.MetaEditor.DeleteDefaultValues();
Im.Line.Same();
DrawAtchDragDrop();
using var child = ImRaii.Child("##meta", -Vector2.One, true);
using var child = Im.Child.Begin("##meta"u8, Im.ContentRegion.Available, true);
if (!child)
return;
@ -75,23 +71,23 @@ public partial class ModEditWindow
if (gr is GenderRace.Unknown)
return false;
ImUtf8.Text($"Dragging .atch for {gr.ToName()}...");
Im.Text($"Dragging .atch for {gr.ToName()}...");
return true;
});
var hasAtch = _editor.Files.Atch.Count > 0;
if (ImUtf8.ButtonEx("Import .atch"u8,
if (ImEx.Button("Import .atch"u8, Vector2.Zero,
_dragDropManager.IsDragging
? ""u8
: hasAtch
? "Drag a .atch file containing its race code in the path here to import its values.\n\nClick to select an .atch file from the mod."u8
: "Drag a .atch file containing its race code in the path here to import its values."u8, default,
: "Drag a .atch file containing its race code in the path here to import its values."u8,
!_dragDropManager.IsDragging && !hasAtch)
&& hasAtch)
ImUtf8.OpenPopup("##atchPopup"u8);
Im.Popup.Open("##atchPopup"u8);
if (_dragDropManager.CreateImGuiTarget("atchDrag", out var files, out _) && files.FirstOrDefault() is { } file)
_metaDrawers.Atch.ImportFile(file);
using var popup = ImUtf8.Popup("##atchPopup"u8);
using var popup = Im.Popup.Begin("##atchPopup"u8);
if (!popup)
return;
@ -103,7 +99,7 @@ public partial class ModEditWindow
foreach (var atchFile in _editor.Files.Atch)
{
if (ImUtf8.Selectable(atchFile.RelPath.Path.Span) && atchFile.File.Exists)
if (Im.Selectable(atchFile.RelPath.Path.Span) && atchFile.File.Exists)
_metaDrawers.Atch.ImportFile(atchFile.File.FullName);
}
}
@ -114,9 +110,9 @@ public partial class ModEditWindow
if (drawer == null)
return;
var oldPos = ImGui.GetCursorPosY();
var header = ImUtf8.CollapsingHeader($"{_editor.MetaEditor.GetCount(type)} {drawer.Label}");
DrawOtherOptionData(type, oldPos, ImGui.GetCursorPos());
var oldPos = Im.Cursor.Y;
var header = Im.Tree.Header($"{_editor.MetaEditor.GetCount(type)} {drawer.Label}");
DrawOtherOptionData(type, oldPos, Im.Cursor.Position);
if (!header)
return;
@ -126,7 +122,7 @@ public partial class ModEditWindow
private static void DrawTable(IMetaDrawer drawer)
{
const TableFlags flags = TableFlags.RowBackground | TableFlags.SizingFixedFit | TableFlags.BordersInnerVertical;
using var table = Im.Table.Begin(drawer.Label, drawer.NumColumns, flags);
using var table = Im.Table.Begin(drawer.Label, drawer.NumColumns, flags);
if (!table)
return;
@ -140,35 +136,35 @@ public partial class ModEditWindow
if (otherOptionData.TotalCount <= 0)
return;
var text = $"{otherOptionData.TotalCount} Edits in other Options";
var size = ImGui.CalcTextSize(text).X;
ImGui.SetCursorPos(new Vector2(Im.ContentRegion.Available.X - size, oldPos + Im.Style.FramePadding.Y));
Utf8StringHandler<TextStringHandlerBuffer> text = $"{otherOptionData.TotalCount} Edits in other Options";
var size = Im.Font.CalculateSize(ref text).X;
Im.Cursor.Position = new Vector2(Im.ContentRegion.Available.X - size, oldPos + Im.Style.FramePadding.Y);
Im.Text(text, ColorId.RedundantAssignment.Value().FullAlpha());
if (Im.Item.Hovered())
{
using var tt = ImUtf8.Tooltip();
using var tt = Im.Tooltip.Begin();
foreach (var name in otherOptionData)
ImUtf8.Text(name);
Im.Text(name);
}
ImGui.SetCursorPos(newPos);
Im.Cursor.Position = newPos;
}
private static void CopyToClipboardButton(string tooltip, Vector2 iconSize, MetaDictionary manipulations)
private static void CopyToClipboardButton(ReadOnlySpan<byte> tooltip, Vector2 iconSize, MetaDictionary manipulations)
{
if (!ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Clipboard.ToIconString(), iconSize, tooltip, false, true))
if (!ImEx.Icon.Button(LunaStyle.ToClipboardIcon, tooltip, iconSize))
return;
var text = Functions.ToCompressedBase64(manipulations, 0);
var text = CompressionFunctions.ToCompressedBase64(manipulations, 0);
if (text.Length > 0)
ImGui.SetClipboardText(text);
Im.Clipboard.Set(text);
}
private void AddFromClipboardButton()
{
if (ImUtf8.Button("Add from Clipboard"u8))
if (Im.Button("Add from Clipboard"u8))
{
var clipboard = ImGuiUtil.GetClipboardText();
var clipboard = Im.Clipboard.GetUtf16();
if (MetaApi.ConvertManips(clipboard, out var manips, out _))
{
@ -183,9 +179,9 @@ public partial class ModEditWindow
private void SetFromClipboardButton()
{
if (ImUtf8.Button("Set from Clipboard"u8))
if (Im.Button("Set from Clipboard"u8))
{
var clipboard = ImGuiUtil.GetClipboardText();
var clipboard = Im.Clipboard.GetUtf16();
if (MetaApi.ConvertManips(clipboard, out var manips, out _))
{
_editor.MetaEditor.SetTo(manips);

View file

@ -1,12 +1,8 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using ImSharp;
using Lumina.Data.Parsing;
using Luna;
using OtterGui;
using OtterGui.Custom;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.GameData;
using Penumbra.GameData.Files;
@ -75,7 +71,7 @@ public partial class ModEditWindow
ret |= DrawModelMaterialDetails(tab, disabled);
if (ImGui.CollapsingHeader($"Meshes ({data.LastFile.Meshes.Length})###meshes"))
if (Im.Tree.Header($"Meshes ({data.LastFile.Meshes.Length})###meshes"))
for (var i = 0; i < data.LastFile.LodCount; ++i)
ret |= DrawModelLodDetails(tab, i, disabled);
@ -89,9 +85,8 @@ public partial class ModEditWindow
if (disabled || tab.Mdl.Version is not MdlFile.V5)
return;
if (!ImUtf8.ButtonEx("Update MDL Version from V5 to V6"u8,
"Try using this if the bone weights of a pre-Dawntrail model seem wrong.\n\nThis is not revertible."u8,
new Vector2(-0.1f, 0), false, 0, new Rgba32(Colors.PressEnterWarningBg).Color))
if (!ImEx.Button("Update MDL Version from V5 to V6"u8, Colors.PressEnterWarningBg, default, Im.ContentRegion.Available with { Y = 0 },
"Try using this if the bone weights of a pre-Dawntrail model seem wrong.\n\nThis is not revertible."u8))
return;
tab.Mdl.ConvertV5ToV6();
@ -100,7 +95,7 @@ public partial class ModEditWindow
private void DrawImportExport(MdlTab tab, bool disabled)
{
if (!ImGui.CollapsingHeader("Import / Export"))
if (!Im.Tree.Header("Import / Export"u8))
return;
var childSize = new Vector2((Im.ContentRegion.Available.X - Im.Style.ItemSpacing.X) / 2, 0);
@ -115,7 +110,7 @@ public partial class ModEditWindow
private void DrawImport(MdlTab tab, Vector2 size, bool _1)
{
using var id = ImRaii.PushId("import");
using var id = Im.Id.Push("import"u8);
_dragDropManager.CreateImGuiSource("ModelDragDrop",
m => m.Extensions.Any(e => ValidModelExtensions.Contains(e.ToLowerInvariant())), m =>
@ -129,11 +124,10 @@ public partial class ModEditWindow
using (ImRaii.FramedGroup("Import", size, headerPreIcon: FontAwesomeIcon.FileImport))
{
ImGui.Checkbox("Keep current materials", ref tab.ImportKeepMaterials);
ImGui.Checkbox("Keep current attributes", ref tab.ImportKeepAttributes);
Im.Checkbox("Keep current materials"u8, ref tab.ImportKeepMaterials);
Im.Checkbox("Keep current attributes"u8, ref tab.ImportKeepAttributes);
if (ImGuiUtil.DrawDisabledButton("Import from glTF", Vector2.Zero, "Imports a glTF file, overriding the content of this mdl.",
tab.PendingIo))
if (ImEx.Button("Import from glTF"u8, Vector2.Zero, "Imports a glTF file, overriding the content of this mdl."u8, tab.PendingIo))
_fileDialog.OpenFilePicker("Load model from glTF.", "glTF{.gltf,.glb}", (success, paths) =>
{
if (success && paths.Count > 0)
@ -150,10 +144,10 @@ public partial class ModEditWindow
private void DrawExport(MdlTab tab, Vector2 size, bool _)
{
using var id = ImRaii.PushId("export");
using var id = Im.Id.Push("export"u8);
using var frame = ImRaii.FramedGroup("Export", size, headerPreIcon: FontAwesomeIcon.FileExport);
if (tab.GamePaths == null)
if (tab.GamePaths is null)
{
Im.Text(tab.IoExceptions.Count is 0 ? "Resolving model game paths."u8 : "Failed to resolve model game paths."u8);
@ -162,18 +156,17 @@ public partial class ModEditWindow
DrawGamePathCombo(tab);
ImGui.Checkbox("##exportGeneratedMissingBones", ref tab.ExportConfig.GenerateMissingBones);
Im.Line.Same();
ImGuiUtil.LabeledHelpMarker("Generate missing bones",
"WARNING: Enabling this option can result in unusable exported meshes.\n"
+ "It is primarily intended to allow exporting models weighted to bones that do not exist.\n"
+ "Before enabling, ensure dependencies are enabled in the current collection, and EST metadata is correctly configured.");
Im.Checkbox("##exportGeneratedMissingBones"u8, ref tab.ExportConfig.GenerateMissingBones);
LunaStyle.DrawAlignedHelpMarkerLabel("Generate Missing Bones"u8,
"WARNING: Enabling this option can result in unusable exported meshes.\n"u8
+ "It is primarily intended to allow exporting models weighted to bones that do not exist.\n"u8
+ "Before enabling, ensure dependencies are enabled in the current collection, and EST metadata is correctly configured."u8);
var gamePath = tab.GamePathIndex >= 0 && tab.GamePathIndex < tab.GamePaths.Count
? tab.GamePaths[tab.GamePathIndex]
: _customGamePath;
if (ImGuiUtil.DrawDisabledButton("Export to glTF", Vector2.Zero, "Exports this mdl file to glTF, for use in 3D authoring applications.",
if (ImEx.Button("Export to glTF"u8, Vector2.Zero, "Exports this mdl file to glTF, for use in 3D authoring applications."u8,
tab.PendingIo || gamePath.IsEmpty))
_fileDialog.OpenSavePicker("Save model as glTF.", ".glb", Path.GetFileNameWithoutExtension(gamePath.Filename().ToString()),
".glb", (valid, path) =>
@ -193,58 +186,58 @@ public partial class ModEditWindow
private static void DrawIoExceptions(MdlTab tab)
{
if (tab.IoExceptions.Count == 0)
if (tab.IoExceptions.Count is 0)
return;
var size = new Vector2(Im.ContentRegion.Available.X, 0);
var size = Im.ContentRegion.Available with { Y = 0 };
using var frame = ImRaii.FramedGroup("Exceptions", size, headerPreIcon: FontAwesomeIcon.TimesCircle,
borderColor: new Rgba32(Colors.RegexWarningBorder).Color);
var spaceAvail = Im.ContentRegion.Available.X - Im.Style.ItemSpacing.X - 100;
foreach (var (index, exception) in tab.IoExceptions.Index())
{
using var id = ImRaii.PushId(index);
var message = $"{exception.GetType().Name}: {exception.Message}";
var textSize = ImGui.CalcTextSize(message).X;
using var id = Im.Id.Push(index);
var message = new StringU8($"{exception.GetType().Name}: {exception.Message}");
var textSize = Im.Font.CalculateSize(message).X;
if (textSize > spaceAvail)
message = message[..(int)Math.Floor(message.Length * (spaceAvail / textSize))] + "...";
message = new StringU8($"{message.Span[..(int)Math.Floor(message.Length * (spaceAvail / textSize))]}...");
using var exceptionNode = ImRaii.TreeNode(message);
using var exceptionNode = Im.Tree.Node(message);
if (exceptionNode)
{
using var indent = ImRaii.PushIndent();
ImGuiUtil.TextWrapped(exception.ToString());
using var indent = Im.Indent();
Im.TextWrapped($"{exception}");
}
}
}
private static void DrawIoWarnings(MdlTab tab)
{
if (tab.IoWarnings.Count == 0)
if (tab.IoWarnings.Count is 0)
return;
var size = new Vector2(Im.ContentRegion.Available.X, 0);
var size = Im.ContentRegion.Available with { Y = 0 };
using var frame = ImRaii.FramedGroup("Warnings", size, headerPreIcon: FontAwesomeIcon.ExclamationCircle, borderColor: 0xFF40FFFF);
var spaceAvail = Im.ContentRegion.Available.X - Im.Style.ItemSpacing.X - 100;
foreach (var (index, warning) in tab.IoWarnings.Index())
{
using var id = ImRaii.PushId(index);
var textSize = ImGui.CalcTextSize(warning).X;
using var id = Im.Id.Push(index);
var textSize = Im.Font.CalculateSize(warning).X;
if (textSize <= spaceAvail)
{
ImRaii.TreeNode(warning, ImGuiTreeNodeFlags.Leaf).Dispose();
Im.Tree.Leaf(warning);
continue;
}
var firstLine = warning[..(int)Math.Floor(warning.Length * (spaceAvail / textSize))] + "...";
var firstLine = new StringU8($"{warning.AsSpan(0, (int)Math.Floor(warning.Length * (spaceAvail / textSize)))}...");
using var warningNode = ImRaii.TreeNode(firstLine);
using var warningNode = Im.Tree.Node(firstLine);
if (warningNode)
{
using var indent = ImRaii.PushIndent();
ImGuiUtil.TextWrapped(warning);
using var indent = Im.Indent();
Im.TextWrapped(warning);
}
}
}
@ -258,7 +251,7 @@ public partial class ModEditWindow
}
Im.Text("No associated game path detected. Valid game paths are currently necessary for exporting."u8);
if (!ImGui.InputTextWithHint("##customInput", "Enter custom game path...", ref _customPath, 256))
if (!Im.Input.Text("##customInput"u8, ref _customPath, "Enter custom game path..."u8))
return;
if (!Utf8GamePath.FromString(_customPath, out _customGamePath))
@ -268,10 +261,9 @@ public partial class ModEditWindow
/// <summary> I disliked the combo with only one selection so turn it into a button in that case. </summary>
private static void DrawComboButton(MdlTab tab)
{
const string label = "Game Path";
var preview = tab.GamePaths![tab.GamePathIndex].ToString();
var labelWidth = ImGui.CalcTextSize(label).X + Im.Style.ItemInnerSpacing.X;
var buttonWidth = Im.ContentRegion.Available.X - labelWidth - Im.Style.ItemSpacing.X;
var preview = tab.GamePaths![tab.GamePathIndex].Path.Span;
var labelWidth = Im.Font.CalculateSize("Game Path"u8).X + Im.Style.ItemInnerSpacing.X;
var buttonWidth = Im.ContentRegion.Available.X - labelWidth - Im.Style.ItemSpacing.X;
if (tab.GamePaths!.Count == 1)
{
using var style = ImStyleDouble.ButtonTextAlign.Push(new Vector2(0, 0.5f));
@ -279,18 +271,18 @@ public partial class ModEditWindow
.Push(ImGuiColor.ButtonHovered, Im.Style[ImGuiColor.FrameBackgroundHovered])
.Push(ImGuiColor.ButtonActive, Im.Style[ImGuiColor.FrameBackgroundActive]);
using var group = Im.Group();
ImGui.Button(preview, new Vector2(buttonWidth, 0));
Im.Button(preview, new Vector2(buttonWidth, 0));
Im.Line.Same(0, Im.Style.ItemInnerSpacing.X);
Im.Text("Game Path"u8);
}
else
{
Im.Item.SetNextWidth(buttonWidth);
using var combo = ImRaii.Combo("Game Path", preview);
using var combo = Im.Combo.Begin("Game Path"u8, preview);
if (combo.Success)
foreach (var (index, path) in tab.GamePaths.Index())
{
if (!ImGui.Selectable(path.ToString(), index == tab.GamePathIndex))
if (!Im.Selectable(path.Path.Span, index == tab.GamePathIndex))
continue;
tab.GamePathIndex = index;
@ -298,42 +290,39 @@ public partial class ModEditWindow
}
if (Im.Item.RightClicked())
ImGui.SetClipboardText(preview);
Im.Clipboard.Set(preview);
Im.Tooltip.OnHover("Right-Click to copy to clipboard."u8, HoveredFlags.AllowWhenDisabled);
}
private void DrawDocumentationLink(string address)
{
var text = "Documentation →"u8;
var framePadding = Im.Style.FramePadding;
var width = ImGui.CalcTextSize(text).X + framePadding.X * 2;
var text = "Documentation →"u8;
var width = Im.Font.CalculateButtonSize(text).X;
// Draw the link button. We set the background colour to transparent to mimic the look of a link.
using var color = ImGuiColor.Button.Push(Vector4.Zero);
SupportButton.Link(Penumbra.Messager, text, address, width, ""u8);
// Draw an underline for the text.
var lineStart = ImGui.GetItemRectMax();
lineStart -= framePadding;
var lineEnd = lineStart with { X = ImGui.GetItemRectMin().X + framePadding.X };
ImGui.GetWindowDrawList().AddLine(lineStart, lineEnd, 0xFFFFFFFF);
var lineStart = Im.Item.LowerRightCorner;
lineStart -= Im.Style.FramePadding;
var lineEnd = lineStart with { X = Im.Item.UpperLeftCorner.X + Im.Style.FramePadding.X };
Im.Window.DrawList.Shape.Line(lineStart, lineEnd, 0xFFFFFFFF);
}
private bool DrawModelMaterialDetails(MdlTab tab, bool disabled)
{
var invalidMaterialCount = tab.Mdl.Materials.Count(material => !tab.ValidateMaterial(material));
var oldPos = ImGui.GetCursorPosY();
var header = ImGui.CollapsingHeader("Materials");
var newPos = ImGui.GetCursorPos();
var oldPos = Im.Cursor.Y;
var header = Im.Tree.Header("Materials"u8);
var newPos = Im.Cursor.Position;
if (invalidMaterialCount > 0)
{
var text = $"{invalidMaterialCount} invalid material{(invalidMaterialCount > 1 ? "s" : "")}";
var size = ImGui.CalcTextSize(text).X;
ImGui.SetCursorPos(new Vector2(Im.ContentRegion.Available.X - size, oldPos + Im.Style.FramePadding.Y));
ImGuiUtil.TextColored(0xFF0000FF, text);
ImGui.SetCursorPos(newPos);
var text = new StringU8($"{invalidMaterialCount} invalid material{(invalidMaterialCount > 1 ? "s" : "")}");
var size = Im.Font.CalculateSize(text).X;
Im.Cursor.Position = new Vector2(Im.ContentRegion.Available.X - size, oldPos + Im.Style.FramePadding.Y);
Im.Text(text, Rgba32.Red);
Im.Cursor.Position = newPos;
}
if (!header)
@ -354,46 +343,46 @@ public partial class ModEditWindow
table.SetupColumn("help"u8, TableColumnFlags.WidthFixed, UiHelpers.IconButtonSize.X);
}
var inputFlags = disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None;
var inputFlags = disabled ? InputTextFlags.ReadOnly : InputTextFlags.None;
for (var materialIndex = 0; materialIndex < materials.Length; materialIndex++)
ret |= DrawMaterialRow(tab, disabled, materials, materialIndex, inputFlags);
ret |= DrawMaterialRow(table, tab, disabled, materials, materialIndex, inputFlags);
if (materials.Length >= MdlMaterialMaximum || disabled)
return ret;
ImGui.TableNextColumn();
table.NextColumn();
ImGui.TableNextColumn();
Im.Item.SetNextWidth(-1);
ImGui.InputTextWithHint("##newMaterial", "Add new material...", ref _modelNewMaterial, Utf8GamePath.MaxGamePathLength, inputFlags);
table.NextColumn();
Im.Item.SetNextWidth(Im.ContentRegion.Available.X);
Im.Input.Text("##newMaterial"u8, ref _modelNewMaterial, "Add new material..."u8, maxLength: Utf8GamePath.MaxGamePathLength,
flags: inputFlags);
var validName = tab.ValidateMaterial(_modelNewMaterial);
ImGui.TableNextColumn();
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), UiHelpers.IconButtonSize, string.Empty, !validName, true))
table.NextColumn();
if (ImEx.Icon.Button(LunaStyle.AddObjectIcon, StringU8.Empty, !validName))
{
ret |= true;
tab.Mdl.Materials = materials.AddItem(_modelNewMaterial);
_modelNewMaterial = string.Empty;
ret = true;
tab.Mdl.Materials = materials.AddItem(_modelNewMaterial);
_modelNewMaterial = string.Empty;
}
ImGui.TableNextColumn();
table.NextColumn();
if (!validName && _modelNewMaterial.Length > 0)
DrawInvalidMaterialMarker();
return ret;
}
private bool DrawMaterialRow(MdlTab tab, bool disabled, string[] materials, int materialIndex, ImGuiInputTextFlags inputFlags)
private bool DrawMaterialRow(in Im.TableDisposable table, MdlTab tab, bool disabled, string[] materials, int materialIndex,
InputTextFlags inputFlags)
{
using var id = ImRaii.PushId(materialIndex);
using var id = Im.Id.Push(materialIndex);
var ret = false;
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
Im.Text($"Material #{materialIndex + 1}");
table.DrawFrameColumn($"Material #{materialIndex + 1}");
var temp = materials[materialIndex];
ImGui.TableNextColumn();
Im.Item.SetNextWidth(-1);
if (ImGui.InputText($"##material{materialIndex}", ref temp, Utf8GamePath.MaxGamePathLength, inputFlags)
table.NextColumn();
Im.Item.SetNextWidth(Im.ContentRegion.Available.X);
if (Im.Input.Text($"##material{materialIndex}", ref temp, maxLength: Utf8GamePath.MaxGamePathLength, flags: inputFlags)
&& temp.Length > 0
&& temp != materials[materialIndex]
)
@ -405,23 +394,23 @@ public partial class ModEditWindow
if (disabled)
return ret;
ImGui.TableNextColumn();
table.NextColumn();
// Need to have at least one material.
if (materials.Length > 1)
{
var tt = "Delete this material.\nAny meshes targeting this material will be updated to use material #1.";
var modifierActive = _config.DeleteModModifier.IsActive();
if (!modifierActive)
tt += $"\nHold {_config.DeleteModModifier} to delete.";
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), UiHelpers.IconButtonSize, tt, !modifierActive, true))
if (ImEx.Icon.Button(LunaStyle.DeleteIcon,
"Delete this material.\nAny meshes targeting this material will be updated to use material #1."u8, !modifierActive))
{
tab.RemoveMaterial(materialIndex);
ret |= true;
ret = true;
}
if (!modifierActive)
Im.Tooltip.OnHover($"\nHold {_config.DeleteModModifier} to delete.");
}
ImGui.TableNextColumn();
table.NextColumn();
// Add markers to invalid materials.
if (!tab.ValidateMaterial(temp))
DrawInvalidMaterialMarker();
@ -431,20 +420,16 @@ public partial class ModEditWindow
private static void DrawInvalidMaterialMarker()
{
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGuiUtil.TextColored(0xFF0000FF, FontAwesomeIcon.TimesCircle.ToIconString());
}
ImGuiUtil.HoverTooltip(
"Materials must be either relative (e.g. \"/filename.mtrl\")\n"
+ "or absolute (e.g. \"bg/full/path/to/filename.mtrl\"),\n"
+ "and must end in \".mtrl\".");
ImEx.Icon.Draw(FontAwesomeIcon.TimesCircle.Icon(), Rgba32.Red);
Im.Tooltip.OnHover(
"Materials must be either relative (e.g. \"/filename.mtrl\")\n"u8
+ "or absolute (e.g. \"bg/full/path/to/filename.mtrl\"),\n"u8
+ "and must end in \".mtrl\"."u8);
}
private bool DrawModelLodDetails(MdlTab tab, int lodIndex, bool disabled)
{
using var lodNode = ImRaii.TreeNode($"Level of Detail #{lodIndex + 1}", ImGuiTreeNodeFlags.DefaultOpen);
using var lodNode = Im.Tree.Node($"Level of Detail #{lodIndex + 1}", TreeNodeFlags.DefaultOpen);
if (!lodNode)
return false;
@ -459,11 +444,11 @@ public partial class ModEditWindow
private bool DrawModelMeshDetails(MdlTab tab, int meshIndex, bool disabled)
{
using var meshNode = ImRaii.TreeNode($"Mesh #{meshIndex + 1}", ImGuiTreeNodeFlags.DefaultOpen);
using var meshNode = Im.Tree.Node($"Mesh #{meshIndex + 1}", TreeNodeFlags.DefaultOpen);
if (!meshNode)
return false;
using var id = ImRaii.PushId(meshIndex);
using var id = Im.Id.Push(meshIndex);
using var table = Im.Table.Begin(StringU8.Empty, 2, TableFlags.SizingFixedFit);
if (!table)
return false;
@ -475,35 +460,31 @@ public partial class ModEditWindow
var mesh = file.Meshes[meshIndex];
// Vertex elements
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
Im.Text("Vertex Elements"u8);
table.DrawFrameColumn("Vertex Elements"u8);
ImGui.TableNextColumn();
table.NextColumn();
DrawVertexElementDetails(file.VertexDeclarations[meshIndex].VertexElements);
// Mesh material
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
Im.Text("Material"u8);
table.DrawFrameColumn("Material"u8);
ImGui.TableNextColumn();
table.NextColumn();
var ret = DrawMaterialCombo(tab, meshIndex, disabled);
// Sub meshes
for (var subMeshOffset = 0; subMeshOffset < mesh.SubMeshCount; subMeshOffset++)
ret |= DrawSubMeshAttributes(tab, meshIndex, subMeshOffset, disabled);
ret |= DrawSubMeshAttributes(table, tab, meshIndex, subMeshOffset, disabled);
return ret;
}
private static void DrawVertexElementDetails(MdlStructs.VertexElement[] vertexElements)
{
using var node = ImRaii.TreeNode($"Click to expand");
using var node = Im.Tree.Node("Click to expand"u8);
if (!node)
return;
var flags = TableFlags.SizingFixedFit
const TableFlags flags = TableFlags.SizingFixedFit
| TableFlags.RowBackground
| TableFlags.Borders
| TableFlags.NoHostExtendX;
@ -515,28 +496,23 @@ public partial class ModEditWindow
table.SetupColumn("Type"u8);
table.SetupColumn("Stream"u8);
table.SetupColumn("Offset"u8);
ImGui.TableHeadersRow();
table.HeaderRow();
foreach (var element in vertexElements)
{
ImGui.TableNextColumn();
Im.Text($"{(MdlFile.VertexUsage)element.Usage}");
ImGui.TableNextColumn();
Im.Text($"{(MdlFile.VertexType)element.Type}");
ImGui.TableNextColumn();
Im.Text($"{element.Stream}");
ImGui.TableNextColumn();
Im.Text($"{element.Offset}");
table.DrawColumn($"{(MdlFile.VertexUsage)element.Usage}");
table.DrawColumn($"{(MdlFile.VertexType)element.Type}");
table.DrawColumn($"{element.Stream}");
table.DrawColumn($"{element.Offset}");
}
}
private static bool DrawMaterialCombo(MdlTab tab, int meshIndex, bool disabled)
{
var mesh = tab.Mdl.Meshes[meshIndex];
using var _ = ImRaii.Disabled(disabled);
Im.Item.SetNextWidth(-1);
using var materialCombo = ImRaii.Combo("##material", tab.Mdl.Materials[mesh.MaterialIndex]);
using var _ = Im.Disabled(disabled);
Im.Item.SetNextWidth(Im.ContentRegion.Available.X);
using var materialCombo = Im.Combo.Begin("##material"u8, tab.Mdl.Materials[mesh.MaterialIndex]);
if (!materialCombo)
return false;
@ -544,7 +520,7 @@ public partial class ModEditWindow
var ret = false;
foreach (var (materialIndex, material) in tab.Mdl.Materials.Index())
{
if (!ImGui.Selectable(material, mesh.MaterialIndex == materialIndex))
if (!Im.Selectable(material, mesh.MaterialIndex == materialIndex))
continue;
tab.Mdl.Meshes[meshIndex].MaterialIndex = (ushort)materialIndex;
@ -554,23 +530,21 @@ public partial class ModEditWindow
return ret;
}
private bool DrawSubMeshAttributes(MdlTab tab, int meshIndex, int subMeshOffset, bool disabled)
private bool DrawSubMeshAttributes(in Im.TableDisposable table, MdlTab tab, int meshIndex, int subMeshOffset, bool disabled)
{
using var _ = ImRaii.PushId(subMeshOffset);
using var _ = Im.Id.Push(subMeshOffset);
var mesh = tab.Mdl.Meshes[meshIndex];
var subMeshIndex = mesh.SubMeshIndex + subMeshOffset;
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
Im.Text($"Attributes #{subMeshOffset + 1}");
table.DrawFrameColumn($"Attributes #{subMeshOffset + 1}");
ImGui.TableNextColumn();
table.NextColumn();
var data = disabled ? _preview : _main;
var widget = data.SubMeshAttributeTags[subMeshIndex];
var attributes = tab.GetSubMeshAttributes(subMeshIndex);
if (attributes == null)
if (attributes is null)
{
attributes = ["invalid attribute data"];
disabled = true;
@ -590,7 +564,7 @@ public partial class ModEditWindow
private bool DrawOtherModelDetails(LoadedData data)
{
using var header = ImRaii.CollapsingHeader("Further Content");
using var header = Im.Tree.HeaderId("Further Content"u8);
if (!header)
return false;
@ -599,68 +573,46 @@ public partial class ModEditWindow
{
if (table)
{
ImGuiUtil.DrawTableColumn("Version");
ImGuiUtil.DrawTableColumn($"0x{data.LastFile.Version:X}");
ImGuiUtil.DrawTableColumn("Radius");
ImGuiUtil.DrawTableColumn(data.LastFile.Radius.ToString(CultureInfo.InvariantCulture));
ImGuiUtil.DrawTableColumn("Model Clip Out Distance");
ImGuiUtil.DrawTableColumn(data.LastFile.ModelClipOutDistance.ToString(CultureInfo.InvariantCulture));
ImGuiUtil.DrawTableColumn("Shadow Clip Out Distance");
ImGuiUtil.DrawTableColumn(data.LastFile.ShadowClipOutDistance.ToString(CultureInfo.InvariantCulture));
ImGuiUtil.DrawTableColumn("LOD Count");
ImGuiUtil.DrawTableColumn(data.LastFile.LodCount.ToString());
ImGuiUtil.DrawTableColumn("Enable Index Buffer Streaming");
ImGuiUtil.DrawTableColumn(data.LastFile.EnableIndexBufferStreaming.ToString());
ImGuiUtil.DrawTableColumn("Enable Edge Geometry");
ImGuiUtil.DrawTableColumn(data.LastFile.EnableEdgeGeometry.ToString());
ImGuiUtil.DrawTableColumn("Flags 1");
ImGuiUtil.DrawTableColumn(data.LastFile.Flags1.ToString());
ImGuiUtil.DrawTableColumn("Flags 2");
ImGuiUtil.DrawTableColumn(data.LastFile.Flags2.ToString());
ImGuiUtil.DrawTableColumn("Vertex Declarations");
ImGuiUtil.DrawTableColumn(data.LastFile.VertexDeclarations.Length.ToString());
ImGuiUtil.DrawTableColumn("Bone Bounding Boxes");
ImGuiUtil.DrawTableColumn(data.LastFile.BoneBoundingBoxes.Length.ToString());
ImGuiUtil.DrawTableColumn("Bone Tables");
ImGuiUtil.DrawTableColumn(data.LastFile.BoneTables.Length.ToString());
ImGuiUtil.DrawTableColumn("Element IDs");
ImGuiUtil.DrawTableColumn(data.LastFile.ElementIds.Length.ToString());
ImGuiUtil.DrawTableColumn("Extra LoDs");
ImGuiUtil.DrawTableColumn(data.LastFile.ExtraLods.Length.ToString());
ImGuiUtil.DrawTableColumn("Meshes");
ImGuiUtil.DrawTableColumn(data.LastFile.Meshes.Length.ToString());
ImGuiUtil.DrawTableColumn("Shape Meshes");
ImGuiUtil.DrawTableColumn(data.LastFile.ShapeMeshes.Length.ToString());
ImGuiUtil.DrawTableColumn("LoDs");
ImGuiUtil.DrawTableColumn(data.LastFile.Lods.Length.ToString());
ImGuiUtil.DrawTableColumn("Vertex Declarations");
ImGuiUtil.DrawTableColumn(data.LastFile.VertexDeclarations.Length.ToString());
ImGuiUtil.DrawTableColumn("Stack Size");
ImGuiUtil.DrawTableColumn(data.LastFile.StackSize.ToString());
table.DrawDataPair("Version"u8, $"0x{data.LastFile.Version:X}");
table.DrawDataPair("Radius"u8, data.LastFile.Radius.ToString(CultureInfo.InvariantCulture));
table.DrawDataPair("Model Clip Out Distance"u8, data.LastFile.ModelClipOutDistance.ToString(CultureInfo.InvariantCulture));
table.DrawDataPair("Shadow Clip Out Distance"u8, data.LastFile.ShadowClipOutDistance.ToString(CultureInfo.InvariantCulture));
table.DrawDataPair("LOD Count"u8, data.LastFile.LodCount);
table.DrawDataPair("Enable Index Buffer Streaming"u8, data.LastFile.EnableIndexBufferStreaming);
table.DrawDataPair("Enable Edge Geometry"u8, data.LastFile.EnableEdgeGeometry);
table.DrawDataPair("Flags 1"u8, data.LastFile.Flags1);
table.DrawDataPair("Flags 2"u8, data.LastFile.Flags2);
table.DrawDataPair("Vertex Declarations"u8, data.LastFile.VertexDeclarations.Length);
table.DrawDataPair("Bone Bounding Boxes"u8, data.LastFile.BoneBoundingBoxes.Length);
table.DrawDataPair("Bone Tables"u8, data.LastFile.BoneTables.Length);
table.DrawDataPair("Element IDs"u8, data.LastFile.ElementIds.Length);
table.DrawDataPair("Extra LoDs"u8, data.LastFile.ExtraLods.Length);
table.DrawDataPair("Meshes"u8, data.LastFile.Meshes.Length);
table.DrawDataPair("Shape Meshes"u8, data.LastFile.ShapeMeshes.Length);
table.DrawDataPair("LoDs"u8, data.LastFile.Lods.Length);
table.DrawDataPair("Vertex Declarations"u8, data.LastFile.VertexDeclarations.Length);
table.DrawDataPair("Stack Size"u8, data.LastFile.StackSize);
foreach (var (lod, triCount) in data.LodTriCount.Index())
{
ImGuiUtil.DrawTableColumn($"LOD #{lod + 1} Triangle Count");
ImGuiUtil.DrawTableColumn(triCount.ToString());
}
table.DrawDataPair($"LOD #{lod + 1} Triangle Count", triCount);
}
}
using (var materials = ImRaii.TreeNode("Materials", ImGuiTreeNodeFlags.DefaultOpen))
using (var materials = Im.Tree.Node("Materials"u8, TreeNodeFlags.DefaultOpen))
{
if (materials)
foreach (var material in data.LastFile.Materials)
ImRaii.TreeNode(material, ImGuiTreeNodeFlags.Leaf).Dispose();
Im.Tree.Leaf(material);
}
using (var attributes = ImRaii.TreeNode("Attributes", ImGuiTreeNodeFlags.DefaultOpen))
using (var attributes = Im.Tree.Node("Attributes"u8, TreeNodeFlags.DefaultOpen))
{
if (attributes)
for (var i = 0; i < data.LastFile.Attributes.Length; ++i)
{
using var id = ImUtf8.PushId(i);
using var id = Im.Id.Push(i);
ref var attribute = ref data.LastFile.Attributes[i];
var name = attribute;
if (ImUtf8.InputText("##attribute"u8, ref name, "Attribute Name..."u8) && name.Length > 0 && name != attribute)
if (Im.Input.Text("##attribute"u8, ref name, "Attribute Name..."u8) && name.Length > 0 && name != attribute)
{
attribute = name;
ret = true;
@ -668,15 +620,15 @@ public partial class ModEditWindow
}
}
using (var bones = ImRaii.TreeNode("Bones", ImGuiTreeNodeFlags.DefaultOpen))
using (var bones = Im.Tree.Node("Bones"u8, TreeNodeFlags.DefaultOpen))
{
if (bones)
for (var i = 0; i < data.LastFile.Bones.Length; ++i)
{
using var id = ImUtf8.PushId(i);
using var id = Im.Id.Push(i);
ref var bone = ref data.LastFile.Bones[i];
var name = bone;
if (ImUtf8.InputText("##bone"u8, ref name, "Bone Name..."u8) && name.Length > 0 && name != bone)
if (Im.Input.Text("##bone"u8, ref name, "Bone Name..."u8) && name.Length > 0 && name != bone)
{
bone = name;
ret = true;
@ -684,15 +636,15 @@ public partial class ModEditWindow
}
}
using (var shapes = ImRaii.TreeNode("Shapes", ImGuiTreeNodeFlags.DefaultOpen))
using (var shapes = Im.Tree.Node("Shapes"u8, TreeNodeFlags.DefaultOpen))
{
if (shapes)
for (var i = 0; i < data.LastFile.Shapes.Length; ++i)
{
using var id = ImUtf8.PushId(i);
using var id = Im.Id.Push(i);
ref var shape = ref data.LastFile.Shapes[i];
var name = shape.ShapeName;
if (ImUtf8.InputText("##shape"u8, ref name, "Shape Name..."u8) && name.Length > 0 && name != shape.ShapeName)
if (Im.Input.Text("##shape"u8, ref name, "Shape Name..."u8) && name.Length > 0 && name != shape.ShapeName)
{
shape.ShapeName = name;
ret = true;
@ -702,7 +654,7 @@ public partial class ModEditWindow
if (data.LastFile.RemainingData.Length > 0)
{
using var t = ImRaii.TreeNode($"Additional Data (Size: {data.LastFile.RemainingData.Length})###AdditionalData");
using var t = Im.Tree.Node($"Additional Data (Size: {data.LastFile.RemainingData.Length})###AdditionalData");
if (t)
Widget.DrawHexViewer(data.LastFile.RemainingData);
}

View file

@ -1,51 +1,45 @@
using Dalamud.Interface;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Luna;
using OtterGui.Raii;
using OtterGui;
using Penumbra.GameData;
using Penumbra.GameData.Files;
using Penumbra.GameData.Interop;
using Penumbra.String;
using static Penumbra.GameData.Files.ShpkFile;
using OtterGui.Widgets;
using OtterGui.Text;
using Penumbra.GameData.Structs;
namespace Penumbra.UI.AdvancedWindow;
public partial class ModEditWindow
{
private static readonly CiByteString DisassemblyLabel = CiByteString.FromSpanUnsafe("##disassembly"u8, true, true, true);
private static readonly StringU8 DisassemblyLabel = new("##disassembly"u8);
private readonly FileEditor<ShpkTab> _shaderPackageTab;
private static bool DrawShaderPackagePanel(ShpkTab file, bool disabled)
{
var dummyHeight = new Vector2(Im.Style.TextHeight / 2);
DrawShaderPackageSummary(file);
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
Im.Dummy(dummyHeight);
DrawShaderPackageFilterSection(file);
var ret = false;
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
Im.Dummy(dummyHeight);
ret |= DrawShaderPackageShaderArray(file, "Vertex Shader", file.Shpk.VertexShaders, disabled);
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
Im.Dummy(dummyHeight);
ret |= DrawShaderPackageShaderArray(file, "Pixel Shader", file.Shpk.PixelShaders, disabled);
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
Im.Dummy(dummyHeight);
ret |= DrawShaderPackageMaterialParamLayout(file, disabled);
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
Im.Dummy(dummyHeight);
ret |= DrawShaderPackageResources(file, disabled);
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
Im.Dummy(dummyHeight);
DrawShaderPackageSelection(file);
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
Im.Dummy(dummyHeight);
DrawOtherShaderPackageDetails(file);
ret |= file.Shpk.IsChanged();
@ -56,17 +50,17 @@ public partial class ModEditWindow
private static void DrawShaderPackageSummary(ShpkTab tab)
{
if (tab.Shpk.IsLegacy)
ImUtf8.Text("This legacy shader package will not work in the current version of the game. Do not attempt to load it.",
ImGuiUtil.HalfBlendText(0x80u)); // Half red
ImUtf8.Text(tab.Header);
Im.Text("This legacy shader package will not work in the current version of the game. Do not attempt to load it."u8,
ImGuiColor.Text.Get().HalfBlend(new Rgba32(0x80)));
Im.Text(tab.Header);
if (!tab.Shpk.Disassembled)
ImUtf8.Text("Your system doesn't support disassembling shaders. Some functionality will be missing.",
ImGuiUtil.HalfBlendText(0x80u)); // Half red
Im.Text("Your system doesn't support disassembling shaders. Some functionality will be missing."u8,
ImGuiColor.Text.Get().HalfBlend(new Rgba32(0x80))); // Half red
}
private static void DrawShaderExportButton(ShpkTab tab, string objectName, Shader shader, int idx)
{
if (!ImUtf8.Button($"Export Shader Program Blob ({shader.Blob.Length} bytes)"))
if (!Im.Button($"Export Shader Program Blob ({shader.Blob.Length} bytes)"))
return;
var defaultName = objectName[0] switch
@ -102,7 +96,7 @@ public partial class ModEditWindow
private static void DrawShaderImportButton(ShpkTab tab, string objectName, Shader[] shaders, int idx)
{
if (!ImUtf8.Button("Replace Shader Program Blob"u8))
if (!Im.Button("Replace Shader Program Blob"u8))
return;
tab.FileDialog.OpenFilePicker($"Replace {objectName} #{idx} Program Blob...", "Shader Program Blobs{.o,.cso,.dxbc,.dxil}",
@ -141,58 +135,52 @@ public partial class ModEditWindow
private static unsafe void DrawRawDisassembly(Shader shader)
{
using var tree = ImUtf8.TreeNode("Raw Program Disassembly"u8);
using var tree = Im.Tree.Node("Raw Program Disassembly"u8);
if (!tree)
return;
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
var size = new Vector2(Im.ContentRegion.Available.X, Im.Style.TextHeight * 20);
ImGuiNative.InputTextMultiline(DisassemblyLabel.Path, shader.Disassembly!.RawDisassembly.Path,
(uint)shader.Disassembly!.RawDisassembly.Length + 1, size,
ImGuiInputTextFlags.ReadOnly, null, null);
using var font = Im.Font.PushMono();
var size = Im.ContentRegion.Available with { Y = Im.Style.TextHeight * 20 };
Im.Input.MultiLine(DisassemblyLabel,
new Span<byte>(shader.Disassembly!.RawDisassembly.Path, shader.Disassembly.RawDisassembly.Length + 1), out ulong _, size,
InputTextFlags.ReadOnly);
}
private static void DrawShaderUsage(ShpkTab tab, Shader shader)
{
using (var node = ImUtf8.TreeNode("Used with Shader Keys"u8))
using (var node = Im.Tree.Node("Used with Shader Keys"u8))
{
if (node)
{
foreach (var (keyIdx, key) in shader.SystemValues!.Index())
{
ImUtf8.TreeNode(
$"Used with System Key {tab.TryResolveName(tab.Shpk.SystemKeys[keyIdx].Id)} \u2208 {{ {tab.NameSetToString(key)} }}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
Im.Tree.Leaf(
$"Used with System Key {tab.TryResolveName(tab.Shpk.SystemKeys[keyIdx].Id)} \u2208 {{ {tab.NameSetToString(key)} }}");
}
foreach (var (keyIdx, key) in shader.SceneValues!.Index())
{
ImUtf8.TreeNode(
$"Used with Scene Key {tab.TryResolveName(tab.Shpk.SceneKeys[keyIdx].Id)} \u2208 {{ {tab.NameSetToString(key)} }}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
Im.Tree.Leaf(
$"Used with Scene Key {tab.TryResolveName(tab.Shpk.SceneKeys[keyIdx].Id)} \u2208 {{ {tab.NameSetToString(key)} }}");
}
foreach (var (keyIdx, key) in shader.MaterialValues!.Index())
{
ImUtf8.TreeNode(
$"Used with Material Key {tab.TryResolveName(tab.Shpk.MaterialKeys[keyIdx].Id)} \u2208 {{ {tab.NameSetToString(key)} }}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
Im.Tree.Leaf(
$"Used with Material Key {tab.TryResolveName(tab.Shpk.MaterialKeys[keyIdx].Id)} \u2208 {{ {tab.NameSetToString(key)} }}");
}
foreach (var (keyIdx, key) in shader.SubViewValues!.Index())
{
ImUtf8.TreeNode($"Used with Sub-View Key #{keyIdx} \u2208 {{ {tab.NameSetToString(key)} }}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
}
Im.Tree.Leaf($"Used with Sub-View Key #{keyIdx} \u2208 {{ {tab.NameSetToString(key)} }}");
}
}
ImUtf8.TreeNode($"Used in Passes: {tab.NameSetToString(shader.Passes)}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
Im.Tree.Leaf($"Used in Passes: {tab.NameSetToString(shader.Passes)}");
}
private static void DrawShaderPackageFilterSection(ShpkTab tab)
{
if (!ImUtf8.CollapsingHeader(tab.FilterPopCount == tab.FilterMaximumPopCount ? "Filters###Filters"u8 : "Filters (ACTIVE)###Filters"u8))
if (!Im.Tree.Header(tab.FilterPopCount == tab.FilterMaximumPopCount ? "Filters###Filters"u8 : "Filters (ACTIVE)###Filters"u8))
return;
foreach (var (keyIdx, key) in tab.Shpk.SystemKeys.Index())
@ -207,25 +195,26 @@ public partial class ModEditWindow
foreach (var (keyIdx, _) in tab.Shpk.SubViewKeys.Index())
DrawShaderPackageFilterSet(tab, $"Sub-View Key #{keyIdx}", ref tab.FilterSubViewValues[keyIdx]);
DrawShaderPackageFilterSet(tab, "Passes", ref tab.FilterPasses);
DrawShaderPackageFilterSet(tab, "Passes"u8, ref tab.FilterPasses);
}
private static void DrawShaderPackageFilterSet(ShpkTab tab, string label, ref SharedSet<uint, uint> values)
private static void DrawShaderPackageFilterSet(ShpkTab tab, Utf8StringHandler<LabelStringHandlerBuffer> label,
ref SharedSet<uint, uint> values)
{
if (values.PossibleValues == null)
if (values.PossibleValues is null)
{
ImUtf8.TreeNode(label, ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
Im.Tree.Leaf(label);
return;
}
using var node = ImUtf8.TreeNode(label);
using var node = Im.Tree.Node(label);
if (!node)
return;
foreach (var value in values.PossibleValues)
{
var contains = values.Contains(value);
if (!ImUtf8.Checkbox($"{tab.TryResolveName(value)}", ref contains))
if (!Im.Checkbox($"{tab.TryResolveName(value)}", ref contains))
continue;
if (contains)
@ -249,7 +238,7 @@ public partial class ModEditWindow
private static bool DrawShaderPackageShaderArray(ShpkTab tab, string objectName, Shader[] shaders, bool disabled)
{
if (shaders.Length == 0 || !ImUtf8.CollapsingHeader($"{objectName}s"))
if (shaders.Length is 0 || !Im.Tree.Header($"{objectName}s"))
return false;
var ret = false;
@ -259,7 +248,7 @@ public partial class ModEditWindow
if (!tab.IsFilterMatch(shader))
continue;
using var t = ImUtf8.TreeNode($"{objectName} #{idx}");
using var t = Im.Tree.Node($"{objectName} #{idx}");
if (!t)
continue;
@ -270,22 +259,22 @@ public partial class ModEditWindow
DrawShaderImportButton(tab, objectName, shaders, idx);
}
ret |= DrawShaderPackageResourceArray("Constant Buffers", "slot", true, shader.Constants, false, true);
ret |= DrawShaderPackageResourceArray("Samplers", "slot", false, shader.Samplers, false, true);
ret |= DrawShaderPackageResourceArray("Constant Buffers"u8, "slot", true, shader.Constants, false, true);
ret |= DrawShaderPackageResourceArray("Samplers"u8, "slot", false, shader.Samplers, false, true);
if (!tab.Shpk.IsLegacy)
ret |= DrawShaderPackageResourceArray("Textures", "slot", false, shader.Textures, false, true);
ret |= DrawShaderPackageResourceArray("Unordered Access Views", "slot", true, shader.Uavs, false, true);
ret |= DrawShaderPackageResourceArray("Textures"u8, "slot", false, shader.Textures, false, true);
ret |= DrawShaderPackageResourceArray("Unordered Access Views"u8, "slot", true, shader.Uavs, false, true);
if (shader.DeclaredInputs != 0)
ImUtf8.TreeNode($"Declared Inputs: {shader.DeclaredInputs}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
if (shader.UsedInputs != 0)
ImUtf8.TreeNode($"Used Inputs: {shader.UsedInputs}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
if (shader.DeclaredInputs is not 0)
Im.Tree.Leaf($"Declared Inputs: {shader.DeclaredInputs}");
if (shader.UsedInputs is not 0)
Im.Tree.Leaf($"Used Inputs: {shader.UsedInputs}");
if (shader.AdditionalHeader.Length > 8)
{
using var t2 = ImUtf8.TreeNode($"Additional Header (Size: {shader.AdditionalHeader.Length})###AdditionalHeader");
using var t2 = Im.Tree.Node($"Additional Header (Size: {shader.AdditionalHeader.Length})###AdditionalHeader");
if (t2)
Widget.DrawHexViewer(shader.AdditionalHeader);
ImEx.HexViewer(shader.AdditionalHeader);
}
if (tab.Shpk.Disassembled)
@ -303,43 +292,42 @@ public partial class ModEditWindow
if (!disabled)
{
Im.Item.SetNextWidth(Im.Style.GlobalScale * 150.0f);
if (ImGuiUtil.InputUInt16($"{char.ToUpper(slotLabel[0])}{slotLabel[1..].ToLower()}", ref resource.Slot, ImGuiInputTextFlags.None))
if (Im.Input.Scalar($"{char.ToUpper(slotLabel[0])}{slotLabel[1..].ToLower()}", ref resource.Slot))
ret = true;
}
if (resource.Used == null)
if (resource.Used is null)
return ret;
var usedString = UsedComponentString(withSize, false, resource);
if (usedString.Length > 0)
{
ImUtf8.TreeNode(hasFilter ? $"Globally Used: {usedString}" : $"Used: {usedString}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
Im.Tree.Leaf(hasFilter ? $"Globally Used: {usedString}" : $"Used: {usedString}");
if (hasFilter)
{
var filteredUsedString = UsedComponentString(withSize, true, resource);
if (filteredUsedString.Length > 0)
ImUtf8.TreeNode($"Used within Filters: {filteredUsedString}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet)
.Dispose();
Im.Tree.Leaf($"Used within Filters: {filteredUsedString}");
else
ImUtf8.TreeNode("Unused within Filters"u8, ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
Im.Tree.Leaf("Unused within Filters"u8);
}
}
else
{
ImUtf8.TreeNode(hasFilter ? "Globally Unused"u8 : "Unused"u8, ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
Im.Tree.Leaf(hasFilter ? "Globally Unused"u8 : "Unused"u8);
}
return ret;
}
private static bool DrawShaderPackageResourceArray(string arrayName, string slotLabel, bool withSize, Resource[] resources, bool hasFilter,
private static bool DrawShaderPackageResourceArray(ReadOnlySpan<byte> arrayName, string slotLabel, bool withSize, Resource[] resources,
bool hasFilter,
bool disabled)
{
if (resources.Length == 0)
if (resources.Length is 0)
return false;
using var t = ImRaii.TreeNode(arrayName);
using var t = Im.Tree.Node(arrayName);
if (!t)
return false;
@ -349,8 +337,8 @@ public partial class ModEditWindow
ref var buf = ref resources[idx];
var name = $"#{idx}: {buf.Name} (ID: 0x{buf.Id:X8}), {slotLabel}: {buf.Slot}"
+ (withSize ? $", size: {buf.Size} registers###{idx}: {buf.Name} (ID: 0x{buf.Id:X8})" : string.Empty);
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
using var t2 = ImUtf8.TreeNode(name, !disabled || buf.Used != null ? 0 : ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet);
using var font = Im.Font.PushMono();
using var t2 = Im.Tree.Node(name, !disabled || buf.Used is not null ? 0 : TreeNodeFlags.Leaf | TreeNodeFlags.Bullet);
font.Pop();
if (t2)
ret |= DrawShaderPackageResource(slotLabel, withSize, ref buf, hasFilter, disabled);
@ -359,27 +347,26 @@ public partial class ModEditWindow
return ret;
}
private static bool DrawMaterialParamLayoutHeader(string label)
private static bool DrawMaterialParamLayoutHeader(Utf8StringHandler<LabelStringHandlerBuffer> label)
{
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
var pos = ImGui.GetCursorScreenPos()
+ new Vector2(ImGui.CalcTextSize(label).X + 3 * Im.Style.ItemInnerSpacing.X + Im.Style.FrameHeight,
using var font = Im.Font.PushMono();
var pos = Im.Cursor.ScreenPosition
+ new Vector2(Im.Font.CalculateSize(ref label).X + 3 * Im.Style.ItemInnerSpacing.X + Im.Style.FrameHeight,
Im.Style.FramePadding.Y);
var ret = ImUtf8.CollapsingHeader(label);
ImGui.GetWindowDrawList()
.AddText(UiBuilder.DefaultFont, UiBuilder.DefaultFont.FontSize, pos, ImGuiColor.Text.Get().Color, "Layout");
var ret = Im.Tree.Header(label);
Im.Window.DrawList.Text(Im.Font.Default, Im.Font.Default.Size, pos, ImGuiColor.Text.Get().Color, "Layout"u8);
return ret;
}
private static bool DrawMaterialParamLayoutBufferSize(ShpkFile file, Resource? materialParams)
{
var isSizeWellDefined = (file.MaterialParamsSize & 0xF) == 0
var isSizeWellDefined = (file.MaterialParamsSize & 0xF) is 0
&& (!materialParams.HasValue || file.MaterialParamsSize == materialParams.Value.Size << 4);
if (isSizeWellDefined)
return true;
ImUtf8.Text(materialParams.HasValue
Im.Text(materialParams.HasValue
? $"Buffer size mismatch: {file.MaterialParamsSize} bytes ≠ {materialParams.Value.Size} registers ({materialParams.Value.Size << 4} bytes)"
: $"Buffer size mismatch: {file.MaterialParamsSize} bytes, not a multiple of 16");
return false;
@ -387,9 +374,9 @@ public partial class ModEditWindow
private static bool DrawShaderPackageMaterialMatrix(ShpkTab tab, bool disabled)
{
ImUtf8.Text(tab.Shpk.Disassembled
? "Parameter positions (continuations are grayed out, globally unused values are red, unused values within filters are yellow):"
: "Parameter positions (continuations are grayed out):");
Im.Text(tab.Shpk.Disassembled
? "Parameter positions (continuations are grayed out, globally unused values are red, unused values within filters are yellow):"u8
: "Parameter positions (continuations are grayed out):"u8);
using var table = Im.Table.Begin("##MaterialParamLayout"u8, 5,
TableFlags.SizingFixedFit | TableFlags.RowBackground);
@ -401,34 +388,34 @@ public partial class ModEditWindow
table.SetupColumn("y"u8, TableColumnFlags.WidthFixed, 250 * Im.Style.GlobalScale);
table.SetupColumn("z"u8, TableColumnFlags.WidthFixed, 250 * Im.Style.GlobalScale);
table.SetupColumn("w"u8, TableColumnFlags.WidthFixed, 250 * Im.Style.GlobalScale);
ImGui.TableHeadersRow();
table.HeaderRow();
var textColorStart = ImGuiColor.Text.Get().Color;
var textColorStart = ImGuiColor.Text.Get();
var ret = false;
for (var i = 0; i < tab.Matrix.GetLength(0); ++i)
{
ImGui.TableNextColumn();
ImGui.TableHeader($" [{i}]");
table.NextColumn();
table.Header($" [{i}]");
for (var j = 0; j < 4; ++j)
{
var (name, tooltip, idx, colorType) = tab.Matrix[i, j];
var color = textColorStart;
if (!colorType.HasFlag(ShpkTab.ColorType.Used))
color = ImGuiUtil.HalfBlend(color, 0x80u); // Half red
color = color.HalfBlend(new Rgba32(0x80)); // Half red
else if (!colorType.HasFlag(ShpkTab.ColorType.FilteredUsed))
color = ImGuiUtil.HalfBlend(color, 0x8080u); // Half yellow
color = color.HalfBlend(0x8080u); // Half yellow
if (colorType.HasFlag(ShpkTab.ColorType.Continuation))
color = ImGuiUtil.HalfTransparent(color); // Half opacity
using var _ = ImRaii.PushId(i * 4 + j);
color = color.HalfTransparent(); // Half opacity
using var _ = Im.Id.Push(i * 4 + j);
var deletable = !disabled && idx >= 0;
using (ImRaii.PushFont(UiBuilder.MonoFont, tooltip.Length > 0))
using (Im.Font.Mono.Push(tooltip.Length > 0))
{
using (ImGuiColor.Text.Push(color))
{
ImGui.TableNextColumn();
ImUtf8.Selectable(name);
if (deletable && Im.Item.RightClicked() && ImGui.GetIO().KeyCtrl)
table.NextColumn();
Im.Selectable(name);
if (deletable && Im.Item.RightClicked() && Im.Io.KeyControl)
{
tab.Shpk.MaterialParams = tab.Shpk.MaterialParams.RemoveItems(idx);
ret = true;
@ -449,7 +436,7 @@ public partial class ModEditWindow
private static void DrawShaderPackageMaterialDevkitExport(ShpkTab tab)
{
if (!ImUtf8.Button("Export globally unused parameters as material dev-kit file"u8))
if (!Im.Button("Export globally unused parameters as material dev-kit file"u8))
return;
tab.FileDialog.OpenSavePicker("Export material dev-kit file", ".json", $"{Path.GetFileNameWithoutExtension(tab.FilePath)}.json",
@ -480,13 +467,13 @@ public partial class ModEditWindow
private static void DrawShaderPackageMisalignedParameters(ShpkTab tab)
{
using var t = ImUtf8.TreeNode("Misaligned / Overflowing Parameters"u8);
using var t = Im.Tree.Node("Misaligned / Overflowing Parameters"u8);
if (!t)
return;
using var _ = ImRaii.PushFont(UiBuilder.MonoFont);
using var _ = Im.Font.PushMono();
foreach (var name in tab.MalformedParameters)
ImUtf8.TreeNode(name, ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
Im.Tree.Leaf(name);
}
private static void DrawShaderPackageStartCombo(ShpkTab tab)
@ -495,17 +482,17 @@ public partial class ModEditWindow
using (Im.Font.PushMono())
{
Im.Item.SetNextWidth(Im.Style.GlobalScale * 400);
using var c = ImUtf8.Combo("##Start", tab.Orphans[tab.NewMaterialParamStart].Name);
using var c = Im.Combo.Begin("##Start"u8, tab.Orphans[tab.NewMaterialParamStart].Name);
if (c)
foreach (var(idx, start) in tab.Orphans.Index())
foreach (var (idx, start) in tab.Orphans.Index())
{
if (ImGui.Selectable(start.Name, idx == tab.NewMaterialParamStart))
if (Im.Selectable(start.Name, idx == tab.NewMaterialParamStart))
tab.UpdateOrphanStart(idx);
}
}
Im.Line.Same();
ImUtf8.Text("Start"u8);
Im.Text("Start"u8);
}
private static void DrawShaderPackageEndCombo(ShpkTab tab)
@ -514,7 +501,7 @@ public partial class ModEditWindow
using (Im.Font.PushMono())
{
Im.Item.SetNextWidth(Im.Style.GlobalScale * 400);
using var c = ImUtf8.Combo("##End", tab.Orphans[tab.NewMaterialParamEnd].Name);
using var c = Im.Combo.Begin("##End"u8, tab.Orphans[tab.NewMaterialParamEnd].Name);
if (c)
{
var current = tab.Orphans[tab.NewMaterialParamStart].Index;
@ -524,19 +511,19 @@ public partial class ModEditWindow
if (current++ != next.Index)
break;
if (ImGui.Selectable(next.Name, i == tab.NewMaterialParamEnd))
if (Im.Selectable(next.Name, i == tab.NewMaterialParamEnd))
tab.NewMaterialParamEnd = i;
}
}
}
Im.Line.Same();
ImUtf8.Text("End"u8);
Im.Text("End"u8);
}
private static bool DrawShaderPackageNewParameter(ShpkTab tab)
{
if (tab.Orphans.Count == 0)
if (tab.Orphans.Count is 0)
return false;
DrawShaderPackageStartCombo(tab);
@ -544,14 +531,15 @@ public partial class ModEditWindow
Im.Item.SetNextWidth(Im.Style.GlobalScale * 400);
var newName = tab.NewMaterialParamName.Value!;
if (ImUtf8.InputText("Name", ref newName))
if (Im.Input.Text("Name"u8, ref newName))
tab.NewMaterialParamName = newName;
var tooltip = tab.UsedIds.Contains(tab.NewMaterialParamName.Crc32)
? "The ID is already in use. Please choose a different name."u8
: ""u8;
if (!ImUtf8.ButtonEx($"Add {tab.NewMaterialParamName} (0x{tab.NewMaterialParamName.Crc32:X8})", tooltip,
new Vector2(400 * Im.Style.GlobalScale, Im.Style.FrameHeight), tooltip.Length > 0))
if (!ImEx.Button($"Add {tab.NewMaterialParamName} (0x{tab.NewMaterialParamName.Crc32:X8})",
new Vector2(400 * Im.Style.GlobalScale, Im.Style.FrameHeight),
tooltip, tooltip.Length > 0))
return false;
tab.Shpk.MaterialParams = tab.Shpk.MaterialParams.AddItem(new MaterialParam
@ -592,38 +580,36 @@ public partial class ModEditWindow
{
var ret = false;
if (!ImUtf8.CollapsingHeader("Shader Resources"u8))
if (!Im.Tree.Header("Shader Resources"u8))
return false;
var hasFilters = tab.FilterPopCount != tab.FilterMaximumPopCount;
ret |= DrawShaderPackageResourceArray("Constant Buffers", "type", true, tab.Shpk.Constants, hasFilters, disabled);
ret |= DrawShaderPackageResourceArray("Samplers", "type", false, tab.Shpk.Samplers, hasFilters, disabled);
ret |= DrawShaderPackageResourceArray("Constant Buffers"u8, "type", true, tab.Shpk.Constants, hasFilters, disabled);
ret |= DrawShaderPackageResourceArray("Samplers"u8, "type", false, tab.Shpk.Samplers, hasFilters, disabled);
if (!tab.Shpk.IsLegacy)
ret |= DrawShaderPackageResourceArray("Textures", "type", false, tab.Shpk.Textures, hasFilters, disabled);
ret |= DrawShaderPackageResourceArray("Unordered Access Views", "type", false, tab.Shpk.Uavs, hasFilters, disabled);
ret |= DrawShaderPackageResourceArray("Textures"u8, "type", false, tab.Shpk.Textures, hasFilters, disabled);
ret |= DrawShaderPackageResourceArray("Unordered Access Views"u8, "type", false, tab.Shpk.Uavs, hasFilters, disabled);
return ret;
}
private static void DrawKeyArray(ShpkTab tab, string arrayName, bool withId, IReadOnlyCollection<ShpkFile.Key> keys)
private static void DrawKeyArray(ShpkTab tab, ReadOnlySpan<byte> arrayName, bool withId, IReadOnlyCollection<ShpkFile.Key> keys)
{
if (keys.Count == 0)
if (keys.Count is 0)
return;
using var t = ImUtf8.TreeNode(arrayName);
using var t = Im.Tree.Node(arrayName);
if (!t)
return;
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
using var font = Im.Font.PushMono();
foreach (var (idx, key) in keys.Index())
{
using var t2 = ImUtf8.TreeNode(withId ? $"#{idx}: {tab.TryResolveName(key.Id)} (0x{key.Id:X8})" : $"#{idx}");
using var t2 = Im.Tree.Node(withId ? $"#{idx}: {tab.TryResolveName(key.Id)} (0x{key.Id:X8})" : $"#{idx}");
if (t2)
{
ImUtf8.TreeNode($"Default Value: {tab.TryResolveName(key.DefaultValue)} (0x{key.DefaultValue:X8})",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
ImUtf8.TreeNode($"Known Values: {tab.NameSetToString(key.Values, true)}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet)
.Dispose();
Im.Tree.Leaf($"Default Value: {tab.TryResolveName(key.DefaultValue)} (0x{key.DefaultValue:X8})");
Im.Tree.Leaf($"Known Values: {tab.NameSetToString(key.Values, true)}");
}
}
}
@ -633,96 +619,86 @@ public partial class ModEditWindow
if (tab.Shpk.Nodes.Length <= 0)
return;
using var t = ImUtf8.TreeNode($"Nodes ({tab.Shpk.Nodes.Length})###Nodes");
using var t = Im.Tree.Node($"Nodes ({tab.Shpk.Nodes.Length})###Nodes");
if (!t)
return;
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
using var font = Im.Font.PushMono();
foreach (var (idx, node) in tab.Shpk.Nodes.Index())
{
if (!tab.IsFilterMatch(node))
continue;
using var t2 = ImUtf8.TreeNode($"#{idx:D4}: Selector: 0x{node.Selector:X8}");
using var t2 = Im.Tree.Node($"#{idx:D4}: Selector: 0x{node.Selector:X8}");
if (!t2)
continue;
foreach (var (keyIdx, key) in node.SystemKeys.Index())
{
ImUtf8.TreeNode(
$"System Key {tab.TryResolveName(tab.Shpk.SystemKeys[keyIdx].Id)} = {tab.TryResolveName(key)} / \u2208 {{ {tab.NameSetToString(node.SystemValues![keyIdx])} }}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
Im.Tree.Leaf(
$"System Key {tab.TryResolveName(tab.Shpk.SystemKeys[keyIdx].Id)} = {tab.TryResolveName(key)} / \u2208 {{ {tab.NameSetToString(node.SystemValues![keyIdx])} }}");
}
foreach (var (keyIdx, key) in node.SceneKeys.Index())
{
ImUtf8.TreeNode(
$"Scene Key {tab.TryResolveName(tab.Shpk.SceneKeys[keyIdx].Id)} = {tab.TryResolveName(key)} / \u2208 {{ {tab.NameSetToString(node.SceneValues![keyIdx])} }}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
Im.Tree.Leaf(
$"Scene Key {tab.TryResolveName(tab.Shpk.SceneKeys[keyIdx].Id)} = {tab.TryResolveName(key)} / \u2208 {{ {tab.NameSetToString(node.SceneValues![keyIdx])} }}");
}
foreach (var (keyIdx, key) in node.MaterialKeys.Index())
{
ImUtf8.TreeNode(
$"Material Key {tab.TryResolveName(tab.Shpk.MaterialKeys[keyIdx].Id)} = {tab.TryResolveName(key)} / \u2208 {{ {tab.NameSetToString(node.MaterialValues![keyIdx])} }}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
Im.Tree.Leaf(
$"Material Key {tab.TryResolveName(tab.Shpk.MaterialKeys[keyIdx].Id)} = {tab.TryResolveName(key)} / \u2208 {{ {tab.NameSetToString(node.MaterialValues![keyIdx])} }}");
}
foreach (var (keyIdx, key) in node.SubViewKeys.Index())
{
ImUtf8.TreeNode(
$"Sub-View Key #{keyIdx} = {tab.TryResolveName(key)} / \u2208 {{ {tab.NameSetToString(node.SubViewValues![keyIdx])} }}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
Im.Tree.Leaf(
$"Sub-View Key #{keyIdx} = {tab.TryResolveName(key)} / \u2208 {{ {tab.NameSetToString(node.SubViewValues![keyIdx])} }}");
}
ImUtf8.TreeNode($"Pass Indices: {string.Join(' ', node.PassIndices.Select(c => $"{c:X2}"))}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
Im.Tree.Leaf($"Pass Indices: {string.Join(' ', node.PassIndices.Select(c => $"{c:X2}"))}");
foreach (var (passIdx, pass) in node.Passes.Index())
{
ImUtf8.TreeNode(
$"Pass #{passIdx}: ID: {tab.TryResolveName(pass.Id)}, Vertex Shader #{pass.VertexShader}, Pixel Shader #{pass.PixelShader}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet)
.Dispose();
Im.Tree.Leaf(
$"Pass #{passIdx}: ID: {tab.TryResolveName(pass.Id)}, Vertex Shader #{pass.VertexShader}, Pixel Shader #{pass.PixelShader}");
}
}
}
private static void DrawShaderPackageSelection(ShpkTab tab)
{
if (!ImUtf8.CollapsingHeader("Shader Selection"u8))
if (!Im.Tree.Header("Shader Selection"u8))
return;
DrawKeyArray(tab, "System Keys", true, tab.Shpk.SystemKeys);
DrawKeyArray(tab, "Scene Keys", true, tab.Shpk.SceneKeys);
DrawKeyArray(tab, "Material Keys", true, tab.Shpk.MaterialKeys);
DrawKeyArray(tab, "Sub-View Keys", false, tab.Shpk.SubViewKeys);
DrawKeyArray(tab, "System Keys"u8, true, tab.Shpk.SystemKeys);
DrawKeyArray(tab, "Scene Keys"u8, true, tab.Shpk.SceneKeys);
DrawKeyArray(tab, "Material Keys"u8, true, tab.Shpk.MaterialKeys);
DrawKeyArray(tab, "Sub-View Keys"u8, false, tab.Shpk.SubViewKeys);
DrawShaderPackageNodes(tab);
using var t = ImUtf8.TreeNode($"Node Selectors ({tab.Shpk.NodeSelectors.Count})###NodeSelectors");
using var t = Im.Tree.Node($"Node Selectors ({tab.Shpk.NodeSelectors.Count})###NodeSelectors");
if (t)
{
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
using var font = Im.Font.PushMono();
foreach (var selector in tab.Shpk.NodeSelectors)
{
ImUtf8.TreeNode($"#{selector.Value:D4}: Selector: 0x{selector.Key:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet)
.Dispose();
}
Im.Tree.Leaf($"#{selector.Value:D4}: Selector: 0x{selector.Key:X8}");
}
}
private static void DrawOtherShaderPackageDetails(ShpkTab tab)
{
if (!ImUtf8.CollapsingHeader("Further Content"u8))
if (!Im.Tree.Header("Further Content"u8))
return;
ImUtf8.TreeNode($"Version: 0x{tab.Shpk.Version:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
Im.Tree.Leaf($"Version: 0x{tab.Shpk.Version:X8}");
if (tab.Shpk.AdditionalData.Length > 0)
{
using var t = ImUtf8.TreeNode($"Additional Data (Size: {tab.Shpk.AdditionalData.Length})###AdditionalData");
using var t = Im.Tree.Node($"Additional Data (Size: {tab.Shpk.AdditionalData.Length})###AdditionalData");
if (t)
Widget.DrawHexViewer(tab.Shpk.AdditionalData);
ImEx.HexViewer(tab.Shpk.AdditionalData);
}
}
@ -737,10 +713,8 @@ public partial class ModEditWindow
{
switch (components)
{
case 0: break;
case DisassembledShader.VectorComponents.All:
sb.Append($"[{i}], ");
break;
case 0: break;
case DisassembledShader.VectorComponents.All: sb.Append($"[{i}], "); break;
default:
sb.Append($"[{i}].");
foreach (var c in components.ToString().Where(char.IsUpper))
@ -753,10 +727,8 @@ public partial class ModEditWindow
switch (usedDynamically ?? 0)
{
case 0: break;
case DisassembledShader.VectorComponents.All:
sb.Append("[*], ");
break;
case 0: break;
case DisassembledShader.VectorComponents.All: sb.Append("[*], "); break;
default:
sb.Append("[*].");
foreach (var c in usedDynamically!.Value.ToString().Where(char.IsUpper))
@ -782,6 +754,6 @@ public partial class ModEditWindow
sb.Append("Alpha, ");
}
return sb.Length == 0 ? string.Empty : sb.ToString(0, sb.Length - 2);
return sb.Length is 0 ? string.Empty : sb.ToString(0, sb.Length - 2);
}
}

View file

@ -1,8 +1,5 @@
using Dalamud.Bindings.ImGui;
using ImSharp;
using Luna;
using OtterGui;
using OtterGui.Raii;
using OtterTex;
using Penumbra.Communication;
using Penumbra.Import.Textures;
@ -41,18 +38,18 @@ public partial class ModEditWindow
"Save the current texture compressed via BC7 compression.\nThis offers a 4:1 compression ratio and has almost indistinguishable quality, but may take a while.\n\nGeneric format that can be used for most textures."),
};
private void DrawInputChild(string label, Texture tex, Vector2 size, Vector2 imageSize)
private void DrawInputChild(ReadOnlySpan<byte> label, Texture tex, Vector2 size, Vector2 imageSize)
{
using (var child = ImRaii.Child(label, size, true))
using (var child = Im.Child.Begin(label, size, true))
{
if (!child)
return;
using var id = ImRaii.PushId(label);
using var id = Im.Id.Push(label);
ImEx.TextFramed(label, Im.ContentRegion.Available with { Y = 0 }, ImGuiColor.FrameBackground.Get());
Im.Line.New();
using (ImRaii.Disabled(!_center.SaveTask.IsCompleted))
using (Im.Disabled(!_center.SaveTask.IsCompleted))
{
TextureDrawer.PathInputBox(_textures, tex, ref tex.TmpPath, "##input"u8, "Import Image..."u8,
"Can import game paths as well as your own files."u8, Mod!.ModPath.FullName, _fileDialog, _config.DefaultModImportPath);
@ -69,7 +66,7 @@ public partial class ModEditWindow
}
Im.Line.New();
using var child2 = ImRaii.Child("image");
using var child2 = Im.Child.Begin("image"u8);
if (child2)
TextureDrawer.Draw(tex, imageSize);
}
@ -82,44 +79,43 @@ public partial class ModEditWindow
{
var (text, desc) = SaveAsStrings[_currentSaveAs];
Im.Item.SetNextWidth(-Im.Style.FrameHeight - Im.Style.ItemSpacing.X);
using var combo = ImRaii.Combo("##format", text);
ImGuiUtil.HoverTooltip(desc);
using var combo = Im.Combo.Begin("##format"u8, text);
Im.Tooltip.OnHover(desc);
if (!combo)
return;
foreach (var (idx, (newText, newDesc)) in SaveAsStrings.Index())
{
if (ImGui.Selectable(newText, idx == _currentSaveAs))
if (Im.Selectable(newText, idx == _currentSaveAs))
_currentSaveAs = idx;
ImGuiUtil.SelectableHelpMarker(newDesc);
LunaStyle.DrawRightAlignedHelpMarker(newDesc);
}
}
private void RedrawOnSaveBox()
{
var redraw = _config.Ephemeral.ForceRedrawOnFileChange;
if (ImGui.Checkbox("Redraw on Save", ref redraw))
if (Im.Checkbox("Redraw on Save"u8, ref redraw))
{
_config.Ephemeral.ForceRedrawOnFileChange = redraw;
_config.Ephemeral.Save();
}
ImGuiUtil.HoverTooltip("Force a redraw of your player character whenever you save a file here.");
Im.Tooltip.OnHover("Force a redraw of your player character whenever you save a file here."u8);
}
private void MipMapInput()
{
ImGui.Checkbox("##mipMaps", ref _addMipMaps);
ImGuiUtil.HoverTooltip(
"Add the appropriate number of MipMaps to the file.");
Im.Checkbox("##mipMaps"u8, ref _addMipMaps);
Im.Tooltip.OnHover("Add the appropriate number of MipMaps to the file."u8);
}
private bool _forceTextureStartPath = true;
private void DrawOutputChild(Vector2 size, Vector2 imageSize)
{
using var child = ImRaii.Child("Output", size, true);
using var child = Im.Child.Begin("Output"u8, size, true);
if (!child)
return;
@ -133,14 +129,13 @@ public partial class ModEditWindow
var canSaveInPlace = Path.IsPathRooted(_left.Path) && _left.Type is TextureType.Tex or TextureType.Dds or TextureType.Png;
var isActive = _config.DeleteModModifier.IsActive();
var tt = isActive
? "This saves the texture in place. This is not revertible."
: $"This saves the texture in place. This is not revertible. Hold {_config.DeleteModModifier} to save.";
var buttonSize2 = new Vector2((Im.ContentRegion.Available.X - Im.Style.ItemSpacing.X) / 2, 0);
var buttonSize3 = new Vector2((Im.ContentRegion.Available.X - Im.Style.ItemSpacing.X * 2) / 3, 0);
if (ImGuiUtil.DrawDisabledButton("Save in place", buttonSize2,
tt, !isActive || !canSaveInPlace || _center.IsLeftCopy && _currentSaveAs == (int)CombinedTexture.TextureSaveType.AsIs))
if (ImEx.Button("Save in place"u8, buttonSize2,
isActive
? "This saves the texture in place. This is not revertible."u8
: $"This saves the texture in place. This is not revertible. Hold {_config.DeleteModModifier} to save.",
!isActive || !canSaveInPlace || _center.IsLeftCopy && _currentSaveAs is (int)CombinedTexture.TextureSaveType.AsIs))
{
_center.SaveAs(_left.Type, _textures, _left.Path, (CombinedTexture.TextureSaveType)_currentSaveAs, _addMipMaps);
AddChangeTask(_left.Path);
@ -148,23 +143,23 @@ public partial class ModEditWindow
}
Im.Line.Same();
if (ImGui.Button("Save as TEX", buttonSize2))
if (Im.Button("Save as TEX"u8, buttonSize2))
OpenSaveAsDialog(".tex");
if (ImGui.Button("Export as TGA", buttonSize3))
if (Im.Button("Export as TGA"u8, buttonSize3))
OpenSaveAsDialog(".tga");
Im.Line.Same();
if (ImGui.Button("Export as PNG", buttonSize3))
if (Im.Button("Export as PNG"u8, buttonSize3))
OpenSaveAsDialog(".png");
Im.Line.Same();
if (ImGui.Button("Export as DDS", buttonSize3))
if (Im.Button("Export as DDS"u8, buttonSize3))
OpenSaveAsDialog(".dds");
Im.Line.New();
var canConvertInPlace = canSaveInPlace && _left.Type is TextureType.Tex && _center.IsLeftCopy;
if (ImGuiUtil.DrawDisabledButton("Convert to BC7", buttonSize3,
"This converts the texture to BC7 format in place. This is not revertible.",
if (ImEx.Button("Convert to BC7"u8, buttonSize3,
"This converts the texture to BC7 format in place. This is not revertible."u8,
!canConvertInPlace || _left.Format is DXGIFormat.BC7Typeless or DXGIFormat.BC7UNorm or DXGIFormat.BC7UNormSRGB))
{
_center.SaveAsTex(_textures, _left.Path, CombinedTexture.TextureSaveType.BC7, _left.MipMaps > 1);
@ -173,8 +168,8 @@ public partial class ModEditWindow
}
Im.Line.Same();
if (ImGuiUtil.DrawDisabledButton("Convert to BC3", buttonSize3,
"This converts the texture to BC3 format in place. This is not revertible.",
if (ImEx.Button("Convert to BC3"u8, buttonSize3,
"This converts the texture to BC3 format in place. This is not revertible."u8,
!canConvertInPlace || _left.Format is DXGIFormat.BC3Typeless or DXGIFormat.BC3UNorm or DXGIFormat.BC3UNormSRGB))
{
_center.SaveAsTex(_textures, _left.Path, CombinedTexture.TextureSaveType.BC3, _left.MipMaps > 1);
@ -183,8 +178,8 @@ public partial class ModEditWindow
}
Im.Line.Same();
if (ImGuiUtil.DrawDisabledButton("Convert to RGBA", buttonSize3,
"This converts the texture to RGBA format in place. This is not revertible.",
if (ImEx.Button("Convert to RGBA"u8, buttonSize3,
"This converts the texture to RGBA format in place. This is not revertible."u8,
!canConvertInPlace
|| _left.Format is DXGIFormat.B8G8R8A8UNorm or DXGIFormat.B8G8R8A8Typeless or DXGIFormat.B8G8R8A8UNormSRGB))
{
@ -199,22 +194,22 @@ public partial class ModEditWindow
case TaskStatus.WaitingForActivation:
case TaskStatus.WaitingToRun:
case TaskStatus.Running:
ImGuiUtil.DrawTextButton("Computing...", -Vector2.UnitX, new Rgba32(Colors.PressEnterWarningBg).Color);
ImEx.TextFramed("Computing..."u8, Im.ContentRegion.Available with { Y = 0 }, Colors.PressEnterWarningBg);
break;
case TaskStatus.Canceled:
case TaskStatus.Faulted:
{
Im.Text("Could not save file:"u8);
using var color = ImGuiColor.Text.Push(new Vector4(1, 0, 0, 1));
ImGuiUtil.TextWrapped(_center.SaveTask.Exception?.ToString() ?? "Unknown Error");
Im.TextWrapped(_center.SaveTask.Exception?.ToString() ?? "Unknown Error");
break;
}
default: ImGui.Dummy(new Vector2(1, Im.Style.FrameHeight)); break;
default: Im.Dummy(new Vector2(1, Im.Style.FrameHeight)); break;
}
Im.Line.New();
using var child2 = ImRaii.Child("image");
using var child2 = Im.Child.Begin("image"u8);
if (child2)
_center.Draw(_textures, imageSize);
}
@ -292,7 +287,7 @@ public partial class ModEditWindow
private void DrawTextureTab()
{
using var tab = ImRaii.TabItem("Textures");
using var tab = Im.TabBar.BeginItem("Textures"u8);
if (!tab)
return;
@ -309,13 +304,13 @@ public partial class ModEditWindow
});
var childWidth = GetChildWidth();
var imageSize = new Vector2(childWidth.X - Im.Style.FramePadding.X * 2);
DrawInputChild("Input Texture", _left, childWidth, imageSize);
DrawInputChild("Input Texture"u8, _left, childWidth, imageSize);
Im.Line.Same();
DrawOutputChild(childWidth, imageSize);
if (!_overlayCollapsed)
{
Im.Line.Same();
DrawInputChild("Overlay Texture", _right, childWidth, imageSize);
DrawInputChild("Overlay Texture"u8, _right, childWidth, imageSize);
}
Im.Line.Same();
@ -330,12 +325,12 @@ public partial class ModEditWindow
private void DrawOverlayCollapseButton()
{
var (label, tooltip) = _overlayCollapsed
? (">", "Show a third panel in which you can import an additional texture as an overlay for the primary texture.")
: ("<", "Hide the overlay texture panel and clear the currently loaded overlay texture, if any.");
if (ImGui.Button(label, new Vector2(Im.Style.TextHeight, Im.ContentRegion.Available.Y)))
? RefTuple.Create(">"u8, "Show a third panel in which you can import an additional texture as an overlay for the primary texture."u8)
: RefTuple.Create("<"u8, "Hide the overlay texture panel and clear the currently loaded overlay texture, if any."u8);
if (Im.Button(label, Im.ContentRegion.Available with { X = Im.Style.TextHeight }))
_overlayCollapsed = !_overlayCollapsed;
ImGuiUtil.HoverTooltip(tooltip);
Im.Tooltip.OnHover(tooltip);
}
private static bool GetFirstTexture(IEnumerable<string> files, [NotNullWhen(true)] out string? file)
@ -345,10 +340,10 @@ public partial class ModEditWindow
}
private static readonly string[] ValidTextureExtensions =
{
[
".png",
".dds",
".tex",
".tga",
};
];
}

View file

@ -1,13 +1,10 @@
using Dalamud.Interface;
using System.Collections.Frozen;
using Dalamud.Interface.Components;
using Dalamud.Interface.DragDrop;
using Dalamud.Plugin.Services;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Luna;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.Api.Enums;
using Penumbra.Collections.Manager;
using Penumbra.Communication;
@ -27,6 +24,7 @@ using Penumbra.String.Classes;
using Penumbra.UI.AdvancedWindow.Materials;
using Penumbra.UI.AdvancedWindow.Meta;
using Penumbra.UI.Classes;
using Penumbra.UI.Combos;
using MdlMaterialEditor = Penumbra.Mods.Editor.MdlMaterialEditor;
namespace Penumbra.UI.AdvancedWindow;
@ -154,7 +152,7 @@ public partial class ModEditWindow : IndexedWindow, IDisposable
sb.Append($" | {subMods} Options");
if (size > 0)
sb.Append($" | {_editor.Files.Available.Count} Files ({Functions.HumanReadableSize(size)})");
sb.Append($" | {_editor.Files.Available.Count} Files ({FormattingFunctions.HumanReadableSize(size)})");
if (unused > 0)
sb.Append($" | {unused} Unused Files");
@ -203,12 +201,12 @@ public partial class ModEditWindow : IndexedWindow, IDisposable
var thickness = (int)(20 * Im.Style.GlobalScale);
var offsetX = Im.ContentRegion.Available.X / 2 - radius;
var offsetY = Im.ContentRegion.Available.Y / 2 - radius;
ImGui.SetCursorPos(ImGui.GetCursorPos() + new Vector2(offsetX, offsetY));
Im.Cursor.Position += new Vector2(offsetX, offsetY);
ImEx.Spinner("##spinner"u8, radius, thickness, ImGuiColor.Text.Get());
return;
}
using var tabBar = ImUtf8.TabBar("##tabs"u8);
using var tabBar = Im.TabBar.Begin("##tabs"u8);
if (!tabBar)
return;
@ -223,7 +221,7 @@ public partial class ModEditWindow : IndexedWindow, IDisposable
_materialTab.Draw();
DrawTextureTab();
_shaderPackageTab.Draw();
using (var tab = ImUtf8.TabItem("Item Swap"u8))
using (var tab = tabBar.Item("Item Swap"u8))
{
if (tab)
_itemSwapTab.DrawContent();
@ -235,6 +233,15 @@ public partial class ModEditWindow : IndexedWindow, IDisposable
DrawMaterialReassignmentTab();
}
private static readonly FrozenDictionary<GenderRace, StringU8> RaceCodeNames = Enum.GetValues<GenderRace>().ToFrozenDictionary(v => v, v =>
{
if (v is GenderRace.Unknown)
return new StringU8("All Races and Genders");
var (gender, race) = v.Split();
return new StringU8($"({v.ToRaceCode()}) {race.ToNameU8()} {gender.ToNameU8()} ");
});
/// <summary> A row of three buttonSizes and a help marker that can be used for material suffix changing. </summary>
private static class MaterialSuffix
{
@ -242,25 +249,16 @@ public partial class ModEditWindow : IndexedWindow, IDisposable
private static string _materialSuffixTo = string.Empty;
private static GenderRace _raceCode = GenderRace.Unknown;
private static string RaceCodeName(GenderRace raceCode)
{
if (raceCode == GenderRace.Unknown)
return "All Races and Genders";
var (gender, race) = raceCode.Split();
return $"({raceCode.ToRaceCode()}) {race.ToName()} {gender.ToName()} ";
}
private static void DrawRaceCodeCombo(Vector2 buttonSize)
{
Im.Item.SetNextWidth(buttonSize.X);
using var combo = ImRaii.Combo("##RaceCode", RaceCodeName(_raceCode));
using var combo = Im.Combo.Begin("##RaceCode"u8, RaceCodeNames[_raceCode]);
if (!combo)
return;
foreach (var raceCode in Enum.GetValues<GenderRace>())
foreach (var (raceCode, name) in RaceCodeNames)
{
if (ImGui.Selectable(RaceCodeName(raceCode), _raceCode == raceCode))
if (Im.Selectable(name, _raceCode == raceCode))
_raceCode = raceCode;
}
}
@ -270,175 +268,172 @@ public partial class ModEditWindow : IndexedWindow, IDisposable
DrawRaceCodeCombo(buttonSize);
Im.Line.Same();
Im.Item.SetNextWidth(buttonSize.X);
ImGui.InputTextWithHint("##suffixFrom", "From...", ref _materialSuffixFrom, 32);
Im.Input.Text("##suffixFrom"u8, ref _materialSuffixFrom, "From..."u8);
Im.Line.Same();
Im.Item.SetNextWidth(buttonSize.X);
ImGui.InputTextWithHint("##suffixTo", "To...", ref _materialSuffixTo, 32);
Im.Input.Text("##suffixTo"u8, ref _materialSuffixTo, "To..."u8);
Im.Line.Same();
var disabled = !MdlMaterialEditor.ValidString(_materialSuffixTo);
var tt = _materialSuffixTo.Length == 0
Utf8StringHandler<TextStringHandlerBuffer> tt = _materialSuffixTo.Length is 0
? "Please enter a target suffix."
: _materialSuffixFrom == _materialSuffixTo
? "The source and target are identical."
: disabled
? "The suffix is invalid."
: _materialSuffixFrom.Length == 0
? _raceCode == GenderRace.Unknown
: _materialSuffixFrom.Length is 0
? _raceCode is GenderRace.Unknown
? "Convert all skin material suffices to the target."
: "Convert all skin material suffices for the given race code to the target."
: _raceCode == GenderRace.Unknown
: _raceCode is GenderRace.Unknown
? $"Convert all skin material suffices that are currently '{_materialSuffixFrom}' to '{_materialSuffixTo}'."
: $"Convert all skin material suffices for the given race code that are currently '{_materialSuffixFrom}' to '{_materialSuffixTo}'.";
if (ImGuiUtil.DrawDisabledButton("Change Material Suffix", buttonSize, tt, disabled))
if (ImEx.Button("Change Material Suffix"u8, buttonSize, tt, disabled))
editor.MdlMaterialEditor.ReplaceAllMaterials(_materialSuffixTo, _materialSuffixFrom, _raceCode);
var anyChanges = editor.MdlMaterialEditor.ModelFiles.Any(m => m.Changed);
if (ImGuiUtil.DrawDisabledButton("Save All Changes", buttonSize,
anyChanges ? "Irreversibly rewrites all currently applied changes to model files." : "No changes made yet.", !anyChanges))
if (ImEx.Button("Save All Changes"u8, buttonSize,
anyChanges ? "Irreversibly rewrites all currently applied changes to model files."u8 : "No changes made yet."u8,
!anyChanges))
editor.MdlMaterialEditor.SaveAllModels(editor.Compactor);
Im.Line.Same();
if (ImGuiUtil.DrawDisabledButton("Revert All Changes", buttonSize,
anyChanges ? "Revert all currently made and unsaved changes." : "No changes made yet.", !anyChanges))
if (ImEx.Button("Revert All Changes"u8, buttonSize,
anyChanges ? "Revert all currently made and unsaved changes."u8 : "No changes made yet."u8, !anyChanges))
editor.MdlMaterialEditor.RestoreAllModels();
Im.Line.Same();
ImGuiComponents.HelpMarker(
"Model files refer to the skin material they should use. This skin material is always the same, but modders have started using different suffices to differentiate between body types.\n"
+ "This option allows you to switch the suffix of all model files to another. This changes the files, so you do this on your own risk.\n"
+ "If you do not know what the currently used suffix of this mod is, you can leave 'From' blank and it will replace all suffices with 'To', instead of only the matching ones.");
Im.Line.SameInner();
LunaStyle.DrawAlignedHelpMarker(
"Model files refer to the skin material they should use. This skin material is always the same, but modders have started using different suffices to differentiate between body types.\n"u8
+ "This option allows you to switch the suffix of all model files to another. This changes the files, so you do this on your own risk.\n"u8
+ "If you do not know what the currently used suffix of this mod is, you can leave 'From' blank and it will replace all suffices with 'To', instead of only the matching ones."u8);
}
}
private void DrawMissingFilesTab()
{
if (_editor.Files.Missing.Count == 0)
if (_editor.Files.Missing.Count is 0)
return;
using var tab = ImRaii.TabItem("Missing Files");
using var tab = Im.TabBar.BeginItem("Missing Files"u8);
if (!tab)
return;
Im.Line.New();
if (ImGui.Button("Remove Missing Files from Mod"))
if (Im.Button("Remove Missing Files from Mod"u8))
_editor.FileEditor.RemoveMissingPaths(Mod!, _editor.Option!);
using var child = ImRaii.Child("##unusedFiles", -Vector2.One, true);
using var child = Im.Child.Begin("##unusedFiles"u8, Im.ContentRegion.Available, true);
if (!child)
return;
using var table = Im.Table.Begin("##missingFiles"u8, 1, TableFlags.RowBackground, -Vector2.One);
using var table = Im.Table.Begin("##missingFiles"u8, 1, TableFlags.RowBackground, Im.ContentRegion.Available);
if (!table)
return;
foreach (var path in _editor.Files.Missing)
{
ImGui.TableNextColumn();
Im.Text(path.FullName);
}
table.DrawColumn(path.FullName);
}
private void DrawDuplicatesTab()
{
using var tab = ImRaii.TabItem("Duplicates");
using var tab = Im.TabBar.BeginItem("Duplicates"u8);
if (!tab)
return;
if (_editor.Duplicates.Worker.IsCompleted)
{
if (ImGuiUtil.DrawDisabledButton("Scan for Duplicates", Vector2.Zero,
"Search for identical files in this mod. This may take a while.", false))
if (ImEx.Button("Scan for Duplicates"u8, Vector2.Zero,
"Search for identical files in this mod. This may take a while."u8))
_editor.Duplicates.StartDuplicateCheck(_editor.Files.Available);
}
else
{
if (ImGuiUtil.DrawDisabledButton("Cancel Scanning for Duplicates", Vector2.Zero, "Cancel the current scanning operation...", false))
if (ImEx.Button("Cancel Scanning for Duplicates"u8, Vector2.Zero, "Cancel the current scanning operation..."u8))
_editor.Duplicates.Clear();
}
const string desc =
"Tries to create a unique copy of a file for every game path manipulated and put them in [Groupname]/[Optionname]/[GamePath] order.\n"
+ "This will also delete all unused files and directories if it succeeds.\n"
+ "Care was taken that a failure should not destroy the mod but revert to its original state, but you use this at your own risk anyway.";
var modifier = _config.DeleteModModifier.IsActive();
var tt = _allowReduplicate ? desc :
modifier ? desc : desc + $"\n\nNo duplicates detected! Hold {_config.DeleteModModifier} to force normalization anyway.";
if (_editor.ModNormalizer.Running)
{
ImGui.ProgressBar((float)_editor.ModNormalizer.Step / _editor.ModNormalizer.TotalSteps,
Im.ProgressBar((float)_editor.ModNormalizer.Step / _editor.ModNormalizer.TotalSteps,
new Vector2(300 * Im.Style.GlobalScale, Im.Style.FrameHeight),
$"{_editor.ModNormalizer.Step} / {_editor.ModNormalizer.TotalSteps}");
}
else if (ImGuiUtil.DrawDisabledButton("Re-Duplicate and Normalize Mod", Vector2.Zero, tt, !_allowReduplicate && !modifier))
else if (ImEx.Button("Re-Duplicate and Normalize Mod"u8, Vector2.Zero,
"Tries to create a unique copy of a file for every game path manipulated and put them in [Groupname]/[Optionname]/[GamePath] order.\n"u8
+ "This will also delete all unused files and directories if it succeeds.\n"u8
+ "Care was taken that a failure should not destroy the mod but revert to its original state, but you use this at your own risk anyway."u8,
!_allowReduplicate && !modifier))
{
_editor.ModNormalizer.Normalize(Mod!);
_editor.ModNormalizer.Worker.ContinueWith(_ => _editor.LoadMod(Mod!, _editor.GroupIdx, _editor.DataIdx), TaskScheduler.Default);
}
if (_allowReduplicate && !modifier)
Im.Tooltip.OnHover($"\n\nNo duplicates detected! Hold {_config.DeleteModModifier} to force normalization anyway.");
if (!_editor.Duplicates.Worker.IsCompleted)
return;
if (_editor.Duplicates.Duplicates.Count == 0)
if (_editor.Duplicates.Duplicates.Count is 0)
{
Im.Line.New();
Im.Text("No duplicates found."u8);
return;
}
if (ImGui.Button("Delete and Redirect Duplicates"))
if (Im.Button("Delete and Redirect Duplicates"u8))
_editor.Duplicates.DeleteDuplicates(_editor.Files, _editor.Mod!, _editor.Option!, true);
if (_editor.Duplicates.SavedSpace > 0)
{
Im.Line.Same();
Im.Text($"Frees up {Functions.HumanReadableSize(_editor.Duplicates.SavedSpace)} from your hard drive.");
Im.Text($"Frees up {FormattingFunctions.HumanReadableSize(_editor.Duplicates.SavedSpace)} from your hard drive.");
}
using var child = ImRaii.Child("##duptable", -Vector2.One, true);
using var child = Im.Child.Begin("##duptable"u8, Im.ContentRegion.Available, true);
if (!child)
return;
using var table = Im.Table.Begin("##duplicates"u8, 3, TableFlags.RowBackground | TableFlags.SizingFixedFit, -Vector2.One);
using var table = Im.Table.Begin("##duplicates"u8, 3, TableFlags.RowBackground | TableFlags.SizingFixedFit, Im.ContentRegion.Available);
if (!table)
return;
var width = ImGui.CalcTextSize("NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN ").X;
var width = Im.Font.CalculateSize("NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN "u8).X;
table.SetupColumn("file"u8, TableColumnFlags.WidthStretch);
table.SetupColumn("size"u8, TableColumnFlags.WidthFixed, ImGui.CalcTextSize("NNN.NNN "u8).X);
table.SetupColumn("size"u8, TableColumnFlags.WidthFixed, Im.Font.CalculateSize("NNN.NNN "u8).X);
table.SetupColumn("hash"u8, TableColumnFlags.WidthFixed,
ImGui.GetWindowWidth() > 2 * width ? width : Im.Font.CalculateSize("NNNNNNNN... "u8).X);
Im.Window.Width > 2 * width ? width : Im.Font.CalculateSize("NNNNNNNN... "u8).X);
foreach (var (set, size, hash) in _editor.Duplicates.Duplicates.Where(s => s.Paths.Length > 1))
{
ImGui.TableNextColumn();
using var tree = ImRaii.TreeNode(set[0].FullName[(Mod!.ModPath.FullName.Length + 1)..],
ImGuiTreeNodeFlags.NoTreePushOnOpen);
ImGui.TableNextColumn();
ImGuiUtil.RightAlign(Functions.HumanReadableSize(size));
ImGui.TableNextColumn();
using (var _ = ImRaii.PushFont(UiBuilder.MonoFont))
table.NextColumn();
using var tree = Im.Tree.Node(set[0].FullName[(Mod!.ModPath.FullName.Length + 1)..],
TreeNodeFlags.NoTreePushOnOpen);
table.NextColumn();
ImEx.TextRightAligned(FormattingFunctions.HumanReadableSize(size));
table.NextColumn();
using (var _ = Im.Font.PushMono())
{
if (ImGui.GetWindowWidth() > 2 * width)
ImGuiUtil.RightAlign(string.Concat(hash.Select(b => b.ToString("X2"))));
if (Im.Window.Width > 2 * width)
ImEx.TextRightAligned(FormattingFunctions.BytewiseHex(hash));
else
ImGuiUtil.RightAlign(string.Concat(hash.Take(4).Select(b => b.ToString("X2"))) + "...");
ImEx.TextRightAligned($"{FormattingFunctions.BytewiseHex(hash.AsSpan(4))}...");
}
if (!tree)
continue;
using var indent = ImRaii.PushIndent();
using var indent = Im.Indent();
foreach (var duplicate in set.Skip(1))
{
ImGui.TableNextColumn();
ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, Colors.RedTableBgTint);
using var node = ImRaii.TreeNode(duplicate.FullName[(Mod!.ModPath.FullName.Length + 1)..], ImGuiTreeNodeFlags.Leaf);
ImGui.TableNextColumn();
ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, Colors.RedTableBgTint);
ImGui.TableNextColumn();
ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, Colors.RedTableBgTint);
table.NextColumn();
table.SetBackgroundColor(TableBackgroundTarget.Cell, Colors.RedTableBgTint);
Im.Tree.Leaf(duplicate.FullName.AsSpan(Mod!.ModPath.FullName.Length + 1), TreeNodeFlags.Leaf);
table.NextColumn();
table.SetBackgroundColor(TableBackgroundTarget.Cell, Colors.RedTableBgTint);
table.NextColumn();
table.SetBackgroundColor(TableBackgroundTarget.Cell, Colors.RedTableBgTint);
}
}
}
@ -448,7 +443,7 @@ public partial class ModEditWindow : IndexedWindow, IDisposable
using var style = ImStyleDouble.ItemSpacing.Push(Vector2.Zero).Push(ImStyleSingle.FrameRounding, 0);
var width = new Vector2(Im.ContentRegion.Available.X / 3, 0);
var ret = false;
if (ImUtf8.ButtonEx("Default Option"u8, "Switch to the default option for the mod.\nThis resets unsaved changes."u8, width,
if (ImEx.Button("Default Option"u8, width, "Switch to the default option for the mod.\nThis resets unsaved changes."u8,
_editor.Option is DefaultSubMod))
{
_editor.LoadOption(-1, 0).Wait();
@ -456,17 +451,16 @@ public partial class ModEditWindow : IndexedWindow, IDisposable
}
Im.Line.Same();
if (ImUtf8.ButtonEx("Refresh Data"u8, "Refresh data for the current option.\nThis resets unsaved changes."u8, width))
if (ImEx.Button("Refresh Data"u8, width, "Refresh data for the current option.\nThis resets unsaved changes."u8))
{
_editor.LoadMod(_editor.Mod!, _editor.GroupIdx, _editor.DataIdx).Wait();
ret = true;
}
Im.Line.Same();
if (_optionSelect.Draw(width.X))
if (_optionSelect.Draw("##option"u8, _editor.Option?.GetFullName() ?? string.Empty, default, width.X, out var option))
{
var (groupIdx, dataIdx) = _optionSelect.CurrentSelection.Index;
_editor.LoadOption(groupIdx, dataIdx).Wait();
_editor.LoadOption(option.GroupIndex, option.DataIndex).Wait();
ret = true;
}
@ -478,36 +472,36 @@ public partial class ModEditWindow : IndexedWindow, IDisposable
private void DrawSwapTab()
{
using var tab = ImRaii.TabItem("File Swaps");
using var tab = Im.TabBar.BeginItem("File Swaps"u8);
if (!tab)
return;
DrawOptionSelectHeader();
var setsEqual = !_editor.SwapEditor.Changes;
var tt = setsEqual ? "No changes staged." : "Apply the currently staged changes to the option.";
var tt = setsEqual ? "No changes staged."u8 : "Apply the currently staged changes to the option."u8;
Im.Line.New();
if (ImGuiUtil.DrawDisabledButton("Apply Changes", Vector2.Zero, tt, setsEqual))
if (ImEx.Button("Apply Changes"u8, Vector2.Zero, tt, setsEqual))
_editor.SwapEditor.Apply(_editor.Option!);
Im.Line.Same();
tt = setsEqual ? "No changes staged." : "Revert all currently staged changes.";
if (ImGuiUtil.DrawDisabledButton("Revert Changes", Vector2.Zero, tt, setsEqual))
tt = setsEqual ? "No changes staged."u8 : "Revert all currently staged changes."u8;
if (ImEx.Button("Revert Changes"u8, Vector2.Zero, tt, setsEqual))
_editor.SwapEditor.Revert(_editor.Option!);
var otherSwaps = _editor.Mod!.TotalSwapCount - _editor.Option!.FileSwaps.Count;
if (otherSwaps > 0)
{
Im.Line.Same();
ImGuiUtil.DrawTextButton($"There are {otherSwaps} file swaps configured in other options.", Vector2.Zero,
ImEx.TextFramed($"There are {otherSwaps} file swaps configured in other options.", Vector2.Zero,
ColorId.RedundantAssignment.Value().Color);
}
using var child = ImRaii.Child("##swaps", -Vector2.One, true);
using var child = Im.Child.Begin("##swaps"u8, Im.ContentRegion.Available, true);
if (!child)
return;
using var table = Im.Table.Begin("##table"u8, 3, TableFlags.RowBackground, -Vector2.One);
using var table = Im.Table.Begin("##table"u8, 3, TableFlags.RowBackground, Im.ContentRegion.Available);
if (!table)
return;
@ -520,46 +514,45 @@ public partial class ModEditWindow : IndexedWindow, IDisposable
foreach (var (gamePath, file) in _editor.SwapEditor.Swaps.ToList())
{
using var id = ImRaii.PushId(idx++);
ImGui.TableNextColumn();
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), iconSize, "Delete this swap.", false, true))
using var id = Im.Id.Push(idx++);
table.NextColumn();
if (ImEx.Icon.Button(LunaStyle.DeleteIcon, "Delete this swap."u8))
_editor.SwapEditor.Remove(gamePath);
ImGui.TableNextColumn();
table.NextColumn();
var tmp = file.FullName;
Im.Item.SetNextWidth(-1);
if (ImGui.InputText("##value", ref tmp, Utf8GamePath.MaxGamePathLength) && tmp.Length > 0)
if (Im.Input.Text("##value"u8, ref tmp, maxLength: Utf8GamePath.MaxGamePathLength) && tmp.Length > 0)
_editor.SwapEditor.Change(gamePath, new FullPath(tmp));
ImGui.TableNextColumn();
table.NextColumn();
tmp = gamePath.Path.ToString();
Im.Item.SetNextWidth(-1);
if (ImGui.InputText("##key", ref tmp, Utf8GamePath.MaxGamePathLength)
if (Im.Input.Text("##key"u8, ref tmp, maxLength: Utf8GamePath.MaxGamePathLength)
&& Utf8GamePath.FromString(tmp, out var path)
&& !_editor.SwapEditor.Swaps.ContainsKey(path))
_editor.SwapEditor.Change(gamePath, path);
}
ImGui.TableNextColumn();
table.NextColumn();
var addable = Utf8GamePath.FromString(_newSwapKey, out var newPath)
&& newPath.Length > 0
&& _newSwapValue.Length > 0
&& _newSwapValue != _newSwapKey
&& !_editor.SwapEditor.Swaps.ContainsKey(newPath);
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), iconSize, "Add a new file swap to this option.", !addable,
true))
if (ImEx.Icon.Button(LunaStyle.AddObjectIcon, "Add a new file swap to this option."u8, !addable))
{
_editor.SwapEditor.Add(newPath, new FullPath(_newSwapValue));
_newSwapKey = string.Empty;
_newSwapValue = string.Empty;
}
ImGui.TableNextColumn();
table.NextColumn();
Im.Item.SetNextWidth(-1);
ImGui.InputTextWithHint("##swapKey", "Load this file...", ref _newSwapValue, Utf8GamePath.MaxGamePathLength);
ImGui.TableNextColumn();
Im.Input.Text("##swapKey"u8, ref _newSwapValue, "Load this file..."u8, maxLength: Utf8GamePath.MaxGamePathLength);
table.NextColumn();
Im.Item.SetNextWidth(-1);
ImGui.InputTextWithHint("##swapValue", "... instead of this file.", ref _newSwapKey, Utf8GamePath.MaxGamePathLength);
Im.Input.Text("##swapValue"u8, ref _newSwapKey, "... instead of this file."u8, maxLength: Utf8GamePath.MaxGamePathLength);
}
/// <summary>
@ -573,10 +566,10 @@ public partial class ModEditWindow : IndexedWindow, IDisposable
internal FullPath FindBestMatch(Utf8GamePath path)
{
var currentFile = _activeCollections.Current.ResolvePath(path);
if (currentFile != null)
if (currentFile is not null)
return currentFile.Value;
if (Mod != null)
if (Mod is not null)
{
foreach (var option in Mod.Groups.OrderByDescending(g => g.Priority))
{
@ -594,22 +587,16 @@ public partial class ModEditWindow : IndexedWindow, IDisposable
internal HashSet<Utf8GamePath> FindPathsStartingWith(CiByteString prefix)
{
var ret = new HashSet<Utf8GamePath>();
foreach (var path in _activeCollections.Current.ResolvedFiles.Keys)
{
if (path.Path.StartsWith(prefix))
ret.Add(path);
}
if (Mod != null)
if (Mod is not null)
foreach (var option in Mod.AllDataContainers)
{
foreach (var path in option.Files.Keys)
{
if (path.Path.StartsWith(prefix))
ret.Add(path);
}
}
foreach (var path in option.Files.Keys.Where(path => path.Path.StartsWith(prefix)))
ret.Add(path);
return ret;
}

View file

@ -48,7 +48,7 @@ public class ModMergeTab(ModMerger modMerger, ModComboWithoutCurrent combo) : IU
using (Im.Group())
{
Im.Text("Merge "u8);
Im.Line.Same(0, 0);
Im.Line.NoSpacing();
if (size - textSize < minComboSize)
{
Im.Text("selected mod"u8, ColorId.FolderLine.Value());
@ -59,7 +59,7 @@ public class ModMergeTab(ModMerger modMerger, ModComboWithoutCurrent combo) : IU
Im.Text(modMerger.MergeFromMod!.Name, ColorId.FolderLine.Value());
}
Im.Line.Same(0, 0);
Im.Line.NoSpacing();
Im.Text(" into"u8);
}

View file

@ -1,44 +0,0 @@
using Dalamud.Bindings.ImGui;
using ImSharp;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
using MouseWheelType = OtterGui.Widgets.MouseWheelType;
namespace Penumbra.UI.AdvancedWindow;
public sealed class OptionSelectCombo(ModEditor editor, ModEditWindow window)
: FilterComboCache<(string FullName, (int Group, int Data) Index)>(
() => window.Mod!.AllDataContainers.Select(c => (c.GetFullName(), c.GetDataIndices())).ToList(), MouseWheelType.Control, Penumbra.Log)
{
private readonly Im.ColorStyleDisposable _border = new();
protected override void DrawCombo(string label, string preview, string tooltip, int currentSelected, float previewWidth, float itemHeight,
ImGuiComboFlags flags)
{
_border.Push(ImStyleBorder.Frame, ColorId.FolderLine.Value());
base.DrawCombo(label, preview, tooltip, currentSelected, previewWidth, itemHeight, flags);
_border.Dispose();
}
protected override void DrawFilter(int currentSelected, float width)
{
_border.Dispose();
base.DrawFilter(currentSelected, width);
}
public bool Draw(float width)
{
var flags = window.Mod!.AllDataContainers.Count() switch
{
0 => ImGuiComboFlags.NoArrowButton,
> 8 => ImGuiComboFlags.HeightLargest,
_ => ImGuiComboFlags.None,
};
return Draw("##optionSelector", editor.Option!.GetFullName(), string.Empty, width, Im.Style.TextHeight, flags);
}
protected override bool DrawSelectable(int globalIdx, bool selected)
=> ImUtf8.Selectable(Items[globalIdx].FullName, selected);
}

View file

@ -115,8 +115,7 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
_seenInheritedCollections.Contains(inheritance));
_seenInheritedCollections.Add(inheritance);
Im.Tree.Node($"{Name(inheritance)}###{inheritance.Identity.Id}",
TreeNodeFlags.NoTreePushOnOpen | TreeNodeFlags.Leaf | TreeNodeFlags.Bullet).Dispose();
Im.Tree.Leaf($"{Name(inheritance)}###{inheritance.Identity.Id}", TreeNodeFlags.NoTreePushOnOpen);
var (minRect, maxRect) = (Im.Item.UpperLeftCorner, Im.Item.LowerRightCorner);
DrawInheritanceTreeClicks(inheritance, false);

View file

@ -0,0 +1,11 @@
using OtterGui.Widgets;
using Penumbra.GameData.Files.AtchStructs;
namespace Penumbra.UI.Combos;
internal sealed class AtchPointCombo(Func<IReadOnlyList<AtchType>> generator)
: FilterComboCache<AtchType>(generator, MouseWheelType.Control, Penumbra.Log)
{
protected override string ToString(AtchType obj)
=> obj.ToName();
}

View file

@ -3,7 +3,7 @@ using OtterGui;
using Penumbra.GameData.Enums;
using Penumbra.Meta.Manipulations;
namespace Penumbra.UI.Classes;
namespace Penumbra.UI.Combos;
public static class Combos
{
@ -31,7 +31,7 @@ public static class Combos
public static bool RspAttribute(string label, RspAttribute current, out RspAttribute attribute, float unscaledWidth = 200)
=> ImGuiUtil.GenericEnumCombo(label, unscaledWidth * Im.Style.GlobalScale, current, out attribute,
RspAttributeExtensions.ToFullString, 0, 1);
RspAttributeExtensions.ToName, 0, 1);
public static bool EstSlot(string label, EstType current, out EstType attribute, float unscaledWidth = 200)
=> ImGuiUtil.GenericEnumCombo(label, unscaledWidth * Im.Style.GlobalScale, current, out attribute);

View file

@ -0,0 +1,61 @@
using ImSharp;
using Penumbra.Mods.Editor;
using Penumbra.Mods.SubMods;
using Penumbra.UI.AdvancedWindow;
using Penumbra.UI.Classes;
namespace Penumbra.UI.Combos;
public sealed class OptionSelectCombo : FilterComboBase<OptionSelectCombo.Option>
{
private sealed class OptionFilter : Utf8FilterBase<Option>
{
protected override ReadOnlySpan<byte> ToFilterString(in Option item, int globalIndex)
=> item.FullName;
}
public sealed class Option(IModDataContainer container)
{
public readonly IModDataContainer? Container = container;
public readonly StringU8 FullName = new(container.GetFullName());
public readonly int GroupIndex = container.Group?.GetIndex() ?? -1;
public readonly int DataIndex = container.GetDataIndices().DataIndex;
}
private readonly Im.ColorStyleDisposable _border = new();
private readonly ModEditor _editor;
private readonly ModEditWindow _window;
public OptionSelectCombo(ModEditor editor, ModEditWindow window)
{
_editor = editor;
_window = window;
Filter = new OptionFilter();
}
protected override IEnumerable<Option> GetItems()
=> _window.Mod?.AllDataContainers.Select(c => new Option(c)) ?? [];
protected override float ItemHeight
=> Im.Style.TextHeightWithSpacing;
protected override bool DrawItem(in Option item, int globalIndex, bool selected)
=> Im.Selectable(item.FullName, selected);
protected override bool IsSelected(Option item, int globalIndex)
=> _editor.Option == item.Container;
protected override void PreDrawCombo(float width)
{
Flags = _editor.Mod?.AllDataContainers.Count() switch
{
null or 0 or 1 => ComboFlags.NoArrowButton,
> 8 => ComboFlags.HeightLargest,
_ => ComboFlags.None,
};
_border.Push(ImStyleBorder.Frame, ColorId.FolderLine.Value());
}
protected override void PostDrawCombo(float width)
=> _border.Dispose();
}

View file

@ -1,284 +1,292 @@
using Dalamud.Interface.Components;
using Dalamud.Bindings.ImGui;
using ImSharp;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
using Penumbra.Mods;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Settings;
using Penumbra.Mods.SubMods;
using MouseWheelType = OtterGui.Widgets.MouseWheelType;
namespace Penumbra.UI.ModsTab.Groups;
public sealed class ModGroupDrawer : Luna.IUiService
{
private readonly List<(IModGroup, int)> _blockGroupCache = [];
private bool _temporary;
private bool _locked;
private TemporaryModSettings? _tempSettings;
private ModSettings? _settings;
private readonly SingleGroupCombo _combo;
private readonly Configuration _config;
private readonly CollectionManager _collectionManager;
public ModGroupDrawer(Configuration config, CollectionManager collectionManager)
{
_config = config;
_collectionManager = collectionManager;
_combo = new SingleGroupCombo(this);
}
private sealed class SingleGroupCombo(ModGroupDrawer parent)
: FilterComboCache<IModOption>(() => _group!.Options, MouseWheelType.Control, Penumbra.Log)
{
private static IModGroup? _group;
private static int _groupIdx;
protected override bool DrawSelectable(int globalIdx, bool selected)
{
var option = _group!.Options[globalIdx];
var ret = ImUtf8.Selectable(option.Name, globalIdx == CurrentSelectionIdx);
if (option.Description.Length > 0)
ImUtf8.SelectableHelpMarker(option.Description);
return ret;
}
protected override string ToString(IModOption obj)
=> obj.Name;
public void Draw(IModGroup group, int groupIndex, int currentOption)
{
_group = group;
_groupIdx = groupIndex;
CurrentSelectionIdx = currentOption;
CurrentSelection = _group.Options[CurrentSelectionIdx];
if (Draw(string.Empty, CurrentSelection.Name, string.Empty, ref CurrentSelectionIdx, UiHelpers.InputTextWidth.X * 3 / 4,
Im.Style.TextHeightWithSpacing))
parent.SetModSetting(_group, _groupIdx, Setting.Single(CurrentSelectionIdx));
}
}
public void Draw(Mod mod, ModSettings settings, TemporaryModSettings? tempSettings)
{
if (mod.Groups.Count <= 0)
return;
_blockGroupCache.Clear();
_settings = settings;
_tempSettings = tempSettings;
_temporary = tempSettings != null;
_locked = (tempSettings?.Lock ?? 0) > 0;
var useDummy = true;
foreach (var (idx, group) in mod.Groups.Index())
{
if (!group.IsOption)
continue;
switch (group.Behaviour)
{
case GroupDrawBehaviour.SingleSelection when group.Options.Count <= _config.SingleGroupRadioMax:
case GroupDrawBehaviour.MultiSelection:
_blockGroupCache.Add((group, idx));
break;
case GroupDrawBehaviour.SingleSelection:
ImGuiUtil.Dummy(UiHelpers.DefaultSpace, useDummy);
useDummy = false;
DrawSingleGroupCombo(group, idx, settings.IsEmpty ? group.DefaultSettings : settings.Settings[idx]);
break;
}
}
useDummy = true;
foreach (var (group, idx) in _blockGroupCache)
{
ImGuiUtil.Dummy(UiHelpers.DefaultSpace, useDummy);
useDummy = false;
var option = settings.IsEmpty ? group.DefaultSettings : settings.Settings[idx];
if (group.Behaviour is GroupDrawBehaviour.MultiSelection)
DrawMultiGroup(group, idx, option);
else
DrawSingleGroupRadio(group, idx, option);
}
}
/// <summary>
/// Draw a single group selector as a combo box.
/// If a description is provided, add a help marker besides it.
/// </summary>
private void DrawSingleGroupCombo(IModGroup group, int groupIdx, Setting setting)
{
using var id = ImUtf8.PushId(groupIdx);
var selectedOption = setting.AsIndex;
using var disabled = ImRaii.Disabled(_locked);
_combo.Draw(group, groupIdx, selectedOption);
Im.Line.Same();
if (group.Description.Length > 0)
ImUtf8.LabeledHelpMarker(group.Name, group.Description);
else
ImUtf8.Text(group.Name);
}
/// <summary>
/// Draw a single group selector as a set of radio buttons.
/// If a description is provided, add a help marker besides it.
/// </summary>
private void DrawSingleGroupRadio(IModGroup group, int groupIdx, Setting setting)
{
using var id = ImUtf8.PushId(groupIdx);
var selectedOption = setting.AsIndex;
var minWidth = Widget.BeginFramedGroup(group.Name, group.Description);
var options = group.Options;
DrawCollapseHandling(options, minWidth, DrawOptions);
Widget.EndFramedGroup();
return;
void DrawOptions()
{
using var disabled = ImRaii.Disabled(_locked);
for (var idx = 0; idx < group.Options.Count; ++idx)
{
using var i = ImUtf8.PushId(idx);
var option = options[idx];
if (ImUtf8.RadioButton(option.Name, selectedOption == idx))
SetModSetting(group, groupIdx, Setting.Single(idx));
if (option.Description.Length <= 0)
continue;
Im.Line.Same();
ImGuiComponents.HelpMarker(option.Description);
}
}
}
/// <summary>
/// Draw a multi group selector as a bordered set of checkboxes.
/// If a description is provided, add a help marker in the title.
/// </summary>
private void DrawMultiGroup(IModGroup group, int groupIdx, Setting setting)
{
using var id = ImUtf8.PushId(groupIdx);
var minWidth = Widget.BeginFramedGroup(group.Name, group.Description);
var options = group.Options;
DrawCollapseHandling(options, minWidth, DrawOptions);
Widget.EndFramedGroup();
var label = $"##multi{groupIdx}";
if (Im.Item.RightClicked())
ImUtf8.OpenPopup($"##multi{groupIdx}");
DrawMultiPopup(group, groupIdx, label);
return;
void DrawOptions()
{
using var disabled = ImRaii.Disabled(_locked);
for (var idx = 0; idx < options.Count; ++idx)
{
using var i = ImUtf8.PushId(idx);
var option = options[idx];
var enabled = setting.HasFlag(idx);
if (ImUtf8.Checkbox(option.Name, ref enabled))
SetModSetting(group, groupIdx, setting.SetBit(idx, enabled));
if (option.Description.Length > 0)
{
Im.Line.Same();
ImGuiComponents.HelpMarker(option.Description);
}
}
}
}
private void DrawMultiPopup(IModGroup group, int groupIdx, string label)
{
using var style = ImStyleSingle.PopupBorderThickness.Push(Im.Style.GlobalScale);
using var popup = ImRaii.Popup(label);
if (!popup)
return;
Im.Text(group.Name);
using var disabled = ImRaii.Disabled(_locked);
Im.Separator();
if (ImUtf8.Selectable("Enable All"u8))
SetModSetting(group, groupIdx, Setting.AllBits(group.Options.Count));
if (ImUtf8.Selectable("Disable All"u8))
SetModSetting(group, groupIdx, Setting.Zero);
}
private void DrawCollapseHandling(IReadOnlyList<IModOption> options, float minWidth, Action draw)
{
if (options.Count <= _config.OptionGroupCollapsibleMin)
{
draw();
}
else
{
var collapseId = ImUtf8.GetId("Collapse");
var shown = ImGui.GetStateStorage().GetBool(collapseId, true);
var buttonTextShow = $"Show {options.Count} Options";
var buttonTextHide = $"Hide {options.Count} Options";
var buttonWidth = Math.Max(ImUtf8.CalcTextSize(buttonTextShow).X, ImUtf8.CalcTextSize(buttonTextHide).X)
+ 2 * Im.Style.FramePadding.X;
minWidth = Math.Max(buttonWidth, minWidth);
if (shown)
{
var pos = ImGui.GetCursorPos();
ImGui.Dummy(UiHelpers.IconButtonSize);
using (Im.Group())
{
draw();
}
var width = Math.Max(ImGui.GetItemRectSize().X, minWidth);
var endPos = ImGui.GetCursorPos();
ImGui.SetCursorPos(pos);
if (ImUtf8.Button(buttonTextHide, new Vector2(width, 0)))
ImGui.GetStateStorage().SetBool(collapseId, !shown);
ImGui.SetCursorPos(endPos);
}
else
{
var optionWidth = options.Max(o => ImUtf8.CalcTextSize(o.Name).X)
+ Im.Style.ItemInnerSpacing.X
+ Im.Style.FrameHeight
+ Im.Style.FramePadding.X;
var width = Math.Max(optionWidth, minWidth);
if (ImUtf8.Button(buttonTextShow, new Vector2(width, 0)))
ImGui.GetStateStorage().SetBool(collapseId, !shown);
}
}
}
private ModCollection Current
=> _collectionManager.Active.Current;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private void SetModSetting(IModGroup group, int groupIdx, Setting setting)
{
if (_temporary || _config.DefaultTemporaryMode)
{
_tempSettings ??= new TemporaryModSettings(group.Mod, _settings);
_tempSettings!.ForceInherit = false;
_tempSettings!.Settings[groupIdx] = setting;
_collectionManager.Editor.SetTemporarySettings(Current, group.Mod, _tempSettings);
}
else
{
_collectionManager.Editor.SetModSetting(Current, group.Mod, groupIdx, setting);
}
}
}
using ImSharp;
using Luna;
using OtterGui.Widgets;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
using Penumbra.Mods;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Settings;
using Penumbra.Mods.SubMods;
using MouseWheelType = OtterGui.Widgets.MouseWheelType;
namespace Penumbra.UI.ModsTab.Groups;
public sealed class ModGroupDrawer : IUiService
{
private readonly List<(IModGroup, int)> _blockGroupCache = [];
private bool _temporary;
private bool _locked;
private TemporaryModSettings? _tempSettings;
private ModSettings? _settings;
private readonly SingleGroupCombo _combo;
private readonly Configuration _config;
private readonly CollectionManager _collectionManager;
public ModGroupDrawer(Configuration config, CollectionManager collectionManager)
{
_config = config;
_collectionManager = collectionManager;
_combo = new SingleGroupCombo(this);
}
private sealed class SingleGroupCombo(ModGroupDrawer parent)
: FilterComboCache<IModOption>(() => _group!.Options, MouseWheelType.Control, Penumbra.Log)
{
private static IModGroup? _group;
private static int _groupIdx;
protected override bool DrawSelectable(int globalIdx, bool selected)
{
var option = _group!.Options[globalIdx];
var ret = Im.Selectable(option.Name, globalIdx == CurrentSelectionIdx);
if (option.Description.Length > 0)
LunaStyle.DrawHelpMarker(option.Description, treatAsHovered: Im.Item.Hovered());
return ret;
}
protected override string ToString(IModOption obj)
=> obj.Name;
public void Draw(IModGroup group, int groupIndex, int currentOption)
{
_group = group;
_groupIdx = groupIndex;
CurrentSelectionIdx = currentOption;
CurrentSelection = _group.Options[CurrentSelectionIdx];
if (Draw(string.Empty, CurrentSelection.Name, string.Empty, ref CurrentSelectionIdx, UiHelpers.InputTextWidth.X * 3 / 4,
Im.Style.TextHeightWithSpacing))
parent.SetModSetting(_group, _groupIdx, Setting.Single(CurrentSelectionIdx));
}
}
public void Draw(Mod mod, ModSettings settings, TemporaryModSettings? tempSettings)
{
if (mod.Groups.Count <= 0)
return;
_blockGroupCache.Clear();
_settings = settings;
_tempSettings = tempSettings;
_temporary = tempSettings != null;
_locked = (tempSettings?.Lock ?? 0) > 0;
var useDummy = true;
foreach (var (idx, group) in mod.Groups.Index())
{
if (!group.IsOption)
continue;
switch (group.Behaviour)
{
case GroupDrawBehaviour.SingleSelection when group.Options.Count <= _config.SingleGroupRadioMax:
case GroupDrawBehaviour.MultiSelection:
_blockGroupCache.Add((group, idx));
break;
case GroupDrawBehaviour.SingleSelection:
if (useDummy)
{
Im.Dummy(UiHelpers.DefaultSpace);
useDummy = false;
}
DrawSingleGroupCombo(group, idx, settings.IsEmpty ? group.DefaultSettings : settings.Settings[idx]);
break;
}
}
useDummy = true;
foreach (var (group, idx) in _blockGroupCache)
{
if (useDummy)
{
Im.Dummy(UiHelpers.DefaultSpace);
useDummy = false;
}
var option = settings.IsEmpty ? group.DefaultSettings : settings.Settings[idx];
if (group.Behaviour is GroupDrawBehaviour.MultiSelection)
DrawMultiGroup(group, idx, option);
else
DrawSingleGroupRadio(group, idx, option);
}
}
/// <summary>
/// Draw a single group selector as a combo box.
/// If a description is provided, add a help marker besides it.
/// </summary>
private void DrawSingleGroupCombo(IModGroup group, int groupIdx, Setting setting)
{
using var id = Im.Id.Push(groupIdx);
var selectedOption = setting.AsIndex;
using var disabled = Im.Disabled(_locked);
_combo.Draw(group, groupIdx, selectedOption);
if (group.Description.Length > 0)
{
LunaStyle.DrawHelpMarkerLabel(group.Name, group.Description);
}
else
{
Im.Line.SameInner();
Im.Text(group.Name);
}
}
/// <summary>
/// Draw a single group selector as a set of radio buttons.
/// If a description is provided, add a help marker besides it.
/// </summary>
private void DrawSingleGroupRadio(IModGroup group, int groupIdx, Setting setting)
{
using var id = Im.Id.Push(groupIdx);
var selectedOption = setting.AsIndex;
var minWidth = Widget.BeginFramedGroup(group.Name, group.Description);
var options = group.Options;
DrawCollapseHandling(options, minWidth, DrawOptions);
Widget.EndFramedGroup();
return;
void DrawOptions()
{
using var disabled = Im.Disabled(_locked);
for (var idx = 0; idx < group.Options.Count; ++idx)
{
using var i = Im.Id.Push(idx);
var option = options[idx];
if (Im.RadioButton(option.Name, selectedOption == idx))
SetModSetting(group, groupIdx, Setting.Single(idx));
if (option.Description.Length is 0)
continue;
Im.Line.SameInner();
LunaStyle.DrawAlignedHelpMarker(option.Description, treatAsHovered: Im.Item.Hovered());
}
}
}
/// <summary>
/// Draw a multi group selector as a bordered set of checkboxes.
/// If a description is provided, add a help marker in the title.
/// </summary>
private void DrawMultiGroup(IModGroup group, int groupIdx, Setting setting)
{
using var id = Im.Id.Push(groupIdx);
var minWidth = Widget.BeginFramedGroup(group.Name, group.Description);
var options = group.Options;
DrawCollapseHandling(options, minWidth, DrawOptions);
Widget.EndFramedGroup();
var label = new StringU8($"##multi{groupIdx}");
if (Im.Item.RightClicked())
Im.Popup.Open(label);
DrawMultiPopup(group, groupIdx, label);
return;
void DrawOptions()
{
using var disabled = Im.Disabled(_locked);
for (var idx = 0; idx < options.Count; ++idx)
{
using var i = Im.Id.Push(idx);
var option = options[idx];
var enabled = setting.HasFlag(idx);
if (Im.Checkbox(option.Name, ref enabled))
SetModSetting(group, groupIdx, setting.SetBit(idx, enabled));
if (option.Description.Length > 0)
{
Im.Line.SameInner();
LunaStyle.DrawAlignedHelpMarker(option.Description, treatAsHovered: Im.Item.Hovered());
}
}
}
}
private void DrawMultiPopup(IModGroup group, int groupIdx, StringU8 label)
{
using var style = ImStyleSingle.PopupBorderThickness.Push(Im.Style.GlobalScale);
using var popup = Im.Popup.Begin(label);
if (!popup)
return;
Im.Text(group.Name);
using var disabled = Im.Disabled(_locked);
Im.Separator();
if (Im.Selectable("Enable All"u8))
SetModSetting(group, groupIdx, Setting.AllBits(group.Options.Count));
if (Im.Selectable("Disable All"u8))
SetModSetting(group, groupIdx, Setting.Zero);
}
private void DrawCollapseHandling(IReadOnlyList<IModOption> options, float minWidth, Action draw)
{
if (options.Count <= _config.OptionGroupCollapsibleMin)
{
draw();
}
else
{
var collapseId = Im.Id.Get("Collapse"u8);
var shown = Im.State.Storage.GetBool(collapseId, true);
var buttonTextShow = new StringU8($"Show {options.Count} Options");
var buttonTextHide = new StringU8($"Hide {options.Count} Options");
var buttonWidth = Math.Max(Im.Font.CalculateSize(buttonTextShow).X, Im.Font.CalculateSize(buttonTextHide).X)
+ 2 * Im.Style.FramePadding.X;
minWidth = Math.Max(buttonWidth, minWidth);
if (shown)
{
var pos = Im.Cursor.Position;
Im.FrameDummy();
using (Im.Group())
{
draw();
}
var width = Math.Max(Im.Item.Size.X, minWidth);
var endPos = Im.Cursor.Position;
Im.Cursor.Position = pos;
if (Im.Button(buttonTextHide, new Vector2(width, 0)))
Im.State.Storage.SetBool(collapseId, !shown);
Im.Cursor.Position = endPos;
}
else
{
var optionWidth = options.Max(o => Im.Font.CalculateSize(o.Name).X)
+ Im.Style.ItemInnerSpacing.X
+ Im.Style.FrameHeight
+ Im.Style.FramePadding.X;
var width = Math.Max(optionWidth, minWidth);
if (Im.Button(buttonTextShow, new Vector2(width, 0)))
Im.State.Storage.SetBool(collapseId, !shown);
}
}
}
private ModCollection Current
=> _collectionManager.Active.Current;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private void SetModSetting(IModGroup group, int groupIdx, Setting setting)
{
if (_temporary || _config.DefaultTemporaryMode)
{
_tempSettings ??= new TemporaryModSettings(group.Mod, _settings);
_tempSettings!.ForceInherit = false;
_tempSettings!.Settings[groupIdx] = setting;
_collectionManager.Editor.SetTemporarySettings(Current, group.Mod, _tempSettings);
}
else
{
_collectionManager.Editor.SetModSetting(Current, group.Mod, groupIdx, setting);
}
}
}

View file

@ -1,11 +1,7 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using ImSharp;
using Luna;
using OtterGui;
using OtterGui.Services;
using OtterGui.Text;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -41,7 +37,7 @@ public class ModPanelChangedItemsTab(
public IIdentifiedObjectData Data;
public ByteString Text;
public ByteString ModelData;
public uint Id;
public ImGuiId Id;
public int Children;
public ChangedItemIconFlag Icon;
public bool Expandable;
@ -62,7 +58,7 @@ public class ModPanelChangedItemsTab(
Children = 0,
};
public static Container Parent(string text, IIdentifiedObjectData data, uint id, int children, bool expanded)
public static Container Parent(string text, IIdentifiedObjectData data, ImGuiId id, int children, bool expanded)
=> new()
{
Child = false,
@ -191,8 +187,8 @@ public class ModPanelChangedItemsTab(
}
else
{
var id = ImUtf8.GetId($"{mainItem.Item.PrimaryId}{(int)mainItem.Item.Type}");
var expanded = ImGui.GetStateStorage().GetBool(id, defaultExpansion);
var id = Im.Id.Get($"{mainItem.Item.PrimaryId}{(int)mainItem.Item.Type}");
var expanded = Im.State.Storage.GetBool(id, defaultExpansion);
Data.Add(Container.Parent(mainItem.Item.Name, mainItem, id, list.Count - 1, expanded));
AnyExpandable = true;
if (!expanded)
@ -214,8 +210,6 @@ public class ModPanelChangedItemsTab(
public bool IsVisible
=> selector.Selected!.ChangedItems.Count > 0;
private ImGuiStoragePtr _stateStorage;
private Vector2 _buttonSize;
private Rgba32 _starColor;
@ -226,7 +220,6 @@ public class ModPanelChangedItemsTab(
drawer.DrawTypeFilter();
_stateStorage = ImGui.GetStateStorage();
cache.Update(selector.Selected, drawer, config.Ephemeral.ChangedItemFilter, config.ChangedItemDisplay);
Im.Separator();
_buttonSize = new Vector2(Im.Style.ItemSpacing.Y + Im.Style.FrameHeight);
@ -258,17 +251,17 @@ public class ModPanelChangedItemsTab(
private void DrawContainerExpandable(ChangedItemsCache.Container obj, int idx)
{
using var id = ImUtf8.PushId(idx);
ImGui.TableNextColumn();
using var id = Im.Id.Push(idx);
Im.Table.NextColumn();
if (obj.Expandable)
{
if (ImUtf8.IconButton(obj.Expanded ? FontAwesomeIcon.CaretDown : FontAwesomeIcon.CaretRight,
obj.Expanded ? "Hide the other items using the same model." :
if (ImEx.Icon.Button(obj.Expanded ? LunaStyle.ExpandDownIcon : LunaStyle.CollapseUpIcon,
obj.Expanded ? "Hide the other items using the same model."u8 :
obj.Children > 1 ? $"Show {obj.Children} other items using the same model." :
"Show one other item using the same model.",
"Show one other item using the same model."u8,
_buttonSize))
{
_stateStorage.SetBool(obj.Id, !obj.Expanded);
Im.State.Storage.SetBool(obj.Id, !obj.Expanded);
if (cacheService.TryGetCache<ChangedItemsCache>(_cacheId, out var cache))
cache.Reset();
}
@ -279,7 +272,7 @@ public class ModPanelChangedItemsTab(
}
else
{
ImGui.Dummy(_buttonSize);
Im.Dummy(_buttonSize);
}
DrawBaseContainer(obj, idx);
@ -287,7 +280,7 @@ public class ModPanelChangedItemsTab(
private void DrawContainer(ChangedItemsCache.Container obj, int idx)
{
using var id = ImUtf8.PushId(idx);
using var id = Im.Id.Push(idx);
DrawBaseContainer(obj, idx);
}
@ -301,7 +294,7 @@ public class ModPanelChangedItemsTab(
"Prefer displaying this item instead of the current primary item.\n\nRight-click for more options."u8,
textColor: textColor, size: _buttonSize))
dataEditor.AddPreferredItem(selector.Selected!, item.Item.Id, false, true);
using var context = ImUtf8.PopupContextItem("StarContext"u8);
using var context = Im.Popup.BeginContextItem("StarContext"u8);
if (!context)
return;
@ -314,10 +307,10 @@ public class ModPanelChangedItemsTab(
if (cache.Data[idx].Data is IdentifiedItem it)
{
if (selector.Selected!.PreferredChangedItems.Contains(it.Item.Id)
&& ImUtf8.MenuItem("Remove Parent from Local Preferred Items"u8))
&& Im.Menu.Item("Remove Parent from Local Preferred Items"u8))
dataEditor.RemovePreferredItem(selector.Selected!, it.Item.Id, false);
if (selector.Selected!.DefaultPreferredItems.Contains(it.Item.Id)
&& ImUtf8.MenuItem("Remove Parent from Default Preferred Items"u8))
&& Im.Menu.Item("Remove Parent from Default Preferred Items"u8))
dataEditor.RemovePreferredItem(selector.Selected!, it.Item.Id, true);
}
@ -327,19 +320,19 @@ public class ModPanelChangedItemsTab(
var enabled = !selector.Selected!.DefaultPreferredItems.Contains(item.Item.Id);
if (enabled)
{
if (ImUtf8.MenuItem("Add to Local and Default Preferred Changed Items"u8))
if (Im.Menu.Item("Add to Local and Default Preferred Changed Items"u8))
dataEditor.AddPreferredItem(selector.Selected!, item.Item.Id, true, true);
}
else
{
if (ImUtf8.MenuItem("Remove from Default Preferred Changed Items"u8))
if (Im.Menu.Item("Remove from Default Preferred Changed Items"u8))
dataEditor.RemovePreferredItem(selector.Selected!, item.Item.Id, true);
}
if (ImUtf8.MenuItem("Reset Local Preferred Items to Default"u8))
if (Im.Menu.Item("Reset Local Preferred Items to Default"u8))
dataEditor.ResetPreferredItems(selector.Selected!);
if (ImUtf8.MenuItem("Clear Local and Default Preferred Items not Changed by the Mod"u8))
if (Im.Menu.Item("Clear Local and Default Preferred Items not Changed by the Mod"u8))
dataEditor.ClearInvalidPreferredItems(selector.Selected!);
}
@ -347,11 +340,11 @@ public class ModPanelChangedItemsTab(
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DrawBaseContainer(in ChangedItemsCache.Container obj, int _)
{
ImGui.TableNextColumn();
using var indent = ImRaii.PushIndent(1, obj.Child);
Im.Table.NextColumn();
using var indent = Im.Indent(1, obj.Child);
drawer.DrawCategoryIcon(obj.Icon, _buttonSize.Y);
Im.Line.Same(0, 0);
var clicked = ImUtf8.Selectable(obj.Text.Span, false, ImGuiSelectableFlags.None, _buttonSize with { X = 0 });
Im.Line.NoSpacing();
var clicked = Im.Selectable(obj.Text.Span, false, SelectableFlags.None, _buttonSize with { X = 0 });
drawer.ChangedItemHandling(obj.Data, clicked);
ChangedItemDrawer.DrawModelData(obj.ModelData.Span, _buttonSize.Y);
}

View file

@ -277,11 +277,10 @@ public class ModPanelEditTab(
Reset();
}
Im.Line.Same();
if (LunaStyle.DrawAlignedHelpMarker())
Im.Tooltip.Set(
"The mod directory name is used to correspond stored settings and sort orders, otherwise it has no influence on anything that is displayed.\n"u8
+ "This can currently not be used on pre-existing folders and does not support merges or overwriting."u8);
Im.Line.SameInner();
LunaStyle.DrawAlignedHelpMarker(StringU8.Empty,
"The mod directory name is used to correspond stored settings and sort orders, otherwise it has no influence on anything that is displayed.\n"u8
+ "This can currently not be used on pre-existing folders and does not support merges or overwriting."u8);
}
}
@ -297,15 +296,15 @@ public class ModPanelEditTab(
public const int Path = -6;
// Temporary strings
private static string? _currentEdit;
private static int _currentField = None;
private static int _optionIndex = None;
private static string? _currentEdit;
private static int _currentField = None;
private static int _optionIndex = None;
public static void Reset()
{
_currentEdit = null;
_currentField = None;
_optionIndex = None;
_currentEdit = null;
_currentField = None;
_optionIndex = None;
}
public static bool Text(ReadOnlySpan<byte> label, int field, int option, string oldValue, out string value, float width)

View file

@ -173,12 +173,8 @@ public class ModPanelSettingsTab(
_currentPriority = null;
}
var hovered = LunaStyle.DrawHelpMarker();
Im.Line.SameInner();
Im.Text("Priority"u8);
if (hovered || Im.Item.Hovered())
Im.Tooltip.Set("Mods with a higher number here take precedence before Mods with a lower number.\n"u8
+ "That means, if Mod A should overwrite changes from Mod B, Mod A should have a higher priority number than Mod B."u8);
LunaStyle.DrawAlignedHelpMarkerLabel("Priority"u8, "Mods with a higher number here take precedence before Mods with a lower number.\n"u8
+ "That means, if Mod A should overwrite changes from Mod B, Mod A should have a higher priority number than Mod B."u8);
}
/// <summary>

View file

@ -59,7 +59,7 @@ internal sealed unsafe class CachedRecord(Record record)
{
public readonly Record Record = record;
public readonly string PathU16 = record.Path.ToString();
public readonly StringU8 TypeName = new(record.RecordType.ToName());
public readonly StringU8 TypeName = new(record.RecordType.ToNameU8());
public readonly StringU8 Time = new($"{record.Time.ToLongTimeString()}.{record.Time.Millisecond:D4}");
public readonly StringPair Crc64 = new($"{record.Crc64:X16}");
public readonly StringU8 Collection = record.Collection is null ? StringU8.Empty : new StringU8(record.Collection.Identity.Name);
@ -197,7 +197,7 @@ internal sealed class ResourceWatcherTable : TableBase<CachedRecord, TableCache<
=> item.TypeName;
protected override IReadOnlyList<(RecordType Value, StringU8 Name)> EnumData
=> Enum.GetValues<RecordType>().Select(t => (t, new StringU8(t.ToName()))).ToArray();
=> Enum.GetValues<RecordType>().Select(t => (t, new StringU8(t.ToNameU8()))).ToArray();
protected override RecordType GetValue(in CachedRecord item, int globalIndex)
=> item.Record.RecordType;

View file

@ -34,18 +34,16 @@ public static class AtchDrawer
if (!tree)
continue;
Im.Tree.Node(entry.Accessory ? "Accessory"u8 : "Weapon"u8, TreeNodeFlags.Bullet | TreeNodeFlags.Leaf).Dispose();
Im.Tree.Leaf(entry.Accessory ? "Accessory"u8 : "Weapon"u8);
foreach (var (i, state) in entry.Entries.Index())
{
id.Push(i);
using var t = Im.Tree.Node(state.Bone);
if (t)
{
Im.Tree.Node($"Scale: {state.Scale}", TreeNodeFlags.Bullet | TreeNodeFlags.Leaf).Dispose();
Im.Tree.Node($"Offset: {state.Offset.X} | {state.Offset.Y} | {state.Offset.Z}",
TreeNodeFlags.Bullet | TreeNodeFlags.Leaf).Dispose();
Im.Tree.Node($"Rotation: {state.Rotation.X} | {state.Rotation.Y} | {state.Rotation.Z}",
TreeNodeFlags.Bullet | TreeNodeFlags.Leaf).Dispose();
Im.Tree.Leaf($"Scale: {state.Scale}");
Im.Tree.Leaf($"Offset: {state.Offset.X} | {state.Offset.Y} | {state.Offset.Z}");
Im.Tree.Leaf($"Rotation: {state.Rotation.X} | {state.Rotation.Y} | {state.Rotation.Z}");
}
id.Pop();

View file

@ -7,7 +7,6 @@ using FFXIVClientStructs.FFXIV.Client.Game.Group;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using FFXIVClientStructs.FFXIV.Client.System.Resource;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Colors;
using ImSharp;
using Luna;
@ -47,7 +46,7 @@ public class Diagnostics(ServiceManager provider) : IUiService
{
public void DrawDiagnostics()
{
if (!ImGui.CollapsingHeader("Diagnostics"))
if (!Im.Tree.Header("Diagnostics"u8))
return;
using var table = Im.Table.Begin("##data"u8, 4, TableFlags.RowBackground);
@ -214,7 +213,7 @@ public sealed class DebugTab : Window, ITab<TabType>
private unsafe void DrawCollectionCaches()
{
if (!ImGui.CollapsingHeader(
if (!Im.Tree.Header(
$"Collections ({_collectionManager.Caches.Count}/{_collectionManager.Storage.Count - 1} Caches)###Collections"))
return;
@ -242,21 +241,21 @@ public sealed class DebugTab : Window, ITab<TabType>
collection.Inheritance.FlatHierarchy.Count);
for (var i = 0; i < max; ++i)
{
ImGui.TableNextColumn();
table.NextColumn();
if (i < collection.Inheritance.DirectlyInheritsFrom.Count)
ImUtf8.Text(collection.Inheritance.DirectlyInheritsFrom[i].Identity.Name);
else
ImGui.Dummy(new Vector2(200 * Im.Style.GlobalScale, Im.Style.TextHeight));
ImGui.TableNextColumn();
Im.Dummy(new Vector2(200 * Im.Style.GlobalScale, Im.Style.TextHeight));
table.NextColumn();
if (i < collection.Inheritance.DirectlyInheritedBy.Count)
ImUtf8.Text(collection.Inheritance.DirectlyInheritedBy[i].Identity.Name);
else
ImGui.Dummy(new Vector2(200 * Im.Style.GlobalScale, Im.Style.TextHeight));
ImGui.TableNextColumn();
Im.Dummy(new Vector2(200 * Im.Style.GlobalScale, Im.Style.TextHeight));
table.NextColumn();
if (i < collection.Inheritance.FlatHierarchy.Count)
ImUtf8.Text(collection.Inheritance.FlatHierarchy[i].Identity.Name);
else
ImGui.Dummy(new Vector2(200 * Im.Style.GlobalScale, Im.Style.TextHeight));
Im.Dummy(new Vector2(200 * Im.Style.GlobalScale, Im.Style.TextHeight));
}
}
}
@ -266,10 +265,7 @@ public sealed class DebugTab : Window, ITab<TabType>
{
if (resourceNode)
foreach (var (path, resource) in collection.Cache!.CustomResources)
{
ImUtf8.TreeNode($"{path} -> 0x{(ulong)resource.ResourceHandle:X}",
ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.Leaf).Dispose();
}
Im.Tree.Leaf($"{path} -> 0x{(ulong)resource.ResourceHandle:X}");
}
using var modNode = ImUtf8.TreeNode("Enabled Mods"u8);
@ -282,18 +278,16 @@ public sealed class DebugTab : Window, ITab<TabType>
continue;
foreach (var path in paths)
Im.Tree.Node(path.Path.Span, TreeNodeFlags.Bullet | TreeNodeFlags.Leaf).Dispose();
Im.Tree.Leaf(path.Path.Span);
foreach (var manip in manips)
Im.Tree.Node($"{manip}", TreeNodeFlags.Bullet | TreeNodeFlags.Leaf).Dispose();
Im.Tree.Leaf($"{manip}");
}
}
else
{
using var color = ImGuiColor.Text.Push(ColorId.UndefinedMod.Value());
Im.Tree.Node($"{collection.Identity.Name} (Change Counter {collection.Counters.Change})",
TreeNodeFlags.Bullet | TreeNodeFlags.Leaf).Dispose();
Im.Tree.Leaf($"{collection.Identity.Name} (Change Counter {collection.Counters.Change})");
}
}
}
@ -301,11 +295,11 @@ public sealed class DebugTab : Window, ITab<TabType>
/// <summary> Draw general information about mod and collection state. </summary>
private void DrawDebugTabGeneral()
{
if (!ImGui.CollapsingHeader("General"))
if (!Im.Tree.Header("General"u8))
return;
var separateWindow = _config.Ephemeral.DebugSeparateWindow;
if (ImGui.Checkbox("Draw as Separate Window", ref separateWindow))
if (Im.Checkbox("Draw as Separate Window"u8, ref separateWindow))
{
IsOpen = true;
_config.Ephemeral.DebugSeparateWindow = separateWindow;
@ -316,18 +310,18 @@ public sealed class DebugTab : Window, ITab<TabType>
{
if (table)
{
PrintValue("Penumbra Version", $"{_validityChecker.Version} {DebugVersionString}");
PrintValue("Git Commit Hash", _validityChecker.CommitHash);
PrintValue("Selected Collection", _collectionManager.Active.Current.Identity.Name);
PrintValue(" has Cache", _collectionManager.Active.Current.HasCache.ToString());
PrintValue("Base Collection", _collectionManager.Active.Default.Identity.Name);
PrintValue(" has Cache", _collectionManager.Active.Default.HasCache.ToString());
PrintValue("Mod Manager BasePath", _modManager.BasePath.Name);
PrintValue("Mod Manager BasePath-Full", _modManager.BasePath.FullName);
PrintValue("Mod Manager BasePath IsRooted", Path.IsPathRooted(_config.ModDirectory).ToString());
PrintValue("Mod Manager BasePath Exists", Directory.Exists(_modManager.BasePath.FullName).ToString());
PrintValue("Mod Manager Valid", _modManager.Valid.ToString());
PrintValue("Web Server Enabled", _httpApi.Enabled.ToString());
table.DrawDataPair("Penumbra Version"u8, $"{_validityChecker.Version} {DebugVersionString}");
table.DrawDataPair("Git Commit Hash"u8, _validityChecker.CommitHash);
table.DrawDataPair("Selected Collection"u8, _collectionManager.Active.Current.Identity.Name);
table.DrawDataPair(" has Cache"u8, _collectionManager.Active.Current.HasCache.ToString());
table.DrawDataPair("Base Collection"u8, _collectionManager.Active.Default.Identity.Name);
table.DrawDataPair(" has Cache"u8, _collectionManager.Active.Default.HasCache.ToString());
table.DrawDataPair("Mod Manager BasePath"u8, _modManager.BasePath.Name);
table.DrawDataPair("Mod Manager BasePath-Full"u8, _modManager.BasePath.FullName);
table.DrawDataPair("Mod Manager BasePath IsRooted"u8, Path.IsPathRooted(_config.ModDirectory).ToString());
table.DrawDataPair("Mod Manager BasePath Exists"u8, Directory.Exists(_modManager.BasePath.FullName).ToString());
table.DrawDataPair("Mod Manager Valid"u8, _modManager.Valid.ToString());
table.DrawDataPair("Web Server Enabled"u8, _httpApi.Enabled.ToString());
}
}
@ -343,8 +337,8 @@ public sealed class DebugTab : Window, ITab<TabType>
var lastIndex = -1;
foreach (var mod in _modManager)
{
PrintValue(mod.Name, mod.Index.ToString("D5"));
ImGui.TableNextColumn();
table.DrawDataPair(mod.Name, $"{mod.Index:D5}");
table.NextColumn();
var index = mod.Index;
if (index != lastIndex + 1)
Im.Text("!!!"u8);
@ -362,27 +356,24 @@ public sealed class DebugTab : Window, ITab<TabType>
if (table)
{
var importing = _modImporter.IsImporting(out var importer);
PrintValue("Is Importing", importing.ToString());
PrintValue("Importer State", (importer?.State ?? ImporterState.None).ToString());
PrintValue("Import Window Was Drawn", _importPopup.WasDrawn.ToString());
PrintValue("Import Popup Was Drawn", _importPopup.PopupWasDrawn.ToString());
ImGui.TableNextColumn();
Im.Text("Import Batches"u8);
ImGui.TableNextColumn();
table.DrawDataPair("Is Importing"u8, importing.ToString());
table.DrawDataPair("Importer State"u8, (importer?.State ?? ImporterState.None).ToString());
table.DrawDataPair("Import Window Was Drawn"u8, _importPopup.WasDrawn.ToString());
table.DrawDataPair("Import Popup Was Drawn"u8, _importPopup.PopupWasDrawn.ToString());
table.DrawColumn("Import Batches"u8);
table.NextColumn();
foreach (var (index, batch) in _modImporter.ModBatches.Index())
{
foreach (var mod in batch)
PrintValue(index.ToString(), mod);
table.DrawDataPair($"{index}", mod);
}
ImGui.TableNextColumn();
Im.Text("Addable Mods"u8);
ImGui.TableNextColumn();
table.DrawColumn("Addable Mods"u8);
table.NextColumn();
foreach (var mod in _modImporter.AddableMods)
{
ImGui.TableNextColumn();
ImGui.TableNextColumn();
Im.Text(mod.Name);
table.NextColumn();
table.DrawColumn(mod.Name);
}
}
}
@ -396,15 +387,15 @@ public sealed class DebugTab : Window, ITab<TabType>
if (table)
{
foreach (var important in _framework.Important)
PrintValue(important, "Immediate");
table.DrawDataPair(important, "Immediate"u8);
foreach (var (idx, onTick) in _framework.OnTick.Index())
PrintValue(onTick, $"{idx + 1} Tick(s) From Now");
table.DrawDataPair(onTick, $"{idx + 1} Tick(s) From Now");
foreach (var (time, name) in _framework.Delayed)
{
var span = time - DateTime.UtcNow;
PrintValue(name, $"After {span.Minutes:D2}:{span.Seconds:D2}.{span.Milliseconds / 10:D2} (+ Ticks)");
table.DrawDataPair(name, $"After {span.Minutes:D2}:{span.Seconds:D2}.{span.Milliseconds / 10:D2} (+ Ticks)");
}
}
}
@ -417,10 +408,7 @@ public sealed class DebugTab : Window, ITab<TabType>
using var table = Im.Table.Begin("##Tasks"u8, 2, TableFlags.RowBackground);
if (table)
foreach (var task in _textureManager.Tasks)
{
ImGuiUtil.DrawTableColumn(task.Key.ToString()!);
ImGuiUtil.DrawTableColumn(task.Value.Item1.Status.ToString());
}
table.DrawDataPair(task.Key.ToString()!, $"{task.Value.Item1.Status}");
}
}
@ -431,63 +419,63 @@ public sealed class DebugTab : Window, ITab<TabType>
using var table = Im.Table.Begin("##redraws"u8, 3, TableFlags.RowBackground);
if (table)
{
ImGuiUtil.DrawTableColumn("In GPose");
ImGuiUtil.DrawTableColumn(_redraws.InGPose.ToString());
ImGui.TableNextColumn();
table.DrawDataPair("In GPose"u8, $"{_redraws.InGPose}");
table.NextColumn();
ImGuiUtil.DrawTableColumn("Target");
ImGuiUtil.DrawTableColumn(_redraws.Target.ToString());
ImGui.TableNextColumn();
table.DrawDataPair("Target"u8, $"{_redraws.Target}");
table.NextColumn();
foreach (var (idx, objectIdx) in _redraws.Queue.Index())
{
var (actualIdx, state) = objectIdx < 0 ? (~objectIdx, "Queued") : (objectIdx, "Invisible");
ImGuiUtil.DrawTableColumn($"Redraw Queue #{idx}");
ImGuiUtil.DrawTableColumn(actualIdx.ToString());
ImGuiUtil.DrawTableColumn(state);
var (actualIdx, state) =
objectIdx < 0 ? RefTuple.Create(~objectIdx, "Queued"u8) : RefTuple.Create(objectIdx, "Invisible"u8);
table.DrawColumn($"Redraw Queue #{idx}");
table.DrawColumn($"{actualIdx}");
table.DrawColumn(state);
}
foreach (var (idx, objectIdx) in _redraws.AfterGPoseQueue.Index())
{
var (actualIdx, state) = objectIdx < 0 ? (~objectIdx, "Queued") : (objectIdx, "Invisible");
ImGuiUtil.DrawTableColumn($"GPose Queue #{idx}");
ImGuiUtil.DrawTableColumn(actualIdx.ToString());
ImGuiUtil.DrawTableColumn(state);
var (actualIdx, state) =
objectIdx < 0 ? RefTuple.Create(~objectIdx, "Queued"u8) : RefTuple.Create(objectIdx, "Invisible"u8);
table.DrawColumn($"GPose Queue #{idx}");
table.DrawColumn($"{actualIdx}");
table.DrawColumn(state);
}
foreach (var (idx, name) in _redraws.GPoseNames.OfType<string>().Index())
{
ImGuiUtil.DrawTableColumn($"GPose Name #{idx}");
ImGuiUtil.DrawTableColumn(name);
ImGui.TableNextColumn();
table.DrawColumn($"GPose Name #{idx}");
table.DrawColumn(name);
table.NextColumn();
}
}
}
}
using (var tree = ImUtf8.TreeNode("String Memory"u8))
using (var tree = Im.Tree.Node("String Memory"u8))
{
if (tree)
{
using (ImUtf8.Group())
using (Im.Group())
{
ImUtf8.Text("Currently Allocated Strings"u8);
ImUtf8.Text("Total Allocated Strings"u8);
ImUtf8.Text("Free'd Allocated Strings"u8);
ImUtf8.Text("Currently Allocated Bytes"u8);
ImUtf8.Text("Total Allocated Bytes"u8);
ImUtf8.Text("Free'd Allocated Bytes"u8);
Im.Text("Currently Allocated Strings"u8);
Im.Text("Total Allocated Strings"u8);
Im.Text("Free'd Allocated Strings"u8);
Im.Text("Currently Allocated Bytes"u8);
Im.Text("Total Allocated Bytes"u8);
Im.Text("Free'd Allocated Bytes"u8);
}
Im.Line.Same();
using (ImUtf8.Group())
{
ImUtf8.Text($"{PenumbraStringMemory.CurrentStrings}");
ImUtf8.Text($"{PenumbraStringMemory.AllocatedStrings}");
ImUtf8.Text($"{PenumbraStringMemory.FreedStrings}");
ImUtf8.Text($"{PenumbraStringMemory.CurrentBytes}");
ImUtf8.Text($"{PenumbraStringMemory.AllocatedBytes}");
ImUtf8.Text($"{PenumbraStringMemory.FreedBytes}");
Im.Text($"{PenumbraStringMemory.CurrentStrings}");
Im.Text($"{PenumbraStringMemory.AllocatedStrings}");
Im.Text($"{PenumbraStringMemory.FreedStrings}");
Im.Text($"{PenumbraStringMemory.CurrentBytes}");
Im.Text($"{PenumbraStringMemory.AllocatedBytes}");
Im.Text($"{PenumbraStringMemory.FreedBytes}");
}
}
}
@ -497,7 +485,7 @@ public sealed class DebugTab : Window, ITab<TabType>
private void DrawPerformanceTab()
{
if (!ImGui.CollapsingHeader("Performance"))
if (!Im.Tree.Node("Performance"u8))
return;
using (var start = Im.Tree.Node("Startup Performance"u8, TreeNodeFlags.DefaultOpen))
@ -509,10 +497,10 @@ public sealed class DebugTab : Window, ITab<TabType>
private unsafe void DrawActorsDebug()
{
if (!ImGui.CollapsingHeader("Actors"))
if (!Im.Tree.Node("Actors"u8))
return;
using (var objectTree = ImUtf8.TreeNode("Object Manager"u8))
using (var objectTree = Im.Tree.Node("Object Manager"u8))
{
if (objectTree)
{
@ -523,36 +511,36 @@ public sealed class DebugTab : Window, ITab<TabType>
if (!table)
return;
DrawSpecial("Current Player", _actors.GetCurrentPlayer());
DrawSpecial("Current Inspect", _actors.GetInspectPlayer());
DrawSpecial("Current Card", _actors.GetCardPlayer());
DrawSpecial("Current Glamour", _actors.GetGlamourPlayer());
DrawSpecial("Current Player"u8, _actors.GetCurrentPlayer());
DrawSpecial("Current Inspect"u8, _actors.GetInspectPlayer());
DrawSpecial("Current Card"u8, _actors.GetCardPlayer());
DrawSpecial("Current Glamour"u8, _actors.GetGlamourPlayer());
foreach (var obj in _objects)
{
ImGuiUtil.DrawTableColumn(obj.Address != nint.Zero ? $"{((GameObject*)obj.Address)->ObjectIndex}" : "NULL");
ImGui.TableNextColumn();
table.DrawColumn(obj.Address != nint.Zero ? $"{((GameObject*)obj.Address)->ObjectIndex}" : "NULL"u8);
table.NextColumn();
Penumbra.Dynamis.DrawPointer(obj.Address);
ImGui.TableNextColumn();
table.NextColumn();
if (obj.Address != nint.Zero)
Penumbra.Dynamis.DrawPointer((nint)((Character*)obj.Address)->GameObject.GetDrawObject());
var identifier = _actors.FromObject(obj, out _, false, true, false);
ImGuiUtil.DrawTableColumn(_actors.ToString(identifier));
table.DrawColumn(_actors.ToString(identifier));
var id = obj.AsObject->ObjectKind is ObjectKind.BattleNpc
? $"{identifier.DataId} | {obj.AsObject->BaseId}"
: identifier.DataId.ToString();
ImGuiUtil.DrawTableColumn(id);
ImGui.TableNextColumn();
table.DrawColumn(id);
table.NextColumn();
Penumbra.Dynamis.DrawPointer(obj.Address != nint.Zero ? *(nint*)obj.Address : nint.Zero);
ImGuiUtil.DrawTableColumn(obj.Address != nint.Zero ? $"0x{obj.AsObject->EntityId:X}" : "NULL");
ImGuiUtil.DrawTableColumn(obj.Address != nint.Zero
table.DrawColumn(obj.Address != nint.Zero ? $"0x{obj.AsObject->EntityId:X}" : "NULL");
table.DrawColumn(obj.Address != nint.Zero
? obj.AsObject->IsCharacter() ? $"Character: {obj.AsCharacter->ObjectKind}" : "No Character"
: "NULL");
}
}
}
using (var shapeTree = ImUtf8.TreeNode("Shape Inspector"u8))
using (var shapeTree = Im.Tree.Node("Shape Inspector"u8))
{
if (shapeTree)
_shapeInspector.Draw();
@ -560,19 +548,19 @@ public sealed class DebugTab : Window, ITab<TabType>
return;
void DrawSpecial(string name, ActorIdentifier id)
void DrawSpecial(ReadOnlySpan<byte> name, ActorIdentifier id)
{
if (!id.IsValid)
return;
ImGuiUtil.DrawTableColumn(name);
ImGuiUtil.DrawTableColumn(string.Empty);
ImGuiUtil.DrawTableColumn(string.Empty);
ImGuiUtil.DrawTableColumn(_actors.ToString(id));
ImGuiUtil.DrawTableColumn(string.Empty);
ImGuiUtil.DrawTableColumn(string.Empty);
ImGuiUtil.DrawTableColumn(string.Empty);
ImGuiUtil.DrawTableColumn(string.Empty);
Im.Table.DrawColumn(name);
Im.Table.DrawColumn(StringU8.Empty);
Im.Table.DrawColumn(StringU8.Empty);
Im.Table.DrawColumn(_actors.ToString(id));
Im.Table.DrawColumn(StringU8.Empty);
Im.Table.DrawColumn(StringU8.Empty);
Im.Table.DrawColumn(StringU8.Empty);
Im.Table.DrawColumn(StringU8.Empty);
}
}
@ -582,7 +570,7 @@ public sealed class DebugTab : Window, ITab<TabType>
/// </summary>
private unsafe void DrawPathResolverDebug()
{
if (!ImGui.CollapsingHeader("Path Resolver"))
if (!Im.Tree.Node("Path Resolver"u8))
return;
Im.Text(
@ -598,25 +586,25 @@ public sealed class DebugTab : Window, ITab<TabType>
.ThenBy(kvp => kvp.Value.Item3)
.ThenBy(kvp => kvp.Key.Address))
{
ImGui.TableNextColumn();
ImUtf8.CopyOnClickSelectable($"{drawObject}");
ImUtf8.DrawTableColumn($"{gameObjectPtr.Index}");
table.NextColumn();
Penumbra.Dynamis.DrawPointer(drawObject.Address);
table.DrawColumn($"{gameObjectPtr.Index}");
using (ImGuiColor.Text.Push(new Vector4(1, 0, 0, 1), gameObjectPtr.Index != idx))
{
ImUtf8.DrawTableColumn($"{idx}");
table.DrawColumn($"{idx}");
}
ImUtf8.DrawTableColumn(child ? "Child"u8 : "Main"u8);
ImGui.TableNextColumn();
ImUtf8.CopyOnClickSelectable($"{gameObjectPtr}");
table.DrawColumn(child ? "Child"u8 : "Main"u8);
table.NextColumn();
Penumbra.Dynamis.DrawPointer(gameObjectPtr);
using (ImGuiColor.Text.Push(new Vector4(1, 0, 0, 1), _objects[idx] != gameObjectPtr))
{
ImUtf8.DrawTableColumn($"{_objects[idx]}");
table.DrawColumn($"{_objects[idx]}");
}
ImUtf8.DrawTableColumn(gameObjectPtr.Utf8Name.Span);
table.DrawColumn(gameObjectPtr.Utf8Name.Span);
var collection = _collectionResolver.IdentifyCollection(gameObjectPtr.AsObject, true);
ImUtf8.DrawTableColumn(collection.ModCollection.Identity.Name);
table.DrawColumn(collection.ModCollection.Identity.Name);
}
}
}
@ -629,10 +617,9 @@ public sealed class DebugTab : Window, ITab<TabType>
if (table)
foreach (var data in _pathState.CurrentData)
{
ImGui.TableNextColumn();
Im.Text($"{data.AssociatedGameObject:X}");
ImGui.TableNextColumn();
Im.Text(data.ModCollection.Identity.Name);
table.NextColumn();
Penumbra.Dynamis.DrawPointer(data.AssociatedGameObject);
table.DrawColumn(data.ModCollection.Identity.Name);
}
}
}
@ -644,27 +631,27 @@ public sealed class DebugTab : Window, ITab<TabType>
using var table = Im.Table.Begin("###ResourceCollectionResolverTable"u8, 4, TableFlags.SizingFixedFit);
if (table)
{
ImGuiUtil.DrawTableColumn("Current Mtrl Data");
ImGuiUtil.DrawTableColumn(_subfileHelper.MtrlData.ModCollection.Identity.Name);
ImGuiUtil.DrawTableColumn($"0x{_subfileHelper.MtrlData.AssociatedGameObject:X}");
ImGui.TableNextColumn();
table.DrawColumn("Current Mtrl Data"u8);
table.DrawColumn(_subfileHelper.MtrlData.ModCollection.Identity.Name);
table.DrawColumn($"0x{_subfileHelper.MtrlData.AssociatedGameObject:X}");
table.NextColumn();
ImGuiUtil.DrawTableColumn("Current Avfx Data");
ImGuiUtil.DrawTableColumn(_subfileHelper.AvfxData.ModCollection.Identity.Name);
ImGuiUtil.DrawTableColumn($"0x{_subfileHelper.AvfxData.AssociatedGameObject:X}");
ImGui.TableNextColumn();
table.DrawColumn("Current Avfx Data"u8);
table.DrawColumn(_subfileHelper.AvfxData.ModCollection.Identity.Name);
table.DrawColumn($"0x{_subfileHelper.AvfxData.AssociatedGameObject:X}");
table.NextColumn();
ImGuiUtil.DrawTableColumn("Current Resources");
ImGuiUtil.DrawTableColumn(_subfileHelper.Count.ToString());
ImGui.TableNextColumn();
ImGui.TableNextColumn();
table.DrawColumn("Current Resources"u8);
table.DrawColumn($"{_subfileHelper.Count}");
table.NextColumn();
table.NextColumn();
foreach (var (resource, resolve) in _subfileHelper)
{
ImGuiUtil.DrawTableColumn($"0x{resource:X}");
ImGuiUtil.DrawTableColumn(resolve.ModCollection.Identity.Name);
ImGuiUtil.DrawTableColumn($"0x{resolve.AssociatedGameObject:X}");
ImGuiUtil.DrawTableColumn($"{((ResourceHandle*)resource)->FileName()}");
table.DrawColumn($"0x{resource:X}");
table.DrawColumn(resolve.ModCollection.Identity.Name);
table.DrawColumn($"0x{resolve.AssociatedGameObject:X}");
table.DrawColumn($"{((ResourceHandle*)resource)->FileName()}");
}
}
}
@ -679,10 +666,10 @@ public sealed class DebugTab : Window, ITab<TabType>
foreach (var (address, identifier, collection) in _identifiedCollectionCache
.OrderBy(kvp => ((GameObject*)kvp.Address)->ObjectIndex))
{
ImGuiUtil.DrawTableColumn($"{((GameObject*)address)->ObjectIndex}");
ImGuiUtil.DrawTableColumn($"0x{address:X}");
ImGuiUtil.DrawTableColumn(identifier.ToString());
ImGuiUtil.DrawTableColumn(collection.Identity.Name);
table.DrawColumn($"{((GameObject*)address)->ObjectIndex}");
table.DrawColumn($"0x{address:X}");
table.DrawColumn($"{identifier}");
table.DrawColumn(collection.Identity.Name);
}
}
}
@ -695,8 +682,8 @@ public sealed class DebugTab : Window, ITab<TabType>
if (table)
foreach (var (idx, actor) in _cutsceneService.Actors)
{
ImGuiUtil.DrawTableColumn($"Cutscene Actor {idx}");
ImGuiUtil.DrawTableColumn(actor.Name.ToString());
table.DrawColumn($"Cutscene Actor {idx}");
table.DrawColumn(((Actor)actor.Address).StoredName());
}
}
}
@ -708,13 +695,13 @@ public sealed class DebugTab : Window, ITab<TabType>
using var table = Im.Table.Begin("###PGroupTable"u8, 2, TableFlags.SizingFixedFit);
if (table)
{
ImGuiUtil.DrawTableColumn("Group Members");
ImGuiUtil.DrawTableColumn(GroupManager.Instance()->MainGroup.MemberCount.ToString());
table.DrawColumn("Group Members"u8);
table.DrawColumn($"{GroupManager.Instance()->MainGroup.MemberCount}");
for (var i = 0; i < 8; ++i)
{
ImGuiUtil.DrawTableColumn($"Member #{i}");
table.DrawColumn($"Member #{i}");
var member = GroupManager.Instance()->MainGroup.GetPartyMemberByIndex(i);
ImGuiUtil.DrawTableColumn(member == null ? "NULL" : new ByteString(member->Name).ToString());
table.DrawColumn(member is null ? "NULL"u8 : member->Name);
}
}
}
@ -728,8 +715,8 @@ public sealed class DebugTab : Window, ITab<TabType>
if (agent->Data == null)
agent = &AgentBannerMIP.Instance()->AgentBannerInterface;
ImUtf8.Text("Agent: ");
Im.Line.Same(0, 0);
Im.Text("Agent: "u8);
Im.Line.NoSpacing();
Penumbra.Dynamis.DrawPointer((nint)agent);
if (agent->Data != null)
{
@ -767,7 +754,7 @@ public sealed class DebugTab : Window, ITab<TabType>
private void DrawData()
{
if (!ImGui.CollapsingHeader("Game Data"))
if (!Im.Tree.Node("Game Data"u8))
return;
DrawEmotes();
@ -865,8 +852,8 @@ public sealed class DebugTab : Window, ITab<TabType>
if (!mainTree)
return;
ImGui.InputText("File Name", ref _emoteSearchFile, 256);
ImGui.InputText("Emote Name", ref _emoteSearchName, 256);
Im.Input.Text("File Name"u8, ref _emoteSearchFile);
Im.Input.Text("Emote Name"u8, ref _emoteSearchName);
using var table = Im.Table.Begin("##table"u8, 2, TableFlags.RowBackground | TableFlags.ScrollY | TableFlags.SizingFixedFit,
new Vector2(-1, 12 * Im.Style.TextHeightWithSpacing));
if (!table)
@ -879,10 +866,8 @@ public sealed class DebugTab : Window, ITab<TabType>
|| p.Value.Any(s => s.Name.ToDalamudString().TextValue.Contains(_emoteSearchName, StringComparison.OrdinalIgnoreCase))),
p =>
{
ImGui.TableNextColumn();
Im.Text(p.Key);
ImGui.TableNextColumn();
Im.Text(StringU8.Join(", "u8, p.Value.Select(v => v.Name.ToDalamudString().TextValue)));
Im.Table.DrawColumn(p.Key);
Im.Table.DrawColumn(StringU8.Join(", "u8, p.Value.Select(v => v.Name.ToDalamudString().TextValue)));
});
ImGuiClip.DrawEndDummy(dummy, Im.Style.TextHeightWithSpacing);
}
@ -896,7 +881,7 @@ public sealed class DebugTab : Window, ITab<TabType>
if (!mainTree)
return;
if (ImGui.InputText("Key", ref _tmbKeyFilter, 256))
if (Im.Input.Text("Key"u8, ref _tmbKeyFilter))
_tmbKeyFilterU8 = CiByteString.FromString(_tmbKeyFilter, out var r, MetaDataComputation.All) ? r : CiByteString.Empty;
using var table = Im.Table.Begin("##table"u8, 2, TableFlags.RowBackground | TableFlags.ScrollY | TableFlags.SizingFixedFit,
new Vector2(-1, 12 * Im.Style.TextHeightWithSpacing));
@ -951,9 +936,9 @@ public sealed class DebugTab : Window, ITab<TabType>
foreach (var list in data.Colors)
{
var color = list[i];
ImGui.TableNextColumn();
table.NextColumn();
var frame = new Vector2(Im.Style.TextHeight);
ImGui.ColorButton("###color", new Vector4(MtrlTab.PseudoSqrtRgb((Vector3)color), 1), 0, frame);
Im.Color.Button("###color"u8, new Vector4(MtrlTab.PseudoSqrtRgb((Vector3)color), 1), 0, frame);
Im.Line.Same();
Im.Text($"{color.Red:F6} | {color.Green:F6} | {color.Blue:F6}");
}
@ -970,11 +955,11 @@ public sealed class DebugTab : Window, ITab<TabType>
private void DrawShaderReplacementFixer()
{
if (!ImGui.CollapsingHeader("Shader Replacement Fixer"))
if (!Im.Tree.Header("Shader Replacement Fixer"u8))
return;
var enableShaderReplacementFixer = _shaderReplacementFixer.Enabled;
if (ImGui.Checkbox("Enable Shader Replacement Fixer", ref enableShaderReplacementFixer))
if (Im.Checkbox("Enable Shader Replacement Fixer"u8, ref enableShaderReplacementFixer))
_shaderReplacementFixer.Enabled = enableShaderReplacementFixer;
if (!enableShaderReplacementFixer)
@ -990,7 +975,7 @@ public sealed class DebugTab : Window, ITab<TabType>
table.SetupColumn("Shader Package Name"u8, TableColumnFlags.WidthStretch, 0.6f);
table.SetupColumn("Materials with Modded ShPk"u8, TableColumnFlags.WidthStretch, 0.2f);
table.SetupColumn("\u0394 Slow-Path Calls"u8, TableColumnFlags.WidthStretch, 0.2f);
ImGui.TableHeadersRow();
table.HeaderRow();
table.DrawColumn("characterglass.shpk"u8);
table.DrawColumn($"{_shaderReplacementFixer.ModdedCharacterGlassShpkCount}");
@ -1034,7 +1019,7 @@ public sealed class DebugTab : Window, ITab<TabType>
{
var player = _objects[0];
var name = player.Valid ? player.StoredName() : "NULL"u8;
if (!ImGui.CollapsingHeader($"Player Model Info: {name}##Draw") || !player.Valid)
if (!Im.Tree.Header($"Player Model Info: {name}##Draw") || !player.Valid)
return;
DrawCopyableAddress("PlayerCharacter"u8, player.Address);
@ -1062,37 +1047,35 @@ public sealed class DebugTab : Window, ITab<TabType>
if (!table)
return;
ImGui.TableNextColumn();
ImGui.TableHeader("Slot");
ImGui.TableNextColumn();
ImGui.TableHeader("Imc Ptr");
ImGui.TableNextColumn();
ImGui.TableHeader("Imc File");
ImGui.TableNextColumn();
ImGui.TableHeader("Model Ptr");
ImGui.TableNextColumn();
ImGui.TableHeader("Model File");
table.NextColumn();
table.Header("Slot"u8);
table.NextColumn();
table.Header("Imc Ptr"u8);
table.NextColumn();
table.Header("Imc File"u8);
table.NextColumn();
table.Header("Model Ptr"u8);
table.NextColumn();
table.Header("Model File"u8);
for (var i = 0; i < model.AsCharacterBase->SlotCount; ++i)
{
var imc = (ResourceHandle*)model.AsCharacterBase->IMCArray[i];
ImGui.TableNextRow();
ImGui.TableNextColumn();
Im.Text($"Slot {i}");
ImGui.TableNextColumn();
table.NextRow();
table.DrawColumn($"Slot {i}");
table.NextColumn();
Penumbra.Dynamis.DrawPointer((nint)imc);
ImGui.TableNextColumn();
table.NextColumn();
if (imc is not null)
Im.Text(imc->FileName().Span);
var mdl = (RenderModel*)model.AsCharacterBase->Models[i];
ImGui.TableNextColumn();
table.NextColumn();
Penumbra.Dynamis.DrawPointer((nint)mdl);
if (mdl == null || mdl->ResourceHandle == null || mdl->ResourceHandle->Category != ResourceCategory.Chara)
if (mdl is null || mdl->ResourceHandle is null || mdl->ResourceHandle->Category is not ResourceCategory.Chara)
continue;
ImGui.TableNextColumn();
Im.Text(mdl->ResourceHandle->FileName().Span);
table.DrawColumn(mdl->ResourceHandle->FileName().Span);
}
}
@ -1101,17 +1084,17 @@ public sealed class DebugTab : Window, ITab<TabType>
private void DrawCrcCache()
{
var header = ImUtf8.CollapsingHeader("CRC Cache"u8);
var header = Im.Tree.Header("CRC Cache"u8);
if (!header)
return;
if (ImUtf8.InputText("##crcInput"u8, ref _crcInput, "Input path for CRC..."u8))
if (Im.Input.Text("##crcInput"u8, ref _crcInput, "Input path for CRC..."u8))
_crcPath = new FullPath(_crcInput);
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
ImUtf8.Text($" CRC32: {_crcPath.InternalName.CiCrc32:X8}");
ImUtf8.Text($"CI CRC32: {_crcPath.InternalName.Crc32:X8}");
ImUtf8.Text($" CRC64: {_crcPath.Crc64:X16}");
Im.Text($" CRC32: {_crcPath.InternalName.CiCrc32:X8}");
Im.Text($"CI CRC32: {_crcPath.InternalName.Crc32:X8}");
Im.Text($" CRC64: {_crcPath.Crc64:X16}");
using var table = Im.Table.Begin("table"u8, 2);
if (!table)
@ -1119,14 +1102,12 @@ public sealed class DebugTab : Window, ITab<TabType>
table.SetupColumn("Hash"u8, TableColumnFlags.WidthFixed, 18 * UiBuilder.MonoFont.GetCharAdvance('0'));
table.SetupColumn("Type"u8, TableColumnFlags.WidthFixed, 5 * UiBuilder.MonoFont.GetCharAdvance('0'));
ImGui.TableHeadersRow();
table.HeaderRow();
foreach (var (hash, type) in _rsfService.CustomCache)
{
ImGui.TableNextColumn();
ImUtf8.Text($"{hash:X16}");
ImGui.TableNextColumn();
ImUtf8.Text($"{type}");
table.DrawColumn($"{hash:X16}");
table.DrawColumn($"{type}");
}
}
@ -1149,7 +1130,7 @@ public sealed class DebugTab : Window, ITab<TabType>
table.SetupColumn("Resource Handle"u8, TableColumnFlags.WidthStretch, 0.2f);
table.SetupColumn("Actual Path"u8, TableColumnFlags.WidthStretch, 0.4f);
table.SetupColumn("Original Path"u8, TableColumnFlags.WidthStretch, 0.4f);
ImGui.TableHeadersRow();
table.HeaderRow();
foreach (var (handle, original) in ongoingLoads)
{
@ -1162,8 +1143,8 @@ public sealed class DebugTab : Window, ITab<TabType>
/// <summary> Draw resources with unusual reference count. </summary>
private unsafe void DrawResourceProblems()
{
var header = ImGui.CollapsingHeader("Resource Problems");
ImGuiUtil.HoverTooltip("Draw resources with unusually high reference count to detect overflows.");
var header = Im.Tree.HeaderId("Resource Problems"u8);
Im.Tooltip.OnHover("Draw resources with unusually high reference count to detect overflows."u8);
if (!header)
return;
@ -1197,7 +1178,7 @@ public sealed class DebugTab : Window, ITab<TabType>
using var id = ImRaii.PushId("CloudApiTester"u8);
if (ImUtf8.InputText("Path"u8, ref _cloudTesterPath, flags: ImGuiInputTextFlags.EnterReturnsTrue))
if (Im.Input.Text("Path"u8, ref _cloudTesterPath, flags: InputTextFlags.EnterReturnsTrue))
try
{
_cloudTesterReturn = CloudApi.IsCloudSynced(_cloudTesterPath);
@ -1232,15 +1213,6 @@ public sealed class DebugTab : Window, ITab<TabType>
_ipcTester.Draw();
}
/// <summary> Helper to print a property and its value in a 2-column table. </summary>
private static void PrintValue(string name, string value)
{
ImGui.TableNextColumn();
Im.Text(name);
ImGui.TableNextColumn();
Im.Text(value);
}
public override void Draw()
=> DrawContent();
@ -1253,9 +1225,6 @@ public sealed class DebugTab : Window, ITab<TabType>
_config.Ephemeral.Save();
}
public static unsafe void DrawCopyableAddress(ReadOnlySpan<byte> label, void* address)
=> DrawCopyableAddress(label, (nint)address);
public static unsafe void DrawCopyableAddress(ReadOnlySpan<byte> label, nint address)
{
Penumbra.Dynamis.DrawPointer(address);

View file

@ -93,7 +93,7 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
{
using var color = ImGuiColor.Text.Push(disabledColor, !value);
Im.Text("All, "u8);
Im.Line.Same(0, 0);
Im.Line.NoSpacing();
}
foreach (var slot in ShapeAttributeManager.UsedModels)
@ -103,7 +103,7 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
using var color = ImGuiColor.Text.Push(disabledColor, !value2);
Im.Text($"All {slot.ToNameU8()}, ");
Im.Line.Same(0, 0);
Im.Line.NoSpacing();
}
foreach (var gr in ShapeAttributeHashSet.GenderRaceValues.Skip(1))
@ -112,7 +112,7 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
{
using var color = ImGuiColor.Text.Push(disabledColor, !value3);
Im.Text($"All {gr.ToNameU8()}, ");
Im.Line.Same(0, 0);
Im.Line.NoSpacing();
}
else
{
@ -123,7 +123,7 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
using var color = ImGuiColor.Text.Push(disabledColor, !value4);
Im.Text($"All {gr.ToNameU8()} {slot.ToNameU8()}, ");
Im.Line.Same(0, 0);
Im.Line.NoSpacing();
}
}
}
@ -138,7 +138,7 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
{
using var color = ImGuiColor.Text.Push(disabledColor, !enabled);
Im.Text($"{slot.ToNameU8()} {id.Id:D4}, ");
Im.Line.Same(0, 0);
Im.Line.NoSpacing();
}
}
else
@ -153,7 +153,7 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
{
using var color = ImGuiColor.Text.Push(disabledColor, !enabled);
Im.Text($"{gr.ToNameU8()} {slot.ToNameU8()} #{id.Id:D4}, ");
Im.Line.Same(0, 0);
Im.Line.NoSpacing();
}
currentFlags &= ~0x3u;
@ -204,10 +204,10 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
var disabled = (mask & (1u << flag)) is 0;
using var color = ImGuiColor.Text.Push(disabledColor, disabled);
Im.Text(shape.AsSpan());
Im.Line.Same(0, 0);
Im.Line.NoSpacing();
Im.Text(", "u8);
if (idx % 8 < 7)
Im.Line.Same(0, 0);
Im.Line.NoSpacing();
}
}
else
@ -260,10 +260,10 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
var disabled = (mask & (1u << flag)) is 0;
using var color = ImGuiColor.Text.Push(disabledColor, disabled);
Im.Text(attribute.AsSpan());
Im.Line.Same(0, 0);
Im.Line.NoSpacing();
Im.Text(", "u8);
if (idx % 8 < 7)
Im.Line.Same(0, 0);
Im.Line.NoSpacing();
}
}
else

View file

@ -31,7 +31,7 @@ public sealed class ModsTab(
{
private readonly ActiveCollections _activeCollections = collectionManager.Active;
public bool IsVisible
public bool IsEnabled
=> modManager.Valid;
public ReadOnlySpan<byte> Label
@ -102,6 +102,7 @@ public sealed class ModsTab(
{
ImEx.TextFramed(LunaStyle.HelpMarker.Span, frameHeight, frameColor);
}
Im.Line.Same();
ImEx.TextFramed("Redraw: "u8, frameHeight, frameColor);
}

View file

@ -293,7 +293,7 @@ public sealed class SettingsTab : ITab<TabType>
_tutorial.OpenTutorial(BasicTutorialSteps.ModDirectory);
Im.Line.Same();
var pos = Im.Cursor.Y;
var pos = Im.Cursor.X;
Im.Line.New();
if (_config.ModDirectory != _newModDirectory