mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-15 05:04:15 +01:00
Make OnScreen a task.
This commit is contained in:
parent
185be81e73
commit
3f86698615
7 changed files with 157 additions and 151 deletions
|
|
@ -11,8 +11,10 @@ public partial class ModManager
|
||||||
|
|
||||||
public event ModPathChangeDelegate ModPathChanged;
|
public event ModPathChangeDelegate ModPathChanged;
|
||||||
|
|
||||||
// Rename/Move a mod directory.
|
/// <summary>
|
||||||
// Updates all collection settings and sort order settings.
|
/// Rename/Move a mod directory.
|
||||||
|
/// Updates all collection settings and sort order settings.
|
||||||
|
/// </summary>
|
||||||
public void MoveModDirectory(int idx, string newName)
|
public void MoveModDirectory(int idx, string newName)
|
||||||
{
|
{
|
||||||
var mod = this[idx];
|
var mod = this[idx];
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
using Penumbra.UI.Classes;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
|
||||||
|
|
@ -12,19 +12,15 @@ using Penumbra.Util;
|
||||||
|
|
||||||
namespace Penumbra.Mods;
|
namespace Penumbra.Mods;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class ModOptionEditor
|
public class ModOptionEditor
|
||||||
{
|
{
|
||||||
private readonly CommunicatorService _communicator;
|
private readonly CommunicatorService _communicator;
|
||||||
private readonly FilenameService _filenames;
|
|
||||||
private readonly SaveService _saveService;
|
private readonly SaveService _saveService;
|
||||||
|
|
||||||
public ModOptionEditor(CommunicatorService communicator, SaveService saveService, FilenameService filenames)
|
public ModOptionEditor(CommunicatorService communicator, SaveService saveService)
|
||||||
{
|
{
|
||||||
_communicator = communicator;
|
_communicator = communicator;
|
||||||
_saveService = saveService;
|
_saveService = saveService;
|
||||||
_filenames = filenames;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change the type of a group given by mod and index to type, if possible. </summary>
|
/// <summary> Change the type of a group given by mod and index to type, if possible. </summary>
|
||||||
|
|
@ -209,7 +205,7 @@ public class ModOptionEditor
|
||||||
/// <summary> Add a new empty option of the given name for the given group. </summary>
|
/// <summary> Add a new empty option of the given name for the given group. </summary>
|
||||||
public void AddOption(Mod mod, int groupIdx, string newName)
|
public void AddOption(Mod mod, int groupIdx, string newName)
|
||||||
{
|
{
|
||||||
var group = mod._groups[groupIdx];
|
var group = mod._groups[groupIdx];
|
||||||
var subMod = new SubMod(mod) { Name = newName };
|
var subMod = new SubMod(mod) { Name = newName };
|
||||||
subMod.SetPosition(groupIdx, group.Count);
|
subMod.SetPosition(groupIdx, group.Count);
|
||||||
switch (group)
|
switch (group)
|
||||||
|
|
@ -319,7 +315,7 @@ public class ModOptionEditor
|
||||||
/// <summary> Add additional file redirections to a given option, keeping already existing ones. Only fires an event if anything is actually added.</summary>
|
/// <summary> Add additional file redirections to a given option, keeping already existing ones. Only fires an event if anything is actually added.</summary>
|
||||||
public void OptionAddFiles(Mod mod, int groupIdx, int optionIdx, Dictionary<Utf8GamePath, FullPath> additions)
|
public void OptionAddFiles(Mod mod, int groupIdx, int optionIdx, Dictionary<Utf8GamePath, FullPath> additions)
|
||||||
{
|
{
|
||||||
var subMod = GetSubMod(mod, groupIdx, optionIdx);
|
var subMod = GetSubMod(mod, groupIdx, optionIdx);
|
||||||
var oldCount = subMod.FileData.Count;
|
var oldCount = subMod.FileData.Count;
|
||||||
subMod.FileData.AddFrom(additions);
|
subMod.FileData.AddFrom(additions);
|
||||||
if (oldCount != subMod.FileData.Count)
|
if (oldCount != subMod.FileData.Count)
|
||||||
|
|
|
||||||
|
|
@ -17,29 +17,26 @@ namespace Penumbra.UI.AdvancedWindow;
|
||||||
|
|
||||||
public partial class ModEditWindow
|
public partial class ModEditWindow
|
||||||
{
|
{
|
||||||
private ResourceTreeViewer? _quickImportViewer;
|
private readonly ResourceTreeViewer _quickImportViewer;
|
||||||
private Dictionary<FullPath, IWritable?>? _quickImportWritables;
|
private readonly Dictionary<FullPath, IWritable?> _quickImportWritables = new();
|
||||||
private Dictionary<(Utf8GamePath, IWritable?), QuickImportAction>? _quickImportActions;
|
private readonly Dictionary<(Utf8GamePath, IWritable?), QuickImportAction> _quickImportActions = new();
|
||||||
|
|
||||||
private void DrawQuickImportTab()
|
private void DrawQuickImportTab()
|
||||||
{
|
{
|
||||||
using var tab = ImRaii.TabItem("Import from Screen");
|
using var tab = ImRaii.TabItem("Import from Screen");
|
||||||
if (!tab)
|
if (!tab)
|
||||||
{
|
{
|
||||||
_quickImportActions = null;
|
_quickImportActions.Clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_quickImportViewer ??= new ResourceTreeViewer(_config, _resourceTreeFactory, "Import from Screen tab", 2, OnQuickImportRefresh, DrawQuickImportActions);
|
|
||||||
_quickImportWritables ??= new Dictionary<FullPath, IWritable?>();
|
|
||||||
_quickImportActions ??= new Dictionary<(Utf8GamePath, IWritable?), QuickImportAction>();
|
|
||||||
|
|
||||||
_quickImportViewer.Draw();
|
_quickImportViewer.Draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnQuickImportRefresh()
|
private void OnQuickImportRefresh()
|
||||||
{
|
{
|
||||||
_quickImportWritables?.Clear();
|
_quickImportWritables.Clear();
|
||||||
_quickImportActions?.Clear();
|
_quickImportActions.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawQuickImportActions(ResourceNode resourceNode, Vector2 buttonSize)
|
private void DrawQuickImportActions(ResourceNode resourceNode, Vector2 buttonSize)
|
||||||
|
|
@ -56,34 +53,43 @@ public partial class ModEditWindow
|
||||||
var file = _gameData.GetFile(path);
|
var file = _gameData.GetFile(path);
|
||||||
writable = file == null ? null : new RawGameFileWritable(file);
|
writable = file == null ? null : new RawGameFileWritable(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
_quickImportWritables.Add(resourceNode.FullPath, writable);
|
_quickImportWritables.Add(resourceNode.FullPath, writable);
|
||||||
}
|
}
|
||||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Save.ToIconString(), buttonSize, "Export this file.", resourceNode.FullPath.FullName.Length == 0 || 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
|
||||||
_fileDialog.OpenSavePicker($"Export {Path.GetFileName(fullPathStr)} to...", ext, Path.GetFileNameWithoutExtension(fullPathStr), ext, (success, name) =>
|
? Path.GetExtension(resourceNode.GamePath.ToString())
|
||||||
{
|
: Path.GetExtension(fullPathStr);
|
||||||
if (!success)
|
_fileDialog.OpenSavePicker($"Export {Path.GetFileName(fullPathStr)} to...", ext, Path.GetFileNameWithoutExtension(fullPathStr), ext,
|
||||||
return;
|
(success, name) =>
|
||||||
|
{
|
||||||
|
if (!success)
|
||||||
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
File.WriteAllBytes(name, writable!.Write());
|
File.WriteAllBytes(name, writable!.Write());
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Penumbra.Log.Error($"Could not export {fullPathStr}:\n{e}");
|
Penumbra.Log.Error($"Could not export {fullPathStr}:\n{e}");
|
||||||
}
|
}
|
||||||
}, null, false);
|
}, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (!_quickImportActions!.TryGetValue((resourceNode.GamePath, writable), out var quickImport))
|
if (!_quickImportActions!.TryGetValue((resourceNode.GamePath, writable), out var quickImport))
|
||||||
{
|
{
|
||||||
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(), buttonSize, $"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));
|
||||||
|
|
@ -92,7 +98,8 @@ public partial class ModEditWindow
|
||||||
|
|
||||||
private record class RawFileWritable(string Path) : IWritable
|
private record class RawFileWritable(string Path) : IWritable
|
||||||
{
|
{
|
||||||
public bool Valid => true;
|
public bool Valid
|
||||||
|
=> true;
|
||||||
|
|
||||||
public byte[] Write()
|
public byte[] Write()
|
||||||
=> File.ReadAllBytes(Path);
|
=> File.ReadAllBytes(Path);
|
||||||
|
|
@ -100,7 +107,8 @@ public partial class ModEditWindow
|
||||||
|
|
||||||
private record class RawGameFileWritable(FileResource FileResource) : IWritable
|
private record class RawGameFileWritable(FileResource FileResource) : IWritable
|
||||||
{
|
{
|
||||||
public bool Valid => true;
|
public bool Valid
|
||||||
|
=> true;
|
||||||
|
|
||||||
public byte[] Write()
|
public byte[] Write()
|
||||||
=> FileResource.Data;
|
=> FileResource.Data;
|
||||||
|
|
@ -110,16 +118,21 @@ public partial class ModEditWindow
|
||||||
{
|
{
|
||||||
public const string FallbackOptionName = "the current option";
|
public const string FallbackOptionName = "the current option";
|
||||||
|
|
||||||
private readonly string _optionName;
|
private readonly string _optionName;
|
||||||
private readonly Utf8GamePath _gamePath;
|
private readonly Utf8GamePath _gamePath;
|
||||||
private readonly ModEditor _editor;
|
private readonly ModEditor _editor;
|
||||||
private readonly IWritable? _file;
|
private readonly IWritable? _file;
|
||||||
private readonly string? _targetPath;
|
private readonly string? _targetPath;
|
||||||
private readonly int _subDirs;
|
private readonly int _subDirs;
|
||||||
|
|
||||||
public string OptionName => _optionName;
|
public string OptionName
|
||||||
public Utf8GamePath GamePath => _gamePath;
|
=> _optionName;
|
||||||
public bool CanExecute => !_gamePath.IsEmpty && _editor.Mod != null && _file != null && _targetPath != null;
|
|
||||||
|
public Utf8GamePath GamePath
|
||||||
|
=> _gamePath;
|
||||||
|
|
||||||
|
public bool CanExecute
|
||||||
|
=> !_gamePath.IsEmpty && _editor.Mod != null && _file != null && _targetPath != null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a non-executable QuickImportAction.
|
/// Creates a non-executable QuickImportAction.
|
||||||
|
|
@ -127,11 +140,11 @@ public partial class ModEditWindow
|
||||||
private QuickImportAction(ModEditor editor, string optionName, Utf8GamePath gamePath)
|
private QuickImportAction(ModEditor editor, string optionName, Utf8GamePath gamePath)
|
||||||
{
|
{
|
||||||
_optionName = optionName;
|
_optionName = optionName;
|
||||||
_gamePath = gamePath;
|
_gamePath = gamePath;
|
||||||
_editor = editor;
|
_editor = editor;
|
||||||
_file = null;
|
_file = null;
|
||||||
_targetPath = null;
|
_targetPath = null;
|
||||||
_subDirs = 0;
|
_subDirs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -140,41 +153,35 @@ public partial class ModEditWindow
|
||||||
private QuickImportAction(string optionName, Utf8GamePath gamePath, ModEditor editor, IWritable file, string targetPath, int subDirs)
|
private QuickImportAction(string optionName, Utf8GamePath gamePath, ModEditor editor, IWritable file, string targetPath, int subDirs)
|
||||||
{
|
{
|
||||||
_optionName = optionName;
|
_optionName = optionName;
|
||||||
_gamePath = gamePath;
|
_gamePath = gamePath;
|
||||||
_editor = editor;
|
_editor = editor;
|
||||||
_file = file;
|
_file = file;
|
||||||
_targetPath = targetPath;
|
_targetPath = targetPath;
|
||||||
_subDirs = subDirs;
|
_subDirs = subDirs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static QuickImportAction Prepare(ModEditWindow owner, Utf8GamePath gamePath, IWritable? file)
|
public static QuickImportAction Prepare(ModEditWindow owner, Utf8GamePath gamePath, IWritable? file)
|
||||||
{
|
{
|
||||||
var editor = owner._editor;
|
var editor = owner._editor;
|
||||||
if (editor == null)
|
if (editor == null)
|
||||||
{
|
|
||||||
return new QuickImportAction(owner._editor, FallbackOptionName, gamePath);
|
return new QuickImportAction(owner._editor, FallbackOptionName, gamePath);
|
||||||
}
|
|
||||||
var subMod = editor.Option;
|
var subMod = editor.Option;
|
||||||
var optionName = subMod!.FullName;
|
var optionName = subMod!.FullName;
|
||||||
if (gamePath.IsEmpty || file == null || editor.FileEditor.Changes)
|
if (gamePath.IsEmpty || file == null || editor.FileEditor.Changes)
|
||||||
{
|
|
||||||
return new QuickImportAction(editor, optionName, gamePath);
|
return new QuickImportAction(editor, optionName, gamePath);
|
||||||
}
|
|
||||||
if (subMod.Files.ContainsKey(gamePath) || subMod.FileSwaps.ContainsKey(gamePath))
|
if (subMod.Files.ContainsKey(gamePath) || subMod.FileSwaps.ContainsKey(gamePath))
|
||||||
{
|
|
||||||
return new QuickImportAction(editor, optionName, gamePath);
|
return new QuickImportAction(editor, optionName, gamePath);
|
||||||
}
|
|
||||||
var mod = owner._mod;
|
var mod = owner._mod;
|
||||||
if (mod == null)
|
if (mod == null)
|
||||||
{
|
|
||||||
return new QuickImportAction(editor, optionName, gamePath);
|
return new QuickImportAction(editor, optionName, gamePath);
|
||||||
}
|
|
||||||
var (preferredPath, subDirs) = GetPreferredPath(mod, subMod);
|
var (preferredPath, subDirs) = GetPreferredPath(mod, subMod);
|
||||||
var targetPath = new FullPath(Path.Combine(preferredPath.FullName, gamePath.ToString())).FullName;
|
var targetPath = new FullPath(Path.Combine(preferredPath.FullName, gamePath.ToString())).FullName;
|
||||||
if (File.Exists(targetPath))
|
if (File.Exists(targetPath))
|
||||||
{
|
|
||||||
return new QuickImportAction(editor, optionName, gamePath);
|
return new QuickImportAction(editor, optionName, gamePath);
|
||||||
}
|
|
||||||
|
|
||||||
return new QuickImportAction(optionName, gamePath, editor, file, targetPath, subDirs);
|
return new QuickImportAction(optionName, gamePath, editor, file, targetPath, subDirs);
|
||||||
}
|
}
|
||||||
|
|
@ -182,26 +189,26 @@ public partial class ModEditWindow
|
||||||
public FileRegistry Execute()
|
public FileRegistry Execute()
|
||||||
{
|
{
|
||||||
if (!CanExecute)
|
if (!CanExecute)
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
|
||||||
var directory = Path.GetDirectoryName(_targetPath);
|
var directory = Path.GetDirectoryName(_targetPath);
|
||||||
if (directory != null)
|
if (directory != null)
|
||||||
{
|
|
||||||
Directory.CreateDirectory(directory);
|
Directory.CreateDirectory(directory);
|
||||||
}
|
|
||||||
File.WriteAllBytes(_targetPath!, _file!.Write());
|
File.WriteAllBytes(_targetPath!, _file!.Write());
|
||||||
_editor.FileEditor.Revert(_editor.Mod!, _editor.Option!);
|
_editor.FileEditor.Revert(_editor.Mod!, _editor.Option!);
|
||||||
var fileRegistry = _editor.Files.Available.First(file => file.File.FullName == _targetPath);
|
var fileRegistry = _editor.Files.Available.First(file => file.File.FullName == _targetPath);
|
||||||
_editor.FileEditor.AddPathsToSelected(_editor.Option!, new []{ fileRegistry }, _subDirs);
|
_editor.FileEditor.AddPathsToSelected(_editor.Option!, new[]
|
||||||
_editor.FileEditor.Apply(_editor.Mod!, (SubMod) _editor.Option!);
|
{
|
||||||
|
fileRegistry,
|
||||||
|
}, _subDirs);
|
||||||
|
_editor.FileEditor.Apply(_editor.Mod!, (SubMod)_editor.Option!);
|
||||||
|
|
||||||
return fileRegistry;
|
return fileRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (DirectoryInfo, int) GetPreferredPath(Mod mod, ISubMod subMod)
|
private static (DirectoryInfo, int) GetPreferredPath(Mod mod, ISubMod subMod)
|
||||||
{
|
{
|
||||||
var path = mod.ModPath;
|
var path = mod.ModPath;
|
||||||
var subDirs = 0;
|
var subDirs = 0;
|
||||||
if (subMod == mod.Default)
|
if (subMod == mod.Default)
|
||||||
return (path, subDirs);
|
return (path, subDirs);
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ using Penumbra.Mods;
|
||||||
using Penumbra.String.Classes;
|
using Penumbra.String.Classes;
|
||||||
using Penumbra.UI.Classes;
|
using Penumbra.UI.Classes;
|
||||||
using Penumbra.Util;
|
using Penumbra.Util;
|
||||||
using static Penumbra.Mods.Mod;
|
|
||||||
|
|
||||||
namespace Penumbra.UI.AdvancedWindow;
|
namespace Penumbra.UI.AdvancedWindow;
|
||||||
|
|
||||||
|
|
@ -25,11 +24,10 @@ public partial class ModEditWindow : Window, IDisposable
|
||||||
{
|
{
|
||||||
private const string WindowBaseLabel = "###SubModEdit";
|
private const string WindowBaseLabel = "###SubModEdit";
|
||||||
|
|
||||||
private readonly ModEditor _editor;
|
private readonly ModEditor _editor;
|
||||||
private readonly Configuration _config;
|
private readonly Configuration _config;
|
||||||
private readonly ItemSwapTab _itemSwapTab;
|
private readonly ItemSwapTab _itemSwapTab;
|
||||||
private readonly ResourceTreeFactory _resourceTreeFactory;
|
private readonly DataManager _gameData;
|
||||||
private readonly DataManager _gameData;
|
|
||||||
|
|
||||||
private Mod? _mod;
|
private Mod? _mod;
|
||||||
private Vector2 _iconSize = Vector2.Zero;
|
private Vector2 _iconSize = Vector2.Zero;
|
||||||
|
|
@ -56,7 +54,7 @@ public partial class ModEditWindow : Window, IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ChangeOption(SubMod? subMod)
|
public void ChangeOption(SubMod? subMod)
|
||||||
=> _editor.LoadOption(subMod?.GroupIdx ?? -1, subMod?.GroupIdx ?? 0);
|
=> _editor.LoadOption(subMod?.GroupIdx ?? -1, subMod?.OptionIdx ?? 0);
|
||||||
|
|
||||||
public void UpdateModels()
|
public void UpdateModels()
|
||||||
{
|
{
|
||||||
|
|
@ -495,12 +493,11 @@ public partial class ModEditWindow : Window, IDisposable
|
||||||
Configuration config, ModEditor editor, ResourceTreeFactory resourceTreeFactory)
|
Configuration config, ModEditor editor, ResourceTreeFactory resourceTreeFactory)
|
||||||
: base(WindowBaseLabel)
|
: base(WindowBaseLabel)
|
||||||
{
|
{
|
||||||
_itemSwapTab = itemSwapTab;
|
_itemSwapTab = itemSwapTab;
|
||||||
_config = config;
|
_config = config;
|
||||||
_editor = editor;
|
_editor = editor;
|
||||||
_gameData = gameData;
|
_gameData = gameData;
|
||||||
_resourceTreeFactory = resourceTreeFactory;
|
_fileDialog = fileDialog;
|
||||||
_fileDialog = fileDialog;
|
|
||||||
_materialTab = new FileEditor<MtrlTab>(this, gameData, config, _fileDialog, "Materials", ".mtrl",
|
_materialTab = new FileEditor<MtrlTab>(this, gameData, config, _fileDialog, "Materials", ".mtrl",
|
||||||
() => _editor.Files.Mtrl, DrawMaterialPanel, () => _mod?.ModPath.FullName ?? string.Empty,
|
() => _editor.Files.Mtrl, DrawMaterialPanel, () => _mod?.ModPath.FullName ?? string.Empty,
|
||||||
bytes => new MtrlTab(this, new MtrlFile(bytes)));
|
bytes => new MtrlTab(this, new MtrlFile(bytes)));
|
||||||
|
|
@ -508,7 +505,8 @@ public partial class ModEditWindow : Window, IDisposable
|
||||||
() => _editor.Files.Mdl, DrawModelPanel, () => _mod?.ModPath.FullName ?? string.Empty, null);
|
() => _editor.Files.Mdl, DrawModelPanel, () => _mod?.ModPath.FullName ?? string.Empty, null);
|
||||||
_shaderPackageTab = new FileEditor<ShpkTab>(this, gameData, config, _fileDialog, "Shader Packages", ".shpk",
|
_shaderPackageTab = new FileEditor<ShpkTab>(this, gameData, config, _fileDialog, "Shader Packages", ".shpk",
|
||||||
() => _editor.Files.Shpk, DrawShaderPackagePanel, () => _mod?.ModPath.FullName ?? string.Empty, null);
|
() => _editor.Files.Shpk, DrawShaderPackagePanel, () => _mod?.ModPath.FullName ?? string.Empty, null);
|
||||||
_center = new CombinedTexture(_left, _right);
|
_center = new CombinedTexture(_left, _right);
|
||||||
|
_quickImportViewer = new ResourceTreeViewer(_config, resourceTreeFactory, 2, OnQuickImportRefresh, DrawQuickImportActions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using OtterGui.Raii;
|
using OtterGui.Raii;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
using Penumbra.Interop.ResourceTree;
|
using Penumbra.Interop.ResourceTree;
|
||||||
|
using Penumbra.UI.Classes;
|
||||||
|
|
||||||
namespace Penumbra.UI.AdvancedWindow;
|
namespace Penumbra.UI.AdvancedWindow;
|
||||||
|
|
||||||
|
|
@ -13,86 +15,92 @@ public class ResourceTreeViewer
|
||||||
{
|
{
|
||||||
private readonly Configuration _config;
|
private readonly Configuration _config;
|
||||||
private readonly ResourceTreeFactory _treeFactory;
|
private readonly ResourceTreeFactory _treeFactory;
|
||||||
private readonly string _name;
|
|
||||||
private readonly int _actionCapacity;
|
private readonly int _actionCapacity;
|
||||||
private readonly Action _onRefresh;
|
private readonly Action _onRefresh;
|
||||||
private readonly Action<ResourceNode, Vector2> _drawActions;
|
private readonly Action<ResourceNode, Vector2> _drawActions;
|
||||||
private readonly HashSet<ResourceNode> _unfolded;
|
private readonly HashSet<ResourceNode> _unfolded;
|
||||||
private ResourceTree[]? _trees;
|
|
||||||
|
|
||||||
public ResourceTreeViewer(Configuration config, ResourceTreeFactory treeFactory, string name, int actionCapacity, Action onRefresh,
|
private Task<ResourceTree[]>? _task;
|
||||||
|
|
||||||
|
public ResourceTreeViewer(Configuration config, ResourceTreeFactory treeFactory, int actionCapacity, Action onRefresh,
|
||||||
Action<ResourceNode, Vector2> drawActions)
|
Action<ResourceNode, Vector2> drawActions)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
_treeFactory = treeFactory;
|
_treeFactory = treeFactory;
|
||||||
_name = name;
|
|
||||||
_actionCapacity = actionCapacity;
|
_actionCapacity = actionCapacity;
|
||||||
_onRefresh = onRefresh;
|
_onRefresh = onRefresh;
|
||||||
_drawActions = drawActions;
|
_drawActions = drawActions;
|
||||||
_unfolded = new HashSet<ResourceNode>();
|
_unfolded = new HashSet<ResourceNode>();
|
||||||
_trees = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw()
|
public void Draw()
|
||||||
{
|
{
|
||||||
if (ImGui.Button("Refresh Character List"))
|
if (ImGui.Button("Refresh Character List") || _task == null)
|
||||||
{
|
_task = RefreshCharacterList();
|
||||||
try
|
|
||||||
{
|
|
||||||
_trees = _treeFactory.FromObjectTable();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Penumbra.Log.Error($"Could not get character list for {_name}:\n{e}");
|
|
||||||
_trees = Array.Empty<ResourceTree>();
|
|
||||||
}
|
|
||||||
|
|
||||||
_unfolded.Clear();
|
using var child = ImRaii.Child("##Data");
|
||||||
_onRefresh();
|
if (!child)
|
||||||
}
|
return;
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_trees ??= _treeFactory.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 textColorNonPlayer = ImGui.GetColorU32(ImGuiCol.Text);
|
||||||
var textColorPlayer = (textColorNonPlayer & 0xFF000000u) | ((textColorNonPlayer & 0x00FEFEFE) >> 1) | 0x8000u; // Half green
|
var textColorPlayer = (textColorNonPlayer & 0xFF000000u) | ((textColorNonPlayer & 0x00FEFEFE) >> 1) | 0x8000u; // Half green
|
||||||
|
if (!_task.IsCompleted)
|
||||||
foreach (var (tree, index) in _trees.WithIndex())
|
|
||||||
{
|
{
|
||||||
using (var c = ImRaii.PushColor(ImGuiCol.Text, tree.PlayerRelated ? textColorPlayer : textColorNonPlayer))
|
ImGui.NewLine();
|
||||||
|
ImGui.TextUnformatted("Calculating character list...");
|
||||||
|
}
|
||||||
|
else if (_task.Exception != null)
|
||||||
|
{
|
||||||
|
ImGui.NewLine();
|
||||||
|
using var color = ImRaii.PushColor(ImGuiCol.Text, Colors.RegexWarningBorder);
|
||||||
|
ImGui.TextUnformatted($"Error during calculation of character list:\n\n{_task.Exception}");
|
||||||
|
}
|
||||||
|
else if (_task.IsCompletedSuccessfully)
|
||||||
|
{
|
||||||
|
foreach (var (tree, index) in _task.Result.WithIndex())
|
||||||
{
|
{
|
||||||
if (!ImGui.CollapsingHeader($"{tree.Name}##{index}", index == 0 ? ImGuiTreeNodeFlags.DefaultOpen : 0))
|
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;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 Task<ResourceTree[]> RefreshCharacterList()
|
||||||
|
=> Task.Run(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _treeFactory.FromObjectTable();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_unfolded.Clear();
|
||||||
|
_onRefresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
private void DrawNodes(IEnumerable<ResourceNode> resourceNodes, int level)
|
private void DrawNodes(IEnumerable<ResourceNode> resourceNodes, int level)
|
||||||
{
|
{
|
||||||
var debugMode = _config.DebugMode;
|
var debugMode = _config.DebugMode;
|
||||||
|
|
@ -105,7 +113,7 @@ public class ResourceTreeViewer
|
||||||
|
|
||||||
using var id = ImRaii.PushId(index);
|
using var id = ImRaii.PushId(index);
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
var unfolded = _unfolded!.Contains(resourceNode);
|
var unfolded = _unfolded.Contains(resourceNode);
|
||||||
using (var indent = ImRaii.PushIndent(level))
|
using (var indent = ImRaii.PushIndent(level))
|
||||||
{
|
{
|
||||||
ImGui.TableHeader((resourceNode.Children.Count > 0 ? unfolded ? "[-] " : "[+] " : string.Empty) + resourceNode.Name);
|
ImGui.TableHeader((resourceNode.Children.Count > 0 ? unfolded ? "[-] " : "[+] " : string.Empty) + resourceNode.Name);
|
||||||
|
|
|
||||||
|
|
@ -7,22 +7,18 @@ namespace Penumbra.UI.Tabs;
|
||||||
|
|
||||||
public class OnScreenTab : ITab
|
public class OnScreenTab : ITab
|
||||||
{
|
{
|
||||||
private readonly Configuration _config;
|
private readonly Configuration _config;
|
||||||
private readonly ResourceTreeFactory _treeFactory;
|
private ResourceTreeViewer _viewer;
|
||||||
private ResourceTreeViewer? _viewer;
|
|
||||||
|
|
||||||
public OnScreenTab(Configuration config, ResourceTreeFactory treeFactory)
|
public OnScreenTab(Configuration config, ResourceTreeFactory treeFactory)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
_treeFactory = treeFactory;
|
_viewer = new ResourceTreeViewer(_config, treeFactory, 0, delegate { }, delegate { });
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> Label
|
public ReadOnlySpan<byte> Label
|
||||||
=> "On-Screen"u8;
|
=> "On-Screen"u8;
|
||||||
|
|
||||||
public void DrawContent()
|
public void DrawContent()
|
||||||
{
|
=> _viewer.Draw();
|
||||||
_viewer ??= new ResourceTreeViewer(_config, _treeFactory, "On-Screen tab", 0, delegate { }, delegate { });
|
|
||||||
_viewer.Draw();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue