mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-02-20 14:57:50 +01:00
Wow, I accidentally the whole UI
This commit is contained in:
parent
dd8c910597
commit
651c7410ac
87 changed files with 7571 additions and 7280 deletions
|
|
@ -30,8 +30,11 @@ public static class Colors
|
|||
public const uint FilterActive = 0x807070FF;
|
||||
public const uint TutorialMarker = 0xFF20FFFF;
|
||||
public const uint TutorialBorder = 0xD00000FF;
|
||||
public const uint ReniColorButton = 0xFFCC648D;
|
||||
public const uint ReniColorHovered = 0xFFB070B0;
|
||||
public const uint ReniColorActive = 0xFF9070E0;
|
||||
|
||||
public static (uint DefaultColor, string Name, string Description) Data( this ColorId color )
|
||||
public static (uint DefaultColor, string Name, string Description) Data(this ColorId color)
|
||||
=> color switch
|
||||
{
|
||||
// @formatter:off
|
||||
|
|
@ -53,6 +56,6 @@ public static class Colors
|
|||
// @formatter:on
|
||||
};
|
||||
|
||||
public static uint Value( this ColorId color )
|
||||
=> Penumbra.Config.Colors.TryGetValue( color, out var value ) ? value : color.Data().DefaultColor;
|
||||
}
|
||||
public static uint Value(this ColorId color, Configuration config)
|
||||
=> config.Colors.TryGetValue(color, out var value) ? value : color.Data().DefaultColor;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,34 +12,34 @@ public static class Combos
|
|||
=> Race( label, 100, current, out race );
|
||||
|
||||
public static bool Race( string label, float unscaledWidth, ModelRace current, out ModelRace race )
|
||||
=> ImGuiUtil.GenericEnumCombo( label, unscaledWidth * ImGuiHelpers.GlobalScale, current, out race, RaceEnumExtensions.ToName, 1 );
|
||||
=> ImGuiUtil.GenericEnumCombo( label, unscaledWidth * UiHelpers.Scale, current, out race, RaceEnumExtensions.ToName, 1 );
|
||||
|
||||
public static bool Gender( string label, Gender current, out Gender gender )
|
||||
=> Gender( label, 120, current, out gender );
|
||||
|
||||
public static bool Gender( string label, float unscaledWidth, Gender current, out Gender gender )
|
||||
=> ImGuiUtil.GenericEnumCombo( label, unscaledWidth * ImGuiHelpers.GlobalScale, current, out gender, RaceEnumExtensions.ToName, 1 );
|
||||
=> ImGuiUtil.GenericEnumCombo( label, unscaledWidth * UiHelpers.Scale, current, out gender, RaceEnumExtensions.ToName, 1 );
|
||||
|
||||
public static bool EqdpEquipSlot( string label, EquipSlot current, out EquipSlot slot )
|
||||
=> ImGuiUtil.GenericEnumCombo( label, 100 * ImGuiHelpers.GlobalScale, current, out slot, EquipSlotExtensions.EqdpSlots, EquipSlotExtensions.ToName );
|
||||
=> ImGuiUtil.GenericEnumCombo( label, 100 * UiHelpers.Scale, current, out slot, EquipSlotExtensions.EqdpSlots, EquipSlotExtensions.ToName );
|
||||
|
||||
public static bool EqpEquipSlot( string label, float width, EquipSlot current, out EquipSlot slot )
|
||||
=> ImGuiUtil.GenericEnumCombo( label, width * ImGuiHelpers.GlobalScale, current, out slot, EquipSlotExtensions.EquipmentSlots, EquipSlotExtensions.ToName );
|
||||
=> ImGuiUtil.GenericEnumCombo( label, width * UiHelpers.Scale, current, out slot, EquipSlotExtensions.EquipmentSlots, EquipSlotExtensions.ToName );
|
||||
|
||||
public static bool AccessorySlot( string label, EquipSlot current, out EquipSlot slot )
|
||||
=> ImGuiUtil.GenericEnumCombo( label, 100 * ImGuiHelpers.GlobalScale, current, out slot, EquipSlotExtensions.AccessorySlots, EquipSlotExtensions.ToName );
|
||||
=> ImGuiUtil.GenericEnumCombo( label, 100 * UiHelpers.Scale, current, out slot, EquipSlotExtensions.AccessorySlots, EquipSlotExtensions.ToName );
|
||||
|
||||
public static bool SubRace( string label, SubRace current, out SubRace subRace )
|
||||
=> ImGuiUtil.GenericEnumCombo( label, 150 * ImGuiHelpers.GlobalScale, current, out subRace, RaceEnumExtensions.ToName, 1 );
|
||||
=> ImGuiUtil.GenericEnumCombo( label, 150 * UiHelpers.Scale, current, out subRace, RaceEnumExtensions.ToName, 1 );
|
||||
|
||||
public static bool RspAttribute( string label, RspAttribute current, out RspAttribute attribute )
|
||||
=> ImGuiUtil.GenericEnumCombo( label, 200 * ImGuiHelpers.GlobalScale, current, out attribute,
|
||||
=> ImGuiUtil.GenericEnumCombo( label, 200 * UiHelpers.Scale, current, out attribute,
|
||||
RspAttributeExtensions.ToFullString, 0, 1 );
|
||||
|
||||
public static bool EstSlot( string label, EstManipulation.EstType current, out EstManipulation.EstType attribute )
|
||||
=> ImGuiUtil.GenericEnumCombo( label, 200 * ImGuiHelpers.GlobalScale, current, out attribute );
|
||||
=> ImGuiUtil.GenericEnumCombo( label, 200 * UiHelpers.Scale, current, out attribute );
|
||||
|
||||
public static bool ImcType( string label, ObjectType current, out ObjectType type )
|
||||
=> ImGuiUtil.GenericEnumCombo( label, 110 * ImGuiHelpers.GlobalScale, current, out type, ObjectTypeExtensions.ValidImcTypes,
|
||||
=> ImGuiUtil.GenericEnumCombo( label, 110 * UiHelpers.Scale, current, out type, ObjectTypeExtensions.ValidImcTypes,
|
||||
ObjectTypeExtensions.ToName );
|
||||
}
|
||||
|
|
@ -332,7 +332,7 @@ public class ItemSwapWindow : IDisposable
|
|||
CreateMod();
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 20 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 20 * UiHelpers.Scale);
|
||||
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.");
|
||||
|
|
@ -356,7 +356,7 @@ public class ItemSwapWindow : IDisposable
|
|||
CreateOption();
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 20 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 20 * UiHelpers.Scale);
|
||||
_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"
|
||||
|
|
@ -415,7 +415,7 @@ public class ItemSwapWindow : IDisposable
|
|||
ImGui.TextUnformatted( $"Take {article1}" );
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( 100 * ImGuiHelpers.GlobalScale );
|
||||
ImGui.SetNextItemWidth( 100 * UiHelpers.Scale );
|
||||
using( var combo = ImRaii.Combo( "##fromType", _slotFrom is EquipSlot.Head ? "Hat" : _slotFrom.ToName() ) )
|
||||
{
|
||||
if( combo )
|
||||
|
|
@ -444,7 +444,7 @@ public class ItemSwapWindow : IDisposable
|
|||
ImGui.TextUnformatted( $"and put {article2} on {article1}" );
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( 100 * ImGuiHelpers.GlobalScale );
|
||||
ImGui.SetNextItemWidth( 100 * UiHelpers.Scale );
|
||||
using( var combo = ImRaii.Combo( "##toType", _slotTo.ToName() ) )
|
||||
{
|
||||
if( combo )
|
||||
|
|
@ -636,7 +636,7 @@ public class ItemSwapWindow : IDisposable
|
|||
ImGui.TextUnformatted(text);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth(InputWidth * ImGuiHelpers.GlobalScale);
|
||||
ImGui.SetNextItemWidth(InputWidth * UiHelpers.Scale);
|
||||
if (ImGui.InputInt("##targetId", ref _targetId, 0, 0))
|
||||
_targetId = Math.Clamp(_targetId, 0, byte.MaxValue);
|
||||
|
||||
|
|
@ -650,7 +650,7 @@ public class ItemSwapWindow : IDisposable
|
|||
ImGui.TextUnformatted(text);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth(InputWidth * ImGuiHelpers.GlobalScale);
|
||||
ImGui.SetNextItemWidth(InputWidth * UiHelpers.Scale);
|
||||
if (ImGui.InputInt("##sourceId", ref _sourceId, 0, 0))
|
||||
_sourceId = Math.Clamp(_sourceId, 0, byte.MaxValue);
|
||||
|
||||
|
|
@ -714,7 +714,7 @@ public class ItemSwapWindow : IDisposable
|
|||
return;
|
||||
|
||||
ImGui.NewLine();
|
||||
DrawHeaderLine(300 * ImGuiHelpers.GlobalScale);
|
||||
DrawHeaderLine(300 * UiHelpers.Scale);
|
||||
ImGui.NewLine();
|
||||
|
||||
DrawSwapBar();
|
||||
|
|
|
|||
|
|
@ -5,26 +5,27 @@ using System.Numerics;
|
|||
using System.Reflection;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.ImGuiFileDialog;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.GameData.Files;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.UI.Classes;
|
||||
|
||||
public partial class ModEditWindow
|
||||
{
|
||||
private class FileEditor< T > where T : class, IWritable
|
||||
private class FileEditor<T> where T : class, IWritable
|
||||
{
|
||||
private readonly string _tabName;
|
||||
private readonly string _fileType;
|
||||
private readonly Func< IReadOnlyList< Mod.Editor.FileRegistry > > _getFiles;
|
||||
private readonly Func< T, bool, bool > _drawEdit;
|
||||
private readonly Func< string > _getInitialPath;
|
||||
private readonly Func< byte[], T? > _parseFile;
|
||||
private readonly string _tabName;
|
||||
private readonly string _fileType;
|
||||
private readonly Func<IReadOnlyList<Mod.Editor.FileRegistry>> _getFiles;
|
||||
private readonly Func<T, bool, bool> _drawEdit;
|
||||
private readonly Func<string> _getInitialPath;
|
||||
private readonly Func<byte[], T?> _parseFile;
|
||||
|
||||
private Mod.Editor.FileRegistry? _currentPath;
|
||||
private T? _currentFile;
|
||||
|
|
@ -36,29 +37,28 @@ public partial class ModEditWindow
|
|||
private T? _defaultFile;
|
||||
private Exception? _defaultException;
|
||||
|
||||
private IReadOnlyList< Mod.Editor.FileRegistry > _list = null!;
|
||||
private IReadOnlyList<Mod.Editor.FileRegistry> _list = null!;
|
||||
|
||||
private readonly FileDialogManager _fileDialog = ConfigWindow.SetupFileManager();
|
||||
private readonly FileDialogService _fileDialog;
|
||||
|
||||
public FileEditor( string tabName, string fileType, Func< IReadOnlyList< Mod.Editor.FileRegistry > > getFiles,
|
||||
Func< T, bool, bool > drawEdit, Func< string > getInitialPath, Func< byte[], T? >? parseFile )
|
||||
public FileEditor(string tabName, string fileType, FileDialogService fileDialog, Func<IReadOnlyList<Mod.Editor.FileRegistry>> getFiles,
|
||||
Func<T, bool, bool> drawEdit, Func<string> getInitialPath, Func<byte[], T?>? parseFile)
|
||||
{
|
||||
_tabName = tabName;
|
||||
_fileType = fileType;
|
||||
_getFiles = getFiles;
|
||||
_drawEdit = drawEdit;
|
||||
_getInitialPath = getInitialPath;
|
||||
_fileDialog = fileDialog;
|
||||
_parseFile = parseFile ?? DefaultParseFile;
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
_list = _getFiles();
|
||||
using var tab = ImRaii.TabItem( _tabName );
|
||||
if( !tab )
|
||||
{
|
||||
using var tab = ImRaii.TabItem(_tabName);
|
||||
if (!tab)
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui.NewLine();
|
||||
DrawFileSelectCombo();
|
||||
|
|
@ -67,35 +67,35 @@ public partial class ModEditWindow
|
|||
ResetButton();
|
||||
ImGui.SameLine();
|
||||
DefaultInput();
|
||||
ImGui.Dummy( new Vector2( ImGui.GetTextLineHeight() / 2 ) );
|
||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||
|
||||
DrawFilePanel();
|
||||
}
|
||||
|
||||
private void DefaultInput()
|
||||
{
|
||||
using var spacing = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = 3 * ImGuiHelpers.GlobalScale } );
|
||||
ImGui.SetNextItemWidth( ImGui.GetContentRegionAvail().X - 3 * ImGuiHelpers.GlobalScale - ImGui.GetFrameHeight() );
|
||||
ImGui.InputTextWithHint( "##defaultInput", "Input game path to compare...", ref _defaultPath, Utf8GamePath.MaxGamePathLength );
|
||||
using var spacing = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = 3 * UiHelpers.Scale });
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - 3 * UiHelpers.Scale - ImGui.GetFrameHeight());
|
||||
ImGui.InputTextWithHint("##defaultInput", "Input game path to compare...", ref _defaultPath, Utf8GamePath.MaxGamePathLength);
|
||||
_inInput = ImGui.IsItemActive();
|
||||
if( ImGui.IsItemDeactivatedAfterEdit() && _defaultPath.Length > 0 )
|
||||
if (ImGui.IsItemDeactivatedAfterEdit() && _defaultPath.Length > 0)
|
||||
{
|
||||
_fileDialog.Reset();
|
||||
try
|
||||
{
|
||||
var file = DalamudServices.GameData.GetFile( _defaultPath );
|
||||
if( file != null )
|
||||
var file = DalamudServices.SGameData.GetFile(_defaultPath);
|
||||
if (file != null)
|
||||
{
|
||||
_defaultException = null;
|
||||
_defaultFile = _parseFile( file.Data );
|
||||
_defaultFile = _parseFile(file.Data);
|
||||
}
|
||||
else
|
||||
{
|
||||
_defaultFile = null;
|
||||
_defaultException = new Exception( "File does not exist." );
|
||||
_defaultException = new Exception("File does not exist.");
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
catch (Exception e)
|
||||
{
|
||||
_defaultFile = null;
|
||||
_defaultException = e;
|
||||
|
|
@ -103,25 +103,23 @@ public partial class ModEditWindow
|
|||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Save.ToIconString(), new Vector2( ImGui.GetFrameHeight() ), "Export this file.", _defaultFile == null, true ) )
|
||||
{
|
||||
_fileDialog.SaveFileDialog( $"Export {_defaultPath} to...", _fileType, Path.GetFileNameWithoutExtension( _defaultPath ), _fileType, ( success, name ) =>
|
||||
{
|
||||
if( !success )
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Save.ToIconString(), new Vector2(ImGui.GetFrameHeight()), "Export this file.",
|
||||
_defaultFile == null, true))
|
||||
_fileDialog.OpenSavePicker($"Export {_defaultPath} to...", _fileType, Path.GetFileNameWithoutExtension(_defaultPath), _fileType,
|
||||
(success, name) =>
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllBytes( name, _defaultFile?.Write() ?? throw new Exception( "File invalid." ) );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
Penumbra.Log.Error( $"Could not export {_defaultPath}:\n{e}" );
|
||||
}
|
||||
}, _getInitialPath() );
|
||||
}
|
||||
try
|
||||
{
|
||||
File.WriteAllBytes(name, _defaultFile?.Write() ?? throw new Exception("File invalid."));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.ChatService.NotificationMessage($"Could not export {_defaultPath}:\n{e}", "Error", NotificationType.Error);
|
||||
}
|
||||
}, _getInitialPath(), false);
|
||||
|
||||
_fileDialog.Draw();
|
||||
}
|
||||
|
|
@ -136,64 +134,58 @@ public partial class ModEditWindow
|
|||
|
||||
private void DrawFileSelectCombo()
|
||||
{
|
||||
ImGui.SetNextItemWidth( ImGui.GetContentRegionAvail().X );
|
||||
using var combo = ImRaii.Combo( "##fileSelect", _currentPath?.RelPath.ToString() ?? $"Select {_fileType} File..." );
|
||||
if( !combo )
|
||||
{
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||
using var combo = ImRaii.Combo("##fileSelect", _currentPath?.RelPath.ToString() ?? $"Select {_fileType} File...");
|
||||
if (!combo)
|
||||
return;
|
||||
}
|
||||
|
||||
foreach( var file in _list )
|
||||
foreach (var file in _list)
|
||||
{
|
||||
if( ImGui.Selectable( file.RelPath.ToString(), ReferenceEquals( file, _currentPath ) ) )
|
||||
{
|
||||
UpdateCurrentFile( file );
|
||||
}
|
||||
if (ImGui.Selectable(file.RelPath.ToString(), ReferenceEquals(file, _currentPath)))
|
||||
UpdateCurrentFile(file);
|
||||
|
||||
if( ImGui.IsItemHovered() )
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
using var tt = ImRaii.Tooltip();
|
||||
ImGui.TextUnformatted( "All Game Paths" );
|
||||
ImGui.TextUnformatted("All Game Paths");
|
||||
ImGui.Separator();
|
||||
using var t = ImRaii.Table( "##Tooltip", 2, ImGuiTableFlags.SizingFixedFit );
|
||||
foreach( var (option, gamePath) in file.SubModUsage )
|
||||
using var t = ImRaii.Table("##Tooltip", 2, ImGuiTableFlags.SizingFixedFit);
|
||||
foreach (var (option, gamePath) in file.SubModUsage)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
ConfigWindow.Text( gamePath.Path );
|
||||
UiHelpers.Text(gamePath.Path);
|
||||
ImGui.TableNextColumn();
|
||||
using var color = ImRaii.PushColor( ImGuiCol.Text, ColorId.ItemId.Value() );
|
||||
ImGui.TextUnformatted( option.FullName );
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.ItemId.Value(Penumbra.Config));
|
||||
ImGui.TextUnformatted(option.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
if( file.SubModUsage.Count > 0 )
|
||||
if (file.SubModUsage.Count > 0)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
using var color = ImRaii.PushColor( ImGuiCol.Text, ColorId.ItemId.Value() );
|
||||
ImGuiUtil.RightAlign( file.SubModUsage[ 0 ].Item2.Path.ToString() );
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.ItemId.Value(Penumbra.Config));
|
||||
ImGuiUtil.RightAlign(file.SubModUsage[0].Item2.Path.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static T? DefaultParseFile( byte[] bytes )
|
||||
=> Activator.CreateInstance( typeof( T ), bytes ) as T;
|
||||
private static T? DefaultParseFile(byte[] bytes)
|
||||
=> Activator.CreateInstance(typeof(T), bytes) as T;
|
||||
|
||||
private void UpdateCurrentFile( Mod.Editor.FileRegistry path )
|
||||
private void UpdateCurrentFile(Mod.Editor.FileRegistry path)
|
||||
{
|
||||
if( ReferenceEquals( _currentPath, path ) )
|
||||
{
|
||||
if (ReferenceEquals(_currentPath, path))
|
||||
return;
|
||||
}
|
||||
|
||||
_changed = false;
|
||||
_currentPath = path;
|
||||
_currentException = null;
|
||||
try
|
||||
{
|
||||
var bytes = File.ReadAllBytes( _currentPath.File.FullName );
|
||||
_currentFile = _parseFile( bytes );
|
||||
var bytes = File.ReadAllBytes(_currentPath.File.FullName);
|
||||
_currentFile = _parseFile(bytes);
|
||||
}
|
||||
catch( Exception e )
|
||||
catch (Exception e)
|
||||
{
|
||||
_currentFile = null;
|
||||
_currentException = e;
|
||||
|
|
@ -202,76 +194,74 @@ public partial class ModEditWindow
|
|||
|
||||
private void SaveButton()
|
||||
{
|
||||
if( ImGuiUtil.DrawDisabledButton( "Save to File", Vector2.Zero,
|
||||
$"Save the selected {_fileType} file with all changes applied. This is not revertible.", !_changed ) )
|
||||
if (ImGuiUtil.DrawDisabledButton("Save to File", Vector2.Zero,
|
||||
$"Save the selected {_fileType} file with all changes applied. This is not revertible.", !_changed))
|
||||
{
|
||||
File.WriteAllBytes( _currentPath!.File.FullName, _currentFile!.Write() );
|
||||
File.WriteAllBytes(_currentPath!.File.FullName, _currentFile!.Write());
|
||||
_changed = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetButton()
|
||||
{
|
||||
if( ImGuiUtil.DrawDisabledButton( "Reset Changes", Vector2.Zero,
|
||||
$"Reset all changes made to the {_fileType} file.", !_changed ) )
|
||||
if (ImGuiUtil.DrawDisabledButton("Reset Changes", Vector2.Zero,
|
||||
$"Reset all changes made to the {_fileType} file.", !_changed))
|
||||
{
|
||||
var tmp = _currentPath;
|
||||
_currentPath = null;
|
||||
UpdateCurrentFile( tmp! );
|
||||
UpdateCurrentFile(tmp!);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawFilePanel()
|
||||
{
|
||||
using var child = ImRaii.Child( "##filePanel", -Vector2.One, true );
|
||||
if( !child )
|
||||
{
|
||||
using var child = ImRaii.Child("##filePanel", -Vector2.One, true);
|
||||
if (!child)
|
||||
return;
|
||||
}
|
||||
|
||||
if( _currentPath != null )
|
||||
if (_currentPath != null)
|
||||
{
|
||||
if( _currentFile == null )
|
||||
if (_currentFile == null)
|
||||
{
|
||||
ImGui.TextUnformatted( $"Could not parse selected {_fileType} file." );
|
||||
if( _currentException != null )
|
||||
ImGui.TextUnformatted($"Could not parse selected {_fileType} file.");
|
||||
if (_currentException != null)
|
||||
{
|
||||
using var tab = ImRaii.PushIndent();
|
||||
ImGuiUtil.TextWrapped( _currentException.ToString() );
|
||||
ImGuiUtil.TextWrapped(_currentException.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using var id = ImRaii.PushId( 0 );
|
||||
_changed |= _drawEdit( _currentFile, false );
|
||||
using var id = ImRaii.PushId(0);
|
||||
_changed |= _drawEdit(_currentFile, false);
|
||||
}
|
||||
}
|
||||
|
||||
if( !_inInput && _defaultPath.Length > 0 )
|
||||
if (!_inInput && _defaultPath.Length > 0)
|
||||
{
|
||||
if( _currentPath != null )
|
||||
if (_currentPath != null)
|
||||
{
|
||||
ImGui.NewLine();
|
||||
ImGui.NewLine();
|
||||
ImGui.TextUnformatted( $"Preview of {_defaultPath}:" );
|
||||
ImGui.TextUnformatted($"Preview of {_defaultPath}:");
|
||||
ImGui.Separator();
|
||||
}
|
||||
|
||||
if( _defaultFile == null )
|
||||
if (_defaultFile == null)
|
||||
{
|
||||
ImGui.TextUnformatted( $"Could not parse provided {_fileType} game file:\n" );
|
||||
if( _defaultException != null )
|
||||
ImGui.TextUnformatted($"Could not parse provided {_fileType} game file:\n");
|
||||
if (_defaultException != null)
|
||||
{
|
||||
using var tab = ImRaii.PushIndent();
|
||||
ImGuiUtil.TextWrapped( _defaultException.ToString() );
|
||||
ImGuiUtil.TextWrapped(_defaultException.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using var id = ImRaii.PushId( 1 );
|
||||
_drawEdit( _defaultFile, true );
|
||||
using var id = ImRaii.PushId(1);
|
||||
_drawEdit(_defaultFile, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,184 +14,156 @@ namespace Penumbra.UI.Classes;
|
|||
|
||||
public partial class ModEditWindow
|
||||
{
|
||||
private readonly HashSet< Mod.Editor.FileRegistry > _selectedFiles = new(256);
|
||||
private LowerString _fileFilter = LowerString.Empty;
|
||||
private bool _showGamePaths = true;
|
||||
private string _gamePathEdit = string.Empty;
|
||||
private int _fileIdx = -1;
|
||||
private int _pathIdx = -1;
|
||||
private int _folderSkip = 0;
|
||||
private bool _overviewMode = false;
|
||||
private LowerString _fileOverviewFilter1 = LowerString.Empty;
|
||||
private LowerString _fileOverviewFilter2 = LowerString.Empty;
|
||||
private LowerString _fileOverviewFilter3 = LowerString.Empty;
|
||||
private readonly HashSet<Mod.Editor.FileRegistry> _selectedFiles = new(256);
|
||||
private LowerString _fileFilter = LowerString.Empty;
|
||||
private bool _showGamePaths = true;
|
||||
private string _gamePathEdit = string.Empty;
|
||||
private int _fileIdx = -1;
|
||||
private int _pathIdx = -1;
|
||||
private int _folderSkip = 0;
|
||||
private bool _overviewMode = false;
|
||||
private LowerString _fileOverviewFilter1 = LowerString.Empty;
|
||||
private LowerString _fileOverviewFilter2 = LowerString.Empty;
|
||||
private LowerString _fileOverviewFilter3 = LowerString.Empty;
|
||||
|
||||
private bool CheckFilter( Mod.Editor.FileRegistry registry )
|
||||
=> _fileFilter.IsEmpty || registry.File.FullName.Contains( _fileFilter.Lower, StringComparison.OrdinalIgnoreCase );
|
||||
private bool CheckFilter(Mod.Editor.FileRegistry registry)
|
||||
=> _fileFilter.IsEmpty || registry.File.FullName.Contains(_fileFilter.Lower, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
private bool CheckFilter( (Mod.Editor.FileRegistry, int) p )
|
||||
=> CheckFilter( p.Item1 );
|
||||
private bool CheckFilter((Mod.Editor.FileRegistry, int) p)
|
||||
=> CheckFilter(p.Item1);
|
||||
|
||||
private void DrawFileTab()
|
||||
{
|
||||
using var tab = ImRaii.TabItem( "File Redirections" );
|
||||
if( !tab )
|
||||
{
|
||||
using var tab = ImRaii.TabItem("File Redirections");
|
||||
if (!tab)
|
||||
return;
|
||||
}
|
||||
|
||||
DrawOptionSelectHeader();
|
||||
DrawButtonHeader();
|
||||
|
||||
if( _overviewMode )
|
||||
{
|
||||
if (_overviewMode)
|
||||
DrawFileManagementOverview();
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawFileManagementNormal();
|
||||
}
|
||||
|
||||
using var child = ImRaii.Child( "##files", -Vector2.One, true );
|
||||
if( !child )
|
||||
{
|
||||
using var child = ImRaii.Child("##files", -Vector2.One, true);
|
||||
if (!child)
|
||||
return;
|
||||
}
|
||||
|
||||
if( _overviewMode )
|
||||
{
|
||||
if (_overviewMode)
|
||||
DrawFilesOverviewMode();
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawFilesNormalMode();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawFilesOverviewMode()
|
||||
{
|
||||
var height = ImGui.GetTextLineHeightWithSpacing() + 2 * ImGui.GetStyle().CellPadding.Y;
|
||||
var skips = ImGuiClip.GetNecessarySkips( height );
|
||||
var skips = ImGuiClip.GetNecessarySkips(height);
|
||||
|
||||
using var list = ImRaii.Table( "##table", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersInnerV, -Vector2.One );
|
||||
using var list = ImRaii.Table("##table", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersInnerV, -Vector2.One);
|
||||
|
||||
if( !list )
|
||||
{
|
||||
if (!list)
|
||||
return;
|
||||
}
|
||||
|
||||
var width = ImGui.GetContentRegionAvail().X / 8;
|
||||
|
||||
ImGui.TableSetupColumn( "##file", ImGuiTableColumnFlags.WidthFixed, width * 3 );
|
||||
ImGui.TableSetupColumn( "##path", ImGuiTableColumnFlags.WidthFixed, width * 3 + ImGui.GetStyle().FrameBorderSize );
|
||||
ImGui.TableSetupColumn( "##option", ImGuiTableColumnFlags.WidthFixed, width * 2 );
|
||||
ImGui.TableSetupColumn("##file", ImGuiTableColumnFlags.WidthFixed, width * 3);
|
||||
ImGui.TableSetupColumn("##path", ImGuiTableColumnFlags.WidthFixed, width * 3 + ImGui.GetStyle().FrameBorderSize);
|
||||
ImGui.TableSetupColumn("##option", ImGuiTableColumnFlags.WidthFixed, width * 2);
|
||||
|
||||
var idx = 0;
|
||||
|
||||
var files = _editor!.AvailableFiles.SelectMany( f =>
|
||||
var files = _editor!.AvailableFiles.SelectMany(f =>
|
||||
{
|
||||
var file = f.RelPath.ToString();
|
||||
return f.SubModUsage.Count == 0
|
||||
? Enumerable.Repeat( ( file, "Unused", string.Empty, 0x40000080u ), 1 )
|
||||
: f.SubModUsage.Select( s => ( file, s.Item2.ToString(), s.Item1.FullName,
|
||||
_editor.CurrentOption == s.Item1 && _mod!.HasOptions ? 0x40008000u : 0u ) );
|
||||
} );
|
||||
? Enumerable.Repeat((file, "Unused", string.Empty, 0x40000080u), 1)
|
||||
: f.SubModUsage.Select(s => (file, s.Item2.ToString(), s.Item1.FullName,
|
||||
_editor.CurrentOption == s.Item1 && _mod!.HasOptions ? 0x40008000u : 0u));
|
||||
});
|
||||
|
||||
void DrawLine( (string, string, string, uint) data )
|
||||
void DrawLine((string, string, string, uint) data)
|
||||
{
|
||||
using var id = ImRaii.PushId( idx++ );
|
||||
using var id = ImRaii.PushId(idx++);
|
||||
ImGui.TableNextColumn();
|
||||
if( data.Item4 != 0 )
|
||||
{
|
||||
ImGui.TableSetBgColor( ImGuiTableBgTarget.CellBg, data.Item4 );
|
||||
}
|
||||
if (data.Item4 != 0)
|
||||
ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, data.Item4);
|
||||
|
||||
ImGuiUtil.CopyOnClickSelectable( data.Item1 );
|
||||
ImGuiUtil.CopyOnClickSelectable(data.Item1);
|
||||
ImGui.TableNextColumn();
|
||||
if( data.Item4 != 0 )
|
||||
{
|
||||
ImGui.TableSetBgColor( ImGuiTableBgTarget.CellBg, data.Item4 );
|
||||
}
|
||||
if (data.Item4 != 0)
|
||||
ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, data.Item4);
|
||||
|
||||
ImGuiUtil.CopyOnClickSelectable( data.Item2 );
|
||||
ImGuiUtil.CopyOnClickSelectable(data.Item2);
|
||||
ImGui.TableNextColumn();
|
||||
if( data.Item4 != 0 )
|
||||
{
|
||||
ImGui.TableSetBgColor( ImGuiTableBgTarget.CellBg, data.Item4 );
|
||||
}
|
||||
if (data.Item4 != 0)
|
||||
ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, data.Item4);
|
||||
|
||||
ImGuiUtil.CopyOnClickSelectable( data.Item3 );
|
||||
ImGuiUtil.CopyOnClickSelectable(data.Item3);
|
||||
}
|
||||
|
||||
bool Filter( (string, string, string, uint) data )
|
||||
=> _fileOverviewFilter1.IsContained( data.Item1 )
|
||||
&& _fileOverviewFilter2.IsContained( data.Item2 )
|
||||
&& _fileOverviewFilter3.IsContained( data.Item3 );
|
||||
bool Filter((string, string, string, uint) data)
|
||||
=> _fileOverviewFilter1.IsContained(data.Item1)
|
||||
&& _fileOverviewFilter2.IsContained(data.Item2)
|
||||
&& _fileOverviewFilter3.IsContained(data.Item3);
|
||||
|
||||
var end = ImGuiClip.FilteredClippedDraw( files, skips, Filter, DrawLine );
|
||||
ImGuiClip.DrawEndDummy( end, height );
|
||||
var end = ImGuiClip.FilteredClippedDraw(files, skips, Filter, DrawLine);
|
||||
ImGuiClip.DrawEndDummy(end, height);
|
||||
}
|
||||
|
||||
private void DrawFilesNormalMode()
|
||||
{
|
||||
using var list = ImRaii.Table( "##table", 1 );
|
||||
using var list = ImRaii.Table("##table", 1);
|
||||
|
||||
if( !list )
|
||||
{
|
||||
if (!list)
|
||||
return;
|
||||
}
|
||||
|
||||
foreach( var (registry, i) in _editor!.AvailableFiles.WithIndex().Where( CheckFilter ) )
|
||||
foreach (var (registry, i) in _editor!.AvailableFiles.WithIndex().Where(CheckFilter))
|
||||
{
|
||||
using var id = ImRaii.PushId( i );
|
||||
using var id = ImRaii.PushId(i);
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
DrawSelectable( registry );
|
||||
DrawSelectable(registry);
|
||||
|
||||
if( !_showGamePaths )
|
||||
{
|
||||
if (!_showGamePaths)
|
||||
continue;
|
||||
}
|
||||
|
||||
using var indent = ImRaii.PushIndent( 50f );
|
||||
for( var j = 0; j < registry.SubModUsage.Count; ++j )
|
||||
using var indent = ImRaii.PushIndent(50f);
|
||||
for (var j = 0; j < registry.SubModUsage.Count; ++j)
|
||||
{
|
||||
var (subMod, gamePath) = registry.SubModUsage[ j ];
|
||||
if( subMod != _editor.CurrentOption )
|
||||
{
|
||||
var (subMod, gamePath) = registry.SubModUsage[j];
|
||||
if (subMod != _editor.CurrentOption)
|
||||
continue;
|
||||
}
|
||||
|
||||
PrintGamePath( i, j, registry, subMod, gamePath );
|
||||
PrintGamePath(i, j, registry, subMod, gamePath);
|
||||
}
|
||||
|
||||
PrintNewGamePath( i, registry, _editor.CurrentOption );
|
||||
PrintNewGamePath(i, registry, _editor.CurrentOption);
|
||||
}
|
||||
}
|
||||
|
||||
private static string DrawFileTooltip( Mod.Editor.FileRegistry registry, ColorId color )
|
||||
private static string DrawFileTooltip(Mod.Editor.FileRegistry registry, ColorId color)
|
||||
{
|
||||
(string, int) GetMulti()
|
||||
{
|
||||
var groups = registry.SubModUsage.GroupBy( s => s.Item1 ).ToArray();
|
||||
return ( string.Join( "\n", groups.Select( g => g.Key.Name ) ), groups.Length );
|
||||
var groups = registry.SubModUsage.GroupBy(s => s.Item1).ToArray();
|
||||
return (string.Join("\n", groups.Select(g => g.Key.Name)), groups.Length);
|
||||
}
|
||||
|
||||
var (text, groupCount) = color switch
|
||||
{
|
||||
ColorId.ConflictingMod => ( string.Empty, 0 ),
|
||||
ColorId.NewMod => ( registry.SubModUsage[ 0 ].Item1.Name, 1 ),
|
||||
ColorId.ConflictingMod => (string.Empty, 0),
|
||||
ColorId.NewMod => (registry.SubModUsage[0].Item1.Name, 1),
|
||||
ColorId.InheritedMod => GetMulti(),
|
||||
_ => ( string.Empty, 0 ),
|
||||
_ => (string.Empty, 0),
|
||||
};
|
||||
|
||||
if( text.Length > 0 && ImGui.IsItemHovered() )
|
||||
{
|
||||
ImGui.SetTooltip( text );
|
||||
}
|
||||
if (text.Length > 0 && ImGui.IsItemHovered())
|
||||
ImGui.SetTooltip(text);
|
||||
|
||||
|
||||
return ( groupCount, registry.SubModUsage.Count ) switch
|
||||
return (groupCount, registry.SubModUsage.Count) switch
|
||||
{
|
||||
(0, 0) => "(unused)",
|
||||
(1, 1) => "(used 1 time)",
|
||||
|
|
@ -200,95 +172,91 @@ public partial class ModEditWindow
|
|||
};
|
||||
}
|
||||
|
||||
private void DrawSelectable( Mod.Editor.FileRegistry registry )
|
||||
private void DrawSelectable(Mod.Editor.FileRegistry registry)
|
||||
{
|
||||
var selected = _selectedFiles.Contains( registry );
|
||||
var color = registry.SubModUsage.Count == 0 ? ColorId.ConflictingMod :
|
||||
registry.CurrentUsage == registry.SubModUsage.Count ? ColorId.NewMod : ColorId.InheritedMod;
|
||||
using var c = ImRaii.PushColor( ImGuiCol.Text, color.Value() );
|
||||
if( ConfigWindow.Selectable( registry.RelPath.Path, selected ) )
|
||||
var selected = _selectedFiles.Contains(registry);
|
||||
var color = registry.SubModUsage.Count == 0 ? ColorId.ConflictingMod :
|
||||
registry.CurrentUsage == registry.SubModUsage.Count ? ColorId.NewMod : ColorId.InheritedMod;
|
||||
using var c = ImRaii.PushColor(ImGuiCol.Text, color.Value(Penumbra.Config));
|
||||
if (UiHelpers.Selectable(registry.RelPath.Path, selected))
|
||||
{
|
||||
if( selected )
|
||||
{
|
||||
_selectedFiles.Remove( registry );
|
||||
}
|
||||
if (selected)
|
||||
_selectedFiles.Remove(registry);
|
||||
else
|
||||
{
|
||||
_selectedFiles.Add( registry );
|
||||
}
|
||||
_selectedFiles.Add(registry);
|
||||
}
|
||||
|
||||
var rightText = DrawFileTooltip( registry, color );
|
||||
var rightText = DrawFileTooltip(registry, color);
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGuiUtil.RightAlign( rightText );
|
||||
ImGuiUtil.RightAlign(rightText);
|
||||
}
|
||||
|
||||
private void PrintGamePath( int i, int j, Mod.Editor.FileRegistry registry, ISubMod subMod, Utf8GamePath gamePath )
|
||||
private void PrintGamePath(int i, int j, Mod.Editor.FileRegistry registry, ISubMod subMod, Utf8GamePath gamePath)
|
||||
{
|
||||
using var id = ImRaii.PushId( j );
|
||||
using var id = ImRaii.PushId(j);
|
||||
ImGui.TableNextColumn();
|
||||
var tmp = _fileIdx == i && _pathIdx == j ? _gamePathEdit : gamePath.ToString();
|
||||
var pos = ImGui.GetCursorPosX() - ImGui.GetFrameHeight();
|
||||
ImGui.SetNextItemWidth( -1 );
|
||||
if( ImGui.InputText( string.Empty, ref tmp, Utf8GamePath.MaxGamePathLength ) )
|
||||
ImGui.SetNextItemWidth(-1);
|
||||
if (ImGui.InputText(string.Empty, ref tmp, Utf8GamePath.MaxGamePathLength))
|
||||
{
|
||||
_fileIdx = i;
|
||||
_pathIdx = j;
|
||||
_gamePathEdit = tmp;
|
||||
}
|
||||
|
||||
ImGuiUtil.HoverTooltip( "Clear completely to remove the path from this mod." );
|
||||
ImGuiUtil.HoverTooltip("Clear completely to remove the path from this mod.");
|
||||
|
||||
if( ImGui.IsItemDeactivatedAfterEdit() )
|
||||
if (ImGui.IsItemDeactivatedAfterEdit())
|
||||
{
|
||||
if( Utf8GamePath.FromString( _gamePathEdit, out var path, false ) )
|
||||
{
|
||||
_editor!.SetGamePath( _fileIdx, _pathIdx, path );
|
||||
}
|
||||
if (Utf8GamePath.FromString(_gamePathEdit, out var path, false))
|
||||
_editor!.SetGamePath(_fileIdx, _pathIdx, path);
|
||||
|
||||
_fileIdx = -1;
|
||||
_pathIdx = -1;
|
||||
}
|
||||
else if( _fileIdx == i && _pathIdx == j && ( !Utf8GamePath.FromString( _gamePathEdit, out var path, false )
|
||||
|| !path.IsEmpty && !path.Equals( gamePath ) && !_editor!.CanAddGamePath( path )) )
|
||||
else if (_fileIdx == i
|
||||
&& _pathIdx == j
|
||||
&& (!Utf8GamePath.FromString(_gamePathEdit, out var path, false)
|
||||
|| !path.IsEmpty && !path.Equals(gamePath) && !_editor!.CanAddGamePath(path)))
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPosX( pos );
|
||||
using var font = ImRaii.PushFont( UiBuilder.IconFont );
|
||||
ImGuiUtil.TextColored( 0xFF0000FF, FontAwesomeIcon.TimesCircle.ToIconString() );
|
||||
ImGui.SetCursorPosX(pos);
|
||||
using var font = ImRaii.PushFont(UiBuilder.IconFont);
|
||||
ImGuiUtil.TextColored(0xFF0000FF, FontAwesomeIcon.TimesCircle.ToIconString());
|
||||
}
|
||||
}
|
||||
|
||||
private void PrintNewGamePath( int i, Mod.Editor.FileRegistry registry, ISubMod subMod )
|
||||
private void PrintNewGamePath(int i, Mod.Editor.FileRegistry registry, ISubMod subMod)
|
||||
{
|
||||
var tmp = _fileIdx == i && _pathIdx == -1 ? _gamePathEdit : string.Empty;
|
||||
var pos = ImGui.GetCursorPosX() - ImGui.GetFrameHeight();
|
||||
ImGui.SetNextItemWidth( -1 );
|
||||
if( ImGui.InputTextWithHint( "##new", "Add New Path...", ref tmp, Utf8GamePath.MaxGamePathLength ) )
|
||||
ImGui.SetNextItemWidth(-1);
|
||||
if (ImGui.InputTextWithHint("##new", "Add New Path...", ref tmp, Utf8GamePath.MaxGamePathLength))
|
||||
{
|
||||
_fileIdx = i;
|
||||
_pathIdx = -1;
|
||||
_gamePathEdit = tmp;
|
||||
}
|
||||
|
||||
if( ImGui.IsItemDeactivatedAfterEdit() )
|
||||
if (ImGui.IsItemDeactivatedAfterEdit())
|
||||
{
|
||||
if( Utf8GamePath.FromString( _gamePathEdit, out var path, false ) && !path.IsEmpty )
|
||||
{
|
||||
_editor!.SetGamePath( _fileIdx, _pathIdx, path );
|
||||
}
|
||||
if (Utf8GamePath.FromString(_gamePathEdit, out var path, false) && !path.IsEmpty)
|
||||
_editor!.SetGamePath(_fileIdx, _pathIdx, path);
|
||||
|
||||
_fileIdx = -1;
|
||||
_pathIdx = -1;
|
||||
}
|
||||
else if( _fileIdx == i && _pathIdx == -1 && (!Utf8GamePath.FromString( _gamePathEdit, out var path, false )
|
||||
|| !path.IsEmpty && !_editor!.CanAddGamePath( path )) )
|
||||
else if (_fileIdx == i
|
||||
&& _pathIdx == -1
|
||||
&& (!Utf8GamePath.FromString(_gamePathEdit, out var path, false)
|
||||
|| !path.IsEmpty && !_editor!.CanAddGamePath(path)))
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPosX( pos );
|
||||
using var font = ImRaii.PushFont( UiBuilder.IconFont );
|
||||
ImGuiUtil.TextColored( 0xFF0000FF, FontAwesomeIcon.TimesCircle.ToIconString() );
|
||||
ImGui.SetCursorPosX(pos);
|
||||
using var font = ImRaii.PushFont(UiBuilder.IconFont);
|
||||
ImGuiUtil.TextColored(0xFF0000FF, FontAwesomeIcon.TimesCircle.ToIconString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -296,115 +264,97 @@ public partial class ModEditWindow
|
|||
{
|
||||
ImGui.NewLine();
|
||||
|
||||
using var spacing = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing, new Vector2( 3 * ImGuiHelpers.GlobalScale, 0 ) );
|
||||
ImGui.SetNextItemWidth( 30 * ImGuiHelpers.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." );
|
||||
using var spacing = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(3 * UiHelpers.Scale, 0));
|
||||
ImGui.SetNextItemWidth(30 * UiHelpers.Scale);
|
||||
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.");
|
||||
ImGui.SameLine();
|
||||
spacing.Pop();
|
||||
if( ImGui.Button( "Add Paths" ) )
|
||||
{
|
||||
_editor!.AddPathsToSelected( _editor!.AvailableFiles.Where( _selectedFiles.Contains ), _folderSkip );
|
||||
}
|
||||
if (ImGui.Button("Add Paths"))
|
||||
_editor!.AddPathsToSelected(_editor!.AvailableFiles.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." );
|
||||
"Add the file path converted to a game path to all selected files for the current option, optionally skipping the first N folders.");
|
||||
|
||||
|
||||
ImGui.SameLine();
|
||||
if( ImGui.Button( "Remove Paths" ) )
|
||||
{
|
||||
_editor!.RemovePathsFromSelected( _editor!.AvailableFiles.Where( _selectedFiles.Contains ) );
|
||||
}
|
||||
if (ImGui.Button("Remove Paths"))
|
||||
_editor!.RemovePathsFromSelected(_editor!.AvailableFiles.Where(_selectedFiles.Contains));
|
||||
|
||||
ImGuiUtil.HoverTooltip( "Remove all game paths associated with the selected files in the current option." );
|
||||
ImGuiUtil.HoverTooltip("Remove all game paths associated with the selected files in the current option.");
|
||||
|
||||
|
||||
ImGui.SameLine();
|
||||
if( ImGui.Button( "Delete Selected Files" ) )
|
||||
{
|
||||
_editor!.DeleteFiles( _editor!.AvailableFiles.Where( _selectedFiles.Contains ) );
|
||||
}
|
||||
if (ImGui.Button("Delete Selected Files"))
|
||||
_editor!.DeleteFiles(_editor!.AvailableFiles.Where(_selectedFiles.Contains));
|
||||
|
||||
ImGuiUtil.HoverTooltip(
|
||||
"Delete all selected files entirely from your filesystem, but not their file associations in the mod, if there are any.\n!!!This can not be reverted!!!" );
|
||||
"Delete all selected files entirely from your filesystem, but not their file associations in the mod, if there are any.\n!!!This can not be reverted!!!");
|
||||
ImGui.SameLine();
|
||||
var changes = _editor!.FileChanges;
|
||||
var tt = changes ? "Apply the current file setup to the currently selected option." : "No changes made.";
|
||||
if( ImGuiUtil.DrawDisabledButton( "Apply Changes", Vector2.Zero, tt, !changes ) )
|
||||
if (ImGuiUtil.DrawDisabledButton("Apply Changes", Vector2.Zero, tt, !changes))
|
||||
{
|
||||
var failedFiles = _editor!.ApplyFiles();
|
||||
if( failedFiles > 0 )
|
||||
{
|
||||
Penumbra.Log.Information( $"Failed to apply {failedFiles} file redirections to {_editor.CurrentOption.FullName}." );
|
||||
}
|
||||
if (failedFiles > 0)
|
||||
Penumbra.Log.Information($"Failed to apply {failedFiles} file redirections to {_editor.CurrentOption.FullName}.");
|
||||
}
|
||||
|
||||
|
||||
ImGui.SameLine();
|
||||
var label = changes ? "Revert Changes" : "Reload Files";
|
||||
var length = new Vector2( ImGui.CalcTextSize( "Revert Changes" ).X, 0 );
|
||||
if( ImGui.Button( label, length ) )
|
||||
{
|
||||
var length = new Vector2(ImGui.CalcTextSize("Revert Changes").X, 0);
|
||||
if (ImGui.Button(label, length))
|
||||
_editor!.RevertFiles();
|
||||
}
|
||||
|
||||
ImGuiUtil.HoverTooltip( "Revert all revertible changes since the last file or option reload or data refresh." );
|
||||
ImGuiUtil.HoverTooltip("Revert all revertible changes since the last file or option reload or data refresh.");
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.Checkbox( "Overview Mode", ref _overviewMode );
|
||||
ImGui.Checkbox("Overview Mode", ref _overviewMode);
|
||||
}
|
||||
|
||||
private void DrawFileManagementNormal()
|
||||
{
|
||||
ImGui.SetNextItemWidth( 250 * ImGuiHelpers.GlobalScale );
|
||||
LowerString.InputWithHint( "##filter", "Filter paths...", ref _fileFilter, Utf8GamePath.MaxGamePathLength );
|
||||
ImGui.SetNextItemWidth(250 * UiHelpers.Scale);
|
||||
LowerString.InputWithHint("##filter", "Filter paths...", ref _fileFilter, Utf8GamePath.MaxGamePathLength);
|
||||
ImGui.SameLine();
|
||||
ImGui.Checkbox( "Show Game Paths", ref _showGamePaths );
|
||||
ImGui.Checkbox("Show Game Paths", ref _showGamePaths);
|
||||
ImGui.SameLine();
|
||||
if( ImGui.Button( "Unselect All" ) )
|
||||
{
|
||||
if (ImGui.Button("Unselect All"))
|
||||
_selectedFiles.Clear();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if( ImGui.Button( "Select Visible" ) )
|
||||
{
|
||||
_selectedFiles.UnionWith( _editor!.AvailableFiles.Where( CheckFilter ) );
|
||||
}
|
||||
if (ImGui.Button("Select Visible"))
|
||||
_selectedFiles.UnionWith(_editor!.AvailableFiles.Where(CheckFilter));
|
||||
|
||||
ImGui.SameLine();
|
||||
if( ImGui.Button( "Select Unused" ) )
|
||||
{
|
||||
_selectedFiles.UnionWith( _editor!.AvailableFiles.Where( f => f.SubModUsage.Count == 0 ) );
|
||||
}
|
||||
if (ImGui.Button("Select Unused"))
|
||||
_selectedFiles.UnionWith(_editor!.AvailableFiles.Where(f => f.SubModUsage.Count == 0));
|
||||
|
||||
ImGui.SameLine();
|
||||
if( ImGui.Button( "Select Used Here" ) )
|
||||
{
|
||||
_selectedFiles.UnionWith( _editor!.AvailableFiles.Where( f => f.CurrentUsage > 0 ) );
|
||||
}
|
||||
if (ImGui.Button("Select Used Here"))
|
||||
_selectedFiles.UnionWith(_editor!.AvailableFiles.Where(f => f.CurrentUsage > 0));
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
ImGuiUtil.RightAlign( $"{_selectedFiles.Count} / {_editor!.AvailableFiles.Count} Files Selected" );
|
||||
ImGuiUtil.RightAlign($"{_selectedFiles.Count} / {_editor!.AvailableFiles.Count} Files Selected");
|
||||
}
|
||||
|
||||
private void DrawFileManagementOverview()
|
||||
{
|
||||
using var style = ImRaii.PushStyle( ImGuiStyleVar.FrameRounding, 0 )
|
||||
.Push( ImGuiStyleVar.ItemSpacing, Vector2.Zero )
|
||||
.Push( ImGuiStyleVar.FrameBorderSize, ImGui.GetStyle().ChildBorderSize );
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 0)
|
||||
.Push(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
|
||||
.Push(ImGuiStyleVar.FrameBorderSize, ImGui.GetStyle().ChildBorderSize);
|
||||
|
||||
var width = ImGui.GetContentRegionAvail().X / 8;
|
||||
|
||||
ImGui.SetNextItemWidth( width * 3 );
|
||||
LowerString.InputWithHint( "##fileFilter", "Filter file...", ref _fileOverviewFilter1, Utf8GamePath.MaxGamePathLength );
|
||||
ImGui.SetNextItemWidth(width * 3);
|
||||
LowerString.InputWithHint("##fileFilter", "Filter file...", ref _fileOverviewFilter1, Utf8GamePath.MaxGamePathLength);
|
||||
ImGui.SameLine();
|
||||
ImGui.SetNextItemWidth( width * 3 );
|
||||
LowerString.InputWithHint( "##pathFilter", "Filter path...", ref _fileOverviewFilter2, Utf8GamePath.MaxGamePathLength );
|
||||
ImGui.SetNextItemWidth(width * 3);
|
||||
LowerString.InputWithHint("##pathFilter", "Filter path...", ref _fileOverviewFilter2, Utf8GamePath.MaxGamePathLength);
|
||||
ImGui.SameLine();
|
||||
ImGui.SetNextItemWidth( width * 2 );
|
||||
LowerString.InputWithHint( "##optionFilter", "Filter option...", ref _fileOverviewFilter3, Utf8GamePath.MaxGamePathLength );
|
||||
ImGui.SetNextItemWidth(width * 2);
|
||||
LowerString.InputWithHint("##optionFilter", "Filter option...", ref _fileOverviewFilter3, Utf8GamePath.MaxGamePathLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -229,8 +229,8 @@ public partial class ModEditWindow
|
|||
var row = file.ColorSets[ colorSetIdx ].Rows[ rowIdx ];
|
||||
var hasDye = file.ColorDyeSets.Length > colorSetIdx;
|
||||
var dye = hasDye ? file.ColorDyeSets[ colorSetIdx ].Rows[ rowIdx ] : new MtrlFile.ColorDyeSet.Row();
|
||||
var floatSize = 70 * ImGuiHelpers.GlobalScale;
|
||||
var intSize = 45 * ImGuiHelpers.GlobalScale;
|
||||
var floatSize = 70 * UiHelpers.Scale;
|
||||
var intSize = 45 * UiHelpers.Scale;
|
||||
ImGui.TableNextColumn();
|
||||
ColorSetCopyClipboardButton( row, dye );
|
||||
ImGui.SameLine();
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ public partial class ModEditWindow
|
|||
LoadedShpkPath = path;
|
||||
var data = LoadedShpkPath.IsRooted
|
||||
? File.ReadAllBytes( LoadedShpkPath.FullName )
|
||||
: DalamudServices.GameData.GetFile( LoadedShpkPath.InternalName.ToString() )?.Data;
|
||||
: DalamudServices.SGameData.GetFile( LoadedShpkPath.InternalName.ToString() )?.Data;
|
||||
AssociatedShpk = data?.Length > 0 ? new ShpkFile( data ) : throw new Exception( "Failure to load file data." );
|
||||
LoadedShpkPathName = path.ToPath();
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ public partial class ModEditWindow
|
|||
{
|
||||
var samplers = Mtrl.GetSamplersByTexture( AssociatedShpk );
|
||||
TextureLabels.Clear();
|
||||
TextureLabelWidth = 50f * ImGuiHelpers.GlobalScale;
|
||||
TextureLabelWidth = 50f * UiHelpers.Scale;
|
||||
using( var _ = ImRaii.PushFont( UiBuilder.MonoFont ) )
|
||||
{
|
||||
for( var i = 0; i < Mtrl.Textures.Length; ++i )
|
||||
|
|
@ -112,7 +112,7 @@ public partial class ModEditWindow
|
|||
}
|
||||
}
|
||||
|
||||
TextureLabelWidth = TextureLabelWidth / ImGuiHelpers.GlobalScale + 4;
|
||||
TextureLabelWidth = TextureLabelWidth / UiHelpers.Scale + 4;
|
||||
}
|
||||
|
||||
public void UpdateShaderKeyLabels()
|
||||
|
|
|
|||
|
|
@ -17,36 +17,35 @@ namespace Penumbra.UI.Classes;
|
|||
|
||||
public partial class ModEditWindow
|
||||
{
|
||||
private readonly FileDialogManager _materialFileDialog = ConfigWindow.SetupFileManager();
|
||||
private readonly FileDialogService _fileDialog;
|
||||
|
||||
private bool DrawPackageNameInput( MtrlTab tab, bool disabled )
|
||||
private bool DrawPackageNameInput(MtrlTab tab, bool disabled)
|
||||
{
|
||||
var ret = false;
|
||||
ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 150.0f );
|
||||
if( ImGui.InputText( "Shader Package Name", ref tab.Mtrl.ShaderPackage.Name, 63, disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None ) )
|
||||
ImGui.SetNextItemWidth(UiHelpers.Scale * 150.0f);
|
||||
if (ImGui.InputText("Shader Package Name", ref tab.Mtrl.ShaderPackage.Name, 63,
|
||||
disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None))
|
||||
{
|
||||
ret = true;
|
||||
tab.AssociatedShpk = null;
|
||||
tab.LoadedShpkPath = FullPath.Empty;
|
||||
}
|
||||
|
||||
if( ImGui.IsItemDeactivatedAfterEdit() )
|
||||
{
|
||||
tab.LoadShpk( tab.FindAssociatedShpk( out _, out _ ) );
|
||||
}
|
||||
if (ImGui.IsItemDeactivatedAfterEdit())
|
||||
tab.LoadShpk(tab.FindAssociatedShpk(out _, out _));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static bool DrawShaderFlagsInput( MtrlFile file, bool disabled )
|
||||
private static bool DrawShaderFlagsInput(MtrlFile file, bool disabled)
|
||||
{
|
||||
var ret = false;
|
||||
var shpkFlags = ( int )file.ShaderPackage.Flags;
|
||||
ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 150.0f );
|
||||
if( ImGui.InputInt( "Shader Package Flags", ref shpkFlags, 0, 0,
|
||||
ImGuiInputTextFlags.CharsHexadecimal | ( disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None ) ) )
|
||||
var shpkFlags = (int)file.ShaderPackage.Flags;
|
||||
ImGui.SetNextItemWidth(UiHelpers.Scale * 150.0f);
|
||||
if (ImGui.InputInt("Shader Package Flags", ref shpkFlags, 0, 0,
|
||||
ImGuiInputTextFlags.CharsHexadecimal | (disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None)))
|
||||
{
|
||||
file.ShaderPackage.Flags = ( uint )shpkFlags;
|
||||
file.ShaderPackage.Flags = (uint)shpkFlags;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
|
|
@ -57,86 +56,72 @@ public partial class ModEditWindow
|
|||
/// Show the currently associated shpk file, if any, and the buttons to associate
|
||||
/// a specific shpk from your drive, the modded shpk by path or the default shpk.
|
||||
/// </summary>
|
||||
private void DrawCustomAssociations( MtrlTab tab )
|
||||
private void DrawCustomAssociations(MtrlTab tab)
|
||||
{
|
||||
var text = tab.AssociatedShpk == null
|
||||
? "Associated .shpk file: None"
|
||||
: $"Associated .shpk file: {tab.LoadedShpkPathName}";
|
||||
|
||||
ImGui.Dummy( new Vector2( ImGui.GetTextLineHeight() / 2 ) );
|
||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||
|
||||
if( ImGui.Selectable( text ) )
|
||||
{
|
||||
ImGui.SetClipboardText( tab.LoadedShpkPathName );
|
||||
}
|
||||
if (ImGui.Selectable(text))
|
||||
ImGui.SetClipboardText(tab.LoadedShpkPathName);
|
||||
|
||||
ImGuiUtil.HoverTooltip( "Click to copy file path to clipboard." );
|
||||
ImGuiUtil.HoverTooltip("Click to copy file path to clipboard.");
|
||||
|
||||
if( ImGui.Button( "Associate Custom .shpk File" ) )
|
||||
{
|
||||
_materialFileDialog.OpenFileDialog( "Associate Custom .shpk File...", ".shpk", ( success, name ) =>
|
||||
if (ImGui.Button("Associate Custom .shpk File"))
|
||||
_fileDialog.OpenFilePicker("Associate Custom .shpk File...", ".shpk", (success, name) =>
|
||||
{
|
||||
if( !success )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (success)
|
||||
tab.LoadShpk(new FullPath(name[0]));
|
||||
}, 1, _mod!.ModPath.FullName, false);
|
||||
|
||||
tab.LoadShpk( new FullPath( name ) );
|
||||
} );
|
||||
}
|
||||
|
||||
var moddedPath = tab.FindAssociatedShpk( out var defaultPath, out var gamePath );
|
||||
var moddedPath = tab.FindAssociatedShpk(out var defaultPath, out var gamePath);
|
||||
ImGui.SameLine();
|
||||
if( ImGuiUtil.DrawDisabledButton( "Associate Default .shpk File", Vector2.Zero, moddedPath.ToPath(), moddedPath.Equals( tab.LoadedShpkPath ) ) )
|
||||
{
|
||||
tab.LoadShpk( moddedPath );
|
||||
}
|
||||
if (ImGuiUtil.DrawDisabledButton("Associate Default .shpk File", Vector2.Zero, moddedPath.ToPath(),
|
||||
moddedPath.Equals(tab.LoadedShpkPath)))
|
||||
tab.LoadShpk(moddedPath);
|
||||
|
||||
if( !gamePath.Path.Equals( moddedPath.InternalName ) )
|
||||
if (!gamePath.Path.Equals(moddedPath.InternalName))
|
||||
{
|
||||
ImGui.SameLine();
|
||||
if( ImGuiUtil.DrawDisabledButton( "Associate Unmodded .shpk File", Vector2.Zero, defaultPath, gamePath.Path.Equals( tab.LoadedShpkPath.InternalName ) ) )
|
||||
{
|
||||
tab.LoadShpk( new FullPath( gamePath ) );
|
||||
}
|
||||
if (ImGuiUtil.DrawDisabledButton("Associate Unmodded .shpk File", Vector2.Zero, defaultPath,
|
||||
gamePath.Path.Equals(tab.LoadedShpkPath.InternalName)))
|
||||
tab.LoadShpk(new FullPath(gamePath));
|
||||
}
|
||||
|
||||
ImGui.Dummy( new Vector2( ImGui.GetTextLineHeight() / 2 ) );
|
||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||
}
|
||||
|
||||
|
||||
private static bool DrawShaderKey( MtrlTab tab, bool disabled, ref int idx )
|
||||
private static bool DrawShaderKey(MtrlTab tab, bool disabled, ref int idx)
|
||||
{
|
||||
var ret = false;
|
||||
using var t2 = ImRaii.TreeNode( tab.ShaderKeyLabels[ idx ], disabled ? ImGuiTreeNodeFlags.Leaf : 0 );
|
||||
if( !t2 || disabled )
|
||||
{
|
||||
using var t2 = ImRaii.TreeNode(tab.ShaderKeyLabels[idx], disabled ? ImGuiTreeNodeFlags.Leaf : 0);
|
||||
if (!t2 || disabled)
|
||||
return ret;
|
||||
}
|
||||
|
||||
var key = tab.Mtrl.ShaderPackage.ShaderKeys[ idx ];
|
||||
var shpkKey = tab.AssociatedShpk?.GetMaterialKeyById( key.Category );
|
||||
if( shpkKey.HasValue )
|
||||
var key = tab.Mtrl.ShaderPackage.ShaderKeys[idx];
|
||||
var shpkKey = tab.AssociatedShpk?.GetMaterialKeyById(key.Category);
|
||||
if (shpkKey.HasValue)
|
||||
{
|
||||
ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 150.0f );
|
||||
using var c = ImRaii.Combo( "Value", $"0x{key.Value:X8}" );
|
||||
if( c )
|
||||
{
|
||||
foreach( var value in shpkKey.Value.Values )
|
||||
ImGui.SetNextItemWidth(UiHelpers.Scale * 150.0f);
|
||||
using var c = ImRaii.Combo("Value", $"0x{key.Value:X8}");
|
||||
if (c)
|
||||
foreach (var value in shpkKey.Value.Values)
|
||||
{
|
||||
if( ImGui.Selectable( $"0x{value:X8}", value == key.Value ) )
|
||||
if (ImGui.Selectable($"0x{value:X8}", value == key.Value))
|
||||
{
|
||||
tab.Mtrl.ShaderPackage.ShaderKeys[ idx ].Value = value;
|
||||
ret = true;
|
||||
tab.Mtrl.ShaderPackage.ShaderKeys[idx].Value = value;
|
||||
ret = true;
|
||||
tab.UpdateShaderKeyLabels();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( ImGui.Button( "Remove Key" ) )
|
||||
if (ImGui.Button("Remove Key"))
|
||||
{
|
||||
tab.Mtrl.ShaderPackage.ShaderKeys = tab.Mtrl.ShaderPackage.ShaderKeys.RemoveItems( idx-- );
|
||||
tab.Mtrl.ShaderPackage.ShaderKeys = tab.Mtrl.ShaderPackage.ShaderKeys.RemoveItems(idx--);
|
||||
ret = true;
|
||||
tab.UpdateShaderKeyLabels();
|
||||
}
|
||||
|
|
@ -144,19 +129,18 @@ public partial class ModEditWindow
|
|||
return ret;
|
||||
}
|
||||
|
||||
private static bool DrawNewShaderKey( MtrlTab tab )
|
||||
private static bool DrawNewShaderKey(MtrlTab tab)
|
||||
{
|
||||
ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 150.0f );
|
||||
ImGui.SetNextItemWidth(UiHelpers.Scale * 150.0f);
|
||||
var ret = false;
|
||||
using( var c = ImRaii.Combo( "##NewConstantId", $"ID: 0x{tab.NewKeyId:X8}" ) )
|
||||
using (var c = ImRaii.Combo("##NewConstantId", $"ID: 0x{tab.NewKeyId:X8}"))
|
||||
{
|
||||
if( c )
|
||||
{
|
||||
foreach( var idx in tab.MissingShaderKeyIndices )
|
||||
if (c)
|
||||
foreach (var idx in tab.MissingShaderKeyIndices)
|
||||
{
|
||||
var key = tab.AssociatedShpk!.MaterialKeys[ idx ];
|
||||
var key = tab.AssociatedShpk!.MaterialKeys[idx];
|
||||
|
||||
if( ImGui.Selectable( $"ID: 0x{key.Id:X8}", key.Id == tab.NewKeyId ) )
|
||||
if (ImGui.Selectable($"ID: 0x{key.Id:X8}", key.Id == tab.NewKeyId))
|
||||
{
|
||||
tab.NewKeyDefault = key.DefaultValue;
|
||||
tab.NewKeyId = key.Id;
|
||||
|
|
@ -164,17 +148,16 @@ public partial class ModEditWindow
|
|||
tab.UpdateShaderKeyLabels();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if( ImGui.Button( "Add Key" ) )
|
||||
if (ImGui.Button("Add Key"))
|
||||
{
|
||||
tab.Mtrl.ShaderPackage.ShaderKeys = tab.Mtrl.ShaderPackage.ShaderKeys.AddItem( new ShaderKey
|
||||
tab.Mtrl.ShaderPackage.ShaderKeys = tab.Mtrl.ShaderPackage.ShaderKeys.AddItem(new ShaderKey
|
||||
{
|
||||
Category = tab.NewKeyId,
|
||||
Value = tab.NewKeyDefault,
|
||||
} );
|
||||
});
|
||||
ret = true;
|
||||
tab.UpdateShaderKeyLabels();
|
||||
}
|
||||
|
|
@ -182,70 +165,59 @@ public partial class ModEditWindow
|
|||
return ret;
|
||||
}
|
||||
|
||||
private static bool DrawMaterialShaderKeys( MtrlTab tab, bool disabled )
|
||||
private static bool DrawMaterialShaderKeys(MtrlTab tab, bool disabled)
|
||||
{
|
||||
if( tab.Mtrl.ShaderPackage.ShaderKeys.Length <= 0 && ( disabled || tab.AssociatedShpk == null || tab.AssociatedShpk.MaterialKeys.Length <= 0 ) )
|
||||
{
|
||||
if (tab.Mtrl.ShaderPackage.ShaderKeys.Length <= 0
|
||||
&& (disabled || tab.AssociatedShpk == null || tab.AssociatedShpk.MaterialKeys.Length <= 0))
|
||||
return false;
|
||||
}
|
||||
|
||||
using var t = ImRaii.TreeNode( "Shader Keys" );
|
||||
if( !t )
|
||||
{
|
||||
using var t = ImRaii.TreeNode("Shader Keys");
|
||||
if (!t)
|
||||
return false;
|
||||
}
|
||||
|
||||
var ret = false;
|
||||
for( var idx = 0; idx < tab.Mtrl.ShaderPackage.ShaderKeys.Length; ++idx )
|
||||
{
|
||||
ret |= DrawShaderKey( tab, disabled, ref idx );
|
||||
}
|
||||
for (var idx = 0; idx < tab.Mtrl.ShaderPackage.ShaderKeys.Length; ++idx)
|
||||
ret |= DrawShaderKey(tab, disabled, ref idx);
|
||||
|
||||
if( !disabled && tab.AssociatedShpk != null && tab.MissingShaderKeyIndices.Count != 0 )
|
||||
{
|
||||
ret |= DrawNewShaderKey( tab );
|
||||
}
|
||||
if (!disabled && tab.AssociatedShpk != null && tab.MissingShaderKeyIndices.Count != 0)
|
||||
ret |= DrawNewShaderKey(tab);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static void DrawMaterialShaders( MtrlTab tab )
|
||||
private static void DrawMaterialShaders(MtrlTab tab)
|
||||
{
|
||||
if( tab.AssociatedShpk == null )
|
||||
{
|
||||
if (tab.AssociatedShpk == null)
|
||||
return;
|
||||
}
|
||||
|
||||
ImRaii.TreeNode( tab.VertexShaders, ImGuiTreeNodeFlags.Leaf ).Dispose();
|
||||
ImRaii.TreeNode( tab.PixelShaders, ImGuiTreeNodeFlags.Leaf ).Dispose();
|
||||
ImRaii.TreeNode(tab.VertexShaders, ImGuiTreeNodeFlags.Leaf).Dispose();
|
||||
ImRaii.TreeNode(tab.PixelShaders, ImGuiTreeNodeFlags.Leaf).Dispose();
|
||||
}
|
||||
|
||||
|
||||
private static bool DrawMaterialConstantValues( MtrlTab tab, bool disabled, ref int idx )
|
||||
private static bool DrawMaterialConstantValues(MtrlTab tab, bool disabled, ref int idx)
|
||||
{
|
||||
var (name, componentOnly, paramValueOffset) = tab.MaterialConstants[ idx ];
|
||||
using var font = ImRaii.PushFont( UiBuilder.MonoFont );
|
||||
using var t2 = ImRaii.TreeNode( name );
|
||||
if( !t2 )
|
||||
{
|
||||
var (name, componentOnly, paramValueOffset) = tab.MaterialConstants[idx];
|
||||
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
|
||||
using var t2 = ImRaii.TreeNode(name);
|
||||
if (!t2)
|
||||
return false;
|
||||
}
|
||||
|
||||
font.Dispose();
|
||||
|
||||
var constant = tab.Mtrl.ShaderPackage.Constants[ idx ];
|
||||
var constant = tab.Mtrl.ShaderPackage.Constants[idx];
|
||||
var ret = false;
|
||||
var values = tab.Mtrl.GetConstantValues( constant );
|
||||
if( values.Length > 0 )
|
||||
var values = tab.Mtrl.GetConstantValues(constant);
|
||||
if (values.Length > 0)
|
||||
{
|
||||
var valueOffset = constant.ByteOffset >> 2;
|
||||
|
||||
for( var valueIdx = 0; valueIdx < values.Length; ++valueIdx )
|
||||
for (var valueIdx = 0; valueIdx < values.Length; ++valueIdx)
|
||||
{
|
||||
var paramName = MaterialParamName( componentOnly, paramValueOffset + valueIdx ) ?? $"#{valueIdx}";
|
||||
ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 150.0f );
|
||||
if( ImGui.InputFloat( $"{paramName} (at 0x{( valueOffset + valueIdx ) << 2:X4})", ref values[ valueIdx ], 0.0f, 0.0f, "%.3f",
|
||||
disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None ) )
|
||||
var paramName = MaterialParamName(componentOnly, paramValueOffset + valueIdx) ?? $"#{valueIdx}";
|
||||
ImGui.SetNextItemWidth(UiHelpers.Scale * 150.0f);
|
||||
if (ImGui.InputFloat($"{paramName} (at 0x{(valueOffset + valueIdx) << 2:X4})", ref values[valueIdx], 0.0f, 0.0f, "%.3f",
|
||||
disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None))
|
||||
{
|
||||
ret = true;
|
||||
tab.UpdateConstantLabels();
|
||||
|
|
@ -254,24 +226,23 @@ public partial class ModEditWindow
|
|||
}
|
||||
else
|
||||
{
|
||||
ImRaii.TreeNode( $"Offset: 0x{constant.ByteOffset:X4}", ImGuiTreeNodeFlags.Leaf ).Dispose();
|
||||
ImRaii.TreeNode( $"Size: 0x{constant.ByteSize:X4}", ImGuiTreeNodeFlags.Leaf ).Dispose();
|
||||
ImRaii.TreeNode($"Offset: 0x{constant.ByteOffset:X4}", ImGuiTreeNodeFlags.Leaf).Dispose();
|
||||
ImRaii.TreeNode($"Size: 0x{constant.ByteSize:X4}", ImGuiTreeNodeFlags.Leaf).Dispose();
|
||||
}
|
||||
|
||||
if( !disabled
|
||||
&& !tab.HasMalformedMaterialConstants
|
||||
&& tab.OrphanedMaterialValues.Count == 0
|
||||
&& tab.AliasedMaterialValueCount == 0
|
||||
&& ImGui.Button( "Remove Constant" ) )
|
||||
if (!disabled
|
||||
&& !tab.HasMalformedMaterialConstants
|
||||
&& tab.OrphanedMaterialValues.Count == 0
|
||||
&& tab.AliasedMaterialValueCount == 0
|
||||
&& ImGui.Button("Remove Constant"))
|
||||
{
|
||||
tab.Mtrl.ShaderPackage.ShaderValues = tab.Mtrl.ShaderPackage.ShaderValues.RemoveItems( constant.ByteOffset >> 2, constant.ByteSize >> 2 );
|
||||
tab.Mtrl.ShaderPackage.Constants = tab.Mtrl.ShaderPackage.Constants.RemoveItems( idx-- );
|
||||
for( var i = 0; i < tab.Mtrl.ShaderPackage.Constants.Length; ++i )
|
||||
tab.Mtrl.ShaderPackage.ShaderValues =
|
||||
tab.Mtrl.ShaderPackage.ShaderValues.RemoveItems(constant.ByteOffset >> 2, constant.ByteSize >> 2);
|
||||
tab.Mtrl.ShaderPackage.Constants = tab.Mtrl.ShaderPackage.Constants.RemoveItems(idx--);
|
||||
for (var i = 0; i < tab.Mtrl.ShaderPackage.Constants.Length; ++i)
|
||||
{
|
||||
if( tab.Mtrl.ShaderPackage.Constants[ i ].ByteOffset >= constant.ByteOffset )
|
||||
{
|
||||
tab.Mtrl.ShaderPackage.Constants[ i ].ByteOffset -= constant.ByteSize;
|
||||
}
|
||||
if (tab.Mtrl.ShaderPackage.Constants[i].ByteOffset >= constant.ByteOffset)
|
||||
tab.Mtrl.ShaderPackage.Constants[i].ByteOffset -= constant.ByteSize;
|
||||
}
|
||||
|
||||
ret = true;
|
||||
|
|
@ -281,21 +252,19 @@ public partial class ModEditWindow
|
|||
return ret;
|
||||
}
|
||||
|
||||
private static bool DrawMaterialOrphans( MtrlTab tab, bool disabled )
|
||||
private static bool DrawMaterialOrphans(MtrlTab tab, bool disabled)
|
||||
{
|
||||
using var t2 = ImRaii.TreeNode( $"Orphan Values ({tab.OrphanedMaterialValues.Count})" );
|
||||
if( !t2 )
|
||||
{
|
||||
using var t2 = ImRaii.TreeNode($"Orphan Values ({tab.OrphanedMaterialValues.Count})");
|
||||
if (!t2)
|
||||
return false;
|
||||
}
|
||||
|
||||
var ret = false;
|
||||
foreach( var idx in tab.OrphanedMaterialValues )
|
||||
foreach (var idx in tab.OrphanedMaterialValues)
|
||||
{
|
||||
ImGui.SetNextItemWidth( ImGui.GetFontSize() * 10.0f );
|
||||
if( ImGui.InputFloat( $"#{idx} (at 0x{idx << 2:X4})",
|
||||
ref tab.Mtrl.ShaderPackage.ShaderValues[ idx ], 0.0f, 0.0f, "%.3f",
|
||||
disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None ) )
|
||||
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 10.0f);
|
||||
if (ImGui.InputFloat($"#{idx} (at 0x{idx << 2:X4})",
|
||||
ref tab.Mtrl.ShaderPackage.ShaderValues[idx], 0.0f, 0.0f, "%.3f",
|
||||
disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None))
|
||||
{
|
||||
ret = true;
|
||||
tab.UpdateConstantLabels();
|
||||
|
|
@ -305,36 +274,34 @@ public partial class ModEditWindow
|
|||
return ret;
|
||||
}
|
||||
|
||||
private static bool DrawNewMaterialParam( MtrlTab tab )
|
||||
private static bool DrawNewMaterialParam(MtrlTab tab)
|
||||
{
|
||||
ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 450.0f );
|
||||
using( var font = ImRaii.PushFont( UiBuilder.MonoFont ) )
|
||||
ImGui.SetNextItemWidth(UiHelpers.Scale * 450.0f);
|
||||
using (var font = ImRaii.PushFont(UiBuilder.MonoFont))
|
||||
{
|
||||
using var c = ImRaii.Combo( "##NewConstantId", tab.MissingMaterialConstants[ tab.NewConstantIdx ].Name );
|
||||
if( c )
|
||||
{
|
||||
foreach( var (constant, idx) in tab.MissingMaterialConstants.WithIndex() )
|
||||
using var c = ImRaii.Combo("##NewConstantId", tab.MissingMaterialConstants[tab.NewConstantIdx].Name);
|
||||
if (c)
|
||||
foreach (var (constant, idx) in tab.MissingMaterialConstants.WithIndex())
|
||||
{
|
||||
if( ImGui.Selectable( constant.Name, constant.Id == tab.NewConstantId ) )
|
||||
if (ImGui.Selectable(constant.Name, constant.Id == tab.NewConstantId))
|
||||
{
|
||||
tab.NewConstantIdx = idx;
|
||||
tab.NewConstantId = constant.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if( ImGui.Button( "Add Constant" ) )
|
||||
if (ImGui.Button("Add Constant"))
|
||||
{
|
||||
var (_, _, byteSize) = tab.MissingMaterialConstants[ tab.NewConstantIdx ];
|
||||
tab.Mtrl.ShaderPackage.Constants = tab.Mtrl.ShaderPackage.Constants.AddItem( new MtrlFile.Constant
|
||||
var (_, _, byteSize) = tab.MissingMaterialConstants[tab.NewConstantIdx];
|
||||
tab.Mtrl.ShaderPackage.Constants = tab.Mtrl.ShaderPackage.Constants.AddItem(new MtrlFile.Constant
|
||||
{
|
||||
Id = tab.NewConstantId,
|
||||
ByteOffset = ( ushort )( tab.Mtrl.ShaderPackage.ShaderValues.Length << 2 ),
|
||||
ByteOffset = (ushort)(tab.Mtrl.ShaderPackage.ShaderValues.Length << 2),
|
||||
ByteSize = byteSize,
|
||||
} );
|
||||
tab.Mtrl.ShaderPackage.ShaderValues = tab.Mtrl.ShaderPackage.ShaderValues.AddItem( 0.0f, byteSize >> 2 );
|
||||
});
|
||||
tab.Mtrl.ShaderPackage.ShaderValues = tab.Mtrl.ShaderPackage.ShaderValues.AddItem(0.0f, byteSize >> 2);
|
||||
tab.UpdateConstantLabels();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -342,92 +309,76 @@ public partial class ModEditWindow
|
|||
return false;
|
||||
}
|
||||
|
||||
private static bool DrawMaterialConstants( MtrlTab tab, bool disabled )
|
||||
private static bool DrawMaterialConstants(MtrlTab tab, bool disabled)
|
||||
{
|
||||
if( tab.Mtrl.ShaderPackage.Constants.Length == 0
|
||||
&& tab.Mtrl.ShaderPackage.ShaderValues.Length == 0
|
||||
&& ( disabled || tab.AssociatedShpk == null || tab.AssociatedShpk.MaterialParams.Length == 0 ) )
|
||||
{
|
||||
if (tab.Mtrl.ShaderPackage.Constants.Length == 0
|
||||
&& tab.Mtrl.ShaderPackage.ShaderValues.Length == 0
|
||||
&& (disabled || tab.AssociatedShpk == null || tab.AssociatedShpk.MaterialParams.Length == 0))
|
||||
return false;
|
||||
}
|
||||
|
||||
using var font = ImRaii.PushFont( UiBuilder.MonoFont );
|
||||
using var t = ImRaii.TreeNode( tab.MaterialConstantLabel );
|
||||
if( !t )
|
||||
{
|
||||
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
|
||||
using var t = ImRaii.TreeNode(tab.MaterialConstantLabel);
|
||||
if (!t)
|
||||
return false;
|
||||
}
|
||||
|
||||
font.Dispose();
|
||||
var ret = false;
|
||||
for( var idx = 0; idx < tab.Mtrl.ShaderPackage.Constants.Length; ++idx )
|
||||
{
|
||||
ret |= DrawMaterialConstantValues( tab, disabled, ref idx );
|
||||
}
|
||||
for (var idx = 0; idx < tab.Mtrl.ShaderPackage.Constants.Length; ++idx)
|
||||
ret |= DrawMaterialConstantValues(tab, disabled, ref idx);
|
||||
|
||||
if( tab.OrphanedMaterialValues.Count > 0 )
|
||||
{
|
||||
ret |= DrawMaterialOrphans( tab, disabled );
|
||||
}
|
||||
else if( !disabled && !tab.HasMalformedMaterialConstants && tab.MissingMaterialConstants.Count > 0 )
|
||||
{
|
||||
ret |= DrawNewMaterialParam( tab );
|
||||
}
|
||||
if (tab.OrphanedMaterialValues.Count > 0)
|
||||
ret |= DrawMaterialOrphans(tab, disabled);
|
||||
else if (!disabled && !tab.HasMalformedMaterialConstants && tab.MissingMaterialConstants.Count > 0)
|
||||
ret |= DrawNewMaterialParam(tab);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static bool DrawMaterialSampler( MtrlTab tab, bool disabled, ref int idx )
|
||||
private static bool DrawMaterialSampler(MtrlTab tab, bool disabled, ref int idx)
|
||||
{
|
||||
var (label, filename) = tab.Samplers[ idx ];
|
||||
using var tree = ImRaii.TreeNode( label );
|
||||
if( !tree )
|
||||
{
|
||||
var (label, filename) = tab.Samplers[idx];
|
||||
using var tree = ImRaii.TreeNode(label);
|
||||
if (!tree)
|
||||
return false;
|
||||
}
|
||||
|
||||
ImRaii.TreeNode( filename, ImGuiTreeNodeFlags.Leaf ).Dispose();
|
||||
ImRaii.TreeNode(filename, ImGuiTreeNodeFlags.Leaf).Dispose();
|
||||
var ret = false;
|
||||
var sampler = tab.Mtrl.ShaderPackage.Samplers[ idx ];
|
||||
var sampler = tab.Mtrl.ShaderPackage.Samplers[idx];
|
||||
|
||||
// FIXME this probably doesn't belong here
|
||||
static unsafe bool InputHexUInt16( string label, ref ushort v, ImGuiInputTextFlags flags )
|
||||
static unsafe bool InputHexUInt16(string label, ref ushort v, ImGuiInputTextFlags flags)
|
||||
{
|
||||
fixed( ushort* v2 = &v )
|
||||
fixed (ushort* v2 = &v)
|
||||
{
|
||||
return ImGui.InputScalar( label, ImGuiDataType.U16, ( nint )v2, IntPtr.Zero, IntPtr.Zero, "%04X", flags );
|
||||
return ImGui.InputScalar(label, ImGuiDataType.U16, (nint)v2, IntPtr.Zero, IntPtr.Zero, "%04X", flags);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 150.0f );
|
||||
if( InputHexUInt16( "Texture Flags", ref tab.Mtrl.Textures[ sampler.TextureIndex ].Flags,
|
||||
disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None ) )
|
||||
{
|
||||
ImGui.SetNextItemWidth(UiHelpers.Scale * 150.0f);
|
||||
if (InputHexUInt16("Texture Flags", ref tab.Mtrl.Textures[sampler.TextureIndex].Flags,
|
||||
disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None))
|
||||
ret = true;
|
||||
|
||||
var samplerFlags = (int)sampler.Flags;
|
||||
ImGui.SetNextItemWidth(UiHelpers.Scale * 150.0f);
|
||||
if (ImGui.InputInt("Sampler Flags", ref samplerFlags, 0, 0,
|
||||
ImGuiInputTextFlags.CharsHexadecimal | (disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None)))
|
||||
{
|
||||
tab.Mtrl.ShaderPackage.Samplers[idx].Flags = (uint)samplerFlags;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
var samplerFlags = ( int )sampler.Flags;
|
||||
ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 150.0f );
|
||||
if( ImGui.InputInt( "Sampler Flags", ref samplerFlags, 0, 0,
|
||||
ImGuiInputTextFlags.CharsHexadecimal | ( disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None ) ) )
|
||||
if (!disabled
|
||||
&& tab.OrphanedSamplers.Count == 0
|
||||
&& tab.AliasedSamplerCount == 0
|
||||
&& ImGui.Button("Remove Sampler"))
|
||||
{
|
||||
tab.Mtrl.ShaderPackage.Samplers[ idx ].Flags = ( uint )samplerFlags;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if( !disabled
|
||||
&& tab.OrphanedSamplers.Count == 0
|
||||
&& tab.AliasedSamplerCount == 0
|
||||
&& ImGui.Button( "Remove Sampler" ) )
|
||||
{
|
||||
tab.Mtrl.Textures = tab.Mtrl.Textures.RemoveItems( sampler.TextureIndex );
|
||||
tab.Mtrl.ShaderPackage.Samplers = tab.Mtrl.ShaderPackage.Samplers.RemoveItems( idx-- );
|
||||
for( var i = 0; i < tab.Mtrl.ShaderPackage.Samplers.Length; ++i )
|
||||
tab.Mtrl.Textures = tab.Mtrl.Textures.RemoveItems(sampler.TextureIndex);
|
||||
tab.Mtrl.ShaderPackage.Samplers = tab.Mtrl.ShaderPackage.Samplers.RemoveItems(idx--);
|
||||
for (var i = 0; i < tab.Mtrl.ShaderPackage.Samplers.Length; ++i)
|
||||
{
|
||||
if( tab.Mtrl.ShaderPackage.Samplers[ i ].TextureIndex >= sampler.TextureIndex )
|
||||
{
|
||||
--tab.Mtrl.ShaderPackage.Samplers[ i ].TextureIndex;
|
||||
}
|
||||
if (tab.Mtrl.ShaderPackage.Samplers[i].TextureIndex >= sampler.TextureIndex)
|
||||
--tab.Mtrl.ShaderPackage.Samplers[i].TextureIndex;
|
||||
}
|
||||
|
||||
ret = true;
|
||||
|
|
@ -438,114 +389,99 @@ public partial class ModEditWindow
|
|||
return ret;
|
||||
}
|
||||
|
||||
private static bool DrawMaterialNewSampler( MtrlTab tab )
|
||||
private static bool DrawMaterialNewSampler(MtrlTab tab)
|
||||
{
|
||||
var (name, id) = tab.MissingSamplers[ tab.NewSamplerIdx ];
|
||||
ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 450.0f );
|
||||
using( var c = ImRaii.Combo( "##NewSamplerId", $"{name} (ID: 0x{id:X8})" ) )
|
||||
var (name, id) = tab.MissingSamplers[tab.NewSamplerIdx];
|
||||
ImGui.SetNextItemWidth(UiHelpers.Scale * 450.0f);
|
||||
using (var c = ImRaii.Combo("##NewSamplerId", $"{name} (ID: 0x{id:X8})"))
|
||||
{
|
||||
if( c )
|
||||
{
|
||||
foreach( var (sampler, idx) in tab.MissingSamplers.WithIndex() )
|
||||
if (c)
|
||||
foreach (var (sampler, idx) in tab.MissingSamplers.WithIndex())
|
||||
{
|
||||
if( ImGui.Selectable( $"{sampler.Name} (ID: 0x{sampler.Id:X8})", sampler.Id == tab.NewSamplerId ) )
|
||||
if (ImGui.Selectable($"{sampler.Name} (ID: 0x{sampler.Id:X8})", sampler.Id == tab.NewSamplerId))
|
||||
{
|
||||
tab.NewSamplerIdx = idx;
|
||||
tab.NewSamplerId = sampler.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if( !ImGui.Button( "Add Sampler" ) )
|
||||
{
|
||||
if (!ImGui.Button("Add Sampler"))
|
||||
return false;
|
||||
}
|
||||
|
||||
tab.Mtrl.ShaderPackage.Samplers = tab.Mtrl.ShaderPackage.Samplers.AddItem( new Sampler
|
||||
tab.Mtrl.ShaderPackage.Samplers = tab.Mtrl.ShaderPackage.Samplers.AddItem(new Sampler
|
||||
{
|
||||
SamplerId = tab.NewSamplerId,
|
||||
TextureIndex = ( byte )tab.Mtrl.Textures.Length,
|
||||
TextureIndex = (byte)tab.Mtrl.Textures.Length,
|
||||
Flags = 0,
|
||||
} );
|
||||
tab.Mtrl.Textures = tab.Mtrl.Textures.AddItem( new MtrlFile.Texture
|
||||
});
|
||||
tab.Mtrl.Textures = tab.Mtrl.Textures.AddItem(new MtrlFile.Texture
|
||||
{
|
||||
Path = string.Empty,
|
||||
Flags = 0,
|
||||
} );
|
||||
});
|
||||
tab.UpdateSamplers();
|
||||
tab.UpdateTextureLabels();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool DrawMaterialSamplers( MtrlTab tab, bool disabled )
|
||||
private static bool DrawMaterialSamplers(MtrlTab tab, bool disabled)
|
||||
{
|
||||
if( tab.Mtrl.ShaderPackage.Samplers.Length == 0
|
||||
&& tab.Mtrl.Textures.Length == 0
|
||||
&& ( disabled || ( tab.AssociatedShpk?.Samplers.All( sampler => sampler.Slot != 2 ) ?? false ) ) )
|
||||
{
|
||||
if (tab.Mtrl.ShaderPackage.Samplers.Length == 0
|
||||
&& tab.Mtrl.Textures.Length == 0
|
||||
&& (disabled || (tab.AssociatedShpk?.Samplers.All(sampler => sampler.Slot != 2) ?? false)))
|
||||
return false;
|
||||
}
|
||||
|
||||
using var t = ImRaii.TreeNode( "Samplers" );
|
||||
if( !t )
|
||||
{
|
||||
using var t = ImRaii.TreeNode("Samplers");
|
||||
if (!t)
|
||||
return false;
|
||||
}
|
||||
|
||||
var ret = false;
|
||||
for( var idx = 0; idx < tab.Mtrl.ShaderPackage.Samplers.Length; ++idx )
|
||||
{
|
||||
ret |= DrawMaterialSampler( tab, disabled, ref idx );
|
||||
}
|
||||
for (var idx = 0; idx < tab.Mtrl.ShaderPackage.Samplers.Length; ++idx)
|
||||
ret |= DrawMaterialSampler(tab, disabled, ref idx);
|
||||
|
||||
if( tab.OrphanedSamplers.Count > 0 )
|
||||
if (tab.OrphanedSamplers.Count > 0)
|
||||
{
|
||||
using var t2 = ImRaii.TreeNode( $"Orphan Textures ({tab.OrphanedSamplers.Count})" );
|
||||
if( t2 )
|
||||
{
|
||||
foreach( var idx in tab.OrphanedSamplers )
|
||||
using var t2 = ImRaii.TreeNode($"Orphan Textures ({tab.OrphanedSamplers.Count})");
|
||||
if (t2)
|
||||
foreach (var idx in tab.OrphanedSamplers)
|
||||
{
|
||||
ImRaii.TreeNode( $"#{idx}: {Path.GetFileName( tab.Mtrl.Textures[ idx ].Path )} - {tab.Mtrl.Textures[ idx ].Flags:X4}", ImGuiTreeNodeFlags.Leaf )
|
||||
.Dispose();
|
||||
ImRaii.TreeNode($"#{idx}: {Path.GetFileName(tab.Mtrl.Textures[idx].Path)} - {tab.Mtrl.Textures[idx].Flags:X4}",
|
||||
ImGuiTreeNodeFlags.Leaf)
|
||||
.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( !disabled && tab.MissingSamplers.Count > 0 && tab.AliasedSamplerCount == 0 && tab.Mtrl.Textures.Length < 255 )
|
||||
else if (!disabled && tab.MissingSamplers.Count > 0 && tab.AliasedSamplerCount == 0 && tab.Mtrl.Textures.Length < 255)
|
||||
{
|
||||
ret |= DrawMaterialNewSampler( tab );
|
||||
ret |= DrawMaterialNewSampler(tab);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private bool DrawMaterialShaderResources( MtrlTab tab, bool disabled )
|
||||
private bool DrawMaterialShaderResources(MtrlTab tab, bool disabled)
|
||||
{
|
||||
var ret = false;
|
||||
if( !ImGui.CollapsingHeader( "Advanced Shader Resources" ) )
|
||||
{
|
||||
if (!ImGui.CollapsingHeader("Advanced Shader Resources"))
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret |= DrawPackageNameInput( tab, disabled );
|
||||
ret |= DrawShaderFlagsInput( tab.Mtrl, disabled );
|
||||
DrawCustomAssociations( tab );
|
||||
ret |= DrawMaterialShaderKeys( tab, disabled );
|
||||
DrawMaterialShaders( tab );
|
||||
ret |= DrawMaterialConstants( tab, disabled );
|
||||
ret |= DrawMaterialSamplers( tab, disabled );
|
||||
ret |= DrawPackageNameInput(tab, disabled);
|
||||
ret |= DrawShaderFlagsInput(tab.Mtrl, disabled);
|
||||
DrawCustomAssociations(tab);
|
||||
ret |= DrawMaterialShaderKeys(tab, disabled);
|
||||
DrawMaterialShaders(tab);
|
||||
ret |= DrawMaterialConstants(tab, disabled);
|
||||
ret |= DrawMaterialSamplers(tab, disabled);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static string? MaterialParamName( bool componentOnly, int offset )
|
||||
private static string? MaterialParamName(bool componentOnly, int offset)
|
||||
{
|
||||
if( offset < 0 )
|
||||
{
|
||||
if (offset < 0)
|
||||
return null;
|
||||
}
|
||||
|
||||
return ( componentOnly, offset & 0x3 ) switch
|
||||
return (componentOnly, offset & 0x3) switch
|
||||
{
|
||||
(true, 0) => "x",
|
||||
(true, 1) => "y",
|
||||
|
|
@ -559,10 +495,10 @@ public partial class ModEditWindow
|
|||
};
|
||||
}
|
||||
|
||||
private static (string? Name, bool ComponentOnly) MaterialParamRangeName( string prefix, int valueOffset, int valueLength )
|
||||
private static (string? Name, bool ComponentOnly) MaterialParamRangeName(string prefix, int valueOffset, int valueLength)
|
||||
{
|
||||
static string VectorSwizzle( int firstComponent, int lastComponent )
|
||||
=> ( firstComponent, lastComponent ) switch
|
||||
static string VectorSwizzle(int firstComponent, int lastComponent)
|
||||
=> (firstComponent, lastComponent) switch
|
||||
{
|
||||
(0, 4) => " ",
|
||||
(0, 0) => ".x ",
|
||||
|
|
@ -578,28 +514,22 @@ public partial class ModEditWindow
|
|||
_ => string.Empty,
|
||||
};
|
||||
|
||||
if( valueLength == 0 || valueOffset < 0 )
|
||||
{
|
||||
return ( null, false );
|
||||
}
|
||||
if (valueLength == 0 || valueOffset < 0)
|
||||
return (null, false);
|
||||
|
||||
var firstVector = valueOffset >> 2;
|
||||
var lastVector = ( valueOffset + valueLength - 1 ) >> 2;
|
||||
var firstComponent = valueOffset & 0x3;
|
||||
var lastComponent = ( valueOffset + valueLength - 1 ) & 0x3;
|
||||
if( firstVector == lastVector )
|
||||
{
|
||||
return ( $"{prefix}[{firstVector}]{VectorSwizzle( firstComponent, lastComponent )}", true );
|
||||
}
|
||||
var firstVector = valueOffset >> 2;
|
||||
var lastVector = (valueOffset + valueLength - 1) >> 2;
|
||||
var firstComponent = valueOffset & 0x3;
|
||||
var lastComponent = (valueOffset + valueLength - 1) & 0x3;
|
||||
if (firstVector == lastVector)
|
||||
return ($"{prefix}[{firstVector}]{VectorSwizzle(firstComponent, lastComponent)}", true);
|
||||
|
||||
var sb = new StringBuilder( 128 );
|
||||
sb.Append( $"{prefix}[{firstVector}]{VectorSwizzle( firstComponent, 3 ).TrimEnd()}" );
|
||||
for( var i = firstVector + 1; i < lastVector; ++i )
|
||||
{
|
||||
sb.Append( $", [{i}]" );
|
||||
}
|
||||
var sb = new StringBuilder(128);
|
||||
sb.Append($"{prefix}[{firstVector}]{VectorSwizzle(firstComponent, 3).TrimEnd()}");
|
||||
for (var i = firstVector + 1; i < lastVector; ++i)
|
||||
sb.Append($", [{i}]");
|
||||
|
||||
sb.Append( $", [{lastVector}]{VectorSwizzle( 0, lastComponent )}" );
|
||||
return ( sb.ToString(), false );
|
||||
sb.Append($", [{lastVector}]{VectorSwizzle(0, lastComponent)}");
|
||||
return (sb.ToString(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@ public partial class ModEditWindow
|
|||
ImGui.Dummy( new Vector2( ImGui.GetTextLineHeight() / 2 ) );
|
||||
DrawOtherMaterialDetails( tab.Mtrl, disabled );
|
||||
|
||||
_materialFileDialog.Draw();
|
||||
|
||||
return !disabled && ret;
|
||||
}
|
||||
|
||||
|
|
@ -39,7 +37,7 @@ public partial class ModEditWindow
|
|||
var ret = false;
|
||||
using var table = ImRaii.Table( "##Textures", 2 );
|
||||
ImGui.TableSetupColumn( "Path", ImGuiTableColumnFlags.WidthStretch );
|
||||
ImGui.TableSetupColumn( "Name", ImGuiTableColumnFlags.WidthFixed, tab.TextureLabelWidth * ImGuiHelpers.GlobalScale );
|
||||
ImGui.TableSetupColumn( "Name", ImGuiTableColumnFlags.WidthFixed, tab.TextureLabelWidth * UiHelpers.Scale );
|
||||
for( var i = 0; i < tab.Mtrl.Textures.Length; ++i )
|
||||
{
|
||||
using var _ = ImRaii.PushId( i );
|
||||
|
|
@ -80,7 +78,7 @@ public partial class ModEditWindow
|
|||
ret = true;
|
||||
}
|
||||
|
||||
ImGui.SameLine( 200 * ImGuiHelpers.GlobalScale + ImGui.GetStyle().ItemSpacing.X + ImGui.GetStyle().WindowPadding.X );
|
||||
ImGui.SameLine( 200 * UiHelpers.Scale + ImGui.GetStyle().ItemSpacing.X + ImGui.GetStyle().WindowPadding.X );
|
||||
tmp = ( file.ShaderPackage.Flags & backfaceBit ) != 0;
|
||||
if( ImGui.Checkbox( "Hide Backfaces", ref tmp ) )
|
||||
{
|
||||
|
|
@ -171,7 +169,7 @@ public partial class ModEditWindow
|
|||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted( info.Path.FullName[ ( _mod!.ModPath.FullName.Length + 1 ).. ] );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( 400 * ImGuiHelpers.GlobalScale );
|
||||
ImGui.SetNextItemWidth( 400 * UiHelpers.Scale );
|
||||
var tmp = info.CurrentMaterials[ 0 ];
|
||||
if( ImGui.InputText( "##0", ref tmp, 64 ) )
|
||||
{
|
||||
|
|
@ -184,7 +182,7 @@ public partial class ModEditWindow
|
|||
ImGui.TableNextColumn();
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( 400 * ImGuiHelpers.GlobalScale );
|
||||
ImGui.SetNextItemWidth( 400 * UiHelpers.Scale );
|
||||
tmp = info.CurrentMaterials[ i ];
|
||||
if( ImGui.InputText( $"##{i}", ref tmp, 64 ) )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ public partial class ModEditWindow
|
|||
private static EqpManipulation _new = new(Eqp.DefaultEntry, EquipSlot.Head, 1);
|
||||
|
||||
private static float IdWidth
|
||||
=> 100 * ImGuiHelpers.GlobalScale;
|
||||
=> 100 * UiHelpers.Scale;
|
||||
|
||||
public static void DrawNew( Mod.Editor editor, Vector2 iconSize )
|
||||
{
|
||||
|
|
@ -154,7 +154,7 @@ public partial class ModEditWindow
|
|||
using var disabled = ImRaii.Disabled();
|
||||
ImGui.TableNextColumn();
|
||||
using var style = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing,
|
||||
new Vector2( 3 * ImGuiHelpers.GlobalScale, ImGui.GetStyle().ItemSpacing.Y ) );
|
||||
new Vector2( 3 * UiHelpers.Scale, ImGui.GetStyle().ItemSpacing.Y ) );
|
||||
foreach( var flag in Eqp.EqpAttributes[ _new.Slot ] )
|
||||
{
|
||||
var value = defaultEntry.HasFlag( flag );
|
||||
|
|
@ -184,7 +184,7 @@ public partial class ModEditWindow
|
|||
// Values
|
||||
ImGui.TableNextColumn();
|
||||
using var style = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing,
|
||||
new Vector2( 3 * ImGuiHelpers.GlobalScale, ImGui.GetStyle().ItemSpacing.Y ) );
|
||||
new Vector2( 3 * UiHelpers.Scale, ImGui.GetStyle().ItemSpacing.Y ) );
|
||||
var idx = 0;
|
||||
foreach( var flag in Eqp.EqpAttributes[ meta.Slot ] )
|
||||
{
|
||||
|
|
@ -209,7 +209,7 @@ public partial class ModEditWindow
|
|||
private static EqdpManipulation _new = new(EqdpEntry.Invalid, EquipSlot.Head, Gender.Male, ModelRace.Midlander, 1);
|
||||
|
||||
private static float IdWidth
|
||||
=> 100 * ImGuiHelpers.GlobalScale;
|
||||
=> 100 * UiHelpers.Scale;
|
||||
|
||||
public static void DrawNew( Mod.Editor editor, Vector2 iconSize )
|
||||
{
|
||||
|
|
@ -321,10 +321,10 @@ public partial class ModEditWindow
|
|||
private static ImcManipulation _new = new(EquipSlot.Head, 1, 1, new ImcEntry());
|
||||
|
||||
private static float IdWidth
|
||||
=> 80 * ImGuiHelpers.GlobalScale;
|
||||
=> 80 * UiHelpers.Scale;
|
||||
|
||||
private static float SmallIdWidth
|
||||
=> 45 * ImGuiHelpers.GlobalScale;
|
||||
=> 45 * UiHelpers.Scale;
|
||||
|
||||
// Convert throwing to null-return if the file does not exist.
|
||||
private static ImcEntry? GetDefault( ImcManipulation imc )
|
||||
|
|
@ -380,7 +380,7 @@ public partial class ModEditWindow
|
|||
ImGuiUtil.HoverTooltip( PrimaryIdTooltip );
|
||||
|
||||
using var style = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing,
|
||||
new Vector2( 3 * ImGuiHelpers.GlobalScale, ImGui.GetStyle().ItemSpacing.Y ) );
|
||||
new Vector2( 3 * UiHelpers.Scale, ImGui.GetStyle().ItemSpacing.Y ) );
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
// Equipment and accessories are slightly different imcs than other types.
|
||||
|
|
@ -406,7 +406,7 @@ public partial class ModEditWindow
|
|||
}
|
||||
else
|
||||
{
|
||||
if( IdInput( "##imcId2", 100 * ImGuiHelpers.GlobalScale, _new.SecondaryId, out var setId2, 0, ushort.MaxValue, false ) )
|
||||
if( IdInput( "##imcId2", 100 * UiHelpers.Scale, _new.SecondaryId, out var setId2, 0, ushort.MaxValue, false ) )
|
||||
{
|
||||
_new = new ImcManipulation( _new.ObjectType, _new.BodySlot, _new.PrimaryId, setId2, _new.Variant, _new.EquipSlot, _new.Entry ).Copy( GetDefault( _new )
|
||||
?? new ImcEntry() );
|
||||
|
|
@ -435,7 +435,7 @@ public partial class ModEditWindow
|
|||
}
|
||||
else
|
||||
{
|
||||
ImGui.Dummy( new Vector2( 70 * ImGuiHelpers.GlobalScale, 0 ) );
|
||||
ImGui.Dummy( new Vector2( 70 * UiHelpers.Scale, 0 ) );
|
||||
}
|
||||
|
||||
ImGuiUtil.HoverTooltip( VariantIdTooltip );
|
||||
|
|
@ -511,7 +511,7 @@ public partial class ModEditWindow
|
|||
|
||||
// Values
|
||||
using var style = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing,
|
||||
new Vector2( 3 * ImGuiHelpers.GlobalScale, ImGui.GetStyle().ItemSpacing.Y ) );
|
||||
new Vector2( 3 * UiHelpers.Scale, ImGui.GetStyle().ItemSpacing.Y ) );
|
||||
ImGui.TableNextColumn();
|
||||
var defaultEntry = GetDefault( meta ) ?? new ImcEntry();
|
||||
if( IntDragInput( "##imcMaterialId", $"Material ID\nDefault Value: {defaultEntry.MaterialId}", SmallIdWidth, meta.Entry.MaterialId,
|
||||
|
|
@ -572,7 +572,7 @@ public partial class ModEditWindow
|
|||
private static EstManipulation _new = new(Gender.Male, ModelRace.Midlander, EstManipulation.EstType.Body, 1, 0);
|
||||
|
||||
private static float IdWidth
|
||||
=> 100 * ImGuiHelpers.GlobalScale;
|
||||
=> 100 * UiHelpers.Scale;
|
||||
|
||||
public static void DrawNew( Mod.Editor editor, Vector2 iconSize )
|
||||
{
|
||||
|
|
@ -669,13 +669,13 @@ public partial class ModEditWindow
|
|||
private static GmpManipulation _new = new(GmpEntry.Default, 1);
|
||||
|
||||
private static float RotationWidth
|
||||
=> 75 * ImGuiHelpers.GlobalScale;
|
||||
=> 75 * UiHelpers.Scale;
|
||||
|
||||
private static float UnkWidth
|
||||
=> 50 * ImGuiHelpers.GlobalScale;
|
||||
=> 50 * UiHelpers.Scale;
|
||||
|
||||
private static float IdWidth
|
||||
=> 100 * ImGuiHelpers.GlobalScale;
|
||||
=> 100 * UiHelpers.Scale;
|
||||
|
||||
public static void DrawNew( Mod.Editor editor, Vector2 iconSize )
|
||||
{
|
||||
|
|
@ -787,7 +787,7 @@ public partial class ModEditWindow
|
|||
private static RspManipulation _new = new(SubRace.Midlander, RspAttribute.MaleMinSize, 1f);
|
||||
|
||||
private static float FloatWidth
|
||||
=> 150 * ImGuiHelpers.GlobalScale;
|
||||
=> 150 * UiHelpers.Scale;
|
||||
|
||||
public static void DrawNew( Mod.Editor editor, Vector2 iconSize )
|
||||
{
|
||||
|
|
@ -847,7 +847,7 @@ public partial class ModEditWindow
|
|||
var value = meta.Entry;
|
||||
ImGui.SetNextItemWidth( FloatWidth );
|
||||
using var color = ImRaii.PushColor( ImGuiCol.FrameBg,
|
||||
def < value ? ColorId.IncreasedMetaValue.Value() : ColorId.DecreasedMetaValue.Value(),
|
||||
def < value ? ColorId.IncreasedMetaValue.Value(Penumbra.Config) : ColorId.DecreasedMetaValue.Value(Penumbra.Config),
|
||||
def != value );
|
||||
if( ImGui.DragFloat( "##rspValue", ref value, 0.001f, 0.01f, 8f ) && value is >= 0.01f and <= 8f )
|
||||
{
|
||||
|
|
@ -864,7 +864,7 @@ public partial class ModEditWindow
|
|||
{
|
||||
int tmp = currentId;
|
||||
ImGui.SetNextItemWidth( width );
|
||||
using var style = ImRaii.PushStyle( ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale, border );
|
||||
using var style = ImRaii.PushStyle( ImGuiStyleVar.FrameBorderSize, UiHelpers.Scale, border );
|
||||
using var color = ImRaii.PushColor( ImGuiCol.Border, Colors.RegexWarningBorder, border );
|
||||
if( ImGui.InputInt( label, ref tmp, 0 ) )
|
||||
{
|
||||
|
|
@ -880,7 +880,7 @@ public partial class ModEditWindow
|
|||
private static bool Checkmark( string label, string tooltip, bool currentValue, bool defaultValue, out bool newValue )
|
||||
{
|
||||
using var color = ImRaii.PushColor( ImGuiCol.FrameBg,
|
||||
defaultValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value(), defaultValue != currentValue );
|
||||
defaultValue ? ColorId.DecreasedMetaValue.Value(Penumbra.Config) : ColorId.IncreasedMetaValue.Value(Penumbra.Config), defaultValue != currentValue );
|
||||
newValue = currentValue;
|
||||
ImGui.Checkbox( label, ref newValue );
|
||||
ImGuiUtil.HoverTooltip( tooltip, ImGuiHoveredFlags.AllowWhenDisabled );
|
||||
|
|
@ -894,7 +894,7 @@ public partial class ModEditWindow
|
|||
{
|
||||
newValue = currentValue;
|
||||
using var color = ImRaii.PushColor( ImGuiCol.FrameBg,
|
||||
defaultValue > currentValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value(),
|
||||
defaultValue > currentValue ? ColorId.DecreasedMetaValue.Value(Penumbra.Config) : ColorId.IncreasedMetaValue.Value(Penumbra.Config),
|
||||
defaultValue != currentValue );
|
||||
ImGui.SetNextItemWidth( width );
|
||||
if( ImGui.DragInt( label, ref newValue, speed, minValue, maxValue ) )
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ public partial class ModEditWindow
|
|||
};
|
||||
|
||||
var blob = shader.Blob;
|
||||
tab.FileDialog.SaveFileDialog( $"Export {objectName} #{idx} Program Blob to...", tab.Extension, defaultName, tab.Extension, ( success, name ) =>
|
||||
tab.FileDialog.OpenSavePicker( $"Export {objectName} #{idx} Program Blob to...", tab.Extension, defaultName, tab.Extension, ( success, name ) =>
|
||||
{
|
||||
if( !success )
|
||||
{
|
||||
|
|
@ -87,7 +87,7 @@ public partial class ModEditWindow
|
|||
|
||||
Penumbra.ChatService.NotificationMessage( $"Shader Program Blob {defaultName}{tab.Extension} exported successfully to {Path.GetFileName( name )}",
|
||||
"Penumbra Advanced Editing", NotificationType.Success );
|
||||
} );
|
||||
}, null, false );
|
||||
}
|
||||
|
||||
private static void DrawShaderImportButton( ShpkTab tab, string objectName, Shader[] shaders, int idx )
|
||||
|
|
@ -97,7 +97,7 @@ public partial class ModEditWindow
|
|||
return;
|
||||
}
|
||||
|
||||
tab.FileDialog.OpenFileDialog( $"Replace {objectName} #{idx} Program Blob...", "Shader Program Blobs{.o,.cso,.dxbc,.dxil}", ( success, name ) =>
|
||||
tab.FileDialog.OpenFilePicker( $"Replace {objectName} #{idx} Program Blob...", "Shader Program Blobs{.o,.cso,.dxbc,.dxil}", ( success, name ) =>
|
||||
{
|
||||
if( !success )
|
||||
{
|
||||
|
|
@ -106,7 +106,7 @@ public partial class ModEditWindow
|
|||
|
||||
try
|
||||
{
|
||||
shaders[ idx ].Blob = File.ReadAllBytes( name );
|
||||
shaders[ idx ].Blob = File.ReadAllBytes(name[0] );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
|
|
@ -128,7 +128,7 @@ public partial class ModEditWindow
|
|||
}
|
||||
|
||||
tab.Shpk.SetChanged();
|
||||
} );
|
||||
}, 1, null, false );
|
||||
}
|
||||
|
||||
private static unsafe void DrawRawDisassembly( Shader shader )
|
||||
|
|
@ -193,7 +193,7 @@ public partial class ModEditWindow
|
|||
var ret = false;
|
||||
if( !disabled )
|
||||
{
|
||||
ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 150.0f );
|
||||
ImGui.SetNextItemWidth( UiHelpers.Scale * 150.0f );
|
||||
if( ImGuiUtil.InputUInt16( $"{char.ToUpper( slotLabel[ 0 ] )}{slotLabel[ 1.. ].ToLower()}", ref resource.Slot, ImGuiInputTextFlags.None ) )
|
||||
{
|
||||
ret = true;
|
||||
|
|
@ -285,11 +285,11 @@ public partial class ModEditWindow
|
|||
return false;
|
||||
}
|
||||
|
||||
ImGui.TableSetupColumn( string.Empty, ImGuiTableColumnFlags.WidthFixed, 25 * ImGuiHelpers.GlobalScale );
|
||||
ImGui.TableSetupColumn( "x", ImGuiTableColumnFlags.WidthFixed, 100 * ImGuiHelpers.GlobalScale );
|
||||
ImGui.TableSetupColumn( "y", ImGuiTableColumnFlags.WidthFixed, 100 * ImGuiHelpers.GlobalScale );
|
||||
ImGui.TableSetupColumn( "z", ImGuiTableColumnFlags.WidthFixed, 100 * ImGuiHelpers.GlobalScale );
|
||||
ImGui.TableSetupColumn( "w", ImGuiTableColumnFlags.WidthFixed, 100 * ImGuiHelpers.GlobalScale );
|
||||
ImGui.TableSetupColumn( string.Empty, ImGuiTableColumnFlags.WidthFixed, 25 * UiHelpers.Scale );
|
||||
ImGui.TableSetupColumn( "x", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale );
|
||||
ImGui.TableSetupColumn( "y", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale );
|
||||
ImGui.TableSetupColumn( "z", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale );
|
||||
ImGui.TableSetupColumn( "w", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale );
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
var textColorStart = ImGui.GetColorU32( ImGuiCol.Text );
|
||||
|
|
@ -362,7 +362,7 @@ public partial class ModEditWindow
|
|||
using var s = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemInnerSpacing );
|
||||
using( var _ = ImRaii.PushFont( UiBuilder.MonoFont ) )
|
||||
{
|
||||
ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 400 );
|
||||
ImGui.SetNextItemWidth( UiHelpers.Scale * 400 );
|
||||
using var c = ImRaii.Combo( "##Start", tab.Orphans[ tab.NewMaterialParamStart ].Name );
|
||||
if( c )
|
||||
{
|
||||
|
|
@ -385,7 +385,7 @@ public partial class ModEditWindow
|
|||
using var s = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemInnerSpacing );
|
||||
using( var _ = ImRaii.PushFont( UiBuilder.MonoFont ) )
|
||||
{
|
||||
ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 400 );
|
||||
ImGui.SetNextItemWidth( UiHelpers.Scale * 400 );
|
||||
using var c = ImRaii.Combo( "##End", tab.Orphans[ tab.NewMaterialParamEnd ].Name );
|
||||
if( c )
|
||||
{
|
||||
|
|
@ -420,7 +420,7 @@ public partial class ModEditWindow
|
|||
DrawShaderPackageStartCombo( tab );
|
||||
DrawShaderPackageEndCombo( tab );
|
||||
|
||||
ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 400 );
|
||||
ImGui.SetNextItemWidth( UiHelpers.Scale * 400 );
|
||||
if( ImGui.InputText( "Name", ref tab.NewMaterialParamName, 63 ) )
|
||||
{
|
||||
tab.NewMaterialParamId = Crc32.Get( tab.NewMaterialParamName, 0xFFFFFFFFu );
|
||||
|
|
@ -429,7 +429,7 @@ public partial class ModEditWindow
|
|||
var tooltip = tab.UsedIds.Contains( tab.NewMaterialParamId )
|
||||
? "The ID is already in use. Please choose a different name."
|
||||
: string.Empty;
|
||||
if( !ImGuiUtil.DrawDisabledButton( $"Add ID 0x{tab.NewMaterialParamId:X8}", new Vector2( 400 * ImGuiHelpers.GlobalScale, ImGui.GetFrameHeight() ), tooltip,
|
||||
if( !ImGuiUtil.DrawDisabledButton( $"Add ID 0x{tab.NewMaterialParamId:X8}", new Vector2( 400 * UiHelpers.Scale, ImGui.GetFrameHeight() ), tooltip,
|
||||
tooltip.Length > 0 ) )
|
||||
{
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Dalamud.Interface.ImGuiFileDialog;
|
||||
using Dalamud.Utility;
|
||||
using Lumina.Misc;
|
||||
using OtterGui;
|
||||
|
|
@ -16,19 +15,20 @@ public partial class ModEditWindow
|
|||
public readonly ShpkFile Shpk;
|
||||
|
||||
public string NewMaterialParamName = string.Empty;
|
||||
public uint NewMaterialParamId = Crc32.Get( string.Empty, 0xFFFFFFFFu );
|
||||
public uint NewMaterialParamId = Crc32.Get(string.Empty, 0xFFFFFFFFu);
|
||||
public short NewMaterialParamStart;
|
||||
public short NewMaterialParamEnd;
|
||||
|
||||
public readonly FileDialogManager FileDialog = ConfigWindow.SetupFileManager();
|
||||
public readonly FileDialogService FileDialog;
|
||||
|
||||
public readonly string Header;
|
||||
public readonly string Extension;
|
||||
|
||||
public ShpkTab( byte[] bytes )
|
||||
public ShpkTab(FileDialogService fileDialog, byte[] bytes)
|
||||
{
|
||||
Shpk = new ShpkFile( bytes, true );
|
||||
Header = $"Shader Package for DirectX {( int )Shpk.DirectXVersion}";
|
||||
FileDialog = fileDialog;
|
||||
Shpk = new ShpkFile(bytes, true);
|
||||
Header = $"Shader Package for DirectX {(int)Shpk.DirectXVersion}";
|
||||
Extension = Shpk.DirectXVersion switch
|
||||
{
|
||||
ShpkFile.DxVersion.DirectX9 => ".cso",
|
||||
|
|
@ -47,134 +47,130 @@ public partial class ModEditWindow
|
|||
}
|
||||
|
||||
public (string Name, string Tooltip, short Index, ColorType Color)[,] Matrix = null!;
|
||||
public readonly List< string > MalformedParameters = new();
|
||||
public readonly HashSet< uint > UsedIds = new(16);
|
||||
public readonly List< (string Name, short Index) > Orphans = new(16);
|
||||
public readonly List<string> MalformedParameters = new();
|
||||
public readonly HashSet<uint> UsedIds = new(16);
|
||||
public readonly List<(string Name, short Index)> Orphans = new(16);
|
||||
|
||||
public void Update()
|
||||
{
|
||||
var materialParams = Shpk.GetConstantById( ShpkFile.MaterialParamsConstantId );
|
||||
var numParameters = ( ( Shpk.MaterialParamsSize + 0xFu ) & ~0xFu ) >> 4;
|
||||
var materialParams = Shpk.GetConstantById(ShpkFile.MaterialParamsConstantId);
|
||||
var numParameters = ((Shpk.MaterialParamsSize + 0xFu) & ~0xFu) >> 4;
|
||||
Matrix = new (string Name, string Tooltip, short Index, ColorType Color)[numParameters, 4];
|
||||
|
||||
MalformedParameters.Clear();
|
||||
UsedIds.Clear();
|
||||
foreach( var (param, idx) in Shpk.MaterialParams.WithIndex() )
|
||||
foreach (var (param, idx) in Shpk.MaterialParams.WithIndex())
|
||||
{
|
||||
UsedIds.Add( param.Id );
|
||||
UsedIds.Add(param.Id);
|
||||
var iStart = param.ByteOffset >> 4;
|
||||
var jStart = ( param.ByteOffset >> 2 ) & 3;
|
||||
var iEnd = ( param.ByteOffset + param.ByteSize - 1 ) >> 4;
|
||||
var jEnd = ( ( param.ByteOffset + param.ByteSize - 1 ) >> 2 ) & 3;
|
||||
if( ( param.ByteOffset & 0x3 ) != 0 || ( param.ByteSize & 0x3 ) != 0 )
|
||||
var jStart = (param.ByteOffset >> 2) & 3;
|
||||
var iEnd = (param.ByteOffset + param.ByteSize - 1) >> 4;
|
||||
var jEnd = ((param.ByteOffset + param.ByteSize - 1) >> 2) & 3;
|
||||
if ((param.ByteOffset & 0x3) != 0 || (param.ByteSize & 0x3) != 0)
|
||||
{
|
||||
MalformedParameters.Add( $"ID: 0x{param.Id:X8}, offset: 0x{param.ByteOffset:X4}, size: 0x{param.ByteSize:X4}" );
|
||||
MalformedParameters.Add($"ID: 0x{param.Id:X8}, offset: 0x{param.ByteOffset:X4}, size: 0x{param.ByteSize:X4}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if( iEnd >= numParameters )
|
||||
if (iEnd >= numParameters)
|
||||
{
|
||||
MalformedParameters.Add(
|
||||
$"{MaterialParamRangeName( materialParams?.Name ?? string.Empty, param.ByteOffset >> 2, param.ByteSize >> 2 )} (ID: 0x{param.Id:X8})" );
|
||||
$"{MaterialParamRangeName(materialParams?.Name ?? string.Empty, param.ByteOffset >> 2, param.ByteSize >> 2)} (ID: 0x{param.Id:X8})");
|
||||
continue;
|
||||
}
|
||||
|
||||
for( var i = iStart; i <= iEnd; ++i )
|
||||
for (var i = iStart; i <= iEnd; ++i)
|
||||
{
|
||||
var end = i == iEnd ? jEnd : 3;
|
||||
for( var j = i == iStart ? jStart : 0; j <= end; ++j )
|
||||
for (var j = i == iStart ? jStart : 0; j <= end; ++j)
|
||||
{
|
||||
var tt = $"{MaterialParamRangeName( materialParams?.Name ?? string.Empty, param.ByteOffset >> 2, param.ByteSize >> 2 ).Item1} (ID: 0x{param.Id:X8})";
|
||||
Matrix[ i, j ] = ( $"0x{param.Id:X8}", tt, ( short )idx, 0 );
|
||||
var tt =
|
||||
$"{MaterialParamRangeName(materialParams?.Name ?? string.Empty, param.ByteOffset >> 2, param.ByteSize >> 2).Item1} (ID: 0x{param.Id:X8})";
|
||||
Matrix[i, j] = ($"0x{param.Id:X8}", tt, (short)idx, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UpdateOrphans( materialParams );
|
||||
UpdateColors( materialParams );
|
||||
UpdateOrphans(materialParams);
|
||||
UpdateColors(materialParams);
|
||||
}
|
||||
|
||||
public void UpdateOrphanStart( int orphanStart )
|
||||
public void UpdateOrphanStart(int orphanStart)
|
||||
{
|
||||
var oldEnd = Orphans.Count > 0 ? Orphans[ NewMaterialParamEnd ].Index : -1;
|
||||
UpdateOrphanStart( orphanStart, oldEnd );
|
||||
var oldEnd = Orphans.Count > 0 ? Orphans[NewMaterialParamEnd].Index : -1;
|
||||
UpdateOrphanStart(orphanStart, oldEnd);
|
||||
}
|
||||
|
||||
private void UpdateOrphanStart( int orphanStart, int oldEnd )
|
||||
private void UpdateOrphanStart(int orphanStart, int oldEnd)
|
||||
{
|
||||
var count = Math.Min( NewMaterialParamEnd - NewMaterialParamStart + orphanStart + 1, Orphans.Count );
|
||||
NewMaterialParamStart = ( short )orphanStart;
|
||||
var current = Orphans[ NewMaterialParamStart ].Index;
|
||||
for( var i = NewMaterialParamStart; i < count; ++i )
|
||||
var count = Math.Min(NewMaterialParamEnd - NewMaterialParamStart + orphanStart + 1, Orphans.Count);
|
||||
NewMaterialParamStart = (short)orphanStart;
|
||||
var current = Orphans[NewMaterialParamStart].Index;
|
||||
for (var i = NewMaterialParamStart; i < count; ++i)
|
||||
{
|
||||
var next = Orphans[ i ].Index;
|
||||
if( current++ != next )
|
||||
var next = Orphans[i].Index;
|
||||
if (current++ != next)
|
||||
{
|
||||
NewMaterialParamEnd = ( short )( i - 1 );
|
||||
NewMaterialParamEnd = (short)(i - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if( next == oldEnd )
|
||||
if (next == oldEnd)
|
||||
{
|
||||
NewMaterialParamEnd = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NewMaterialParamEnd = ( short )( count - 1 );
|
||||
NewMaterialParamEnd = (short)(count - 1);
|
||||
}
|
||||
|
||||
private void UpdateOrphans( ShpkFile.Resource? materialParams )
|
||||
private void UpdateOrphans(ShpkFile.Resource? materialParams)
|
||||
{
|
||||
var oldStart = Orphans.Count > 0 ? Orphans[ NewMaterialParamStart ].Index : -1;
|
||||
var oldEnd = Orphans.Count > 0 ? Orphans[ NewMaterialParamEnd ].Index : -1;
|
||||
var oldStart = Orphans.Count > 0 ? Orphans[NewMaterialParamStart].Index : -1;
|
||||
var oldEnd = Orphans.Count > 0 ? Orphans[NewMaterialParamEnd].Index : -1;
|
||||
|
||||
Orphans.Clear();
|
||||
short newMaterialParamStart = 0;
|
||||
for( var i = 0; i < Matrix.GetLength( 0 ); ++i )
|
||||
for( var j = 0; j < 4; ++j )
|
||||
for (var i = 0; i < Matrix.GetLength(0); ++i)
|
||||
{
|
||||
if( !Matrix[ i, j ].Name.IsNullOrEmpty() )
|
||||
for (var j = 0; j < 4; ++j)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!Matrix[i, j].Name.IsNullOrEmpty())
|
||||
continue;
|
||||
|
||||
Matrix[ i, j ] = ( "(none)", string.Empty, -1, 0 );
|
||||
var linear = ( short )( 4 * i + j );
|
||||
if( oldStart == linear )
|
||||
{
|
||||
newMaterialParamStart = ( short )Orphans.Count;
|
||||
}
|
||||
Matrix[i, j] = ("(none)", string.Empty, -1, 0);
|
||||
var linear = (short)(4 * i + j);
|
||||
if (oldStart == linear)
|
||||
newMaterialParamStart = (short)Orphans.Count;
|
||||
|
||||
Orphans.Add( ( $"{materialParams?.Name ?? string.Empty}{MaterialParamName( false, linear )}", linear ) );
|
||||
Orphans.Add(($"{materialParams?.Name ?? string.Empty}{MaterialParamName(false, linear)}", linear));
|
||||
}
|
||||
}
|
||||
|
||||
if( Orphans.Count == 0 )
|
||||
{
|
||||
if (Orphans.Count == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateOrphanStart( newMaterialParamStart, oldEnd );
|
||||
UpdateOrphanStart(newMaterialParamStart, oldEnd);
|
||||
}
|
||||
|
||||
private void UpdateColors( ShpkFile.Resource? materialParams )
|
||||
private void UpdateColors(ShpkFile.Resource? materialParams)
|
||||
{
|
||||
var lastIndex = -1;
|
||||
for( var i = 0; i < Matrix.GetLength( 0 ); ++i )
|
||||
for (var i = 0; i < Matrix.GetLength(0); ++i)
|
||||
{
|
||||
var usedComponents = ( materialParams?.Used?[ i ] ?? DisassembledShader.VectorComponents.All ) | ( materialParams?.UsedDynamically ?? 0 );
|
||||
for( var j = 0; j < 4; ++j )
|
||||
var usedComponents = (materialParams?.Used?[i] ?? DisassembledShader.VectorComponents.All)
|
||||
| (materialParams?.UsedDynamically ?? 0);
|
||||
for (var j = 0; j < 4; ++j)
|
||||
{
|
||||
var color = ( ( byte )usedComponents & ( 1 << j ) ) != 0
|
||||
var color = ((byte)usedComponents & (1 << j)) != 0
|
||||
? ColorType.Used
|
||||
: 0;
|
||||
if( Matrix[ i, j ].Index == lastIndex || Matrix[ i, j ].Index < 0 )
|
||||
{
|
||||
if (Matrix[i, j].Index == lastIndex || Matrix[i, j].Index < 0)
|
||||
color |= ColorType.Continuation;
|
||||
}
|
||||
|
||||
lastIndex = Matrix[ i, j ].Index;
|
||||
Matrix[ i, j ].Color = color;
|
||||
lastIndex = Matrix[i, j].Index;
|
||||
Matrix[i, j].Color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -185,4 +181,4 @@ public partial class ModEditWindow
|
|||
public byte[] Write()
|
||||
=> Shpk.Write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,9 @@ using System;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.ImGuiFileDialog;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterTex;
|
||||
using Penumbra.Import.Textures;
|
||||
|
||||
namespace Penumbra.UI.Classes;
|
||||
|
|
@ -18,210 +15,181 @@ public partial class ModEditWindow
|
|||
private readonly Texture _right = new();
|
||||
private readonly CombinedTexture _center;
|
||||
|
||||
private readonly FileDialogManager _dialogManager = ConfigWindow.SetupFileManager();
|
||||
private bool _overlayCollapsed = true;
|
||||
private bool _overlayCollapsed = true;
|
||||
|
||||
private bool _addMipMaps = true;
|
||||
private int _currentSaveAs = 0;
|
||||
private bool _addMipMaps = true;
|
||||
private int _currentSaveAs;
|
||||
|
||||
private static readonly (string, string)[] SaveAsStrings =
|
||||
{
|
||||
( "As Is", "Save the current texture with its own format without additional conversion or compression, if possible." ),
|
||||
( "RGBA (Uncompressed)",
|
||||
"Save the current texture as an uncompressed BGRA bitmap. This requires the most space but technically offers the best quality." ),
|
||||
( "BC3 (Simple Compression)",
|
||||
"Save the current texture compressed via BC3/DXT5 compression. This offers a 4:1 compression ratio and is quick with acceptable quality." ),
|
||||
( "BC7 (Complex Compression)",
|
||||
"Save the current texture compressed via BC7 compression. This offers a 4:1 compression ratio and has almost indistinguishable quality, but may take a while." ),
|
||||
("As Is", "Save the current texture with its own format without additional conversion or compression, if possible."),
|
||||
("RGBA (Uncompressed)",
|
||||
"Save the current texture as an uncompressed BGRA bitmap. This requires the most space but technically offers the best quality."),
|
||||
("BC3 (Simple Compression)",
|
||||
"Save the current texture compressed via BC3/DXT5 compression. This offers a 4:1 compression ratio and is quick with acceptable quality."),
|
||||
("BC7 (Complex Compression)",
|
||||
"Save the current texture compressed via BC7 compression. This offers a 4:1 compression ratio and has almost indistinguishable quality, but may take a while."),
|
||||
};
|
||||
|
||||
private void DrawInputChild( string label, Texture tex, Vector2 size, Vector2 imageSize )
|
||||
private void DrawInputChild(string label, Texture tex, Vector2 size, Vector2 imageSize)
|
||||
{
|
||||
using var child = ImRaii.Child( label, size, true );
|
||||
if( !child )
|
||||
{
|
||||
using var child = ImRaii.Child(label, size, true);
|
||||
if (!child)
|
||||
return;
|
||||
}
|
||||
|
||||
using var id = ImRaii.PushId( label );
|
||||
ImGuiUtil.DrawTextButton( label, new Vector2( -1, 0 ), ImGui.GetColorU32( ImGuiCol.FrameBg ) );
|
||||
using var id = ImRaii.PushId(label);
|
||||
ImGuiUtil.DrawTextButton(label, new Vector2(-1, 0), ImGui.GetColorU32(ImGuiCol.FrameBg));
|
||||
ImGui.NewLine();
|
||||
|
||||
tex.PathInputBox( "##input", "Import Image...", "Can import game paths as well as your own files.", _mod!.ModPath.FullName,
|
||||
_dialogManager );
|
||||
var files = _editor!.TexFiles.SelectMany( f => f.SubModUsage.Select( p => (p.Item2.ToString(), true) )
|
||||
.Prepend( (f.File.FullName, false )));
|
||||
tex.PathSelectBox( "##combo", "Select the textures included in this mod on your drive or the ones they replace from the game files.",
|
||||
files, _mod.ModPath.FullName.Length + 1 );
|
||||
tex.PathInputBox("##input", "Import Image...", "Can import game paths as well as your own files.", _mod!.ModPath.FullName,
|
||||
_fileDialog);
|
||||
var files = _editor!.TexFiles.SelectMany(f => f.SubModUsage.Select(p => (p.Item2.ToString(), true))
|
||||
.Prepend((f.File.FullName, false)));
|
||||
tex.PathSelectBox("##combo", "Select the textures included in this mod on your drive or the ones they replace from the game files.",
|
||||
files, _mod.ModPath.FullName.Length + 1);
|
||||
|
||||
if( tex == _left )
|
||||
{
|
||||
_center.DrawMatrixInputLeft( size.X );
|
||||
}
|
||||
if (tex == _left)
|
||||
_center.DrawMatrixInputLeft(size.X);
|
||||
else
|
||||
{
|
||||
_center.DrawMatrixInputRight( size.X );
|
||||
}
|
||||
_center.DrawMatrixInputRight(size.X);
|
||||
|
||||
ImGui.NewLine();
|
||||
using var child2 = ImRaii.Child( "image" );
|
||||
if( child2 )
|
||||
{
|
||||
tex.Draw( imageSize );
|
||||
}
|
||||
using var child2 = ImRaii.Child("image");
|
||||
if (child2)
|
||||
tex.Draw(imageSize);
|
||||
}
|
||||
|
||||
private void SaveAsCombo()
|
||||
{
|
||||
var (text, desc) = SaveAsStrings[ _currentSaveAs ];
|
||||
ImGui.SetNextItemWidth( -ImGui.GetFrameHeight() - ImGui.GetStyle().ItemSpacing.X );
|
||||
using var combo = ImRaii.Combo( "##format", text );
|
||||
ImGuiUtil.HoverTooltip( desc );
|
||||
if( !combo )
|
||||
{
|
||||
var (text, desc) = SaveAsStrings[_currentSaveAs];
|
||||
ImGui.SetNextItemWidth(-ImGui.GetFrameHeight() - ImGui.GetStyle().ItemSpacing.X);
|
||||
using var combo = ImRaii.Combo("##format", text);
|
||||
ImGuiUtil.HoverTooltip(desc);
|
||||
if (!combo)
|
||||
return;
|
||||
}
|
||||
|
||||
foreach( var ((newText, newDesc), idx) in SaveAsStrings.WithIndex() )
|
||||
foreach (var ((newText, newDesc), idx) in SaveAsStrings.WithIndex())
|
||||
{
|
||||
if( ImGui.Selectable( newText, idx == _currentSaveAs ) )
|
||||
{
|
||||
if (ImGui.Selectable(newText, idx == _currentSaveAs))
|
||||
_currentSaveAs = idx;
|
||||
}
|
||||
|
||||
ImGuiUtil.HoverTooltip( newDesc );
|
||||
ImGuiUtil.HoverTooltip(newDesc);
|
||||
}
|
||||
}
|
||||
|
||||
private void MipMapInput()
|
||||
{
|
||||
ImGui.Checkbox( "##mipMaps", ref _addMipMaps );
|
||||
ImGui.Checkbox("##mipMaps", ref _addMipMaps);
|
||||
ImGuiUtil.HoverTooltip(
|
||||
"Add the appropriate number of MipMaps to the file." );
|
||||
"Add the appropriate number of MipMaps to the file.");
|
||||
}
|
||||
|
||||
private void DrawOutputChild( Vector2 size, Vector2 imageSize )
|
||||
private void DrawOutputChild(Vector2 size, Vector2 imageSize)
|
||||
{
|
||||
using var child = ImRaii.Child( "Output", size, true );
|
||||
if( !child )
|
||||
{
|
||||
using var child = ImRaii.Child("Output", size, true);
|
||||
if (!child)
|
||||
return;
|
||||
}
|
||||
|
||||
if( _center.IsLoaded )
|
||||
if (_center.IsLoaded)
|
||||
{
|
||||
SaveAsCombo();
|
||||
ImGui.SameLine();
|
||||
MipMapInput();
|
||||
if( ImGui.Button( "Save as TEX", -Vector2.UnitX ) )
|
||||
if (ImGui.Button("Save as TEX", -Vector2.UnitX))
|
||||
{
|
||||
var fileName = Path.GetFileNameWithoutExtension( _left.Path.Length > 0 ? _left.Path : _right.Path );
|
||||
_dialogManager.SaveFileDialog( "Save Texture as TEX...", ".tex", fileName, ".tex", ( a, b ) =>
|
||||
var fileName = Path.GetFileNameWithoutExtension(_left.Path.Length > 0 ? _left.Path : _right.Path);
|
||||
_fileDialog.OpenSavePicker("Save Texture as TEX...", ".tex", fileName, ".tex", (a, b) =>
|
||||
{
|
||||
if( a )
|
||||
{
|
||||
_center.SaveAsTex( b, ( CombinedTexture.TextureSaveType )_currentSaveAs, _addMipMaps );
|
||||
}
|
||||
}, _mod!.ModPath.FullName );
|
||||
if (a)
|
||||
_center.SaveAsTex(b, (CombinedTexture.TextureSaveType)_currentSaveAs, _addMipMaps);
|
||||
}, _mod!.ModPath.FullName, false);
|
||||
}
|
||||
|
||||
if( ImGui.Button( "Save as DDS", -Vector2.UnitX ) )
|
||||
if (ImGui.Button("Save as DDS", -Vector2.UnitX))
|
||||
{
|
||||
var fileName = Path.GetFileNameWithoutExtension( _right.Path.Length > 0 ? _right.Path : _left.Path );
|
||||
_dialogManager.SaveFileDialog( "Save Texture as DDS...", ".dds", fileName, ".dds", ( a, b ) =>
|
||||
var fileName = Path.GetFileNameWithoutExtension(_right.Path.Length > 0 ? _right.Path : _left.Path);
|
||||
_fileDialog.OpenSavePicker("Save Texture as DDS...", ".dds", fileName, ".dds", (a, b) =>
|
||||
{
|
||||
if( a )
|
||||
{
|
||||
_center.SaveAsDds( b, ( CombinedTexture.TextureSaveType )_currentSaveAs, _addMipMaps );
|
||||
}
|
||||
}, _mod!.ModPath.FullName );
|
||||
if (a)
|
||||
_center.SaveAsDds(b, (CombinedTexture.TextureSaveType)_currentSaveAs, _addMipMaps);
|
||||
}, _mod!.ModPath.FullName, false);
|
||||
}
|
||||
|
||||
ImGui.NewLine();
|
||||
|
||||
if( ImGui.Button( "Save as PNG", -Vector2.UnitX ) )
|
||||
if (ImGui.Button("Save as PNG", -Vector2.UnitX))
|
||||
{
|
||||
var fileName = Path.GetFileNameWithoutExtension( _right.Path.Length > 0 ? _right.Path : _left.Path );
|
||||
_dialogManager.SaveFileDialog( "Save Texture as PNG...", ".png", fileName, ".png", ( a, b ) =>
|
||||
var fileName = Path.GetFileNameWithoutExtension(_right.Path.Length > 0 ? _right.Path : _left.Path);
|
||||
_fileDialog.OpenSavePicker("Save Texture as PNG...", ".png", fileName, ".png", (a, b) =>
|
||||
{
|
||||
if( a )
|
||||
{
|
||||
_center.SaveAsPng( b );
|
||||
}
|
||||
}, _mod!.ModPath.FullName );
|
||||
if (a)
|
||||
_center.SaveAsPng(b);
|
||||
}, _mod!.ModPath.FullName, false);
|
||||
}
|
||||
|
||||
ImGui.NewLine();
|
||||
}
|
||||
|
||||
if( _center.SaveException != null )
|
||||
if (_center.SaveException != null)
|
||||
{
|
||||
ImGui.TextUnformatted( "Could not save file:" );
|
||||
using var color = ImRaii.PushColor( ImGuiCol.Text, 0xFF0000FF );
|
||||
ImGuiUtil.TextWrapped( _center.SaveException.ToString() );
|
||||
ImGui.TextUnformatted("Could not save file:");
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, 0xFF0000FF);
|
||||
ImGuiUtil.TextWrapped(_center.SaveException.ToString());
|
||||
}
|
||||
|
||||
using var child2 = ImRaii.Child( "image" );
|
||||
if( child2 )
|
||||
{
|
||||
_center.Draw( imageSize );
|
||||
}
|
||||
using var child2 = ImRaii.Child("image");
|
||||
if (child2)
|
||||
_center.Draw(imageSize);
|
||||
}
|
||||
|
||||
private Vector2 GetChildWidth()
|
||||
{
|
||||
var windowWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X - ImGui.GetTextLineHeight();
|
||||
if( _overlayCollapsed )
|
||||
if (_overlayCollapsed)
|
||||
{
|
||||
var width = windowWidth - ImGui.GetStyle().FramePadding.X * 3;
|
||||
return new Vector2( width / 2, -1 );
|
||||
return new Vector2(width / 2, -1);
|
||||
}
|
||||
|
||||
return new Vector2( ( windowWidth - ImGui.GetStyle().FramePadding.X * 5 ) / 3, -1 );
|
||||
return new Vector2((windowWidth - ImGui.GetStyle().FramePadding.X * 5) / 3, -1);
|
||||
}
|
||||
|
||||
private void DrawTextureTab()
|
||||
{
|
||||
_dialogManager.Draw();
|
||||
|
||||
using var tab = ImRaii.TabItem( "Texture Import/Export" );
|
||||
if( !tab )
|
||||
{
|
||||
using var tab = ImRaii.TabItem("Texture Import/Export");
|
||||
if (!tab)
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var childWidth = GetChildWidth();
|
||||
var imageSize = new Vector2( childWidth.X - ImGui.GetStyle().FramePadding.X * 2 );
|
||||
DrawInputChild( "Input Texture", _left, childWidth, imageSize );
|
||||
var imageSize = new Vector2(childWidth.X - ImGui.GetStyle().FramePadding.X * 2);
|
||||
DrawInputChild("Input Texture", _left, childWidth, imageSize);
|
||||
ImGui.SameLine();
|
||||
DrawOutputChild( childWidth, imageSize );
|
||||
if( !_overlayCollapsed )
|
||||
DrawOutputChild(childWidth, imageSize);
|
||||
if (!_overlayCollapsed)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
DrawInputChild( "Overlay Texture", _right, childWidth, imageSize );
|
||||
DrawInputChild("Overlay Texture", _right, childWidth, imageSize);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
DrawOverlayCollapseButton();
|
||||
}
|
||||
catch( Exception e )
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error( $"Unknown Error while drawing textures:\n{e}" );
|
||||
Penumbra.Log.Error($"Unknown Error while drawing textures:\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
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( ImGui.GetTextLineHeight(), ImGui.GetContentRegionAvail().Y ) ) )
|
||||
{
|
||||
? (">", "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(ImGui.GetTextLineHeight(), ImGui.GetContentRegionAvail().Y)))
|
||||
_overlayCollapsed = !_overlayCollapsed;
|
||||
}
|
||||
|
||||
ImGuiUtil.HoverTooltip( tooltip );
|
||||
ImGuiUtil.HoverTooltip(tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -477,21 +477,22 @@ public partial class ModEditWindow : Window, IDisposable
|
|||
return new FullPath(path);
|
||||
}
|
||||
|
||||
public ModEditWindow(CommunicatorService communicator)
|
||||
public ModEditWindow(CommunicatorService communicator, FileDialogService fileDialog)
|
||||
: base(WindowBaseLabel)
|
||||
{
|
||||
_fileDialog = fileDialog;
|
||||
_swapWindow = new ItemSwapWindow(communicator);
|
||||
_materialTab = new FileEditor<MtrlTab>("Materials", ".mtrl",
|
||||
_materialTab = new FileEditor<MtrlTab>("Materials", ".mtrl", _fileDialog,
|
||||
() => _editor?.MtrlFiles ?? Array.Empty<Editor.FileRegistry>(),
|
||||
DrawMaterialPanel,
|
||||
() => _mod?.ModPath.FullName ?? string.Empty,
|
||||
bytes => new MtrlTab(this, new MtrlFile(bytes)));
|
||||
_modelTab = new FileEditor<MdlFile>("Models", ".mdl",
|
||||
_modelTab = new FileEditor<MdlFile>("Models", ".mdl", _fileDialog,
|
||||
() => _editor?.MdlFiles ?? Array.Empty<Editor.FileRegistry>(),
|
||||
DrawModelPanel,
|
||||
() => _mod?.ModPath.FullName ?? string.Empty,
|
||||
null);
|
||||
_shaderPackageTab = new FileEditor<ShpkTab>("Shader Packages", ".shpk",
|
||||
_shaderPackageTab = new FileEditor<ShpkTab>("Shader Packages", ".shpk", _fileDialog,
|
||||
() => _editor?.ShpkFiles ?? Array.Empty<Editor.FileRegistry>(),
|
||||
DrawShaderPackagePanel,
|
||||
() => _mod?.ModPath.FullName ?? string.Empty,
|
||||
|
|
|
|||
|
|
@ -1,313 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using Dalamud.Interface;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Filesystem;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Mods;
|
||||
|
||||
namespace Penumbra.UI.Classes;
|
||||
|
||||
public partial class ModFileSystemSelector
|
||||
{
|
||||
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
|
||||
public struct ModState
|
||||
{
|
||||
public ColorId Color;
|
||||
}
|
||||
|
||||
private const StringComparison IgnoreCase = StringComparison.OrdinalIgnoreCase;
|
||||
private LowerString _modFilter = LowerString.Empty;
|
||||
private int _filterType = -1;
|
||||
private ModFilter _stateFilter = ModFilterExtensions.UnfilteredStateMods;
|
||||
|
||||
private void SetFilterTooltip()
|
||||
{
|
||||
FilterTooltip = "Filter mods for those where their full paths or names contain the given substring.\n"
|
||||
+ "Enter c:[string] to filter for mods changing specific items.\n"
|
||||
+ "Enter t:[string] to filter for mods set to specific tags.\n"
|
||||
+ "Enter n:[string] to filter only for mod names and no paths.\n"
|
||||
+ "Enter a:[string] to filter for mods by specific authors.";
|
||||
}
|
||||
|
||||
// Appropriately identify and set the string filter and its type.
|
||||
protected override bool ChangeFilter( string filterValue )
|
||||
{
|
||||
( _modFilter, _filterType ) = filterValue.Length switch
|
||||
{
|
||||
0 => ( LowerString.Empty, -1 ),
|
||||
> 1 when filterValue[ 1 ] == ':' =>
|
||||
filterValue[ 0 ] switch
|
||||
{
|
||||
'n' => filterValue.Length == 2 ? ( LowerString.Empty, -1 ) : ( new LowerString( filterValue[ 2.. ] ), 1 ),
|
||||
'N' => filterValue.Length == 2 ? ( LowerString.Empty, -1 ) : ( new LowerString( filterValue[ 2.. ] ), 1 ),
|
||||
'a' => filterValue.Length == 2 ? ( LowerString.Empty, -1 ) : ( new LowerString( filterValue[ 2.. ] ), 2 ),
|
||||
'A' => filterValue.Length == 2 ? ( LowerString.Empty, -1 ) : ( new LowerString( filterValue[ 2.. ] ), 2 ),
|
||||
'c' => filterValue.Length == 2 ? ( LowerString.Empty, -1 ) : ( new LowerString( filterValue[ 2.. ] ), 3 ),
|
||||
'C' => filterValue.Length == 2 ? ( LowerString.Empty, -1 ) : ( new LowerString( filterValue[ 2.. ] ), 3 ),
|
||||
't' => filterValue.Length == 2 ? ( LowerString.Empty, -1 ) : ( new LowerString( filterValue[ 2.. ] ), 4 ),
|
||||
'T' => filterValue.Length == 2 ? ( LowerString.Empty, -1 ) : ( new LowerString( filterValue[ 2.. ] ), 4 ),
|
||||
_ => ( new LowerString( filterValue ), 0 ),
|
||||
},
|
||||
_ => ( new LowerString( filterValue ), 0 ),
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check the state filter for a specific pair of has/has-not flags.
|
||||
// Uses count == 0 to check for has-not and count != 0 for has.
|
||||
// Returns true if it should be filtered and false if not.
|
||||
private bool CheckFlags( int count, ModFilter hasNoFlag, ModFilter hasFlag )
|
||||
{
|
||||
return count switch
|
||||
{
|
||||
0 when _stateFilter.HasFlag( hasNoFlag ) => false,
|
||||
0 => true,
|
||||
_ when _stateFilter.HasFlag( hasFlag ) => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
|
||||
// The overwritten filter method also computes the state.
|
||||
// Folders have default state and are filtered out on the direct string instead of the other options.
|
||||
// If any filter is set, they should be hidden by default unless their children are visible,
|
||||
// or they contain the path search string.
|
||||
protected override bool ApplyFiltersAndState( FileSystem< Mod >.IPath path, out ModState state )
|
||||
{
|
||||
if( path is ModFileSystem.Folder f )
|
||||
{
|
||||
state = default;
|
||||
return ModFilterExtensions.UnfilteredStateMods != _stateFilter
|
||||
|| FilterValue.Length > 0 && !f.FullName().Contains( FilterValue, IgnoreCase );
|
||||
}
|
||||
|
||||
return ApplyFiltersAndState( ( ModFileSystem.Leaf )path, out state );
|
||||
}
|
||||
|
||||
// Apply the string filters.
|
||||
private bool ApplyStringFilters( ModFileSystem.Leaf leaf, Mod mod )
|
||||
{
|
||||
return _filterType switch
|
||||
{
|
||||
-1 => false,
|
||||
0 => !( leaf.FullName().Contains( _modFilter.Lower, IgnoreCase ) || mod.Name.Contains( _modFilter ) ),
|
||||
1 => !mod.Name.Contains( _modFilter ),
|
||||
2 => !mod.Author.Contains( _modFilter ),
|
||||
3 => !mod.LowerChangedItemsString.Contains( _modFilter.Lower ),
|
||||
4 => !mod.AllTagsLower.Contains( _modFilter.Lower ),
|
||||
_ => false, // Should never happen
|
||||
};
|
||||
}
|
||||
|
||||
// Only get the text color for a mod if no filters are set.
|
||||
private static ColorId GetTextColor( Mod mod, ModSettings? settings, ModCollection collection )
|
||||
{
|
||||
if( Penumbra.ModManager.NewMods.Contains( mod ) )
|
||||
{
|
||||
return ColorId.NewMod;
|
||||
}
|
||||
|
||||
if( settings == null )
|
||||
{
|
||||
return ColorId.UndefinedMod;
|
||||
}
|
||||
|
||||
if( !settings.Enabled )
|
||||
{
|
||||
return collection != Penumbra.CollectionManager.Current ? ColorId.InheritedDisabledMod : ColorId.DisabledMod;
|
||||
}
|
||||
|
||||
var conflicts = Penumbra.CollectionManager.Current.Conflicts( mod );
|
||||
if( conflicts.Count == 0 )
|
||||
{
|
||||
return collection != Penumbra.CollectionManager.Current ? ColorId.InheritedMod : ColorId.EnabledMod;
|
||||
}
|
||||
|
||||
return conflicts.Any( c => !c.Solved )
|
||||
? ColorId.ConflictingMod
|
||||
: ColorId.HandledConflictMod;
|
||||
}
|
||||
|
||||
private bool CheckStateFilters( Mod mod, ModSettings? settings, ModCollection collection, ref ModState state )
|
||||
{
|
||||
var isNew = Penumbra.ModManager.NewMods.Contains( mod );
|
||||
// Handle mod details.
|
||||
if( CheckFlags( mod.TotalFileCount, ModFilter.HasNoFiles, ModFilter.HasFiles )
|
||||
|| CheckFlags( mod.TotalSwapCount, ModFilter.HasNoFileSwaps, ModFilter.HasFileSwaps )
|
||||
|| CheckFlags( mod.TotalManipulations, ModFilter.HasNoMetaManipulations, ModFilter.HasMetaManipulations )
|
||||
|| CheckFlags( mod.HasOptions ? 1 : 0, ModFilter.HasNoConfig, ModFilter.HasConfig )
|
||||
|| CheckFlags( isNew ? 1 : 0, ModFilter.NotNew, ModFilter.IsNew ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle Favoritism
|
||||
if( !_stateFilter.HasFlag( ModFilter.Favorite ) && mod.Favorite
|
||||
|| !_stateFilter.HasFlag( ModFilter.NotFavorite ) && !mod.Favorite )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle Inheritance
|
||||
if( collection == Penumbra.CollectionManager.Current )
|
||||
{
|
||||
if( !_stateFilter.HasFlag( ModFilter.Uninherited ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state.Color = ColorId.InheritedMod;
|
||||
if( !_stateFilter.HasFlag( ModFilter.Inherited ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle settings.
|
||||
if( settings == null )
|
||||
{
|
||||
state.Color = ColorId.UndefinedMod;
|
||||
if( !_stateFilter.HasFlag( ModFilter.Undefined )
|
||||
|| !_stateFilter.HasFlag( ModFilter.Disabled )
|
||||
|| !_stateFilter.HasFlag( ModFilter.NoConflict ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if( !settings.Enabled )
|
||||
{
|
||||
state.Color = collection == Penumbra.CollectionManager.Current ? ColorId.DisabledMod : ColorId.InheritedDisabledMod;
|
||||
if( !_stateFilter.HasFlag( ModFilter.Disabled )
|
||||
|| !_stateFilter.HasFlag( ModFilter.NoConflict ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !_stateFilter.HasFlag( ModFilter.Enabled ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Conflicts can only be relevant if the mod is enabled.
|
||||
var conflicts = Penumbra.CollectionManager.Current.Conflicts( mod );
|
||||
if( conflicts.Count > 0 )
|
||||
{
|
||||
if( conflicts.Any( c => !c.Solved ) )
|
||||
{
|
||||
if( !_stateFilter.HasFlag( ModFilter.UnsolvedConflict ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
state.Color = ColorId.ConflictingMod;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !_stateFilter.HasFlag( ModFilter.SolvedConflict ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
state.Color = ColorId.HandledConflictMod;
|
||||
}
|
||||
}
|
||||
else if( !_stateFilter.HasFlag( ModFilter.NoConflict ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// isNew color takes precedence before other colors.
|
||||
if( isNew )
|
||||
{
|
||||
state.Color = ColorId.NewMod;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Combined wrapper for handling all filters and setting state.
|
||||
private bool ApplyFiltersAndState( ModFileSystem.Leaf leaf, out ModState state )
|
||||
{
|
||||
state = new ModState { Color = ColorId.EnabledMod };
|
||||
var mod = leaf.Value;
|
||||
var (settings, collection) = Penumbra.CollectionManager.Current[ mod.Index ];
|
||||
|
||||
if( ApplyStringFilters( leaf, mod ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if( _stateFilter != ModFilterExtensions.UnfilteredStateMods )
|
||||
{
|
||||
return CheckStateFilters( mod, settings, collection, ref state );
|
||||
}
|
||||
|
||||
state.Color = GetTextColor( mod, settings, collection );
|
||||
return false;
|
||||
}
|
||||
|
||||
private void DrawFilterCombo( ref bool everything )
|
||||
{
|
||||
using var combo = ImRaii.Combo( "##filterCombo", string.Empty,
|
||||
ImGuiComboFlags.NoPreview | ImGuiComboFlags.PopupAlignLeft | ImGuiComboFlags.HeightLargest );
|
||||
if( combo )
|
||||
{
|
||||
using var style = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing,
|
||||
ImGui.GetStyle().ItemSpacing with { Y = 3 * ImGuiHelpers.GlobalScale } );
|
||||
var flags = ( int )_stateFilter;
|
||||
|
||||
|
||||
if( ImGui.Checkbox( "Everything", ref everything ) )
|
||||
{
|
||||
_stateFilter = everything ? ModFilterExtensions.UnfilteredStateMods : 0;
|
||||
SetFilterDirty();
|
||||
}
|
||||
|
||||
ImGui.Dummy( new Vector2( 0, 5 * ImGuiHelpers.GlobalScale ) );
|
||||
foreach( ModFilter flag in Enum.GetValues( typeof( ModFilter ) ) )
|
||||
{
|
||||
if( ImGui.CheckboxFlags( flag.ToName(), ref flags, ( int )flag ) )
|
||||
{
|
||||
_stateFilter = ( ModFilter )flags;
|
||||
SetFilterDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the state filter combo-button to the right of the filter box.
|
||||
protected override float CustomFilters( float width )
|
||||
{
|
||||
var pos = ImGui.GetCursorPos();
|
||||
var remainingWidth = width - ImGui.GetFrameHeight();
|
||||
var comboPos = new Vector2( pos.X + remainingWidth, pos.Y );
|
||||
|
||||
var everything = _stateFilter == ModFilterExtensions.UnfilteredStateMods;
|
||||
|
||||
ImGui.SetCursorPos( comboPos );
|
||||
// Draw combo button
|
||||
using var color = ImRaii.PushColor( ImGuiCol.Button, Colors.FilterActive, !everything );
|
||||
DrawFilterCombo( ref everything );
|
||||
ConfigWindow.OpenTutorial( ConfigWindow.BasicTutorialSteps.ModFilters );
|
||||
if( ImGui.IsItemClicked( ImGuiMouseButton.Right ) )
|
||||
{
|
||||
_stateFilter = ModFilterExtensions.UnfilteredStateMods;
|
||||
SetFilterDirty();
|
||||
}
|
||||
|
||||
ImGuiUtil.HoverTooltip( "Filter mods for their activation status.\nRight-Click to clear all filters." );
|
||||
ImGui.SetCursorPos( pos );
|
||||
return remainingWidth;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,478 +0,0 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.ImGuiFileDialog;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Filesystem;
|
||||
using OtterGui.FileSystem.Selector;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Import;
|
||||
using Penumbra.Mods;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Services;
|
||||
|
||||
namespace Penumbra.UI.Classes;
|
||||
|
||||
public sealed partial class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSystemSelector.ModState>
|
||||
{
|
||||
private readonly CommunicatorService _communicator;
|
||||
private readonly FileDialogManager _fileManager = ConfigWindow.SetupFileManager();
|
||||
private TexToolsImporter? _import;
|
||||
public ModSettings SelectedSettings { get; private set; } = ModSettings.Empty;
|
||||
public ModCollection SelectedSettingCollection { get; private set; } = ModCollection.Empty;
|
||||
|
||||
public ModFileSystemSelector(CommunicatorService communicator, ModFileSystem fileSystem)
|
||||
: base(fileSystem, DalamudServices.KeyState)
|
||||
{
|
||||
_communicator = communicator;
|
||||
SubscribeRightClickFolder(EnableDescendants, 10);
|
||||
SubscribeRightClickFolder(DisableDescendants, 10);
|
||||
SubscribeRightClickFolder(InheritDescendants, 15);
|
||||
SubscribeRightClickFolder(OwnDescendants, 15);
|
||||
SubscribeRightClickFolder(SetDefaultImportFolder, 100);
|
||||
SubscribeRightClickLeaf(ToggleLeafFavorite, 0);
|
||||
SubscribeRightClickMain(ClearDefaultImportFolder, 100);
|
||||
AddButton(AddNewModButton, 0);
|
||||
AddButton(AddImportModButton, 1);
|
||||
AddButton(AddHelpButton, 2);
|
||||
AddButton(DeleteModButton, 1000);
|
||||
SetFilterTooltip();
|
||||
|
||||
SelectionChanged += OnSelectionChange;
|
||||
_communicator.CollectionChange.Event += OnCollectionChange;
|
||||
Penumbra.CollectionManager.Current.ModSettingChanged += OnSettingChange;
|
||||
Penumbra.CollectionManager.Current.InheritanceChanged += OnInheritanceChange;
|
||||
Penumbra.ModManager.ModDataChanged += OnModDataChange;
|
||||
Penumbra.ModManager.ModDiscoveryStarted += StoreCurrentSelection;
|
||||
Penumbra.ModManager.ModDiscoveryFinished += RestoreLastSelection;
|
||||
OnCollectionChange(CollectionType.Current, null, Penumbra.CollectionManager.Current, "");
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
Penumbra.ModManager.ModDiscoveryStarted -= StoreCurrentSelection;
|
||||
Penumbra.ModManager.ModDiscoveryFinished -= RestoreLastSelection;
|
||||
Penumbra.ModManager.ModDataChanged -= OnModDataChange;
|
||||
Penumbra.CollectionManager.Current.ModSettingChanged -= OnSettingChange;
|
||||
Penumbra.CollectionManager.Current.InheritanceChanged -= OnInheritanceChange;
|
||||
_communicator.CollectionChange.Event -= OnCollectionChange;
|
||||
_import?.Dispose();
|
||||
_import = null;
|
||||
}
|
||||
|
||||
public new ModFileSystem.Leaf? SelectedLeaf
|
||||
=> base.SelectedLeaf;
|
||||
|
||||
// Customization points.
|
||||
public override ISortMode<Mod> SortMode
|
||||
=> Penumbra.Config.SortMode;
|
||||
|
||||
protected override uint ExpandedFolderColor
|
||||
=> ColorId.FolderExpanded.Value();
|
||||
|
||||
protected override uint CollapsedFolderColor
|
||||
=> ColorId.FolderCollapsed.Value();
|
||||
|
||||
protected override uint FolderLineColor
|
||||
=> ColorId.FolderLine.Value();
|
||||
|
||||
protected override bool FoldersDefaultOpen
|
||||
=> Penumbra.Config.OpenFoldersByDefault;
|
||||
|
||||
protected override void DrawPopups()
|
||||
{
|
||||
_fileManager.Draw();
|
||||
DrawHelpPopup();
|
||||
DrawInfoPopup();
|
||||
|
||||
if (ImGuiUtil.OpenNameField("Create New Mod", ref _newModName))
|
||||
try
|
||||
{
|
||||
var newDir = Mod.Creator.CreateModFolder(Penumbra.ModManager.BasePath, _newModName);
|
||||
Mod.Creator.CreateMeta(newDir, _newModName, Penumbra.Config.DefaultModAuthor, string.Empty, "1.0", string.Empty);
|
||||
Mod.Creator.CreateDefaultFiles(newDir);
|
||||
Penumbra.ModManager.AddMod(newDir);
|
||||
_newModName = string.Empty;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error($"Could not create directory for new Mod {_newModName}:\n{e}");
|
||||
}
|
||||
|
||||
while (_modsToAdd.TryDequeue(out var dir))
|
||||
{
|
||||
Penumbra.ModManager.AddMod(dir);
|
||||
var mod = Penumbra.ModManager.LastOrDefault();
|
||||
if (mod != null)
|
||||
{
|
||||
MoveModToDefaultDirectory(mod);
|
||||
SelectByValue(mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DrawLeafName(FileSystem<Mod>.Leaf leaf, in ModState state, bool selected)
|
||||
{
|
||||
var flags = selected ? ImGuiTreeNodeFlags.Selected | LeafFlags : LeafFlags;
|
||||
using var c = ImRaii.PushColor(ImGuiCol.Text, state.Color.Value())
|
||||
.Push(ImGuiCol.HeaderHovered, 0x4000FFFF, leaf.Value.Favorite);
|
||||
using var id = ImRaii.PushId(leaf.Value.Index);
|
||||
ImRaii.TreeNode(leaf.Value.Name, flags).Dispose();
|
||||
}
|
||||
|
||||
|
||||
// Add custom context menu items.
|
||||
private static void EnableDescendants(ModFileSystem.Folder folder)
|
||||
{
|
||||
if (ImGui.MenuItem("Enable Descendants"))
|
||||
SetDescendants(folder, true);
|
||||
}
|
||||
|
||||
private static void DisableDescendants(ModFileSystem.Folder folder)
|
||||
{
|
||||
if (ImGui.MenuItem("Disable Descendants"))
|
||||
SetDescendants(folder, false);
|
||||
}
|
||||
|
||||
private static void InheritDescendants(ModFileSystem.Folder folder)
|
||||
{
|
||||
if (ImGui.MenuItem("Inherit Descendants"))
|
||||
SetDescendants(folder, true, true);
|
||||
}
|
||||
|
||||
private static void OwnDescendants(ModFileSystem.Folder folder)
|
||||
{
|
||||
if (ImGui.MenuItem("Stop Inheriting Descendants"))
|
||||
SetDescendants(folder, false, true);
|
||||
}
|
||||
|
||||
private static void ToggleLeafFavorite(FileSystem<Mod>.Leaf mod)
|
||||
{
|
||||
if (ImGui.MenuItem(mod.Value.Favorite ? "Remove Favorite" : "Mark as Favorite"))
|
||||
Penumbra.ModManager.ChangeModFavorite(mod.Value.Index, !mod.Value.Favorite);
|
||||
}
|
||||
|
||||
private static void SetDefaultImportFolder(ModFileSystem.Folder folder)
|
||||
{
|
||||
if (ImGui.MenuItem("Set As Default Import Folder"))
|
||||
{
|
||||
var newName = folder.FullName();
|
||||
if (newName != Penumbra.Config.DefaultImportFolder)
|
||||
{
|
||||
Penumbra.Config.DefaultImportFolder = newName;
|
||||
Penumbra.Config.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ClearDefaultImportFolder()
|
||||
{
|
||||
if (ImGui.MenuItem("Clear Default Import Folder") && Penumbra.Config.DefaultImportFolder.Length > 0)
|
||||
{
|
||||
Penumbra.Config.DefaultImportFolder = string.Empty;
|
||||
Penumbra.Config.Save();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add custom buttons.
|
||||
private string _newModName = string.Empty;
|
||||
|
||||
private static void AddNewModButton(Vector2 size)
|
||||
{
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), size, "Create a new, empty mod of a given name.",
|
||||
!Penumbra.ModManager.Valid, true))
|
||||
ImGui.OpenPopup("Create New Mod");
|
||||
}
|
||||
|
||||
// Add an import mods button that opens a file selector.
|
||||
// Only set the initial directory once.
|
||||
private bool _hasSetFolder;
|
||||
|
||||
private void AddImportModButton(Vector2 size)
|
||||
{
|
||||
var button = ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.FileImport.ToIconString(), size,
|
||||
"Import one or multiple mods from Tex Tools Mod Pack Files or Penumbra Mod Pack Files.", !Penumbra.ModManager.Valid, true);
|
||||
ConfigWindow.OpenTutorial(ConfigWindow.BasicTutorialSteps.ModImport);
|
||||
if (!button)
|
||||
return;
|
||||
|
||||
var modPath = _hasSetFolder && !Penumbra.Config.AlwaysOpenDefaultImport ? null
|
||||
: Penumbra.Config.DefaultModImportPath.Length > 0 ? Penumbra.Config.DefaultModImportPath
|
||||
: Penumbra.Config.ModDirectory.Length > 0 ? Penumbra.Config.ModDirectory : null;
|
||||
_hasSetFolder = true;
|
||||
|
||||
_fileManager.OpenFileDialog("Import Mod Pack",
|
||||
"Mod Packs{.ttmp,.ttmp2,.pmp},TexTools Mod Packs{.ttmp,.ttmp2},Penumbra Mod Packs{.pmp},Archives{.zip,.7z,.rar}", (s, f) =>
|
||||
{
|
||||
if (s)
|
||||
{
|
||||
_import = new TexToolsImporter(Penumbra.ModManager.BasePath, f.Count, f.Select(file => new FileInfo(file)),
|
||||
AddNewMod);
|
||||
ImGui.OpenPopup("Import Status");
|
||||
}
|
||||
}, 0, modPath);
|
||||
}
|
||||
|
||||
// Draw the progress information for import.
|
||||
private void DrawInfoPopup()
|
||||
{
|
||||
var display = ImGui.GetIO().DisplaySize;
|
||||
var height = Math.Max(display.Y / 4, 15 * ImGui.GetFrameHeightWithSpacing());
|
||||
var width = display.X / 8;
|
||||
var size = new Vector2(width * 2, height);
|
||||
ImGui.SetNextWindowPos(ImGui.GetMainViewport().GetCenter(), ImGuiCond.Always, Vector2.One / 2);
|
||||
ImGui.SetNextWindowSize(size);
|
||||
using var popup = ImRaii.Popup("Import Status", ImGuiWindowFlags.Modal);
|
||||
if (_import == null || !popup.Success)
|
||||
return;
|
||||
|
||||
using (var child = ImRaii.Child("##import", new Vector2(-1, size.Y - ImGui.GetFrameHeight() * 2)))
|
||||
{
|
||||
if (child)
|
||||
_import.DrawProgressInfo(new Vector2(-1, ImGui.GetFrameHeight()));
|
||||
}
|
||||
|
||||
if (_import.State == ImporterState.Done && ImGui.Button("Close", -Vector2.UnitX)
|
||||
|| _import.State != ImporterState.Done && _import.DrawCancelButton(-Vector2.UnitX))
|
||||
{
|
||||
_import?.Dispose();
|
||||
_import = null;
|
||||
ImGui.CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
|
||||
// Mods need to be added thread-safely outside of iteration.
|
||||
private readonly ConcurrentQueue<DirectoryInfo> _modsToAdd = new();
|
||||
|
||||
// Clean up invalid directory if necessary.
|
||||
// Add successfully extracted mods.
|
||||
private void AddNewMod(FileInfo file, DirectoryInfo? dir, Exception? error)
|
||||
{
|
||||
if (error != null)
|
||||
{
|
||||
if (dir != null && Directory.Exists(dir.FullName))
|
||||
try
|
||||
{
|
||||
Directory.Delete(dir.FullName, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error($"Error cleaning up failed mod extraction of {file.FullName} to {dir.FullName}:\n{e}");
|
||||
}
|
||||
|
||||
if (error is not OperationCanceledException)
|
||||
Penumbra.Log.Error($"Error extracting {file.FullName}, mod skipped:\n{error}");
|
||||
}
|
||||
else if (dir != null)
|
||||
{
|
||||
_modsToAdd.Enqueue(dir);
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteModButton(Vector2 size)
|
||||
{
|
||||
var keys = Penumbra.Config.DeleteModModifier.IsActive();
|
||||
var tt = SelectedLeaf == null
|
||||
? "No mod selected."
|
||||
: "Delete the currently selected mod entirely from your drive.\n"
|
||||
+ "This can not be undone.";
|
||||
if (!keys)
|
||||
tt += $"\nHold {Penumbra.Config.DeleteModModifier} while clicking to delete the mod.";
|
||||
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), size, tt, SelectedLeaf == null || !keys, true)
|
||||
&& Selected != null)
|
||||
Penumbra.ModManager.DeleteMod(Selected.Index);
|
||||
}
|
||||
|
||||
private static void AddHelpButton(Vector2 size)
|
||||
{
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.QuestionCircle.ToIconString(), size, "Open extended help.", false, true))
|
||||
ImGui.OpenPopup("ExtendedHelp");
|
||||
|
||||
ConfigWindow.OpenTutorial(ConfigWindow.BasicTutorialSteps.AdvancedHelp);
|
||||
}
|
||||
|
||||
// Helpers.
|
||||
private static void SetDescendants(ModFileSystem.Folder folder, bool enabled, bool inherit = false)
|
||||
{
|
||||
var mods = folder.GetAllDescendants(ISortMode<Mod>.Lexicographical).OfType<ModFileSystem.Leaf>().Select(l =>
|
||||
{
|
||||
// Any mod handled here should not stay new.
|
||||
Penumbra.ModManager.NewMods.Remove(l.Value);
|
||||
return l.Value;
|
||||
});
|
||||
|
||||
if (inherit)
|
||||
Penumbra.CollectionManager.Current.SetMultipleModInheritances(mods, enabled);
|
||||
else
|
||||
Penumbra.CollectionManager.Current.SetMultipleModStates(mods, enabled);
|
||||
}
|
||||
|
||||
// Automatic cache update functions.
|
||||
private void OnSettingChange(ModSettingChange type, int modIdx, int oldValue, int groupIdx, bool inherited)
|
||||
{
|
||||
// TODO: maybe make more efficient
|
||||
SetFilterDirty();
|
||||
if (modIdx == Selected?.Index)
|
||||
OnSelectionChange(Selected, Selected, default);
|
||||
}
|
||||
|
||||
private void OnModDataChange(ModDataChangeType type, Mod mod, string? oldName)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ModDataChangeType.Name:
|
||||
case ModDataChangeType.Author:
|
||||
case ModDataChangeType.ModTags:
|
||||
case ModDataChangeType.LocalTags:
|
||||
case ModDataChangeType.Favorite:
|
||||
SetFilterDirty();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInheritanceChange(bool _)
|
||||
{
|
||||
SetFilterDirty();
|
||||
OnSelectionChange(Selected, Selected, default);
|
||||
}
|
||||
|
||||
private void OnCollectionChange(CollectionType collectionType, ModCollection? oldCollection, ModCollection? newCollection, string _)
|
||||
{
|
||||
if (collectionType != CollectionType.Current || oldCollection == newCollection)
|
||||
return;
|
||||
|
||||
if (oldCollection != null)
|
||||
{
|
||||
oldCollection.ModSettingChanged -= OnSettingChange;
|
||||
oldCollection.InheritanceChanged -= OnInheritanceChange;
|
||||
}
|
||||
|
||||
if (newCollection != null)
|
||||
{
|
||||
newCollection.ModSettingChanged += OnSettingChange;
|
||||
newCollection.InheritanceChanged += OnInheritanceChange;
|
||||
}
|
||||
|
||||
SetFilterDirty();
|
||||
OnSelectionChange(Selected, Selected, default);
|
||||
}
|
||||
|
||||
private void OnSelectionChange(Mod? _1, Mod? newSelection, in ModState _2)
|
||||
{
|
||||
if (newSelection == null)
|
||||
{
|
||||
SelectedSettings = ModSettings.Empty;
|
||||
SelectedSettingCollection = ModCollection.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
(var settings, SelectedSettingCollection) = Penumbra.CollectionManager.Current[newSelection.Index];
|
||||
SelectedSettings = settings ?? ModSettings.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep selections across rediscoveries if possible.
|
||||
private string _lastSelectedDirectory = string.Empty;
|
||||
|
||||
private void StoreCurrentSelection()
|
||||
{
|
||||
_lastSelectedDirectory = Selected?.ModPath.FullName ?? string.Empty;
|
||||
ClearSelection();
|
||||
}
|
||||
|
||||
private void RestoreLastSelection()
|
||||
{
|
||||
if (_lastSelectedDirectory.Length > 0)
|
||||
{
|
||||
var leaf = (ModFileSystem.Leaf?)FileSystem.Root.GetAllDescendants(ISortMode<Mod>.Lexicographical)
|
||||
.FirstOrDefault(l => l is ModFileSystem.Leaf m && m.Value.ModPath.FullName == _lastSelectedDirectory);
|
||||
Select(leaf);
|
||||
_lastSelectedDirectory = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
// If a default import folder is setup, try to move the given mod in there.
|
||||
// If the folder does not exist, create it if possible.
|
||||
private void MoveModToDefaultDirectory(Mod mod)
|
||||
{
|
||||
if (Penumbra.Config.DefaultImportFolder.Length == 0)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var leaf = FileSystem.Root.GetChildren(ISortMode<Mod>.Lexicographical)
|
||||
.FirstOrDefault(f => f is FileSystem<Mod>.Leaf l && l.Value == mod);
|
||||
if (leaf == null)
|
||||
throw new Exception("Mod was not found at root.");
|
||||
|
||||
var folder = FileSystem.FindOrCreateAllFolders(Penumbra.Config.DefaultImportFolder);
|
||||
FileSystem.Move(leaf, folder);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Warning(
|
||||
$"Could not move newly imported mod {mod.Name} to default import folder {Penumbra.Config.DefaultImportFolder}:\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawHelpPopup()
|
||||
{
|
||||
ImGuiUtil.HelpPopup("ExtendedHelp", new Vector2(1000 * ImGuiHelpers.GlobalScale, 34.5f * ImGui.GetTextLineHeightWithSpacing()), () =>
|
||||
{
|
||||
ImGui.Dummy(Vector2.UnitY * ImGui.GetTextLineHeight());
|
||||
ImGui.TextUnformatted("Mod Management");
|
||||
ImGui.BulletText("You can create empty mods or import mods with the buttons in this row.");
|
||||
using var indent = ImRaii.PushIndent();
|
||||
ImGui.BulletText("Supported formats for import are: .ttmp, .ttmp2, .pmp.");
|
||||
ImGui.BulletText(
|
||||
"You can also support .zip, .7z or .rar archives, but only if they already contain Penumbra-styled mods with appropriate metadata.");
|
||||
indent.Pop(1);
|
||||
ImGui.BulletText("You can also create empty mod folders and delete mods.");
|
||||
ImGui.BulletText("For further editing of mods, select them and use the Edit Mod tab in the panel or the Advanced Editing popup.");
|
||||
ImGui.Dummy(Vector2.UnitY * ImGui.GetTextLineHeight());
|
||||
ImGui.TextUnformatted("Mod Selector");
|
||||
ImGui.BulletText("Select a mod to obtain more information or change settings.");
|
||||
ImGui.BulletText("Names are colored according to your config and their current state in the collection:");
|
||||
indent.Push();
|
||||
ImGuiUtil.BulletTextColored(ColorId.EnabledMod.Value(), "enabled in the current collection.");
|
||||
ImGuiUtil.BulletTextColored(ColorId.DisabledMod.Value(), "disabled in the current collection.");
|
||||
ImGuiUtil.BulletTextColored(ColorId.InheritedMod.Value(), "enabled due to inheritance from another collection.");
|
||||
ImGuiUtil.BulletTextColored(ColorId.InheritedDisabledMod.Value(), "disabled due to inheritance from another collection.");
|
||||
ImGuiUtil.BulletTextColored(ColorId.UndefinedMod.Value(), "unconfigured in all inherited collections.");
|
||||
ImGuiUtil.BulletTextColored(ColorId.NewMod.Value(),
|
||||
"newly imported during this session. Will go away when first enabling a mod or when Penumbra is reloaded.");
|
||||
ImGuiUtil.BulletTextColored(ColorId.HandledConflictMod.Value(),
|
||||
"enabled and conflicting with another enabled Mod, but on different priorities (i.e. the conflict is solved).");
|
||||
ImGuiUtil.BulletTextColored(ColorId.ConflictingMod.Value(),
|
||||
"enabled and conflicting with another enabled Mod on the same priority.");
|
||||
ImGuiUtil.BulletTextColored(ColorId.FolderExpanded.Value(), "expanded mod folder.");
|
||||
ImGuiUtil.BulletTextColored(ColorId.FolderCollapsed.Value(), "collapsed mod folder");
|
||||
indent.Pop(1);
|
||||
ImGui.BulletText("Right-click a mod to enter its sort order, which is its name by default, possibly with a duplicate number.");
|
||||
indent.Push();
|
||||
ImGui.BulletText("A sort order differing from the mods name will not be displayed, it will just be used for ordering.");
|
||||
ImGui.BulletText(
|
||||
"If the sort order string contains Forward-Slashes ('/'), the preceding substring will be turned into folders automatically.");
|
||||
indent.Pop(1);
|
||||
ImGui.BulletText(
|
||||
"You can drag and drop mods and subfolders into existing folders. Dropping them onto mods is the same as dropping them onto the parent of the mod.");
|
||||
ImGui.BulletText("Right-clicking a folder opens a context menu.");
|
||||
ImGui.BulletText("Right-clicking empty space allows you to expand or collapse all folders at once.");
|
||||
ImGui.BulletText("Use the Filter Mods... input at the top to filter the list for mods whose name or path contain the text.");
|
||||
indent.Push();
|
||||
ImGui.BulletText("You can enter n:[string] to filter only for names, without path.");
|
||||
ImGui.BulletText("You can enter c:[string] to filter for Changed Items instead.");
|
||||
ImGui.BulletText("You can enter a:[string] to filter for Mod Authors instead.");
|
||||
indent.Pop(1);
|
||||
ImGui.BulletText("Use the expandable menu beside the input to filter for mods fulfilling specific criteria.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Penumbra.UI.Classes;
|
||||
|
||||
[Flags]
|
||||
public enum ModFilter
|
||||
{
|
||||
Enabled = 1 << 0,
|
||||
Disabled = 1 << 1,
|
||||
Favorite = 1 << 2,
|
||||
NotFavorite = 1 << 3,
|
||||
NoConflict = 1 << 4,
|
||||
SolvedConflict = 1 << 5,
|
||||
UnsolvedConflict = 1 << 6,
|
||||
HasNoMetaManipulations = 1 << 7,
|
||||
HasMetaManipulations = 1 << 8,
|
||||
HasNoFileSwaps = 1 << 9,
|
||||
HasFileSwaps = 1 << 10,
|
||||
HasConfig = 1 << 11,
|
||||
HasNoConfig = 1 << 12,
|
||||
HasNoFiles = 1 << 13,
|
||||
HasFiles = 1 << 14,
|
||||
IsNew = 1 << 15,
|
||||
NotNew = 1 << 16,
|
||||
Inherited = 1 << 17,
|
||||
Uninherited = 1 << 18,
|
||||
Undefined = 1 << 19,
|
||||
};
|
||||
|
||||
public static class ModFilterExtensions
|
||||
{
|
||||
public const ModFilter UnfilteredStateMods = ( ModFilter )( ( 1 << 20 ) - 1 );
|
||||
|
||||
public static string ToName( this ModFilter filter )
|
||||
=> filter switch
|
||||
{
|
||||
ModFilter.Enabled => "Enabled",
|
||||
ModFilter.Disabled => "Disabled",
|
||||
ModFilter.Favorite => "Favorite",
|
||||
ModFilter.NotFavorite => "No Favorite",
|
||||
ModFilter.NoConflict => "No Conflicts",
|
||||
ModFilter.SolvedConflict => "Solved Conflicts",
|
||||
ModFilter.UnsolvedConflict => "Unsolved Conflicts",
|
||||
ModFilter.HasNoMetaManipulations => "No Meta Manipulations",
|
||||
ModFilter.HasMetaManipulations => "Meta Manipulations",
|
||||
ModFilter.HasNoFileSwaps => "No File Swaps",
|
||||
ModFilter.HasFileSwaps => "File Swaps",
|
||||
ModFilter.HasNoConfig => "No Configuration",
|
||||
ModFilter.HasConfig => "Configuration",
|
||||
ModFilter.HasNoFiles => "No Files",
|
||||
ModFilter.HasFiles => "Files",
|
||||
ModFilter.IsNew => "Newly Imported",
|
||||
ModFilter.NotNew => "Not Newly Imported",
|
||||
ModFilter.Inherited => "Inherited Configuration",
|
||||
ModFilter.Uninherited => "Own Configuration",
|
||||
ModFilter.Undefined => "Not Configured",
|
||||
_ => throw new ArgumentOutOfRangeException( nameof( filter ), filter, null ),
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue