mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 12:14:17 +01:00
Deduplicate UI code
This commit is contained in:
parent
14eddac6f7
commit
046ef4d72d
3 changed files with 220 additions and 303 deletions
|
|
@ -18,8 +18,7 @@ namespace Penumbra.UI.Classes;
|
||||||
|
|
||||||
public partial class ModEditWindow
|
public partial class ModEditWindow
|
||||||
{
|
{
|
||||||
private ResourceTree[]? _quickImportTrees;
|
private ResourceTreeViewer? _quickImportViewer;
|
||||||
private HashSet<ResourceTree.Node>? _quickImportUnfolded;
|
|
||||||
private Dictionary<FullPath, IWritable?>? _quickImportWritables;
|
private Dictionary<FullPath, IWritable?>? _quickImportWritables;
|
||||||
private Dictionary<(Utf8GamePath, IWritable?), QuickImportAction>? _quickImportActions;
|
private Dictionary<(Utf8GamePath, IWritable?), QuickImportAction>? _quickImportActions;
|
||||||
|
|
||||||
|
|
@ -34,139 +33,22 @@ public partial class ModEditWindow
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_quickImportUnfolded ??= new();
|
_quickImportViewer ??= new( "Import from Screen tab", 2, OnQuickImportRefresh, DrawQuickImportActions );
|
||||||
_quickImportWritables ??= new();
|
_quickImportWritables ??= new();
|
||||||
_quickImportActions ??= new();
|
_quickImportActions ??= new();
|
||||||
|
|
||||||
if( ImGui.Button( "Refresh Character List" ) )
|
_quickImportViewer.Draw();
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_quickImportTrees = ResourceTree.FromObjectTable();
|
|
||||||
}
|
|
||||||
catch( Exception e )
|
|
||||||
{
|
|
||||||
Penumbra.Log.Error( $"Could not get character list for Import from Screen tab:\n{e}" );
|
|
||||||
_quickImportTrees = Array.Empty<ResourceTree>();
|
|
||||||
}
|
|
||||||
_quickImportUnfolded.Clear();
|
|
||||||
_quickImportWritables.Clear();
|
|
||||||
_quickImportActions.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_quickImportTrees ??= ResourceTree.FromObjectTable();
|
|
||||||
}
|
|
||||||
catch( Exception e )
|
|
||||||
{
|
|
||||||
Penumbra.Log.Error( $"Could not get character list for Import from Screen tab:\n{e}" );
|
|
||||||
_quickImportTrees ??= Array.Empty<ResourceTree>();
|
|
||||||
}
|
|
||||||
|
|
||||||
var textColorNonPlayer = ImGui.GetColorU32( ImGuiCol.Text );
|
|
||||||
var textColorPlayer = ( textColorNonPlayer & 0xFF000000u ) | ( ( textColorNonPlayer & 0x00FEFEFE ) >> 1 ) | 0x8000u; // Half green
|
|
||||||
|
|
||||||
foreach( var (tree, index) in _quickImportTrees.WithIndex() )
|
|
||||||
{
|
|
||||||
using( var c = ImRaii.PushColor( ImGuiCol.Text, tree.PlayerRelated ? textColorPlayer : textColorNonPlayer ) )
|
|
||||||
{
|
|
||||||
if( !ImGui.CollapsingHeader( $"{tree.Name}##{index}", ( index == 0 ) ? ImGuiTreeNodeFlags.DefaultOpen : 0 ) )
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
using var id = ImRaii.PushId( index );
|
|
||||||
|
|
||||||
ImGui.Text( $"Collection: {tree.CollectionName}" );
|
|
||||||
|
|
||||||
using var table = ImRaii.Table( "##ResourceTree", 4,
|
|
||||||
ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg );
|
|
||||||
if( !table )
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.TableSetupColumn( string.Empty , ImGuiTableColumnFlags.WidthStretch, 0.2f );
|
|
||||||
ImGui.TableSetupColumn( "Game Path" , ImGuiTableColumnFlags.WidthStretch, 0.3f );
|
|
||||||
ImGui.TableSetupColumn( "Actual Path", ImGuiTableColumnFlags.WidthStretch, 0.5f );
|
|
||||||
ImGui.TableSetupColumn( string.Empty , ImGuiTableColumnFlags.WidthFixed, 3 * ImGuiHelpers.GlobalScale + 2 * ImGui.GetFrameHeight() );
|
|
||||||
ImGui.TableHeadersRow();
|
|
||||||
|
|
||||||
DrawQuickImportNodes( tree.Nodes, 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
_quickImportFileDialog.Draw();
|
_quickImportFileDialog.Draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawQuickImportNodes( IEnumerable<ResourceTree.Node> resourceNodes, int level )
|
private void OnQuickImportRefresh()
|
||||||
{
|
{
|
||||||
var debugMode = Penumbra.Config.DebugMode;
|
_quickImportWritables?.Clear();
|
||||||
var frameHeight = ImGui.GetFrameHeight();
|
_quickImportActions?.Clear();
|
||||||
foreach( var (resourceNode, index) in resourceNodes.WithIndex() )
|
|
||||||
{
|
|
||||||
if( resourceNode.Internal && !debugMode )
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
using var id = ImRaii.PushId( index );
|
|
||||||
ImGui.TableNextColumn();
|
private void DrawQuickImportActions( ResourceTree.Node resourceNode, Vector2 buttonSize )
|
||||||
var unfolded = _quickImportUnfolded!.Contains( resourceNode );
|
|
||||||
using( var indent = ImRaii.PushIndent( level ) )
|
|
||||||
{
|
|
||||||
ImGui.TableHeader( ( ( resourceNode.Children.Count > 0 ) ? ( unfolded ? "[-] " : "[+] " ) : string.Empty ) + resourceNode.Name );
|
|
||||||
if( ImGui.IsItemClicked() && resourceNode.Children.Count > 0 )
|
|
||||||
{
|
|
||||||
if( unfolded )
|
|
||||||
{
|
|
||||||
_quickImportUnfolded.Remove( resourceNode );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_quickImportUnfolded.Add( resourceNode );
|
|
||||||
}
|
|
||||||
unfolded = !unfolded;
|
|
||||||
}
|
|
||||||
if( debugMode )
|
|
||||||
{
|
|
||||||
ImGuiUtil.HoverTooltip( $"Resource Type: {resourceNode.Type}\nSource Address: 0x{resourceNode.SourceAddress.ToString("X" + nint.Size * 2)}" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
var hasGamePaths = resourceNode.PossibleGamePaths.Length > 0;
|
|
||||||
ImGui.Selectable( resourceNode.PossibleGamePaths.Length switch
|
|
||||||
{
|
|
||||||
0 => "(none)",
|
|
||||||
1 => resourceNode.GamePath.ToString(),
|
|
||||||
_ => "(multiple)",
|
|
||||||
}, false, hasGamePaths ? 0 : ImGuiSelectableFlags.Disabled, new Vector2( ImGui.GetContentRegionAvail().X, frameHeight ) );
|
|
||||||
if( hasGamePaths )
|
|
||||||
{
|
|
||||||
var allPaths = string.Join( '\n', resourceNode.PossibleGamePaths );
|
|
||||||
if( ImGui.IsItemClicked() )
|
|
||||||
{
|
|
||||||
ImGui.SetClipboardText( allPaths );
|
|
||||||
}
|
|
||||||
ImGuiUtil.HoverTooltip( $"{allPaths}\n\nClick to copy to clipboard." );
|
|
||||||
}
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
var hasFullPath = resourceNode.FullPath.FullName.Length > 0;
|
|
||||||
if( hasFullPath )
|
|
||||||
{
|
|
||||||
ImGui.Selectable( resourceNode.FullPath.ToString(), false, 0, new Vector2( ImGui.GetContentRegionAvail().X, frameHeight ) );
|
|
||||||
if( ImGui.IsItemClicked() )
|
|
||||||
{
|
|
||||||
ImGui.SetClipboardText( resourceNode.FullPath.ToString() );
|
|
||||||
}
|
|
||||||
ImGuiUtil.HoverTooltip( $"{resourceNode.FullPath}\n\nClick to copy to clipboard." );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.Selectable( "(unavailable)", false, ImGuiSelectableFlags.Disabled, new Vector2( ImGui.GetContentRegionAvail().X, frameHeight ) );
|
|
||||||
ImGuiUtil.HoverTooltip( "The actual path to this file is unavailable.\nIt may be managed by another plug-in." );
|
|
||||||
}
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
using( var spacing = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = 3 * ImGuiHelpers.GlobalScale } ) )
|
|
||||||
{
|
{
|
||||||
if( !_quickImportWritables!.TryGetValue( resourceNode.FullPath, out var writable ) )
|
if( !_quickImportWritables!.TryGetValue( resourceNode.FullPath, out var writable ) )
|
||||||
{
|
{
|
||||||
|
|
@ -182,7 +64,7 @@ public partial class ModEditWindow
|
||||||
}
|
}
|
||||||
_quickImportWritables.Add( resourceNode.FullPath, writable );
|
_quickImportWritables.Add( resourceNode.FullPath, writable );
|
||||||
}
|
}
|
||||||
if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Save.ToIconString(), new Vector2( frameHeight ), "Export this file.", !hasFullPath || writable == null, true ) )
|
if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Save.ToIconString(), buttonSize, "Export this file.", resourceNode.FullPath.FullName.Length == 0 || writable == null, true ) )
|
||||||
{
|
{
|
||||||
var fullPathStr = resourceNode.FullPath.FullName;
|
var fullPathStr = resourceNode.FullPath.FullName;
|
||||||
var ext = ( resourceNode.PossibleGamePaths.Length == 1 ) ? Path.GetExtension( resourceNode.GamePath.ToString() ) : Path.GetExtension( fullPathStr );
|
var ext = ( resourceNode.PossibleGamePaths.Length == 1 ) ? Path.GetExtension( resourceNode.GamePath.ToString() ) : Path.GetExtension( fullPathStr );
|
||||||
|
|
@ -209,18 +91,12 @@ public partial class ModEditWindow
|
||||||
quickImport = QuickImportAction.Prepare( this, resourceNode.GamePath, writable );
|
quickImport = QuickImportAction.Prepare( this, resourceNode.GamePath, writable );
|
||||||
_quickImportActions.Add( (resourceNode.GamePath, writable), quickImport );
|
_quickImportActions.Add( (resourceNode.GamePath, writable), quickImport );
|
||||||
}
|
}
|
||||||
if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.FileImport.ToIconString(), new Vector2( frameHeight ), $"Add a copy of this file to {quickImport.OptionName}.", !quickImport.CanExecute, true ) )
|
if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.FileImport.ToIconString(), buttonSize, $"Add a copy of this file to {quickImport.OptionName}.", !quickImport.CanExecute, true ) )
|
||||||
{
|
{
|
||||||
quickImport.Execute();
|
quickImport.Execute();
|
||||||
_quickImportActions.Remove( (resourceNode.GamePath, writable) );
|
_quickImportActions.Remove( (resourceNode.GamePath, writable) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if( unfolded )
|
|
||||||
{
|
|
||||||
DrawQuickImportNodes( resourceNode.Children, level + 1 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private record class RawFileWritable( string Path ) : IWritable
|
private record class RawFileWritable( string Path ) : IWritable
|
||||||
{
|
{
|
||||||
|
|
|
||||||
174
Penumbra/UI/Classes/ResourceTreeViewer.cs
Normal file
174
Penumbra/UI/Classes/ResourceTreeViewer.cs
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Numerics;
|
||||||
|
using Dalamud.Interface;
|
||||||
|
using ImGuiNET;
|
||||||
|
using OtterGui.Raii;
|
||||||
|
using OtterGui;
|
||||||
|
using Penumbra.Interop;
|
||||||
|
|
||||||
|
namespace Penumbra.UI.Classes;
|
||||||
|
|
||||||
|
public class ResourceTreeViewer
|
||||||
|
{
|
||||||
|
private readonly string _name;
|
||||||
|
private readonly int _actionCapacity;
|
||||||
|
private readonly Action _onRefresh;
|
||||||
|
private readonly Action<ResourceTree.Node, Vector2> _drawActions;
|
||||||
|
private readonly HashSet<ResourceTree.Node> _unfolded;
|
||||||
|
private ResourceTree[]? _trees;
|
||||||
|
|
||||||
|
public ResourceTreeViewer( string name, int actionCapacity, Action onRefresh, Action<ResourceTree.Node, Vector2> drawActions )
|
||||||
|
{
|
||||||
|
_name = name;
|
||||||
|
_actionCapacity = actionCapacity;
|
||||||
|
_onRefresh = onRefresh;
|
||||||
|
_drawActions = drawActions;
|
||||||
|
_unfolded = new();
|
||||||
|
_trees = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw()
|
||||||
|
{
|
||||||
|
if( ImGui.Button( "Refresh Character List" ) )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_trees = ResourceTree.FromObjectTable();
|
||||||
|
}
|
||||||
|
catch( Exception e )
|
||||||
|
{
|
||||||
|
Penumbra.Log.Error( $"Could not get character list for {_name}:\n{e}" );
|
||||||
|
_trees = Array.Empty<ResourceTree>();
|
||||||
|
}
|
||||||
|
_unfolded.Clear();
|
||||||
|
_onRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_trees ??= ResourceTree.FromObjectTable();
|
||||||
|
}
|
||||||
|
catch( Exception e )
|
||||||
|
{
|
||||||
|
Penumbra.Log.Error( $"Could not get character list for {_name}:\n{e}" );
|
||||||
|
_trees ??= Array.Empty<ResourceTree>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var textColorNonPlayer = ImGui.GetColorU32( ImGuiCol.Text );
|
||||||
|
var textColorPlayer = ( textColorNonPlayer & 0xFF000000u ) | ( ( textColorNonPlayer & 0x00FEFEFE ) >> 1 ) | 0x8000u; // Half green
|
||||||
|
|
||||||
|
foreach( var (tree, index) in _trees.WithIndex() )
|
||||||
|
{
|
||||||
|
using( var c = ImRaii.PushColor( ImGuiCol.Text, tree.PlayerRelated ? textColorPlayer : textColorNonPlayer ) )
|
||||||
|
{
|
||||||
|
if( !ImGui.CollapsingHeader( $"{tree.Name}##{index}", ( index == 0 ) ? ImGuiTreeNodeFlags.DefaultOpen : 0 ) )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
using var id = ImRaii.PushId( index );
|
||||||
|
|
||||||
|
ImGui.Text( $"Collection: {tree.CollectionName}" );
|
||||||
|
|
||||||
|
using var table = ImRaii.Table( "##ResourceTree", ( _actionCapacity > 0 ) ? 4 : 3,
|
||||||
|
ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg );
|
||||||
|
if( !table )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.TableSetupColumn( string.Empty , ImGuiTableColumnFlags.WidthStretch, 0.2f );
|
||||||
|
ImGui.TableSetupColumn( "Game Path" , ImGuiTableColumnFlags.WidthStretch, 0.3f );
|
||||||
|
ImGui.TableSetupColumn( "Actual Path", ImGuiTableColumnFlags.WidthStretch, 0.5f );
|
||||||
|
if( _actionCapacity > 0 )
|
||||||
|
{
|
||||||
|
ImGui.TableSetupColumn( string.Empty, ImGuiTableColumnFlags.WidthFixed, (_actionCapacity - 1) * 3 * ImGuiHelpers.GlobalScale + _actionCapacity * ImGui.GetFrameHeight() );
|
||||||
|
}
|
||||||
|
ImGui.TableHeadersRow();
|
||||||
|
|
||||||
|
DrawNodes( tree.Nodes, 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawNodes( IEnumerable<ResourceTree.Node> resourceNodes, int level )
|
||||||
|
{
|
||||||
|
var debugMode = Penumbra.Config.DebugMode;
|
||||||
|
var frameHeight = ImGui.GetFrameHeight();
|
||||||
|
var cellHeight = ( _actionCapacity > 0 ) ? frameHeight : 0.0f;
|
||||||
|
foreach( var (resourceNode, index) in resourceNodes.WithIndex() )
|
||||||
|
{
|
||||||
|
if( resourceNode.Internal && !debugMode )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
using var id = ImRaii.PushId( index );
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
var unfolded = _unfolded!.Contains( resourceNode );
|
||||||
|
using( var indent = ImRaii.PushIndent( level ) )
|
||||||
|
{
|
||||||
|
ImGui.TableHeader( ( ( resourceNode.Children.Count > 0 ) ? ( unfolded ? "[-] " : "[+] " ) : string.Empty ) + resourceNode.Name );
|
||||||
|
if( ImGui.IsItemClicked() && resourceNode.Children.Count > 0 )
|
||||||
|
{
|
||||||
|
if( unfolded )
|
||||||
|
{
|
||||||
|
_unfolded.Remove( resourceNode );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_unfolded.Add( resourceNode );
|
||||||
|
}
|
||||||
|
unfolded = !unfolded;
|
||||||
|
}
|
||||||
|
if( debugMode )
|
||||||
|
{
|
||||||
|
ImGuiUtil.HoverTooltip( $"Resource Type: {resourceNode.Type}\nSource Address: 0x{resourceNode.SourceAddress.ToString( "X" + nint.Size * 2 )}" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
var hasGamePaths = resourceNode.PossibleGamePaths.Length > 0;
|
||||||
|
ImGui.Selectable( resourceNode.PossibleGamePaths.Length switch
|
||||||
|
{
|
||||||
|
0 => "(none)",
|
||||||
|
1 => resourceNode.GamePath.ToString(),
|
||||||
|
_ => "(multiple)",
|
||||||
|
}, false, hasGamePaths ? 0 : ImGuiSelectableFlags.Disabled, new Vector2( ImGui.GetContentRegionAvail().X, cellHeight ) );
|
||||||
|
if( hasGamePaths )
|
||||||
|
{
|
||||||
|
var allPaths = string.Join( '\n', resourceNode.PossibleGamePaths );
|
||||||
|
if( ImGui.IsItemClicked() )
|
||||||
|
{
|
||||||
|
ImGui.SetClipboardText( allPaths );
|
||||||
|
}
|
||||||
|
ImGuiUtil.HoverTooltip( $"{allPaths}\n\nClick to copy to clipboard." );
|
||||||
|
}
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
if( resourceNode.FullPath.FullName.Length > 0 )
|
||||||
|
{
|
||||||
|
ImGui.Selectable( resourceNode.FullPath.ToString(), false, 0, new Vector2( ImGui.GetContentRegionAvail().X, cellHeight ) );
|
||||||
|
if( ImGui.IsItemClicked() )
|
||||||
|
{
|
||||||
|
ImGui.SetClipboardText( resourceNode.FullPath.ToString() );
|
||||||
|
}
|
||||||
|
ImGuiUtil.HoverTooltip( $"{resourceNode.FullPath}\n\nClick to copy to clipboard." );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui.Selectable( "(unavailable)", false, ImGuiSelectableFlags.Disabled, new Vector2( ImGui.GetContentRegionAvail().X, cellHeight ) );
|
||||||
|
ImGuiUtil.HoverTooltip( "The actual path to this file is unavailable.\nIt may be managed by another plug-in." );
|
||||||
|
}
|
||||||
|
if( _actionCapacity > 0 )
|
||||||
|
{
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
using( var spacing = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = 3 * ImGuiHelpers.GlobalScale } ) )
|
||||||
|
{
|
||||||
|
_drawActions( resourceNode, new Vector2( frameHeight ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( unfolded )
|
||||||
|
{
|
||||||
|
DrawNodes( resourceNode.Children, level + 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Numerics;
|
|
||||||
using ImGuiNET;
|
|
||||||
using OtterGui;
|
|
||||||
using OtterGui.Raii;
|
|
||||||
using OtterGui.Widgets;
|
using OtterGui.Widgets;
|
||||||
using Penumbra.Interop;
|
using Penumbra.UI.Classes;
|
||||||
|
|
||||||
namespace Penumbra.UI;
|
namespace Penumbra.UI;
|
||||||
|
|
||||||
|
|
@ -14,143 +8,16 @@ public partial class ConfigWindow
|
||||||
{
|
{
|
||||||
private class OnScreenTab : ITab
|
private class OnScreenTab : ITab
|
||||||
{
|
{
|
||||||
|
private ResourceTreeViewer? _viewer;
|
||||||
|
|
||||||
public ReadOnlySpan<byte> Label
|
public ReadOnlySpan<byte> Label
|
||||||
=> "On-Screen"u8;
|
=> "On-Screen"u8;
|
||||||
|
|
||||||
public void DrawContent()
|
public void DrawContent()
|
||||||
{
|
{
|
||||||
_unfolded ??= new();
|
_viewer ??= new( "On-Screen tab", 0, delegate { }, delegate { } );
|
||||||
|
|
||||||
if( ImGui.Button( "Refresh Character List" ) )
|
_viewer.Draw();
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_trees = ResourceTree.FromObjectTable();
|
|
||||||
}
|
}
|
||||||
catch( Exception e )
|
|
||||||
{
|
|
||||||
Penumbra.Log.Error( $"Could not get character list for On-Screen tab:\n{e}" );
|
|
||||||
_trees = Array.Empty<ResourceTree>();
|
|
||||||
}
|
|
||||||
_unfolded.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_trees ??= ResourceTree.FromObjectTable();
|
|
||||||
}
|
|
||||||
catch( Exception e )
|
|
||||||
{
|
|
||||||
Penumbra.Log.Error( $"Could not get character list for On-Screen tab:\n{e}" );
|
|
||||||
_trees ??= Array.Empty<ResourceTree>();
|
|
||||||
}
|
|
||||||
|
|
||||||
var textColorNonPlayer = ImGui.GetColorU32( ImGuiCol.Text );
|
|
||||||
var textColorPlayer = ( textColorNonPlayer & 0xFF000000u ) | ( ( textColorNonPlayer & 0x00FEFEFE ) >> 1 ) | 0x8000u; // Half green
|
|
||||||
|
|
||||||
foreach( var (tree, index) in _trees.WithIndex() )
|
|
||||||
{
|
|
||||||
using( var c = ImRaii.PushColor( ImGuiCol.Text, tree.PlayerRelated ? textColorPlayer : textColorNonPlayer ) )
|
|
||||||
{
|
|
||||||
if( !ImGui.CollapsingHeader( $"{tree.Name}##{index}", ( index == 0 ) ? ImGuiTreeNodeFlags.DefaultOpen : 0 ) )
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
using var id = ImRaii.PushId( index );
|
|
||||||
|
|
||||||
ImGui.Text( $"Collection: {tree.CollectionName}" );
|
|
||||||
|
|
||||||
using var table = ImRaii.Table( "##ResourceTree", 3,
|
|
||||||
ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg );
|
|
||||||
if( !table )
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.TableSetupColumn( string.Empty, ImGuiTableColumnFlags.WidthStretch, 0.2f );
|
|
||||||
ImGui.TableSetupColumn( "Game Path", ImGuiTableColumnFlags.WidthStretch, 0.3f );
|
|
||||||
ImGui.TableSetupColumn( "Actual Path", ImGuiTableColumnFlags.WidthStretch, 0.5f );
|
|
||||||
ImGui.TableHeadersRow();
|
|
||||||
|
|
||||||
DrawNodes( tree.Nodes, 0 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawNodes( IEnumerable<ResourceTree.Node> resourceNodes, int level )
|
|
||||||
{
|
|
||||||
var debugMode = Penumbra.Config.DebugMode;
|
|
||||||
var frameHeight = ImGui.GetFrameHeight();
|
|
||||||
foreach( var (resourceNode, index) in resourceNodes.WithIndex() )
|
|
||||||
{
|
|
||||||
if( resourceNode.Internal && !debugMode )
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
using var id = ImRaii.PushId( index );
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
var unfolded = _unfolded!.Contains( resourceNode );
|
|
||||||
using( var indent = ImRaii.PushIndent( level ) )
|
|
||||||
{
|
|
||||||
ImGui.TableHeader( ( ( resourceNode.Children.Count > 0 ) ? ( unfolded ? "[-] " : "[+] " ) : string.Empty ) + resourceNode.Name );
|
|
||||||
if( ImGui.IsItemClicked() && resourceNode.Children.Count > 0 )
|
|
||||||
{
|
|
||||||
if( unfolded )
|
|
||||||
{
|
|
||||||
_unfolded.Remove( resourceNode );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_unfolded.Add( resourceNode );
|
|
||||||
}
|
|
||||||
unfolded = !unfolded;
|
|
||||||
}
|
|
||||||
if( debugMode )
|
|
||||||
{
|
|
||||||
ImGuiUtil.HoverTooltip( $"Resource Type: {resourceNode.Type}\nSource Address: 0x{resourceNode.SourceAddress.ToString( "X" + nint.Size * 2 )}" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
var hasGamePaths = resourceNode.PossibleGamePaths.Length > 0;
|
|
||||||
ImGui.Selectable( resourceNode.PossibleGamePaths.Length switch
|
|
||||||
{
|
|
||||||
0 => "(none)",
|
|
||||||
1 => resourceNode.GamePath.ToString(),
|
|
||||||
_ => "(multiple)",
|
|
||||||
}, false, hasGamePaths ? 0 : ImGuiSelectableFlags.Disabled, new Vector2( ImGui.GetContentRegionAvail().X, frameHeight ) );
|
|
||||||
if( hasGamePaths )
|
|
||||||
{
|
|
||||||
var allPaths = string.Join( '\n', resourceNode.PossibleGamePaths );
|
|
||||||
if( ImGui.IsItemClicked() )
|
|
||||||
{
|
|
||||||
ImGui.SetClipboardText( allPaths );
|
|
||||||
}
|
|
||||||
ImGuiUtil.HoverTooltip( $"{allPaths}\n\nClick to copy to clipboard." );
|
|
||||||
}
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
var hasFullPath = resourceNode.FullPath.FullName.Length > 0;
|
|
||||||
if( hasFullPath )
|
|
||||||
{
|
|
||||||
ImGui.Selectable( resourceNode.FullPath.ToString(), false, 0, new Vector2( ImGui.GetContentRegionAvail().X, frameHeight ) );
|
|
||||||
if( ImGui.IsItemClicked() )
|
|
||||||
{
|
|
||||||
ImGui.SetClipboardText( resourceNode.FullPath.ToString() );
|
|
||||||
}
|
|
||||||
ImGuiUtil.HoverTooltip( $"{resourceNode.FullPath}\n\nClick to copy to clipboard." );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.Selectable( "(unavailable)", false, ImGuiSelectableFlags.Disabled, new Vector2( ImGui.GetContentRegionAvail().X, frameHeight ) );
|
|
||||||
ImGuiUtil.HoverTooltip( "The actual path to this file is unavailable.\nIt may be managed by another plug-in." );
|
|
||||||
}
|
|
||||||
if( unfolded )
|
|
||||||
{
|
|
||||||
DrawNodes( resourceNode.Children, level + 1 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ResourceTree[]? _trees;
|
|
||||||
private HashSet<ResourceTree.Node>? _unfolded;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue