Change most things to new byte strings, introduce new ResourceLoader and Logger fully.

This commit is contained in:
Ottermandias 2022-03-06 16:45:16 +01:00
parent 5d77cd5514
commit f5fccb0235
55 changed files with 2681 additions and 2730 deletions

View file

@ -1,8 +1,8 @@
using System.Numerics;
using System.Security.Cryptography.X509Certificates;
using System.Windows.Forms;
using Dalamud.Interface;
using ImGuiNET;
using Penumbra.GameData.ByteString;
namespace Penumbra.UI.Custom
{
@ -20,6 +20,19 @@ namespace Penumbra.UI.Custom
ImGui.SetTooltip( "Click to copy to clipboard." );
}
}
public static unsafe void CopyOnClickSelectable( Utf8String text )
{
if( ImGuiNative.igSelectable_Bool( text.Path, 0, ImGuiSelectableFlags.None, Vector2.Zero ) != 0 )
{
ImGuiNative.igSetClipboardText( text.Path );
}
if( ImGui.IsItemHovered() )
{
ImGui.SetTooltip( "Click to copy to clipboard." );
}
}
}
public static partial class ImGuiCustom

View file

@ -21,11 +21,21 @@ public partial class SettingsInterface
private string _filePathFilter = string.Empty;
private string _filePathFilterLower = string.Empty;
private readonly float _leftTextLength =
ImGui.CalcTextSize( "chara/human/c0000/obj/body/b0000/material/v0000/mt_c0000b0000_b.mtrl" ).X / ImGuiHelpers.GlobalScale + 40;
private const float LeftTextLength = 600;
private float _arrowLength = 0;
private static void DrawLine( Utf8GamePath path, FullPath name )
{
ImGui.TableNextColumn();
ImGuiCustom.CopyOnClickSelectable( path.Path );
ImGui.TableNextColumn();
ImGuiCustom.PrintIcon( FontAwesomeIcon.LongArrowAltLeft );
ImGui.SameLine();
ImGuiCustom.CopyOnClickSelectable( name.InternalName );
}
private static void DrawLine( string path, string name )
{
ImGui.TableNextColumn();
@ -45,13 +55,13 @@ public partial class SettingsInterface
_arrowLength = ImGui.CalcTextSize( FontAwesomeIcon.LongArrowAltLeft.ToIconString() ).X / ImGuiHelpers.GlobalScale;
}
ImGui.SetNextItemWidth( _leftTextLength * ImGuiHelpers.GlobalScale );
ImGui.SetNextItemWidth( LeftTextLength * ImGuiHelpers.GlobalScale );
if( ImGui.InputTextWithHint( "##effective_changes_gfilter", "Filter game path...", ref _gamePathFilter, 256 ) )
{
_gamePathFilterLower = _gamePathFilter.ToLowerInvariant();
}
ImGui.SameLine( ( _leftTextLength + _arrowLength ) * ImGuiHelpers.GlobalScale + 3 * ImGui.GetStyle().ItemSpacing.X );
ImGui.SameLine( ( LeftTextLength + _arrowLength ) * ImGuiHelpers.GlobalScale + 3 * ImGui.GetStyle().ItemSpacing.X );
ImGui.SetNextItemWidth( -1 );
if( ImGui.InputTextWithHint( "##effective_changes_ffilter", "Filter file path...", ref _filePathFilter, 256 ) )
{
@ -59,7 +69,7 @@ public partial class SettingsInterface
}
}
private bool CheckFilters( KeyValuePair< GamePath, FullPath > kvp )
private bool CheckFilters( KeyValuePair< Utf8GamePath, FullPath > kvp )
{
if( _gamePathFilter.Any() && !kvp.Key.ToString().Contains( _gamePathFilterLower ) )
{
@ -69,7 +79,7 @@ public partial class SettingsInterface
return !_filePathFilter.Any() || kvp.Value.FullName.ToLowerInvariant().Contains( _filePathFilterLower );
}
private bool CheckFilters( KeyValuePair< GamePath, GamePath > kvp )
private bool CheckFilters( KeyValuePair< Utf8GamePath, Utf8GamePath > kvp )
{
if( _gamePathFilter.Any() && !kvp.Key.ToString().Contains( _gamePathFilterLower ) )
{
@ -94,11 +104,6 @@ public partial class SettingsInterface
void DrawFileLines( ModCollectionCache cache )
{
foreach( var (gp, fp) in cache.ResolvedFiles.Where( CheckFilters ) )
{
DrawLine( gp, fp.FullName );
}
foreach( var (gp, fp) in cache.SwappedFiles.Where( CheckFilters ) )
{
DrawLine( gp, fp );
}
@ -139,75 +144,67 @@ public partial class SettingsInterface
var activeCollection = modManager.Collections.ActiveCollection.Cache;
var forcedCollection = modManager.Collections.ForcedCollection.Cache;
var (activeResolved, activeSwap, activeMeta) = activeCollection != null
? ( activeCollection.ResolvedFiles.Count, activeCollection.SwappedFiles.Count, activeCollection.MetaManipulations.Count )
: ( 0, 0, 0 );
var (forcedResolved, forcedSwap, forcedMeta) = forcedCollection != null
? ( forcedCollection.ResolvedFiles.Count, forcedCollection.SwappedFiles.Count, forcedCollection.MetaManipulations.Count )
: ( 0, 0, 0 );
var totalLines = activeResolved + forcedResolved + activeSwap + forcedSwap + activeMeta + forcedMeta;
var (activeResolved, activeMeta) = activeCollection != null
? ( activeCollection.ResolvedFiles.Count, activeCollection.MetaManipulations.Count )
: ( 0, 0 );
var (forcedResolved, forcedMeta) = forcedCollection != null
? ( forcedCollection.ResolvedFiles.Count, forcedCollection.MetaManipulations.Count )
: ( 0, 0 );
var totalLines = activeResolved + forcedResolved + activeMeta + forcedMeta;
if( totalLines == 0 )
{
return;
}
if( ImGui.BeginTable( "##effective_changes", 2, flags, AutoFillSize ) )
if( !ImGui.BeginTable( "##effective_changes", 2, flags, AutoFillSize ) )
{
raii.Push( ImGui.EndTable );
ImGui.TableSetupColumn( "##tableGamePathCol", ImGuiTableColumnFlags.None, _leftTextLength * ImGuiHelpers.GlobalScale );
return;
}
if( _filePathFilter.Any() || _gamePathFilter.Any() )
raii.Push( ImGui.EndTable );
ImGui.TableSetupColumn( "##tableGamePathCol", ImGuiTableColumnFlags.None, LeftTextLength * ImGuiHelpers.GlobalScale );
if( _filePathFilter.Length > 0 || _gamePathFilter.Length > 0 )
{
DrawFilteredRows( activeCollection, forcedCollection );
}
else
{
ImGuiListClipperPtr clipper;
unsafe
{
DrawFilteredRows( activeCollection, forcedCollection );
clipper = new ImGuiListClipperPtr( ImGuiNative.ImGuiListClipper_ImGuiListClipper() );
}
else
clipper.Begin( totalLines );
while( clipper.Step() )
{
ImGuiListClipperPtr clipper;
unsafe
for( var actualRow = clipper.DisplayStart; actualRow < clipper.DisplayEnd; actualRow++ )
{
clipper = new ImGuiListClipperPtr( ImGuiNative.ImGuiListClipper_ImGuiListClipper() );
}
clipper.Begin( totalLines );
while( clipper.Step() )
{
for( var actualRow = clipper.DisplayStart; actualRow < clipper.DisplayEnd; actualRow++ )
var row = actualRow;
ImGui.TableNextRow();
if( row < activeResolved )
{
var row = actualRow;
ImGui.TableNextRow();
if( row < activeResolved )
{
var (gamePath, file) = activeCollection!.ResolvedFiles.ElementAt( row );
DrawLine( gamePath, file.FullName );
}
else if( ( row -= activeResolved ) < activeSwap )
{
var (gamePath, swap) = activeCollection!.SwappedFiles.ElementAt( row );
DrawLine( gamePath, swap );
}
else if( ( row -= activeSwap ) < activeMeta )
{
var (manip, mod) = activeCollection!.MetaManipulations.Manipulations.ElementAt( row );
DrawLine( manip.IdentifierString(), mod.Data.Meta.Name );
}
else if( ( row -= activeMeta ) < forcedResolved )
{
var (gamePath, file) = forcedCollection!.ResolvedFiles.ElementAt( row );
DrawLine( gamePath, file.FullName );
}
else if( ( row -= forcedResolved ) < forcedSwap )
{
var (gamePath, swap) = forcedCollection!.SwappedFiles.ElementAt( row );
DrawLine( gamePath, swap );
}
else
{
row -= forcedSwap;
var (manip, mod) = forcedCollection!.MetaManipulations.Manipulations.ElementAt( row );
DrawLine( manip.IdentifierString(), mod.Data.Meta.Name );
}
var (gamePath, file) = activeCollection!.ResolvedFiles.ElementAt( row );
DrawLine( gamePath, file );
}
else if( ( row -= activeResolved ) < activeMeta )
{
var (manip, mod) = activeCollection!.MetaManipulations.Manipulations.ElementAt( row );
DrawLine( manip.IdentifierString(), mod.Data.Meta.Name );
}
else if( ( row -= activeMeta ) < forcedResolved )
{
var (gamePath, file) = forcedCollection!.ResolvedFiles.ElementAt( row );
DrawLine( gamePath, file );
}
else
{
row -= forcedResolved;
var (manip, mod) = forcedCollection!.MetaManipulations.Manipulations.ElementAt( row );
DrawLine( manip.IdentifierString(), mod.Data.Meta.Name );
}
}
}

View file

@ -11,7 +11,6 @@ using Penumbra.GameData.Util;
using Penumbra.Meta;
using Penumbra.Mod;
using Penumbra.Mods;
using Penumbra.Structs;
using Penumbra.UI.Custom;
using Penumbra.Util;
using ImGui = ImGuiNET.ImGui;
@ -56,7 +55,7 @@ public partial class SettingsInterface
private Option? _selectedOption;
private string _currentGamePaths = "";
private (FullPath name, bool selected, uint color, RelPath relName)[]? _fullFilenameList;
private (FullPath name, bool selected, uint color, Utf8RelPath relName)[]? _fullFilenameList;
private readonly Selector _selector;
private readonly SettingsInterface _base;
@ -218,7 +217,10 @@ public partial class SettingsInterface
indent.Push( 15f );
foreach( var file in files )
{
ImGui.Selectable( file );
unsafe
{
ImGuiNative.igSelectable_Bool( file.Path.Path, 0, ImGuiSelectableFlags.None, Vector2.Zero );
}
}
foreach( var manip in manipulations )
@ -258,13 +260,13 @@ public partial class SettingsInterface
foreach( var (source, target) in Meta.FileSwaps )
{
ImGui.TableNextColumn();
ImGuiCustom.CopyOnClickSelectable( source );
ImGuiCustom.CopyOnClickSelectable( source.Path );
ImGui.TableNextColumn();
ImGuiCustom.PrintIcon( FontAwesomeIcon.LongArrowAltRight );
ImGui.TableNextColumn();
ImGuiCustom.CopyOnClickSelectable( target );
ImGuiCustom.CopyOnClickSelectable( target.InternalName );
ImGui.TableNextRow();
}
@ -278,7 +280,8 @@ public partial class SettingsInterface
}
_fullFilenameList = Mod.Data.Resources.ModFiles
.Select( f => ( f, false, ColorGreen, new RelPath( f, Mod.Data.BasePath ) ) ).ToArray();
.Select( f => ( f, false, ColorGreen, Utf8RelPath.FromFile( f, Mod.Data.BasePath, out var p ) ? p : Utf8RelPath.Empty ) )
.ToArray();
if( Meta.Groups.Count == 0 )
{
@ -339,24 +342,23 @@ public partial class SettingsInterface
}
}
private static int HandleDefaultString( GamePath[] gamePaths, out int removeFolders )
private static int HandleDefaultString( Utf8GamePath[] gamePaths, out int removeFolders )
{
removeFolders = 0;
var defaultIndex =
gamePaths.IndexOf( p => ( ( string )p ).StartsWith( TextDefaultGamePath ) );
var defaultIndex = gamePaths.IndexOf( p => p.Path.StartsWith( DefaultUtf8GamePath ) );
if( defaultIndex < 0 )
{
return defaultIndex;
}
string path = gamePaths[ defaultIndex ];
var path = gamePaths[ defaultIndex ].Path;
if( path.Length == TextDefaultGamePath.Length )
{
return defaultIndex;
}
if( path[ TextDefaultGamePath.Length ] != '-'
|| !int.TryParse( path.Substring( TextDefaultGamePath.Length + 1 ), out removeFolders ) )
if( path[ TextDefaultGamePath.Length ] != ( byte )'-'
|| !int.TryParse( path.Substring( TextDefaultGamePath.Length + 1 ).ToString(), out removeFolders ) )
{
return -1;
}
@ -373,8 +375,9 @@ public partial class SettingsInterface
var option = ( Option )_selectedOption;
var gamePaths = _currentGamePaths.Split( ';' ).Select( p => new GamePath( p ) ).ToArray();
if( gamePaths.Length == 0 || ( ( string )gamePaths[ 0 ] ).Length == 0 )
var gamePaths = _currentGamePaths.Split( ';' )
.Select( p => Utf8GamePath.FromString( p, out var path, false ) ? path : Utf8GamePath.Empty ).Where( p => !p.IsEmpty ).ToArray();
if( gamePaths.Length == 0 )
{
return;
}
@ -517,18 +520,18 @@ public partial class SettingsInterface
{
Selectable( 0, ColorGreen );
using var indent = ImGuiRaii.PushIndent( indentWidth );
var tmpPaths = gamePaths.ToArray();
foreach( var gamePath in tmpPaths )
using var indent = ImGuiRaii.PushIndent( indentWidth );
foreach( var gamePath in gamePaths.ToArray() )
{
string tmp = gamePath;
var tmp = gamePath.ToString();
var old = tmp;
if( ImGui.InputText( $"##{fileName}_{gamePath}", ref tmp, 128, ImGuiInputTextFlags.EnterReturnsTrue )
&& tmp != gamePath )
&& tmp != old )
{
gamePaths.Remove( gamePath );
if( tmp.Length > 0 )
if( tmp.Length > 0 && Utf8GamePath.FromString( tmp, out var p, true ) )
{
gamePaths.Add( new GamePath( tmp ) );
gamePaths.Add( p );
}
else if( gamePaths.Count == 0 )
{

View file

@ -3,136 +3,179 @@ using System.Linq;
using System.Numerics;
using Dalamud.Interface;
using ImGuiNET;
using Penumbra.GameData.ByteString;
using Penumbra.GameData.Util;
using Penumbra.Mod;
using Penumbra.Mods;
using Penumbra.Structs;
using Penumbra.UI.Custom;
using Penumbra.Util;
namespace Penumbra.UI
namespace Penumbra.UI;
public partial class SettingsInterface
{
public partial class SettingsInterface
private partial class PluginDetails
{
private partial class PluginDetails
private const string LabelDescEdit = "##descedit";
private const string LabelNewSingleGroupEdit = "##newSingleGroup";
private const string LabelNewMultiGroup = "##newMultiGroup";
private const string LabelGamePathsEditBox = "##gamePathsEdit";
private const string ButtonAddToGroup = "Add to Group";
private const string ButtonRemoveFromGroup = "Remove from Group";
private const string TooltipAboutEdit = "Use Ctrl+Enter for newlines.";
private const string TextNoOptionAvailable = "[Not Available]";
private const string TextDefaultGamePath = "default";
private static readonly Utf8String DefaultUtf8GamePath = Utf8String.FromStringUnsafe( TextDefaultGamePath, true );
private const char GamePathsSeparator = ';';
private static readonly string TooltipFilesTabEdit =
$"{TooltipFilesTab}\n"
+ $"Red Files are replaced in another group or a different option in this group, but not contained in the current option.";
private static readonly string TooltipGamePathsEdit =
$"Enter all game paths to add or remove, separated by '{GamePathsSeparator}'.\n"
+ $"Use '{TextDefaultGamePath}' to add the original file path."
+ $"Use '{TextDefaultGamePath}-#' to skip the first # relative directories.";
private const float MultiEditBoxWidth = 300f;
private bool DrawEditGroupSelector()
{
private const string LabelDescEdit = "##descedit";
private const string LabelNewSingleGroupEdit = "##newSingleGroup";
private const string LabelNewMultiGroup = "##newMultiGroup";
private const string LabelGamePathsEditBox = "##gamePathsEdit";
private const string ButtonAddToGroup = "Add to Group";
private const string ButtonRemoveFromGroup = "Remove from Group";
private const string TooltipAboutEdit = "Use Ctrl+Enter for newlines.";
private const string TextNoOptionAvailable = "[Not Available]";
private const string TextDefaultGamePath = "default";
private const char GamePathsSeparator = ';';
private static readonly string TooltipFilesTabEdit =
$"{TooltipFilesTab}\n"
+ $"Red Files are replaced in another group or a different option in this group, but not contained in the current option.";
private static readonly string TooltipGamePathsEdit =
$"Enter all game paths to add or remove, separated by '{GamePathsSeparator}'.\n"
+ $"Use '{TextDefaultGamePath}' to add the original file path."
+ $"Use '{TextDefaultGamePath}-#' to skip the first # relative directories.";
private const float MultiEditBoxWidth = 300f;
private bool DrawEditGroupSelector()
ImGui.SetNextItemWidth( OptionSelectionWidth * ImGuiHelpers.GlobalScale );
if( Meta!.Groups.Count == 0 )
{
ImGui.SetNextItemWidth( OptionSelectionWidth * ImGuiHelpers.GlobalScale );
if( Meta!.Groups.Count == 0 )
{
ImGui.Combo( LabelGroupSelect, ref _selectedGroupIndex, TextNoOptionAvailable, 1 );
return false;
}
if( ImGui.Combo( LabelGroupSelect, ref _selectedGroupIndex
, Meta.Groups.Values.Select( g => g.GroupName ).ToArray()
, Meta.Groups.Count ) )
{
SelectGroup();
SelectOption( 0 );
}
return true;
ImGui.Combo( LabelGroupSelect, ref _selectedGroupIndex, TextNoOptionAvailable, 1 );
return false;
}
private bool DrawEditOptionSelector()
if( ImGui.Combo( LabelGroupSelect, ref _selectedGroupIndex
, Meta.Groups.Values.Select( g => g.GroupName ).ToArray()
, Meta.Groups.Count ) )
{
SelectGroup();
SelectOption( 0 );
}
return true;
}
private bool DrawEditOptionSelector()
{
ImGui.SameLine();
ImGui.SetNextItemWidth( OptionSelectionWidth );
if( ( _selectedGroup?.Options.Count ?? 0 ) == 0 )
{
ImGui.Combo( LabelOptionSelect, ref _selectedOptionIndex, TextNoOptionAvailable, 1 );
return false;
}
var group = ( OptionGroup )_selectedGroup!;
if( ImGui.Combo( LabelOptionSelect, ref _selectedOptionIndex, group.Options.Select( o => o.OptionName ).ToArray(),
group.Options.Count ) )
{
SelectOption();
}
return true;
}
private void DrawFileListTabEdit()
{
if( ImGui.BeginTabItem( LabelFileListTab ) )
{
UpdateFilenameList();
if( ImGui.IsItemHovered() )
{
ImGui.SetTooltip( _editMode ? TooltipFilesTabEdit : TooltipFilesTab );
}
ImGui.SetNextItemWidth( -1 );
if( ImGui.BeginListBox( LabelFileListHeader, AutoFillSize - Vector2.UnitY * 1.5f * ImGui.GetTextLineHeight() ) )
{
for( var i = 0; i < Mod!.Data.Resources.ModFiles.Count; ++i )
{
DrawFileAndGamePaths( i );
}
}
ImGui.EndListBox();
DrawGroupRow();
ImGui.EndTabItem();
}
else
{
_fullFilenameList = null;
}
}
private ImGuiRaii.EndStack DrawMultiSelectorEditBegin( OptionGroup group )
{
var groupName = group.GroupName;
if( ImGuiCustom.BeginFramedGroupEdit( ref groupName ) )
{
if( Penumbra.ModManager.ChangeModGroup( group.GroupName, groupName, Mod.Data ) && Mod.Data.Meta.RefreshHasGroupsWithConfig() )
{
_selector.Cache.TriggerFilterReset();
}
}
return ImGuiRaii.DeferredEnd( ImGuiCustom.EndFramedGroup );
}
private void DrawMultiSelectorEditAdd( OptionGroup group, float nameBoxStart )
{
var newOption = "";
ImGui.SetCursorPosX( nameBoxStart );
ImGui.SetNextItemWidth( MultiEditBoxWidth * ImGuiHelpers.GlobalScale );
if( ImGui.InputTextWithHint( $"##new_{group.GroupName}_l", "Add new option...", ref newOption, 64,
ImGuiInputTextFlags.EnterReturnsTrue )
&& newOption.Length != 0 )
{
group.Options.Add( new Option()
{ OptionName = newOption, OptionDesc = "", OptionFiles = new Dictionary< Utf8RelPath, HashSet< Utf8GamePath > >() } );
_selector.SaveCurrentMod();
if( Mod!.Data.Meta.RefreshHasGroupsWithConfig() )
{
_selector.Cache.TriggerFilterReset();
}
}
}
private void DrawMultiSelectorEdit( OptionGroup group )
{
var nameBoxStart = CheckMarkSize;
var flag = Mod!.Settings.Settings[ group.GroupName ];
using var raii = DrawMultiSelectorEditBegin( group );
for( var i = 0; i < group.Options.Count; ++i )
{
var opt = group.Options[ i ];
var label = $"##{group.GroupName}_{i}";
DrawMultiSelectorCheckBox( group, i, flag, label );
ImGui.SameLine();
ImGui.SetNextItemWidth( OptionSelectionWidth );
if( ( _selectedGroup?.Options.Count ?? 0 ) == 0 )
var newName = opt.OptionName;
if( nameBoxStart == CheckMarkSize )
{
ImGui.Combo( LabelOptionSelect, ref _selectedOptionIndex, TextNoOptionAvailable, 1 );
return false;
nameBoxStart = ImGui.GetCursorPosX();
}
var group = ( OptionGroup )_selectedGroup!;
if( ImGui.Combo( LabelOptionSelect, ref _selectedOptionIndex, group.Options.Select( o => o.OptionName ).ToArray(),
group.Options.Count ) )
{
SelectOption();
}
return true;
}
private void DrawFileListTabEdit()
{
if( ImGui.BeginTabItem( LabelFileListTab ) )
{
UpdateFilenameList();
if( ImGui.IsItemHovered() )
{
ImGui.SetTooltip( _editMode ? TooltipFilesTabEdit : TooltipFilesTab );
}
ImGui.SetNextItemWidth( -1 );
if( ImGui.BeginListBox( LabelFileListHeader, AutoFillSize - Vector2.UnitY * 1.5f * ImGui.GetTextLineHeight() ) )
{
for( var i = 0; i < Mod!.Data.Resources.ModFiles.Count; ++i )
{
DrawFileAndGamePaths( i );
}
}
ImGui.EndListBox();
DrawGroupRow();
ImGui.EndTabItem();
}
else
{
_fullFilenameList = null;
}
}
private ImGuiRaii.EndStack DrawMultiSelectorEditBegin( OptionGroup group )
{
var groupName = group.GroupName;
if( ImGuiCustom.BeginFramedGroupEdit( ref groupName ) )
{
if( Penumbra.ModManager.ChangeModGroup( group.GroupName, groupName, Mod.Data ) && Mod.Data.Meta.RefreshHasGroupsWithConfig() )
{
_selector.Cache.TriggerFilterReset();
}
}
return ImGuiRaii.DeferredEnd( ImGuiCustom.EndFramedGroup );
}
private void DrawMultiSelectorEditAdd( OptionGroup group, float nameBoxStart )
{
var newOption = "";
ImGui.SetCursorPosX( nameBoxStart );
ImGui.SetNextItemWidth( MultiEditBoxWidth * ImGuiHelpers.GlobalScale );
if( ImGui.InputTextWithHint( $"##new_{group.GroupName}_l", "Add new option...", ref newOption, 64,
ImGuiInputTextFlags.EnterReturnsTrue )
&& newOption.Length != 0 )
if( ImGui.InputText( $"{label}_l", ref newName, 64, ImGuiInputTextFlags.EnterReturnsTrue ) )
{
group.Options.Add( new Option()
{ OptionName = newOption, OptionDesc = "", OptionFiles = new Dictionary< RelPath, HashSet< GamePath > >() } );
_selector.SaveCurrentMod();
if( newName.Length == 0 )
{
Penumbra.ModManager.RemoveModOption( i, group, Mod.Data );
}
else if( newName != opt.OptionName )
{
group.Options[ i ] = new Option()
{ OptionName = newName, OptionDesc = opt.OptionDesc, OptionFiles = opt.OptionFiles };
_selector.SaveCurrentMod();
}
if( Mod!.Data.Meta.RefreshHasGroupsWithConfig() )
{
_selector.Cache.TriggerFilterReset();
@ -140,244 +183,201 @@ namespace Penumbra.UI
}
}
private void DrawMultiSelectorEdit( OptionGroup group )
DrawMultiSelectorEditAdd( group, nameBoxStart );
}
private void DrawSingleSelectorEditGroup( OptionGroup group )
{
var groupName = group.GroupName;
if( ImGui.InputText( $"##{groupName}_add", ref groupName, 64, ImGuiInputTextFlags.EnterReturnsTrue ) )
{
var nameBoxStart = CheckMarkSize;
var flag = Mod!.Settings.Settings[ group.GroupName ];
using var raii = DrawMultiSelectorEditBegin( group );
for( var i = 0; i < group.Options.Count; ++i )
if( Penumbra.ModManager.ChangeModGroup( group.GroupName, groupName, Mod.Data ) && Mod.Data.Meta.RefreshHasGroupsWithConfig() )
{
var opt = group.Options[ i ];
var label = $"##{group.GroupName}_{i}";
DrawMultiSelectorCheckBox( group, i, flag, label );
ImGui.SameLine();
var newName = opt.OptionName;
if( nameBoxStart == CheckMarkSize )
{
nameBoxStart = ImGui.GetCursorPosX();
}
ImGui.SetNextItemWidth( MultiEditBoxWidth * ImGuiHelpers.GlobalScale );
if( ImGui.InputText( $"{label}_l", ref newName, 64, ImGuiInputTextFlags.EnterReturnsTrue ) )
{
if( newName.Length == 0 )
{
Penumbra.ModManager.RemoveModOption( i, group, Mod.Data );
}
else if( newName != opt.OptionName )
{
group.Options[ i ] = new Option()
{ OptionName = newName, OptionDesc = opt.OptionDesc, OptionFiles = opt.OptionFiles };
_selector.SaveCurrentMod();
}
if( Mod!.Data.Meta.RefreshHasGroupsWithConfig() )
{
_selector.Cache.TriggerFilterReset();
}
}
}
DrawMultiSelectorEditAdd( group, nameBoxStart );
}
private void DrawSingleSelectorEditGroup( OptionGroup group )
{
var groupName = group.GroupName;
if( ImGui.InputText( $"##{groupName}_add", ref groupName, 64, ImGuiInputTextFlags.EnterReturnsTrue ) )
{
if( Penumbra.ModManager.ChangeModGroup( group.GroupName, groupName, Mod.Data ) && Mod.Data.Meta.RefreshHasGroupsWithConfig() )
{
_selector.Cache.TriggerFilterReset();
}
_selector.Cache.TriggerFilterReset();
}
}
}
private float DrawSingleSelectorEdit( OptionGroup group )
private float DrawSingleSelectorEdit( OptionGroup group )
{
var oldSetting = Mod!.Settings.Settings[ group.GroupName ];
var code = oldSetting;
if( ImGuiCustom.RenameableCombo( $"##{group.GroupName}", ref code, out var newName,
group.Options.Select( x => x.OptionName ).ToArray(), group.Options.Count ) )
{
var oldSetting = Mod!.Settings.Settings[ group.GroupName ];
var code = oldSetting;
if( ImGuiCustom.RenameableCombo( $"##{group.GroupName}", ref code, out var newName,
group.Options.Select( x => x.OptionName ).ToArray(), group.Options.Count ) )
if( code == group.Options.Count )
{
if( code == group.Options.Count )
if( newName.Length > 0 )
{
if( newName.Length > 0 )
Mod.Settings.Settings[ group.GroupName ] = code;
group.Options.Add( new Option()
{
Mod.Settings.Settings[ group.GroupName ] = code;
group.Options.Add( new Option()
{
OptionName = newName,
OptionDesc = "",
OptionFiles = new Dictionary< RelPath, HashSet< GamePath > >(),
} );
_selector.SaveCurrentMod();
}
OptionName = newName,
OptionDesc = "",
OptionFiles = new Dictionary< Utf8RelPath, HashSet< Utf8GamePath > >(),
} );
_selector.SaveCurrentMod();
}
}
else
{
if( newName.Length == 0 )
{
Penumbra.ModManager.RemoveModOption( code, group, Mod.Data );
}
else
{
if( newName.Length == 0 )
if( newName != group.Options[ code ].OptionName )
{
Penumbra.ModManager.RemoveModOption( code, group, Mod.Data );
}
else
{
if( newName != group.Options[ code ].OptionName )
group.Options[ code ] = new Option()
{
group.Options[ code ] = new Option()
{
OptionName = newName, OptionDesc = group.Options[ code ].OptionDesc,
OptionFiles = group.Options[ code ].OptionFiles,
};
_selector.SaveCurrentMod();
}
OptionName = newName, OptionDesc = group.Options[ code ].OptionDesc,
OptionFiles = group.Options[ code ].OptionFiles,
};
_selector.SaveCurrentMod();
}
}
if( Mod.Data.Meta.RefreshHasGroupsWithConfig() )
{
_selector.Cache.TriggerFilterReset();
}
}
if( code != oldSetting )
if( Mod.Data.Meta.RefreshHasGroupsWithConfig() )
{
Save();
_selector.Cache.TriggerFilterReset();
}
}
if( code != oldSetting )
{
Save();
}
ImGui.SameLine();
var labelEditPos = ImGui.GetCursorPosX();
DrawSingleSelectorEditGroup( group );
return labelEditPos;
}
private void DrawAddSingleGroupField( float labelEditPos )
{
var newGroup = "";
ImGui.SetCursorPosX( labelEditPos );
if( labelEditPos == CheckMarkSize )
{
ImGui.SetNextItemWidth( MultiEditBoxWidth * ImGuiHelpers.GlobalScale );
}
if( ImGui.InputTextWithHint( LabelNewSingleGroupEdit, "Add new Single Group...", ref newGroup, 64,
ImGuiInputTextFlags.EnterReturnsTrue ) )
{
Penumbra.ModManager.ChangeModGroup( "", newGroup, Mod.Data, SelectType.Single );
// Adds empty group, so can not change filters.
}
}
private void DrawAddMultiGroupField()
{
var newGroup = "";
ImGui.SetCursorPosX( CheckMarkSize );
ImGui.SetNextItemWidth( MultiEditBoxWidth * ImGuiHelpers.GlobalScale );
if( ImGui.InputTextWithHint( LabelNewMultiGroup, "Add new Multi Group...", ref newGroup, 64,
ImGuiInputTextFlags.EnterReturnsTrue ) )
{
Penumbra.ModManager.ChangeModGroup( "", newGroup, Mod.Data, SelectType.Multi );
// Adds empty group, so can not change filters.
}
}
private void DrawGroupSelectorsEdit()
{
var labelEditPos = CheckMarkSize;
var groups = Meta.Groups.Values.ToArray();
foreach( var g in groups.Where( g => g.SelectionType == SelectType.Single ) )
{
labelEditPos = DrawSingleSelectorEdit( g );
}
DrawAddSingleGroupField( labelEditPos );
foreach( var g in groups.Where( g => g.SelectionType == SelectType.Multi ) )
{
DrawMultiSelectorEdit( g );
}
DrawAddMultiGroupField();
}
private void DrawFileSwapTabEdit()
{
if( !ImGui.BeginTabItem( LabelFileSwapTab ) )
{
return;
}
using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTabItem );
ImGui.SetNextItemWidth( -1 );
if( !ImGui.BeginListBox( LabelFileSwapHeader, AutoFillSize ) )
{
return;
}
raii.Push( ImGui.EndListBox );
var swaps = Meta.FileSwaps.Keys.ToArray();
ImGui.PushFont( UiBuilder.IconFont );
var arrowWidth = ImGui.CalcTextSize( FontAwesomeIcon.LongArrowAltRight.ToIconString() ).X;
ImGui.PopFont();
var width = ( ImGui.GetWindowWidth() - arrowWidth - 4 * ImGui.GetStyle().ItemSpacing.X ) / 2;
for( var idx = 0; idx < swaps.Length + 1; ++idx )
{
var key = idx == swaps.Length ? Utf8GamePath.Empty : swaps[ idx ];
var value = idx == swaps.Length ? FullPath.Empty : Meta.FileSwaps[ key ];
var keyString = key.ToString();
var valueString = value.ToString();
ImGui.SetNextItemWidth( width );
if( ImGui.InputTextWithHint( $"##swapLhs_{idx}", "Enter new file to be replaced...", ref keyString,
GamePath.MaxGamePathLength, ImGuiInputTextFlags.EnterReturnsTrue ) )
{
if( Utf8GamePath.FromString( keyString, out var newKey, true ) && newKey.CompareTo( key ) != 0 )
{
if( idx < swaps.Length )
{
Meta.FileSwaps.Remove( key );
}
if( !newKey.IsEmpty )
{
Meta.FileSwaps[ newKey ] = value;
}
_selector.SaveCurrentMod();
_selector.ReloadCurrentMod();
}
}
if( idx >= swaps.Length )
{
continue;
}
ImGui.SameLine();
var labelEditPos = ImGui.GetCursorPosX();
DrawSingleSelectorEditGroup( group );
ImGuiCustom.PrintIcon( FontAwesomeIcon.LongArrowAltRight );
ImGui.SameLine();
return labelEditPos;
}
private void DrawAddSingleGroupField( float labelEditPos )
{
var newGroup = "";
ImGui.SetCursorPosX( labelEditPos );
if( labelEditPos == CheckMarkSize )
ImGui.SetNextItemWidth( width );
if( ImGui.InputTextWithHint( $"##swapRhs_{idx}", "Enter new replacement path...", ref valueString,
GamePath.MaxGamePathLength,
ImGuiInputTextFlags.EnterReturnsTrue ) )
{
ImGui.SetNextItemWidth( MultiEditBoxWidth * ImGuiHelpers.GlobalScale );
}
if( ImGui.InputTextWithHint( LabelNewSingleGroupEdit, "Add new Single Group...", ref newGroup, 64,
ImGuiInputTextFlags.EnterReturnsTrue ) )
{
Penumbra.ModManager.ChangeModGroup( "", newGroup, Mod.Data, SelectType.Single );
// Adds empty group, so can not change filters.
}
}
private void DrawAddMultiGroupField()
{
var newGroup = "";
ImGui.SetCursorPosX( CheckMarkSize );
ImGui.SetNextItemWidth( MultiEditBoxWidth * ImGuiHelpers.GlobalScale );
if( ImGui.InputTextWithHint( LabelNewMultiGroup, "Add new Multi Group...", ref newGroup, 64,
ImGuiInputTextFlags.EnterReturnsTrue ) )
{
Penumbra.ModManager.ChangeModGroup( "", newGroup, Mod.Data, SelectType.Multi );
// Adds empty group, so can not change filters.
}
}
private void DrawGroupSelectorsEdit()
{
var labelEditPos = CheckMarkSize;
var groups = Meta.Groups.Values.ToArray();
foreach( var g in groups.Where( g => g.SelectionType == SelectType.Single ) )
{
labelEditPos = DrawSingleSelectorEdit( g );
}
DrawAddSingleGroupField( labelEditPos );
foreach( var g in groups.Where( g => g.SelectionType == SelectType.Multi ) )
{
DrawMultiSelectorEdit( g );
}
DrawAddMultiGroupField();
}
private void DrawFileSwapTabEdit()
{
if( !ImGui.BeginTabItem( LabelFileSwapTab ) )
{
return;
}
using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTabItem );
ImGui.SetNextItemWidth( -1 );
if( !ImGui.BeginListBox( LabelFileSwapHeader, AutoFillSize ) )
{
return;
}
raii.Push( ImGui.EndListBox );
var swaps = Meta.FileSwaps.Keys.ToArray();
ImGui.PushFont( UiBuilder.IconFont );
var arrowWidth = ImGui.CalcTextSize( FontAwesomeIcon.LongArrowAltRight.ToIconString() ).X;
ImGui.PopFont();
var width = ( ImGui.GetWindowWidth() - arrowWidth - 4 * ImGui.GetStyle().ItemSpacing.X ) / 2;
for( var idx = 0; idx < swaps.Length + 1; ++idx )
{
var key = idx == swaps.Length ? GamePath.GenerateUnchecked( "" ) : swaps[ idx ];
var value = idx == swaps.Length ? GamePath.GenerateUnchecked( "" ) : Meta.FileSwaps[ key ];
string keyString = key;
string valueString = value;
ImGui.SetNextItemWidth( width );
if( ImGui.InputTextWithHint( $"##swapLhs_{idx}", "Enter new file to be replaced...", ref keyString,
GamePath.MaxGamePathLength, ImGuiInputTextFlags.EnterReturnsTrue ) )
var newValue = new FullPath( valueString.ToLowerInvariant() );
if( newValue.CompareTo( value ) != 0 )
{
var newKey = new GamePath( keyString );
if( newKey.CompareTo( key ) != 0 )
{
if( idx < swaps.Length )
{
Meta.FileSwaps.Remove( key );
}
if( newKey != string.Empty )
{
Meta.FileSwaps[ newKey ] = value;
}
_selector.SaveCurrentMod();
_selector.ReloadCurrentMod();
}
}
if( idx >= swaps.Length )
{
continue;
}
ImGui.SameLine();
ImGuiCustom.PrintIcon( FontAwesomeIcon.LongArrowAltRight );
ImGui.SameLine();
ImGui.SetNextItemWidth( width );
if( ImGui.InputTextWithHint( $"##swapRhs_{idx}", "Enter new replacement path...", ref valueString,
GamePath.MaxGamePathLength,
ImGuiInputTextFlags.EnterReturnsTrue ) )
{
var newValue = new GamePath( valueString );
if( newValue.CompareTo( value ) != 0 )
{
Meta.FileSwaps[ key ] = newValue;
_selector.SaveCurrentMod();
_selector.Cache.TriggerListReset();
}
Meta.FileSwaps[ key ] = newValue;
_selector.SaveCurrentMod();
_selector.Cache.TriggerListReset();
}
}
}

View file

@ -488,7 +488,7 @@ public partial class SettingsInterface
using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup );
if( ModPanel.DrawSortOrder( mod.Data, _modManager, this ) )
if( ModPanel.DrawSortOrder( mod.Data, Penumbra.ModManager, this ) )
{
ImGui.CloseCurrentPopup();
}
@ -509,7 +509,7 @@ public partial class SettingsInterface
{
var change = false;
var metaManips = false;
foreach( var _ in folder.AllMods( _modManager.Config.SortFoldersFirst ) )
foreach( var _ in folder.AllMods( Penumbra.ModManager.Config.SortFoldersFirst ) )
{
var (mod, _, _) = Cache.GetMod( currentIdx++ );
if( mod != null )

View file

@ -3,8 +3,10 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Text.RegularExpressions;
using Dalamud.Interface;
using Dalamud.Interface.Components;
using Dalamud.Logging;
using ImGuiNET;
using Penumbra.GameData.ByteString;
using Penumbra.Interop;
@ -131,7 +133,7 @@ public partial class SettingsInterface
private void DrawEnabledBox()
{
var enabled = _config.IsEnabled;
var enabled = _config.EnableMods;
if( ImGui.Checkbox( "Enable Mods", ref enabled ) )
{
_base._penumbra.SetEnabled( enabled );
@ -317,14 +319,84 @@ public partial class SettingsInterface
+ "You usually should not need to do this." );
}
private void DrawEnableFullResourceLoggingBox()
{
var tmp = _config.EnableFullResourceLogging;
if( ImGui.Checkbox( "Enable Full Resource Logging", ref tmp ) && tmp != _config.EnableFullResourceLogging )
{
if( tmp )
{
_base._penumbra.ResourceLoader.EnableFullLogging();
}
else
{
_base._penumbra.ResourceLoader.DisableFullLogging();
}
_config.EnableFullResourceLogging = tmp;
_configChanged = true;
}
ImGui.SameLine();
ImGuiComponents.HelpMarker( "[DEBUG] Enable the logging of all ResourceLoader events indiscriminately." );
}
private void DrawEnableDebugModeBox()
{
var tmp = _config.DebugMode;
if( ImGui.Checkbox( "Enable Debug Mode", ref tmp ) && tmp != _config.DebugMode )
{
if( tmp )
{
_base._penumbra.ResourceLoader.EnableDebug();
}
else
{
_base._penumbra.ResourceLoader.DisableDebug();
}
_config.DebugMode = tmp;
_configChanged = true;
}
ImGui.SameLine();
ImGuiComponents.HelpMarker( "[DEBUG] Enable the Debug Tab and Resource Manager Tab as well as some additional data collection." );
}
private void DrawRequestedResourceLogging()
{
var tmp = _config.EnableResourceLogging;
if( ImGui.Checkbox( "Enable Requested Resource Logging", ref tmp ) )
{
_base._penumbra.ResourceLogger.SetState( tmp );
}
ImGui.SameLine();
ImGuiComponents.HelpMarker( "Log all game paths FFXIV requests to the plugin log.\n"
+ "You can filter the logged paths for those containing the entered string or matching the regex, if the entered string compiles to a valid regex.\n"
+ "Red boundary indicates invalid regex." );
ImGui.SameLine();
var tmpString = Penumbra.Config.ResourceLoggingFilter;
using var color = ImGuiRaii.PushColor( ImGuiCol.Border, 0xFF0000B0, !_base._penumbra.ResourceLogger.ValidRegex );
using var style = ImGuiRaii.PushStyle( ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale,
!_base._penumbra.ResourceLogger.ValidRegex );
if( ImGui.InputTextWithHint( "##ResourceLogFilter", "Filter...", ref tmpString, Utf8GamePath.MaxGamePathLength ) )
{
_base._penumbra.ResourceLogger.SetFilter( tmpString );
}
}
private void DrawAdvancedSettings()
{
DrawTempFolder();
DrawRequestedResourceLogging();
DrawDisableSoundStreamingBox();
DrawLogLoadedFilesBox();
DrawDisableNotificationsBox();
DrawEnableHttpApiBox();
DrawReloadResourceButton();
DrawEnableDebugModeBox();
DrawEnableFullResourceLoggingBox();
}
public static unsafe void Text( Utf8String s )

View file

@ -1,9 +1,7 @@
using System.Numerics;
using Dalamud.Interface;
using ImGuiNET;
using Penumbra.Mods;
using Penumbra.UI.Custom;
using Penumbra.Util;
namespace Penumbra.UI;

View file

@ -5,36 +5,35 @@ using Lumina.Excel.GeneratedSheets;
using Penumbra.GameData.Enums;
using Penumbra.UI.Custom;
namespace Penumbra.UI
namespace Penumbra.UI;
public partial class SettingsInterface
{
public partial class SettingsInterface
internal void DrawChangedItem( string name, object? data, float itemIdOffset = 0 )
{
internal void DrawChangedItem( string name, object? data, float itemIdOffset = 0)
var ret = ImGui.Selectable( name ) ? MouseButton.Left : MouseButton.None;
ret = ImGui.IsItemClicked( ImGuiMouseButton.Right ) ? MouseButton.Right : ret;
ret = ImGui.IsItemClicked( ImGuiMouseButton.Middle ) ? MouseButton.Middle : ret;
if( ret != MouseButton.None )
{
var ret = ImGui.Selectable( name ) ? MouseButton.Left : MouseButton.None;
ret = ImGui.IsItemClicked( ImGuiMouseButton.Right ) ? MouseButton.Right : ret;
ret = ImGui.IsItemClicked( ImGuiMouseButton.Middle ) ? MouseButton.Middle : ret;
_penumbra.Api.InvokeClick( ret, data );
}
if( ret != MouseButton.None )
{
_penumbra.Api.InvokeClick( ret, data );
}
if( _penumbra.Api.HasTooltip && ImGui.IsItemHovered() )
{
ImGui.BeginTooltip();
using var tooltip = ImGuiRaii.DeferredEnd( ImGui.EndTooltip );
_penumbra.Api.InvokeTooltip( data );
}
if( _penumbra.Api.HasTooltip && ImGui.IsItemHovered() )
{
ImGui.BeginTooltip();
using var tooltip = ImGuiRaii.DeferredEnd( ImGui.EndTooltip );
_penumbra.Api.InvokeTooltip( data );
}
if( data is Item it )
{
var modelId = $"({( ( Quad )it.ModelMain ).A})";
var offset = ImGui.CalcTextSize( modelId ).X - ImGui.GetStyle().ItemInnerSpacing.X + itemIdOffset;
if( data is Item it )
{
var modelId = $"({( ( Quad )it.ModelMain ).A})";
var offset = ImGui.CalcTextSize( modelId ).X - ImGui.GetStyle().ItemInnerSpacing.X + itemIdOffset;
ImGui.SameLine( ImGui.GetWindowContentRegionWidth() - offset );
ImGui.TextColored( new Vector4( 0.5f, 0.5f, 0.5f, 1 ), modelId );
}
ImGui.SameLine( ImGui.GetWindowContentRegionWidth() - offset );
ImGui.TextColored( new Vector4( 0.5f, 0.5f, 0.5f, 1 ), modelId );
}
}
}