mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-02-19 06:17:45 +01:00
This is going rather well.
This commit is contained in:
parent
73e2793da6
commit
bdaff7b781
48 changed files with 2944 additions and 2952 deletions
|
|
@ -17,6 +17,7 @@ using Penumbra.GameData.Enums;
|
|||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Mods.ItemSwap;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.UI.Classes;
|
||||
|
|
@ -42,49 +43,61 @@ public class ItemSwapWindow : IDisposable
|
|||
Weapon,
|
||||
}
|
||||
|
||||
private class ItemSelector : FilterComboCache< (string, Item) >
|
||||
private class ItemSelector : FilterComboCache<(string, Item)>
|
||||
{
|
||||
public ItemSelector( FullEquipType type )
|
||||
: base( () => Penumbra.ItemData[ type ].Select( i => ( i.Name.ToDalamudString().TextValue, i ) ).ToArray() )
|
||||
public ItemSelector(FullEquipType type)
|
||||
: base(() => Penumbra.ItemData[type].Select(i => (i.Name.ToDalamudString().TextValue, i)).ToArray())
|
||||
{ }
|
||||
|
||||
protected override string ToString( (string, Item) obj )
|
||||
protected override string ToString((string, Item) obj)
|
||||
=> obj.Item1;
|
||||
}
|
||||
|
||||
private class WeaponSelector : FilterComboCache< FullEquipType >
|
||||
private class WeaponSelector : FilterComboCache<FullEquipType>
|
||||
{
|
||||
public WeaponSelector()
|
||||
: base( FullEquipTypeExtensions.WeaponTypes.Concat( FullEquipTypeExtensions.ToolTypes ) )
|
||||
: base(FullEquipTypeExtensions.WeaponTypes.Concat(FullEquipTypeExtensions.ToolTypes))
|
||||
{ }
|
||||
|
||||
protected override string ToString( FullEquipType type )
|
||||
protected override string ToString(FullEquipType type)
|
||||
=> type.ToName();
|
||||
}
|
||||
|
||||
public ItemSwapWindow()
|
||||
private readonly CommunicatorService _communicator;
|
||||
|
||||
public ItemSwapWindow(CommunicatorService communicator)
|
||||
{
|
||||
Penumbra.CollectionManager.CollectionChanged += OnCollectionChange;
|
||||
_communicator = communicator;
|
||||
_communicator.CollectionChange.Event += OnCollectionChange;
|
||||
Penumbra.CollectionManager.Current.ModSettingChanged += OnSettingChange;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Penumbra.CollectionManager.CollectionChanged += OnCollectionChange;
|
||||
_communicator.CollectionChange.Event -= OnCollectionChange;
|
||||
Penumbra.CollectionManager.Current.ModSettingChanged -= OnSettingChange;
|
||||
}
|
||||
|
||||
private readonly Dictionary< SwapType, (ItemSelector Source, ItemSelector Target, string TextFrom, string TextTo) > _selectors = new()
|
||||
private readonly Dictionary<SwapType, (ItemSelector Source, ItemSelector Target, string TextFrom, string TextTo)> _selectors = new()
|
||||
{
|
||||
[ SwapType.Hat ] = ( new ItemSelector( FullEquipType.Head ), new ItemSelector( FullEquipType.Head ), "Take this Hat", "and put it on this one" ),
|
||||
[ SwapType.Top ] = ( new ItemSelector( FullEquipType.Body ), new ItemSelector( FullEquipType.Body ), "Take this Top", "and put it on this one" ),
|
||||
[ SwapType.Gloves ] = ( new ItemSelector( FullEquipType.Hands ), new ItemSelector( FullEquipType.Hands ), "Take these Gloves", "and put them on these" ),
|
||||
[ SwapType.Pants ] = ( new ItemSelector( FullEquipType.Legs ), new ItemSelector( FullEquipType.Legs ), "Take these Pants", "and put them on these" ),
|
||||
[ SwapType.Shoes ] = ( new ItemSelector( FullEquipType.Feet ), new ItemSelector( FullEquipType.Feet ), "Take these Shoes", "and put them on these" ),
|
||||
[ SwapType.Earrings ] = ( new ItemSelector( FullEquipType.Ears ), new ItemSelector( FullEquipType.Ears ), "Take these Earrings", "and put them on these" ),
|
||||
[ SwapType.Necklace ] = ( new ItemSelector( FullEquipType.Neck ), new ItemSelector( FullEquipType.Neck ), "Take this Necklace", "and put it on this one" ),
|
||||
[ SwapType.Bracelet ] = ( new ItemSelector( FullEquipType.Wrists ), new ItemSelector( FullEquipType.Wrists ), "Take these Bracelets", "and put them on these" ),
|
||||
[ SwapType.Ring ] = ( new ItemSelector( FullEquipType.Finger ), new ItemSelector( FullEquipType.Finger ), "Take this Ring", "and put it on this one" ),
|
||||
[SwapType.Hat] =
|
||||
(new ItemSelector(FullEquipType.Head), new ItemSelector(FullEquipType.Head), "Take this Hat", "and put it on this one"),
|
||||
[SwapType.Top] =
|
||||
(new ItemSelector(FullEquipType.Body), new ItemSelector(FullEquipType.Body), "Take this Top", "and put it on this one"),
|
||||
[SwapType.Gloves] =
|
||||
(new ItemSelector(FullEquipType.Hands), new ItemSelector(FullEquipType.Hands), "Take these Gloves", "and put them on these"),
|
||||
[SwapType.Pants] =
|
||||
(new ItemSelector(FullEquipType.Legs), new ItemSelector(FullEquipType.Legs), "Take these Pants", "and put them on these"),
|
||||
[SwapType.Shoes] =
|
||||
(new ItemSelector(FullEquipType.Feet), new ItemSelector(FullEquipType.Feet), "Take these Shoes", "and put them on these"),
|
||||
[SwapType.Earrings] =
|
||||
(new ItemSelector(FullEquipType.Ears), new ItemSelector(FullEquipType.Ears), "Take these Earrings", "and put them on these"),
|
||||
[SwapType.Necklace] =
|
||||
(new ItemSelector(FullEquipType.Neck), new ItemSelector(FullEquipType.Neck), "Take this Necklace", "and put it on this one"),
|
||||
[SwapType.Bracelet] =
|
||||
(new ItemSelector(FullEquipType.Wrists), new ItemSelector(FullEquipType.Wrists), "Take these Bracelets", "and put them on these"),
|
||||
[SwapType.Ring] = (new ItemSelector(FullEquipType.Finger), new ItemSelector(FullEquipType.Finger), "Take this Ring",
|
||||
"and put it on this one"),
|
||||
};
|
||||
|
||||
private ItemSelector? _weaponSource = null;
|
||||
|
|
@ -117,39 +130,33 @@ public class ItemSwapWindow : IDisposable
|
|||
|
||||
private Item[]? _affectedItems;
|
||||
|
||||
public void UpdateMod( Mod mod, ModSettings? settings )
|
||||
public void UpdateMod(Mod mod, ModSettings? settings)
|
||||
{
|
||||
if( mod == _mod && settings == _modSettings )
|
||||
{
|
||||
if (mod == _mod && settings == _modSettings)
|
||||
return;
|
||||
}
|
||||
|
||||
var oldDefaultName = $"{_mod?.Name.Text ?? "Unknown"} (Swapped)";
|
||||
if( _newModName.Length == 0 || oldDefaultName == _newModName )
|
||||
{
|
||||
if (_newModName.Length == 0 || oldDefaultName == _newModName)
|
||||
_newModName = $"{mod.Name.Text} (Swapped)";
|
||||
}
|
||||
|
||||
_mod = mod;
|
||||
_modSettings = settings;
|
||||
_swapData.LoadMod( _mod, _modSettings );
|
||||
_swapData.LoadMod(_mod, _modSettings);
|
||||
UpdateOption();
|
||||
_dirty = true;
|
||||
}
|
||||
|
||||
private void UpdateState()
|
||||
{
|
||||
if( !_dirty )
|
||||
{
|
||||
if (!_dirty)
|
||||
return;
|
||||
}
|
||||
|
||||
_swapData.Clear();
|
||||
_loadException = null;
|
||||
_affectedItems = null;
|
||||
try
|
||||
{
|
||||
switch( _lastTab )
|
||||
switch (_lastTab)
|
||||
{
|
||||
case SwapType.Hat:
|
||||
case SwapType.Top:
|
||||
|
|
@ -178,27 +185,31 @@ public class ItemSwapWindow : IDisposable
|
|||
}
|
||||
break;
|
||||
case SwapType.Hair when _targetId > 0 && _sourceId > 0:
|
||||
_swapData.LoadCustomization( BodySlot.Hair, Names.CombinedRace( _currentGender, _currentRace ), ( SetId )_sourceId, ( SetId )_targetId,
|
||||
_useCurrentCollection ? Penumbra.CollectionManager.Current : null );
|
||||
_swapData.LoadCustomization(BodySlot.Hair, Names.CombinedRace(_currentGender, _currentRace), (SetId)_sourceId,
|
||||
(SetId)_targetId,
|
||||
_useCurrentCollection ? Penumbra.CollectionManager.Current : null);
|
||||
break;
|
||||
case SwapType.Face when _targetId > 0 && _sourceId > 0:
|
||||
_swapData.LoadCustomization( BodySlot.Face, Names.CombinedRace( _currentGender, _currentRace ), ( SetId )_sourceId, ( SetId )_targetId,
|
||||
_useCurrentCollection ? Penumbra.CollectionManager.Current : null );
|
||||
_swapData.LoadCustomization(BodySlot.Face, Names.CombinedRace(_currentGender, _currentRace), (SetId)_sourceId,
|
||||
(SetId)_targetId,
|
||||
_useCurrentCollection ? Penumbra.CollectionManager.Current : null);
|
||||
break;
|
||||
case SwapType.Ears when _targetId > 0 && _sourceId > 0:
|
||||
_swapData.LoadCustomization( BodySlot.Zear, Names.CombinedRace( _currentGender, ModelRace.Viera ), ( SetId )_sourceId, ( SetId )_targetId,
|
||||
_useCurrentCollection ? Penumbra.CollectionManager.Current : null );
|
||||
_swapData.LoadCustomization(BodySlot.Zear, Names.CombinedRace(_currentGender, ModelRace.Viera), (SetId)_sourceId,
|
||||
(SetId)_targetId,
|
||||
_useCurrentCollection ? Penumbra.CollectionManager.Current : null);
|
||||
break;
|
||||
case SwapType.Tail when _targetId > 0 && _sourceId > 0:
|
||||
_swapData.LoadCustomization( BodySlot.Tail, Names.CombinedRace( _currentGender, _currentRace ), ( SetId )_sourceId, ( SetId )_targetId,
|
||||
_useCurrentCollection ? Penumbra.CollectionManager.Current : null );
|
||||
_swapData.LoadCustomization(BodySlot.Tail, Names.CombinedRace(_currentGender, _currentRace), (SetId)_sourceId,
|
||||
(SetId)_targetId,
|
||||
_useCurrentCollection ? Penumbra.CollectionManager.Current : null);
|
||||
break;
|
||||
case SwapType.Weapon: break;
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error( $"Could not get Customization Data container for {_lastTab}:\n{e}" );
|
||||
Penumbra.Log.Error($"Could not get Customization Data container for {_lastTab}:\n{e}");
|
||||
_loadException = e;
|
||||
_affectedItems = null;
|
||||
_swapData.Clear();
|
||||
|
|
@ -207,13 +218,14 @@ public class ItemSwapWindow : IDisposable
|
|||
_dirty = false;
|
||||
}
|
||||
|
||||
private static string SwapToString( Swap swap )
|
||||
private static string SwapToString(Swap swap)
|
||||
{
|
||||
return swap switch
|
||||
{
|
||||
MetaSwap meta => $"{meta.SwapFrom}: {meta.SwapFrom.EntryToString()} -> {meta.SwapApplied.EntryToString()}",
|
||||
FileSwap file => $"{file.Type}: {file.SwapFromRequestPath} -> {file.SwapToModded.FullName}{( file.DataWasChanged ? " (EDITED)" : string.Empty )}",
|
||||
_ => string.Empty,
|
||||
FileSwap file =>
|
||||
$"{file.Type}: {file.SwapFromRequestPath} -> {file.SwapToModded.FullName}{(file.DataWasChanged ? " (EDITED)" : string.Empty)}",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -222,28 +234,28 @@ public class ItemSwapWindow : IDisposable
|
|||
|
||||
private void UpdateOption()
|
||||
{
|
||||
_selectedGroup = _mod?.Groups.FirstOrDefault( g => g.Name == _newGroupName );
|
||||
_subModValid = _mod != null && _newGroupName.Length > 0 && _newOptionName.Length > 0 && ( _selectedGroup?.All( o => o.Name != _newOptionName ) ?? true );
|
||||
_selectedGroup = _mod?.Groups.FirstOrDefault(g => g.Name == _newGroupName);
|
||||
_subModValid = _mod != null
|
||||
&& _newGroupName.Length > 0
|
||||
&& _newOptionName.Length > 0
|
||||
&& (_selectedGroup?.All(o => o.Name != _newOptionName) ?? true);
|
||||
}
|
||||
|
||||
private void CreateMod()
|
||||
{
|
||||
var newDir = Mod.Creator.CreateModFolder( Penumbra.ModManager.BasePath, _newModName );
|
||||
Mod.Creator.CreateMeta( newDir, _newModName, Penumbra.Config.DefaultModAuthor, CreateDescription(), "1.0", string.Empty );
|
||||
Mod.Creator.CreateDefaultFiles( newDir );
|
||||
Penumbra.ModManager.AddMod( newDir );
|
||||
if( !_swapData.WriteMod( Penumbra.ModManager.Last(), _useFileSwaps ? ItemSwapContainer.WriteType.UseSwaps : ItemSwapContainer.WriteType.NoSwaps ) )
|
||||
{
|
||||
Penumbra.ModManager.DeleteMod( Penumbra.ModManager.Count - 1 );
|
||||
}
|
||||
var newDir = Mod.Creator.CreateModFolder(Penumbra.ModManager.BasePath, _newModName);
|
||||
Mod.Creator.CreateMeta(newDir, _newModName, Penumbra.Config.DefaultModAuthor, CreateDescription(), "1.0", string.Empty);
|
||||
Mod.Creator.CreateDefaultFiles(newDir);
|
||||
Penumbra.ModManager.AddMod(newDir);
|
||||
if (!_swapData.WriteMod(Penumbra.ModManager.Last(),
|
||||
_useFileSwaps ? ItemSwapContainer.WriteType.UseSwaps : ItemSwapContainer.WriteType.NoSwaps))
|
||||
Penumbra.ModManager.DeleteMod(Penumbra.ModManager.Count - 1);
|
||||
}
|
||||
|
||||
private void CreateOption()
|
||||
{
|
||||
if( _mod == null || !_subModValid )
|
||||
{
|
||||
if (_mod == null || !_subModValid)
|
||||
return;
|
||||
}
|
||||
|
||||
var groupCreated = false;
|
||||
var dirCreated = false;
|
||||
|
|
@ -251,52 +263,47 @@ public class ItemSwapWindow : IDisposable
|
|||
DirectoryInfo? optionFolderName = null;
|
||||
try
|
||||
{
|
||||
optionFolderName = Mod.Creator.NewSubFolderName( new DirectoryInfo( Path.Combine( _mod.ModPath.FullName, _selectedGroup?.Name ?? _newGroupName ) ), _newOptionName );
|
||||
if( optionFolderName?.Exists == true )
|
||||
{
|
||||
throw new Exception( $"The folder {optionFolderName.FullName} for the option already exists." );
|
||||
}
|
||||
optionFolderName =
|
||||
Mod.Creator.NewSubFolderName(new DirectoryInfo(Path.Combine(_mod.ModPath.FullName, _selectedGroup?.Name ?? _newGroupName)),
|
||||
_newOptionName);
|
||||
if (optionFolderName?.Exists == true)
|
||||
throw new Exception($"The folder {optionFolderName.FullName} for the option already exists.");
|
||||
|
||||
if( optionFolderName != null )
|
||||
if (optionFolderName != null)
|
||||
{
|
||||
if( _selectedGroup == null )
|
||||
if (_selectedGroup == null)
|
||||
{
|
||||
Penumbra.ModManager.AddModGroup( _mod, GroupType.Multi, _newGroupName );
|
||||
Penumbra.ModManager.AddModGroup(_mod, GroupType.Multi, _newGroupName);
|
||||
_selectedGroup = _mod.Groups.Last();
|
||||
groupCreated = true;
|
||||
}
|
||||
|
||||
Penumbra.ModManager.AddOption( _mod, _mod.Groups.IndexOf( _selectedGroup ), _newOptionName );
|
||||
Penumbra.ModManager.AddOption(_mod, _mod.Groups.IndexOf(_selectedGroup), _newOptionName);
|
||||
optionCreated = true;
|
||||
optionFolderName = Directory.CreateDirectory( optionFolderName.FullName );
|
||||
optionFolderName = Directory.CreateDirectory(optionFolderName.FullName);
|
||||
dirCreated = true;
|
||||
if( !_swapData.WriteMod( _mod, _useFileSwaps ? ItemSwapContainer.WriteType.UseSwaps : ItemSwapContainer.WriteType.NoSwaps, optionFolderName,
|
||||
_mod.Groups.IndexOf( _selectedGroup ), _selectedGroup.Count - 1 ) )
|
||||
{
|
||||
throw new Exception( "Failure writing files for mod swap." );
|
||||
}
|
||||
if (!_swapData.WriteMod(_mod, _useFileSwaps ? ItemSwapContainer.WriteType.UseSwaps : ItemSwapContainer.WriteType.NoSwaps,
|
||||
optionFolderName,
|
||||
_mod.Groups.IndexOf(_selectedGroup), _selectedGroup.Count - 1))
|
||||
throw new Exception("Failure writing files for mod swap.");
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
catch (Exception e)
|
||||
{
|
||||
ChatUtil.NotificationMessage( $"Could not create new Swap Option:\n{e}", "Error", NotificationType.Error );
|
||||
ChatUtil.NotificationMessage($"Could not create new Swap Option:\n{e}", "Error", NotificationType.Error);
|
||||
try
|
||||
{
|
||||
if( optionCreated && _selectedGroup != null )
|
||||
{
|
||||
Penumbra.ModManager.DeleteOption( _mod, _mod.Groups.IndexOf( _selectedGroup ), _selectedGroup.Count - 1 );
|
||||
}
|
||||
if (optionCreated && _selectedGroup != null)
|
||||
Penumbra.ModManager.DeleteOption(_mod, _mod.Groups.IndexOf(_selectedGroup), _selectedGroup.Count - 1);
|
||||
|
||||
if( groupCreated )
|
||||
if (groupCreated)
|
||||
{
|
||||
Penumbra.ModManager.DeleteModGroup( _mod, _mod.Groups.IndexOf( _selectedGroup! ) );
|
||||
Penumbra.ModManager.DeleteModGroup(_mod, _mod.Groups.IndexOf(_selectedGroup!));
|
||||
_selectedGroup = null;
|
||||
}
|
||||
|
||||
if( dirCreated && optionFolderName != null )
|
||||
{
|
||||
Directory.Delete( optionFolderName.FullName, true );
|
||||
}
|
||||
if (dirCreated && optionFolderName != null)
|
||||
Directory.Delete(optionFolderName.FullName, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
@ -307,12 +314,12 @@ public class ItemSwapWindow : IDisposable
|
|||
UpdateOption();
|
||||
}
|
||||
|
||||
private void DrawHeaderLine( float width )
|
||||
private void DrawHeaderLine(float width)
|
||||
{
|
||||
var newModAvailable = _loadException == null && _swapData.Loaded;
|
||||
|
||||
ImGui.SetNextItemWidth( width );
|
||||
if( ImGui.InputTextWithHint( "##newModName", "New Mod Name...", ref _newModName, 64 ) )
|
||||
ImGui.SetNextItemWidth(width);
|
||||
if (ImGui.InputTextWithHint("##newModName", "New Mod Name...", ref _newModName, 64))
|
||||
{ }
|
||||
|
||||
ImGui.SameLine();
|
||||
|
|
@ -321,29 +328,23 @@ public class ItemSwapWindow : IDisposable
|
|||
: _newModName.Length == 0
|
||||
? "Please enter a name for your mod."
|
||||
: "Create a new mod of the given name containing only the swap.";
|
||||
if( ImGuiUtil.DrawDisabledButton( "Create New Mod", new Vector2( width / 2, 0 ), tt, !newModAvailable || _newModName.Length == 0 ) )
|
||||
{
|
||||
if (ImGuiUtil.DrawDisabledButton("Create New Mod", new Vector2(width / 2, 0), tt, !newModAvailable || _newModName.Length == 0))
|
||||
CreateMod();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPosX( ImGui.GetCursorPosX() + 20 * ImGuiHelpers.GlobalScale );
|
||||
ImGui.Checkbox( "Use File Swaps", ref _useFileSwaps );
|
||||
ImGuiUtil.HoverTooltip( "Instead of writing every single non-default file to the newly created mod or option,\n"
|
||||
+ "even those available from game files, use File Swaps to default game files where possible." );
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 20 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.Checkbox("Use File Swaps", ref _useFileSwaps);
|
||||
ImGuiUtil.HoverTooltip("Instead of writing every single non-default file to the newly created mod or option,\n"
|
||||
+ "even those available from game files, use File Swaps to default game files where possible.");
|
||||
|
||||
ImGui.SetNextItemWidth( ( width - ImGui.GetStyle().ItemSpacing.X ) / 2 );
|
||||
if( ImGui.InputTextWithHint( "##groupName", "Group Name...", ref _newGroupName, 32 ) )
|
||||
{
|
||||
ImGui.SetNextItemWidth((width - ImGui.GetStyle().ItemSpacing.X) / 2);
|
||||
if (ImGui.InputTextWithHint("##groupName", "Group Name...", ref _newGroupName, 32))
|
||||
UpdateOption();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.SetNextItemWidth( ( width - ImGui.GetStyle().ItemSpacing.X ) / 2 );
|
||||
if( ImGui.InputTextWithHint( "##optionName", "New Option Name...", ref _newOptionName, 32 ) )
|
||||
{
|
||||
ImGui.SetNextItemWidth((width - ImGui.GetStyle().ItemSpacing.X) / 2);
|
||||
if (ImGui.InputTextWithHint("##optionName", "New Option Name...", ref _newOptionName, 32))
|
||||
UpdateOption();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
tt = !_subModValid
|
||||
|
|
@ -351,16 +352,15 @@ public class ItemSwapWindow : IDisposable
|
|||
: !newModAvailable
|
||||
? "Create a new option inside this mod containing only the swap."
|
||||
: "Create a new option (and possibly Multi-Group) inside the currently selected mod containing the swap.";
|
||||
if( ImGuiUtil.DrawDisabledButton( "Create New Option", new Vector2( width / 2, 0 ), tt, !newModAvailable || !_subModValid ) )
|
||||
{
|
||||
if (ImGuiUtil.DrawDisabledButton("Create New Option", new Vector2(width / 2, 0), tt, !newModAvailable || !_subModValid))
|
||||
CreateOption();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPosX( ImGui.GetCursorPosX() + 20 * ImGuiHelpers.GlobalScale );
|
||||
_dirty |= ImGui.Checkbox( "Use Entire Collection", ref _useCurrentCollection );
|
||||
ImGuiUtil.HoverTooltip( "Use all applied mods from the Selected Collection with their current settings and respecting the enabled state of mods and inheritance,\n"
|
||||
+ "instead of using only the selected mod with its current settings in the Selected collection or the default settings, ignoring the enabled state and inheritance." );
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 20 * ImGuiHelpers.GlobalScale);
|
||||
_dirty |= ImGui.Checkbox("Use Entire Collection", ref _useCurrentCollection);
|
||||
ImGuiUtil.HoverTooltip(
|
||||
"Use all applied mods from the Selected Collection with their current settings and respecting the enabled state of mods and inheritance,\n"
|
||||
+ "instead of using only the selected mod with its current settings in the Selected collection or the default settings, ignoring the enabled state and inheritance.");
|
||||
}
|
||||
|
||||
private void DrawSwapBar()
|
||||
|
|
@ -491,61 +491,58 @@ public class ItemSwapWindow : IDisposable
|
|||
return (article1, article2, source ? tuple.Source : tuple.Target);
|
||||
}
|
||||
|
||||
private void DrawEquipmentSwap( SwapType type )
|
||||
private void DrawEquipmentSwap(SwapType type)
|
||||
{
|
||||
using var tab = DrawTab( type );
|
||||
if( !tab )
|
||||
{
|
||||
using var tab = DrawTab(type);
|
||||
if (!tab)
|
||||
return;
|
||||
}
|
||||
|
||||
var (sourceSelector, targetSelector, text1, text2) = _selectors[ type ];
|
||||
using var table = ImRaii.Table( "##settings", 2, ImGuiTableFlags.SizingFixedFit );
|
||||
var (sourceSelector, targetSelector, text1, text2) = _selectors[type];
|
||||
using var table = ImRaii.Table("##settings", 2, ImGuiTableFlags.SizingFixedFit);
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted( text1 );
|
||||
ImGui.TextUnformatted(text1);
|
||||
ImGui.TableNextColumn();
|
||||
_dirty |= sourceSelector.Draw( "##itemSource", sourceSelector.CurrentSelection.Item1 ?? string.Empty, string.Empty, InputWidth * 2, ImGui.GetTextLineHeightWithSpacing() );
|
||||
_dirty |= sourceSelector.Draw("##itemSource", sourceSelector.CurrentSelection.Item1 ?? string.Empty, string.Empty, InputWidth * 2,
|
||||
ImGui.GetTextLineHeightWithSpacing());
|
||||
|
||||
if( type == SwapType.Ring )
|
||||
if (type == SwapType.Ring)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
_dirty |= ImGui.Checkbox( "Swap Right Ring", ref _useRightRing );
|
||||
_dirty |= ImGui.Checkbox("Swap Right Ring", ref _useRightRing);
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted( text2 );
|
||||
ImGui.TextUnformatted(text2);
|
||||
ImGui.TableNextColumn();
|
||||
_dirty |= targetSelector.Draw( "##itemTarget", targetSelector.CurrentSelection.Item1 ?? string.Empty, string.Empty, InputWidth * 2, ImGui.GetTextLineHeightWithSpacing() );
|
||||
if( type == SwapType.Ring )
|
||||
_dirty |= targetSelector.Draw("##itemTarget", targetSelector.CurrentSelection.Item1 ?? string.Empty, string.Empty, InputWidth * 2,
|
||||
ImGui.GetTextLineHeightWithSpacing());
|
||||
if (type == SwapType.Ring)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
_dirty |= ImGui.Checkbox( "Swap Left Ring", ref _useLeftRing );
|
||||
_dirty |= ImGui.Checkbox("Swap Left Ring", ref _useLeftRing);
|
||||
}
|
||||
|
||||
if( _affectedItems is { Length: > 1 } )
|
||||
if (_affectedItems is { Length: > 1 })
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGuiUtil.DrawTextButton( $"which will also affect {_affectedItems.Length - 1} other Items.", Vector2.Zero, Colors.PressEnterWarningBg );
|
||||
if( ImGui.IsItemHovered() )
|
||||
{
|
||||
ImGui.SetTooltip( string.Join( '\n', _affectedItems.Where( i => !ReferenceEquals( i, targetSelector.CurrentSelection.Item2 ) )
|
||||
.Select( i => i.Name.ToDalamudString().TextValue ) ) );
|
||||
}
|
||||
ImGuiUtil.DrawTextButton($"which will also affect {_affectedItems.Length - 1} other Items.", Vector2.Zero,
|
||||
Colors.PressEnterWarningBg);
|
||||
if (ImGui.IsItemHovered())
|
||||
ImGui.SetTooltip(string.Join('\n', _affectedItems.Where(i => !ReferenceEquals(i, targetSelector.CurrentSelection.Item2))
|
||||
.Select(i => i.Name.ToDalamudString().TextValue)));
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawHairSwap()
|
||||
{
|
||||
using var tab = DrawTab( SwapType.Hair );
|
||||
if( !tab )
|
||||
{
|
||||
using var tab = DrawTab(SwapType.Hair);
|
||||
if (!tab)
|
||||
return;
|
||||
}
|
||||
|
||||
using var table = ImRaii.Table( "##settings", 2, ImGuiTableFlags.SizingFixedFit );
|
||||
DrawTargetIdInput( "Take this Hairstyle" );
|
||||
using var table = ImRaii.Table("##settings", 2, ImGuiTableFlags.SizingFixedFit);
|
||||
DrawTargetIdInput("Take this Hairstyle");
|
||||
DrawSourceIdInput();
|
||||
DrawGenderInput();
|
||||
}
|
||||
|
|
@ -553,145 +550,139 @@ public class ItemSwapWindow : IDisposable
|
|||
private void DrawFaceSwap()
|
||||
{
|
||||
using var disabled = ImRaii.Disabled();
|
||||
using var tab = DrawTab( SwapType.Face );
|
||||
if( !tab )
|
||||
{
|
||||
using var tab = DrawTab(SwapType.Face);
|
||||
if (!tab)
|
||||
return;
|
||||
}
|
||||
|
||||
using var table = ImRaii.Table( "##settings", 2, ImGuiTableFlags.SizingFixedFit );
|
||||
DrawTargetIdInput( "Take this Face Type" );
|
||||
using var table = ImRaii.Table("##settings", 2, ImGuiTableFlags.SizingFixedFit);
|
||||
DrawTargetIdInput("Take this Face Type");
|
||||
DrawSourceIdInput();
|
||||
DrawGenderInput();
|
||||
}
|
||||
|
||||
private void DrawTailSwap()
|
||||
{
|
||||
using var tab = DrawTab( SwapType.Tail );
|
||||
if( !tab )
|
||||
{
|
||||
using var tab = DrawTab(SwapType.Tail);
|
||||
if (!tab)
|
||||
return;
|
||||
}
|
||||
|
||||
using var table = ImRaii.Table( "##settings", 2, ImGuiTableFlags.SizingFixedFit );
|
||||
DrawTargetIdInput( "Take this Tail Type" );
|
||||
using var table = ImRaii.Table("##settings", 2, ImGuiTableFlags.SizingFixedFit);
|
||||
DrawTargetIdInput("Take this Tail Type");
|
||||
DrawSourceIdInput();
|
||||
DrawGenderInput( "for all", 2 );
|
||||
DrawGenderInput("for all", 2);
|
||||
}
|
||||
|
||||
|
||||
private void DrawEarSwap()
|
||||
{
|
||||
using var tab = DrawTab( SwapType.Ears );
|
||||
if( !tab )
|
||||
{
|
||||
using var tab = DrawTab(SwapType.Ears);
|
||||
if (!tab)
|
||||
return;
|
||||
}
|
||||
|
||||
using var table = ImRaii.Table( "##settings", 2, ImGuiTableFlags.SizingFixedFit );
|
||||
DrawTargetIdInput( "Take this Ear Type" );
|
||||
using var table = ImRaii.Table("##settings", 2, ImGuiTableFlags.SizingFixedFit);
|
||||
DrawTargetIdInput("Take this Ear Type");
|
||||
DrawSourceIdInput();
|
||||
DrawGenderInput( "for all Viera", 0 );
|
||||
DrawGenderInput("for all Viera", 0);
|
||||
}
|
||||
|
||||
|
||||
private void DrawWeaponSwap()
|
||||
{
|
||||
using var disabled = ImRaii.Disabled();
|
||||
using var tab = DrawTab( SwapType.Weapon );
|
||||
if( !tab )
|
||||
{
|
||||
using var tab = DrawTab(SwapType.Weapon);
|
||||
if (!tab)
|
||||
return;
|
||||
}
|
||||
|
||||
using var table = ImRaii.Table( "##settings", 2, ImGuiTableFlags.SizingFixedFit );
|
||||
using var table = ImRaii.Table("##settings", 2, ImGuiTableFlags.SizingFixedFit);
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted( "Select the weapon or tool you want" );
|
||||
ImGui.TextUnformatted("Select the weapon or tool you want");
|
||||
ImGui.TableNextColumn();
|
||||
if( _slotSelector.Draw( "##weaponSlot", _slotSelector.CurrentSelection.ToName(), string.Empty, InputWidth * 2, ImGui.GetTextLineHeightWithSpacing() ) )
|
||||
if (_slotSelector.Draw("##weaponSlot", _slotSelector.CurrentSelection.ToName(), string.Empty, InputWidth * 2,
|
||||
ImGui.GetTextLineHeightWithSpacing()))
|
||||
{
|
||||
_dirty = true;
|
||||
_weaponSource = new ItemSelector( _slotSelector.CurrentSelection );
|
||||
_weaponTarget = new ItemSelector( _slotSelector.CurrentSelection );
|
||||
_weaponSource = new ItemSelector(_slotSelector.CurrentSelection);
|
||||
_weaponTarget = new ItemSelector(_slotSelector.CurrentSelection);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dirty = _weaponSource == null || _weaponTarget == null;
|
||||
_weaponSource ??= new ItemSelector( _slotSelector.CurrentSelection );
|
||||
_weaponTarget ??= new ItemSelector( _slotSelector.CurrentSelection );
|
||||
_weaponSource ??= new ItemSelector(_slotSelector.CurrentSelection);
|
||||
_weaponTarget ??= new ItemSelector(_slotSelector.CurrentSelection);
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted( "and put this variant of it" );
|
||||
ImGui.TextUnformatted("and put this variant of it");
|
||||
ImGui.TableNextColumn();
|
||||
_dirty |= _weaponSource.Draw( "##weaponSource", _weaponSource.CurrentSelection.Item1 ?? string.Empty, string.Empty, InputWidth * 2, ImGui.GetTextLineHeightWithSpacing() );
|
||||
_dirty |= _weaponSource.Draw("##weaponSource", _weaponSource.CurrentSelection.Item1 ?? string.Empty, string.Empty, InputWidth * 2,
|
||||
ImGui.GetTextLineHeightWithSpacing());
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted( "onto this one" );
|
||||
ImGui.TextUnformatted("onto this one");
|
||||
ImGui.TableNextColumn();
|
||||
_dirty |= _weaponTarget.Draw( "##weaponTarget", _weaponTarget.CurrentSelection.Item1 ?? string.Empty, string.Empty, InputWidth * 2, ImGui.GetTextLineHeightWithSpacing() );
|
||||
_dirty |= _weaponTarget.Draw("##weaponTarget", _weaponTarget.CurrentSelection.Item1 ?? string.Empty, string.Empty, InputWidth * 2,
|
||||
ImGui.GetTextLineHeightWithSpacing());
|
||||
}
|
||||
|
||||
private const float InputWidth = 120;
|
||||
|
||||
private void DrawTargetIdInput( string text = "Take this ID" )
|
||||
private void DrawTargetIdInput(string text = "Take this ID")
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted( text );
|
||||
ImGui.TextUnformatted(text);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( InputWidth * ImGuiHelpers.GlobalScale );
|
||||
if( ImGui.InputInt( "##targetId", ref _targetId, 0, 0 ) )
|
||||
{
|
||||
_targetId = Math.Clamp( _targetId, 0, byte.MaxValue );
|
||||
}
|
||||
ImGui.SetNextItemWidth(InputWidth * ImGuiHelpers.GlobalScale);
|
||||
if (ImGui.InputInt("##targetId", ref _targetId, 0, 0))
|
||||
_targetId = Math.Clamp(_targetId, 0, byte.MaxValue);
|
||||
|
||||
_dirty |= ImGui.IsItemDeactivatedAfterEdit();
|
||||
}
|
||||
|
||||
private void DrawSourceIdInput( string text = "and put it on this one" )
|
||||
private void DrawSourceIdInput(string text = "and put it on this one")
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted( text );
|
||||
ImGui.TextUnformatted(text);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( InputWidth * ImGuiHelpers.GlobalScale );
|
||||
if( ImGui.InputInt( "##sourceId", ref _sourceId, 0, 0 ) )
|
||||
{
|
||||
_sourceId = Math.Clamp( _sourceId, 0, byte.MaxValue );
|
||||
}
|
||||
ImGui.SetNextItemWidth(InputWidth * ImGuiHelpers.GlobalScale);
|
||||
if (ImGui.InputInt("##sourceId", ref _sourceId, 0, 0))
|
||||
_sourceId = Math.Clamp(_sourceId, 0, byte.MaxValue);
|
||||
|
||||
_dirty |= ImGui.IsItemDeactivatedAfterEdit();
|
||||
}
|
||||
|
||||
private void DrawGenderInput( string text = "for all", int drawRace = 1 )
|
||||
private void DrawGenderInput(string text = "for all", int drawRace = 1)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted( text );
|
||||
ImGui.TextUnformatted(text);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
_dirty |= Combos.Gender( "##Gender", InputWidth, _currentGender, out _currentGender );
|
||||
if( drawRace == 1 )
|
||||
_dirty |= Combos.Gender("##Gender", InputWidth, _currentGender, out _currentGender);
|
||||
if (drawRace == 1)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
_dirty |= Combos.Race( "##Race", InputWidth, _currentRace, out _currentRace );
|
||||
_dirty |= Combos.Race("##Race", InputWidth, _currentRace, out _currentRace);
|
||||
}
|
||||
else if( drawRace == 2 )
|
||||
else if (drawRace == 2)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
if( _currentRace is not ModelRace.Miqote and not ModelRace.AuRa and not ModelRace.Hrothgar )
|
||||
{
|
||||
if (_currentRace is not ModelRace.Miqote and not ModelRace.AuRa and not ModelRace.Hrothgar)
|
||||
_currentRace = ModelRace.Miqote;
|
||||
}
|
||||
|
||||
_dirty |= ImGuiUtil.GenericEnumCombo( "##Race", InputWidth, _currentRace, out _currentRace, new[] { ModelRace.Miqote, ModelRace.AuRa, ModelRace.Hrothgar },
|
||||
RaceEnumExtensions.ToName );
|
||||
_dirty |= ImGuiUtil.GenericEnumCombo("##Race", InputWidth, _currentRace, out _currentRace, new[]
|
||||
{
|
||||
ModelRace.Miqote,
|
||||
ModelRace.AuRa,
|
||||
ModelRace.Hrothgar,
|
||||
},
|
||||
RaceEnumExtensions.ToName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -718,72 +709,54 @@ public class ItemSwapWindow : IDisposable
|
|||
|
||||
public void DrawItemSwapPanel()
|
||||
{
|
||||
using var tab = ImRaii.TabItem( "Item Swap (WIP)" );
|
||||
if( !tab )
|
||||
{
|
||||
using var tab = ImRaii.TabItem("Item Swap (WIP)");
|
||||
if (!tab)
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui.NewLine();
|
||||
DrawHeaderLine( 300 * ImGuiHelpers.GlobalScale );
|
||||
DrawHeaderLine(300 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.NewLine();
|
||||
|
||||
DrawSwapBar();
|
||||
|
||||
using var table = ImRaii.ListBox( "##swaps", -Vector2.One );
|
||||
if( _loadException != null )
|
||||
{
|
||||
ImGuiUtil.TextWrapped( $"Could not load Customization Swap:\n{_loadException}" );
|
||||
}
|
||||
else if( _swapData.Loaded )
|
||||
{
|
||||
foreach( var swap in _swapData.Swaps )
|
||||
{
|
||||
DrawSwap( swap );
|
||||
}
|
||||
}
|
||||
using var table = ImRaii.ListBox("##swaps", -Vector2.One);
|
||||
if (_loadException != null)
|
||||
ImGuiUtil.TextWrapped($"Could not load Customization Swap:\n{_loadException}");
|
||||
else if (_swapData.Loaded)
|
||||
foreach (var swap in _swapData.Swaps)
|
||||
DrawSwap(swap);
|
||||
else
|
||||
{
|
||||
ImGui.TextUnformatted( NonExistentText() );
|
||||
}
|
||||
ImGui.TextUnformatted(NonExistentText());
|
||||
}
|
||||
|
||||
private static void DrawSwap( Swap swap )
|
||||
private static void DrawSwap(Swap swap)
|
||||
{
|
||||
var flags = swap.ChildSwaps.Count == 0 ? ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.Leaf : ImGuiTreeNodeFlags.DefaultOpen;
|
||||
using var tree = ImRaii.TreeNode( SwapToString( swap ), flags );
|
||||
if( !tree )
|
||||
{
|
||||
using var tree = ImRaii.TreeNode(SwapToString(swap), flags);
|
||||
if (!tree)
|
||||
return;
|
||||
}
|
||||
|
||||
foreach( var child in swap.ChildSwaps )
|
||||
{
|
||||
DrawSwap( child );
|
||||
}
|
||||
foreach (var child in swap.ChildSwaps)
|
||||
DrawSwap(child);
|
||||
}
|
||||
|
||||
private void OnCollectionChange( CollectionType collectionType, ModCollection? oldCollection,
|
||||
ModCollection? newCollection, string _ )
|
||||
private void OnCollectionChange(CollectionType collectionType, ModCollection? oldCollection,
|
||||
ModCollection? newCollection, string _)
|
||||
{
|
||||
if( collectionType != CollectionType.Current || _mod == null || newCollection == null )
|
||||
{
|
||||
if (collectionType != CollectionType.Current || _mod == null || newCollection == null)
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateMod( _mod, _mod.Index < newCollection.Settings.Count ? newCollection.Settings[ _mod.Index ] : null );
|
||||
UpdateMod(_mod, _mod.Index < newCollection.Settings.Count ? newCollection.Settings[_mod.Index] : null);
|
||||
newCollection.ModSettingChanged += OnSettingChange;
|
||||
if( oldCollection != null )
|
||||
{
|
||||
if (oldCollection != null)
|
||||
oldCollection.ModSettingChanged -= OnSettingChange;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSettingChange( ModSettingChange type, int modIdx, int oldValue, int groupIdx, bool inherited )
|
||||
private void OnSettingChange(ModSettingChange type, int modIdx, int oldValue, int groupIdx, bool inherited)
|
||||
{
|
||||
if( modIdx == _mod?.Index )
|
||||
if (modIdx == _mod?.Index)
|
||||
{
|
||||
_swapData.LoadMod( _mod, _modSettings );
|
||||
_swapData.LoadMod(_mod, _modSettings);
|
||||
_dirty = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ public partial class ModEditWindow
|
|||
|
||||
private static bool DrawPreviewDye( MtrlFile file, bool disabled )
|
||||
{
|
||||
var (dyeId, (name, dyeColor, _)) = Penumbra.StainManager.StainCombo.CurrentSelection;
|
||||
var (dyeId, (name, dyeColor, _)) = Penumbra.StainService.StainCombo.CurrentSelection;
|
||||
var tt = dyeId == 0 ? "Select a preview dye first." : "Apply all preview values corresponding to the dye template and chosen dye where dyeing is enabled.";
|
||||
if( ImGuiUtil.DrawDisabledButton( "Apply Preview Dye", Vector2.Zero, tt, disabled || dyeId == 0 ) )
|
||||
{
|
||||
|
|
@ -106,7 +106,7 @@ public partial class ModEditWindow
|
|||
{
|
||||
for( var i = 0; i < MtrlFile.ColorSet.RowArray.NumRows; ++i )
|
||||
{
|
||||
ret |= file.ApplyDyeTemplate( Penumbra.StainManager.StmFile, j, i, dyeId );
|
||||
ret |= file.ApplyDyeTemplate( Penumbra.StainService.StmFile, j, i, dyeId );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ public partial class ModEditWindow
|
|||
|
||||
ImGui.SameLine();
|
||||
var label = dyeId == 0 ? "Preview Dye###previewDye" : $"{name} (Preview)###previewDye";
|
||||
Penumbra.StainManager.StainCombo.Draw( label, dyeColor, string.Empty, true );
|
||||
Penumbra.StainService.StainCombo.Draw( label, dyeColor, string.Empty, true );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -355,10 +355,10 @@ public partial class ModEditWindow
|
|||
ImGui.TableNextColumn();
|
||||
if( hasDye )
|
||||
{
|
||||
if( Penumbra.StainManager.TemplateCombo.Draw( "##dyeTemplate", dye.Template.ToString(), string.Empty, intSize
|
||||
if( Penumbra.StainService.TemplateCombo.Draw( "##dyeTemplate", dye.Template.ToString(), string.Empty, intSize
|
||||
+ ImGui.GetStyle().ScrollbarSize / 2, ImGui.GetTextLineHeightWithSpacing(), ImGuiComboFlags.NoArrowButton ) )
|
||||
{
|
||||
file.ColorDyeSets[ colorSetIdx ].Rows[ rowIdx ].Template = Penumbra.StainManager.TemplateCombo.CurrentSelection;
|
||||
file.ColorDyeSets[ colorSetIdx ].Rows[ rowIdx ].Template = Penumbra.StainService.TemplateCombo.CurrentSelection;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
|
|
@ -378,8 +378,8 @@ public partial class ModEditWindow
|
|||
|
||||
private static bool DrawDyePreview( MtrlFile file, int colorSetIdx, int rowIdx, bool disabled, MtrlFile.ColorDyeSet.Row dye, float floatSize )
|
||||
{
|
||||
var stain = Penumbra.StainManager.StainCombo.CurrentSelection.Key;
|
||||
if( stain == 0 || !Penumbra.StainManager.StmFile.Entries.TryGetValue( dye.Template, out var entry ) )
|
||||
var stain = Penumbra.StainService.StainCombo.CurrentSelection.Key;
|
||||
if( stain == 0 || !Penumbra.StainService.StmFile.Entries.TryGetValue( dye.Template, out var entry ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -390,7 +390,7 @@ public partial class ModEditWindow
|
|||
var ret = ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.PaintBrush.ToIconString(), new Vector2( ImGui.GetFrameHeight() ),
|
||||
"Apply the selected dye to this row.", disabled, true );
|
||||
|
||||
ret = ret && file.ApplyDyeTemplate( Penumbra.StainManager.StmFile, colorSetIdx, rowIdx, stain );
|
||||
ret = ret && file.ApplyDyeTemplate( Penumbra.StainService.StmFile, colorSetIdx, rowIdx, stain );
|
||||
|
||||
ImGui.SameLine();
|
||||
ColorPicker( "##diffusePreview", string.Empty, values.Diffuse, _ => { }, "D" );
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ using Penumbra.GameData.Enums;
|
|||
using Penumbra.GameData.Files;
|
||||
using Penumbra.Import.Textures;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.String.Classes;
|
||||
using Penumbra.Util;
|
||||
using static Penumbra.Mods.Mod;
|
||||
|
|
@ -22,7 +23,7 @@ namespace Penumbra.UI.Classes;
|
|||
public partial class ModEditWindow : Window, IDisposable
|
||||
{
|
||||
private const string WindowBaseLabel = "###SubModEdit";
|
||||
internal readonly ItemSwapWindow _swapWindow = new();
|
||||
internal readonly ItemSwapWindow _swapWindow;
|
||||
|
||||
private Editor? _editor;
|
||||
private Mod? _mod;
|
||||
|
|
@ -567,9 +568,10 @@ public partial class ModEditWindow : Window, IDisposable
|
|||
return new FullPath( path );
|
||||
}
|
||||
|
||||
public ModEditWindow()
|
||||
public ModEditWindow(CommunicatorService communicator)
|
||||
: base( WindowBaseLabel )
|
||||
{
|
||||
{
|
||||
_swapWindow = new ItemSwapWindow( communicator );
|
||||
_materialTab = new FileEditor< MtrlTab >( "Materials", ".mtrl",
|
||||
() => _editor?.MtrlFiles ?? Array.Empty< Editor.FileRegistry >(),
|
||||
DrawMaterialPanel,
|
||||
|
|
|
|||
|
|
@ -14,41 +14,43 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Services;
|
||||
|
||||
using Penumbra.Services;
|
||||
|
||||
namespace Penumbra.UI.Classes;
|
||||
|
||||
public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, ModFileSystemSelector.ModState >
|
||||
public sealed partial class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSystemSelector.ModState>
|
||||
{
|
||||
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;
|
||||
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( ModFileSystem fileSystem )
|
||||
: base( fileSystem, DalamudServices.KeyState )
|
||||
public ModFileSystemSelector(CommunicatorService communicator, ModFileSystem fileSystem)
|
||||
: base(fileSystem, DalamudServices.KeyState)
|
||||
{
|
||||
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 );
|
||||
_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;
|
||||
Penumbra.CollectionManager.CollectionChanged += OnCollectionChange;
|
||||
_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, "" );
|
||||
OnCollectionChange(CollectionType.Current, null, Penumbra.CollectionManager.Current, "");
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
|
|
@ -59,7 +61,7 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod
|
|||
Penumbra.ModManager.ModDataChanged -= OnModDataChange;
|
||||
Penumbra.CollectionManager.Current.ModSettingChanged -= OnSettingChange;
|
||||
Penumbra.CollectionManager.Current.InheritanceChanged -= OnInheritanceChange;
|
||||
Penumbra.CollectionManager.CollectionChanged -= OnCollectionChange;
|
||||
_communicator.CollectionChange.Event -= OnCollectionChange;
|
||||
_import?.Dispose();
|
||||
_import = null;
|
||||
}
|
||||
|
|
@ -68,7 +70,7 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod
|
|||
=> base.SelectedLeaf;
|
||||
|
||||
// Customization points.
|
||||
public override ISortMode< Mod > SortMode
|
||||
public override ISortMode<Mod> SortMode
|
||||
=> Penumbra.Config.SortMode;
|
||||
|
||||
protected override uint ExpandedFolderColor
|
||||
|
|
@ -89,91 +91,79 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod
|
|||
DrawHelpPopup();
|
||||
DrawInfoPopup();
|
||||
|
||||
if( ImGuiUtil.OpenNameField( "Create New Mod", ref _newModName ) )
|
||||
{
|
||||
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 );
|
||||
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 )
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error( $"Could not create directory for new Mod {_newModName}:\n{e}" );
|
||||
Penumbra.Log.Error($"Could not create directory for new Mod {_newModName}:\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
while( _modsToAdd.TryDequeue( out var dir ) )
|
||||
while (_modsToAdd.TryDequeue(out var dir))
|
||||
{
|
||||
Penumbra.ModManager.AddMod( dir );
|
||||
Penumbra.ModManager.AddMod(dir);
|
||||
var mod = Penumbra.ModManager.LastOrDefault();
|
||||
if( mod != null )
|
||||
if (mod != null)
|
||||
{
|
||||
MoveModToDefaultDirectory( mod );
|
||||
SelectByValue( mod );
|
||||
MoveModToDefaultDirectory(mod);
|
||||
SelectByValue(mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DrawLeafName( FileSystem< Mod >.Leaf leaf, in ModState state, bool selected )
|
||||
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();
|
||||
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 )
|
||||
private static void EnableDescendants(ModFileSystem.Folder folder)
|
||||
{
|
||||
if( ImGui.MenuItem( "Enable Descendants" ) )
|
||||
{
|
||||
SetDescendants( folder, true );
|
||||
}
|
||||
if (ImGui.MenuItem("Enable Descendants"))
|
||||
SetDescendants(folder, true);
|
||||
}
|
||||
|
||||
private static void DisableDescendants( ModFileSystem.Folder folder )
|
||||
private static void DisableDescendants(ModFileSystem.Folder folder)
|
||||
{
|
||||
if( ImGui.MenuItem( "Disable Descendants" ) )
|
||||
{
|
||||
SetDescendants( folder, false );
|
||||
}
|
||||
if (ImGui.MenuItem("Disable Descendants"))
|
||||
SetDescendants(folder, false);
|
||||
}
|
||||
|
||||
private static void InheritDescendants( ModFileSystem.Folder folder )
|
||||
private static void InheritDescendants(ModFileSystem.Folder folder)
|
||||
{
|
||||
if( ImGui.MenuItem( "Inherit Descendants" ) )
|
||||
{
|
||||
SetDescendants( folder, true, true );
|
||||
}
|
||||
if (ImGui.MenuItem("Inherit Descendants"))
|
||||
SetDescendants(folder, true, true);
|
||||
}
|
||||
|
||||
private static void OwnDescendants( ModFileSystem.Folder folder )
|
||||
private static void OwnDescendants(ModFileSystem.Folder folder)
|
||||
{
|
||||
if( ImGui.MenuItem( "Stop Inheriting Descendants" ) )
|
||||
{
|
||||
SetDescendants( folder, false, true );
|
||||
}
|
||||
if (ImGui.MenuItem("Stop Inheriting Descendants"))
|
||||
SetDescendants(folder, false, true);
|
||||
}
|
||||
|
||||
private static void ToggleLeafFavorite( FileSystem< Mod >.Leaf mod )
|
||||
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 );
|
||||
}
|
||||
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 )
|
||||
private static void SetDefaultImportFolder(ModFileSystem.Folder folder)
|
||||
{
|
||||
if( ImGui.MenuItem( "Set As Default Import Folder" ) )
|
||||
if (ImGui.MenuItem("Set As Default Import Folder"))
|
||||
{
|
||||
var newName = folder.FullName();
|
||||
if( newName != Penumbra.Config.DefaultImportFolder )
|
||||
if (newName != Penumbra.Config.DefaultImportFolder)
|
||||
{
|
||||
Penumbra.Config.DefaultImportFolder = newName;
|
||||
Penumbra.Config.Save();
|
||||
|
|
@ -183,7 +173,7 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod
|
|||
|
||||
private static void ClearDefaultImportFolder()
|
||||
{
|
||||
if( ImGui.MenuItem( "Clear Default Import Folder" ) && Penumbra.Config.DefaultImportFolder.Length > 0 )
|
||||
if (ImGui.MenuItem("Clear Default Import Folder") && Penumbra.Config.DefaultImportFolder.Length > 0)
|
||||
{
|
||||
Penumbra.Config.DefaultImportFolder = string.Empty;
|
||||
Penumbra.Config.Save();
|
||||
|
|
@ -194,71 +184,63 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod
|
|||
// Add custom buttons.
|
||||
private string _newModName = string.Empty;
|
||||
|
||||
private static void AddNewModButton( Vector2 size )
|
||||
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" );
|
||||
}
|
||||
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 )
|
||||
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 )
|
||||
{
|
||||
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;
|
||||
: 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 ) =>
|
||||
_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 )
|
||||
if (s)
|
||||
{
|
||||
_import = new TexToolsImporter( Penumbra.ModManager.BasePath, f.Count, f.Select( file => new FileInfo( file ) ),
|
||||
AddNewMod );
|
||||
ImGui.OpenPopup( "Import Status" );
|
||||
_import = new TexToolsImporter(Penumbra.ModManager.BasePath, f.Count, f.Select(file => new FileInfo(file)),
|
||||
AddNewMod);
|
||||
ImGui.OpenPopup("Import Status");
|
||||
}
|
||||
}, 0, modPath );
|
||||
}, 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 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 )
|
||||
{
|
||||
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 ) ) )
|
||||
using (var child = ImRaii.Child("##import", new Vector2(-1, size.Y - ImGui.GetFrameHeight() * 2)))
|
||||
{
|
||||
if( child )
|
||||
{
|
||||
_import.DrawProgressInfo( new Vector2( -1, ImGui.GetFrameHeight() ) );
|
||||
}
|
||||
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 ) )
|
||||
if (_import.State == ImporterState.Done && ImGui.Button("Close", -Vector2.UnitX)
|
||||
|| _import.State != ImporterState.Done && _import.DrawCancelButton(-Vector2.UnitX))
|
||||
{
|
||||
_import?.Dispose();
|
||||
_import = null;
|
||||
|
|
@ -267,100 +249,84 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod
|
|||
}
|
||||
|
||||
// Mods need to be added thread-safely outside of iteration.
|
||||
private readonly ConcurrentQueue< DirectoryInfo > _modsToAdd = new();
|
||||
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 )
|
||||
private void AddNewMod(FileInfo file, DirectoryInfo? dir, Exception? error)
|
||||
{
|
||||
if( error != null )
|
||||
if (error != null)
|
||||
{
|
||||
if( dir != null && Directory.Exists( dir.FullName ) )
|
||||
{
|
||||
if (dir != null && Directory.Exists(dir.FullName))
|
||||
try
|
||||
{
|
||||
Directory.Delete( dir.FullName, true );
|
||||
Directory.Delete(dir.FullName, true);
|
||||
}
|
||||
catch( Exception e )
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error( $"Error cleaning up failed mod extraction of {file.FullName} to {dir.FullName}:\n{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}" );
|
||||
}
|
||||
if (error is not OperationCanceledException)
|
||||
Penumbra.Log.Error($"Error extracting {file.FullName}, mod skipped:\n{error}");
|
||||
}
|
||||
else if( dir != null )
|
||||
else if (dir != null)
|
||||
{
|
||||
_modsToAdd.Enqueue( dir );
|
||||
_modsToAdd.Enqueue(dir);
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteModButton( Vector2 size )
|
||||
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 )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
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 )
|
||||
private static void AddHelpButton(Vector2 size)
|
||||
{
|
||||
if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.QuestionCircle.ToIconString(), size, "Open extended help.", false, true ) )
|
||||
{
|
||||
ImGui.OpenPopup( "ExtendedHelp" );
|
||||
}
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.QuestionCircle.ToIconString(), size, "Open extended help.", false, true))
|
||||
ImGui.OpenPopup("ExtendedHelp");
|
||||
|
||||
ConfigWindow.OpenTutorial( ConfigWindow.BasicTutorialSteps.AdvancedHelp );
|
||||
ConfigWindow.OpenTutorial(ConfigWindow.BasicTutorialSteps.AdvancedHelp);
|
||||
}
|
||||
|
||||
// Helpers.
|
||||
private static void SetDescendants( ModFileSystem.Folder folder, bool enabled, bool inherit = false )
|
||||
private static void SetDescendants(ModFileSystem.Folder folder, bool enabled, bool inherit = false)
|
||||
{
|
||||
var mods = folder.GetAllDescendants( ISortMode< Mod >.Lexicographical ).OfType< ModFileSystem.Leaf >().Select( l =>
|
||||
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 );
|
||||
Penumbra.ModManager.NewMods.Remove(l.Value);
|
||||
return l.Value;
|
||||
} );
|
||||
});
|
||||
|
||||
if( inherit )
|
||||
{
|
||||
Penumbra.CollectionManager.Current.SetMultipleModInheritances( mods, enabled );
|
||||
}
|
||||
if (inherit)
|
||||
Penumbra.CollectionManager.Current.SetMultipleModInheritances(mods, enabled);
|
||||
else
|
||||
{
|
||||
Penumbra.CollectionManager.Current.SetMultipleModStates( mods, enabled );
|
||||
}
|
||||
Penumbra.CollectionManager.Current.SetMultipleModStates(mods, enabled);
|
||||
}
|
||||
|
||||
// Automatic cache update functions.
|
||||
private void OnSettingChange( ModSettingChange type, int modIdx, int oldValue, int groupIdx, bool inherited )
|
||||
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 );
|
||||
}
|
||||
if (modIdx == Selected?.Index)
|
||||
OnSelectionChange(Selected, Selected, default);
|
||||
}
|
||||
|
||||
private void OnModDataChange( ModDataChangeType type, Mod mod, string? oldName )
|
||||
private void OnModDataChange(ModDataChangeType type, Mod mod, string? oldName)
|
||||
{
|
||||
switch( type )
|
||||
switch (type)
|
||||
{
|
||||
case ModDataChangeType.Name:
|
||||
case ModDataChangeType.Author:
|
||||
|
|
@ -372,46 +338,44 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod
|
|||
}
|
||||
}
|
||||
|
||||
private void OnInheritanceChange( bool _ )
|
||||
private void OnInheritanceChange(bool _)
|
||||
{
|
||||
SetFilterDirty();
|
||||
OnSelectionChange( Selected, Selected, default );
|
||||
OnSelectionChange(Selected, Selected, default);
|
||||
}
|
||||
|
||||
private void OnCollectionChange( CollectionType collectionType, ModCollection? oldCollection, ModCollection? newCollection, string _ )
|
||||
private void OnCollectionChange(CollectionType collectionType, ModCollection? oldCollection, ModCollection? newCollection, string _)
|
||||
{
|
||||
if( collectionType != CollectionType.Current || oldCollection == newCollection )
|
||||
{
|
||||
if (collectionType != CollectionType.Current || oldCollection == newCollection)
|
||||
return;
|
||||
}
|
||||
|
||||
if( oldCollection != null )
|
||||
if (oldCollection != null)
|
||||
{
|
||||
oldCollection.ModSettingChanged -= OnSettingChange;
|
||||
oldCollection.InheritanceChanged -= OnInheritanceChange;
|
||||
}
|
||||
|
||||
if( newCollection != null )
|
||||
if (newCollection != null)
|
||||
{
|
||||
newCollection.ModSettingChanged += OnSettingChange;
|
||||
newCollection.InheritanceChanged += OnInheritanceChange;
|
||||
}
|
||||
|
||||
SetFilterDirty();
|
||||
OnSelectionChange( Selected, Selected, default );
|
||||
OnSelectionChange(Selected, Selected, default);
|
||||
}
|
||||
|
||||
private void OnSelectionChange( Mod? _1, Mod? newSelection, in ModState _2 )
|
||||
private void OnSelectionChange(Mod? _1, Mod? newSelection, in ModState _2)
|
||||
{
|
||||
if( newSelection == null )
|
||||
if (newSelection == null)
|
||||
{
|
||||
SelectedSettings = ModSettings.Empty;
|
||||
SelectedSettingCollection = ModCollection.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
( var settings, SelectedSettingCollection ) = Penumbra.CollectionManager.Current[ newSelection.Index ];
|
||||
SelectedSettings = settings ?? ModSettings.Empty;
|
||||
(var settings, SelectedSettingCollection) = Penumbra.CollectionManager.Current[newSelection.Index];
|
||||
SelectedSettings = settings ?? ModSettings.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -426,92 +390,89 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod
|
|||
|
||||
private void RestoreLastSelection()
|
||||
{
|
||||
if( _lastSelectedDirectory.Length > 0 )
|
||||
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 );
|
||||
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 )
|
||||
private void MoveModToDefaultDirectory(Mod mod)
|
||||
{
|
||||
if( Penumbra.Config.DefaultImportFolder.Length == 0 )
|
||||
{
|
||||
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 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 );
|
||||
var folder = FileSystem.FindOrCreateAllFolders(Penumbra.Config.DefaultImportFolder);
|
||||
FileSystem.Move(leaf, folder);
|
||||
}
|
||||
catch( Exception e )
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Warning(
|
||||
$"Could not move newly imported mod {mod.Name} to default import folder {Penumbra.Config.DefaultImportFolder}:\n{e}" );
|
||||
$"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() ), () =>
|
||||
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." );
|
||||
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("Supported formats for import are: .ttmp, .ttmp2, .pmp.");
|
||||
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." );
|
||||
"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();
|
||||
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." );
|
||||
} );
|
||||
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.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue