Make OnScreen a task.

This commit is contained in:
Ottermandias 2023-03-29 14:43:14 +02:00
parent 185be81e73
commit 3f86698615
7 changed files with 157 additions and 151 deletions

View file

@ -11,8 +11,10 @@ public partial class ModManager
public event ModPathChangeDelegate ModPathChanged;
// Rename/Move a mod directory.
// Updates all collection settings and sort order settings.
/// <summary>
/// Rename/Move a mod directory.
/// Updates all collection settings and sort order settings.
/// </summary>
public void MoveModDirectory(int idx, string newName)
{
var mod = this[idx];

View file

@ -1,4 +1,3 @@
using Penumbra.UI.Classes;
using System;
using System.Collections.Concurrent;
using System.IO;

View file

@ -12,19 +12,15 @@ using Penumbra.Util;
namespace Penumbra.Mods;
public class ModOptionEditor
{
private readonly CommunicatorService _communicator;
private readonly FilenameService _filenames;
private readonly SaveService _saveService;
public ModOptionEditor(CommunicatorService communicator, SaveService saveService, FilenameService filenames)
public ModOptionEditor(CommunicatorService communicator, SaveService saveService)
{
_communicator = communicator;
_saveService = saveService;
_filenames = filenames;
}
/// <summary> Change the type of a group given by mod and index to type, if possible. </summary>

View file

@ -17,29 +17,26 @@ namespace Penumbra.UI.AdvancedWindow;
public partial class ModEditWindow
{
private ResourceTreeViewer? _quickImportViewer;
private Dictionary<FullPath, IWritable?>? _quickImportWritables;
private Dictionary<(Utf8GamePath, IWritable?), QuickImportAction>? _quickImportActions;
private readonly ResourceTreeViewer _quickImportViewer;
private readonly Dictionary<FullPath, IWritable?> _quickImportWritables = new();
private readonly Dictionary<(Utf8GamePath, IWritable?), QuickImportAction> _quickImportActions = new();
private void DrawQuickImportTab()
{
using var tab = ImRaii.TabItem("Import from Screen");
if (!tab)
{
_quickImportActions = null;
_quickImportActions.Clear();
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();
}
private void OnQuickImportRefresh()
{
_quickImportWritables?.Clear();
_quickImportActions?.Clear();
_quickImportWritables.Clear();
_quickImportActions.Clear();
}
private void DrawQuickImportActions(ResourceNode resourceNode, Vector2 buttonSize)
@ -56,13 +53,19 @@ public partial class ModEditWindow
var file = _gameData.GetFile(path);
writable = file == null ? null : new RawGameFileWritable(file);
}
_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 ext = resourceNode.PossibleGamePaths.Length == 1 ? Path.GetExtension(resourceNode.GamePath.ToString()) : Path.GetExtension(fullPathStr);
_fileDialog.OpenSavePicker($"Export {Path.GetFileName(fullPathStr)} to...", ext, Path.GetFileNameWithoutExtension(fullPathStr), ext, (success, name) =>
var ext = resourceNode.PossibleGamePaths.Length == 1
? Path.GetExtension(resourceNode.GamePath.ToString())
: Path.GetExtension(fullPathStr);
_fileDialog.OpenSavePicker($"Export {Path.GetFileName(fullPathStr)} to...", ext, Path.GetFileNameWithoutExtension(fullPathStr), ext,
(success, name) =>
{
if (!success)
return;
@ -77,13 +80,16 @@ public partial class ModEditWindow
}
}, null, false);
}
ImGui.SameLine();
if (!_quickImportActions!.TryGetValue((resourceNode.GamePath, writable), out var quickImport))
{
quickImport = QuickImportAction.Prepare(this, resourceNode.GamePath, writable);
_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();
_quickImportActions.Remove((resourceNode.GamePath, writable));
@ -92,7 +98,8 @@ public partial class ModEditWindow
private record class RawFileWritable(string Path) : IWritable
{
public bool Valid => true;
public bool Valid
=> true;
public byte[] Write()
=> File.ReadAllBytes(Path);
@ -100,7 +107,8 @@ public partial class ModEditWindow
private record class RawGameFileWritable(FileResource FileResource) : IWritable
{
public bool Valid => true;
public bool Valid
=> true;
public byte[] Write()
=> FileResource.Data;
@ -117,9 +125,14 @@ public partial class ModEditWindow
private readonly string? _targetPath;
private readonly int _subDirs;
public string OptionName => _optionName;
public Utf8GamePath GamePath => _gamePath;
public bool CanExecute => !_gamePath.IsEmpty && _editor.Mod != null && _file != null && _targetPath != null;
public string OptionName
=> _optionName;
public Utf8GamePath GamePath
=> _gamePath;
public bool CanExecute
=> !_gamePath.IsEmpty && _editor.Mod != null && _file != null && _targetPath != null;
/// <summary>
/// Creates a non-executable QuickImportAction.
@ -151,30 +164,24 @@ public partial class ModEditWindow
{
var editor = owner._editor;
if (editor == null)
{
return new QuickImportAction(owner._editor, FallbackOptionName, gamePath);
}
var subMod = editor.Option;
var optionName = subMod!.FullName;
if (gamePath.IsEmpty || file == null || editor.FileEditor.Changes)
{
return new QuickImportAction(editor, optionName, gamePath);
}
if (subMod.Files.ContainsKey(gamePath) || subMod.FileSwaps.ContainsKey(gamePath))
{
return new QuickImportAction(editor, optionName, gamePath);
}
var mod = owner._mod;
if (mod == null)
{
return new QuickImportAction(editor, optionName, gamePath);
}
var (preferredPath, subDirs) = GetPreferredPath(mod, subMod);
var targetPath = new FullPath(Path.Combine(preferredPath.FullName, gamePath.ToString())).FullName;
if (File.Exists(targetPath))
{
return new QuickImportAction(editor, optionName, gamePath);
}
return new QuickImportAction(optionName, gamePath, editor, file, targetPath, subDirs);
}
@ -182,19 +189,19 @@ public partial class ModEditWindow
public FileRegistry Execute()
{
if (!CanExecute)
{
throw new InvalidOperationException();
}
var directory = Path.GetDirectoryName(_targetPath);
if (directory != null)
{
Directory.CreateDirectory(directory);
}
File.WriteAllBytes(_targetPath!, _file!.Write());
_editor.FileEditor.Revert(_editor.Mod!, _editor.Option!);
var fileRegistry = _editor.Files.Available.First(file => file.File.FullName == _targetPath);
_editor.FileEditor.AddPathsToSelected(_editor.Option!, new []{ fileRegistry }, _subDirs);
_editor.FileEditor.Apply(_editor.Mod!, (SubMod) _editor.Option!);
_editor.FileEditor.AddPathsToSelected(_editor.Option!, new[]
{
fileRegistry,
}, _subDirs);
_editor.FileEditor.Apply(_editor.Mod!, (SubMod)_editor.Option!);
return fileRegistry;
}

View file

@ -17,7 +17,6 @@ using Penumbra.Mods;
using Penumbra.String.Classes;
using Penumbra.UI.Classes;
using Penumbra.Util;
using static Penumbra.Mods.Mod;
namespace Penumbra.UI.AdvancedWindow;
@ -28,7 +27,6 @@ public partial class ModEditWindow : Window, IDisposable
private readonly ModEditor _editor;
private readonly Configuration _config;
private readonly ItemSwapTab _itemSwapTab;
private readonly ResourceTreeFactory _resourceTreeFactory;
private readonly DataManager _gameData;
private Mod? _mod;
@ -56,7 +54,7 @@ public partial class ModEditWindow : Window, IDisposable
}
public void ChangeOption(SubMod? subMod)
=> _editor.LoadOption(subMod?.GroupIdx ?? -1, subMod?.GroupIdx ?? 0);
=> _editor.LoadOption(subMod?.GroupIdx ?? -1, subMod?.OptionIdx ?? 0);
public void UpdateModels()
{
@ -499,7 +497,6 @@ public partial class ModEditWindow : Window, IDisposable
_config = config;
_editor = editor;
_gameData = gameData;
_resourceTreeFactory = resourceTreeFactory;
_fileDialog = fileDialog;
_materialTab = new FileEditor<MtrlTab>(this, gameData, config, _fileDialog, "Materials", ".mtrl",
() => _editor.Files.Mtrl, DrawMaterialPanel, () => _mod?.ModPath.FullName ?? string.Empty,
@ -509,6 +506,7 @@ public partial class ModEditWindow : Window, IDisposable
_shaderPackageTab = new FileEditor<ShpkTab>(this, gameData, config, _fileDialog, "Shader Packages", ".shpk",
() => _editor.Files.Shpk, DrawShaderPackagePanel, () => _mod?.ModPath.FullName ?? string.Empty, null);
_center = new CombinedTexture(_left, _right);
_quickImportViewer = new ResourceTreeViewer(_config, resourceTreeFactory, 2, OnQuickImportRefresh, DrawQuickImportActions);
}
public void Dispose()

View file

@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Threading.Tasks;
using Dalamud.Interface;
using ImGuiNET;
using OtterGui.Raii;
using OtterGui;
using Penumbra.Interop.ResourceTree;
using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow;
@ -13,58 +15,49 @@ public class ResourceTreeViewer
{
private readonly Configuration _config;
private readonly ResourceTreeFactory _treeFactory;
private readonly string _name;
private readonly int _actionCapacity;
private readonly Action _onRefresh;
private readonly Action<ResourceNode, Vector2> _drawActions;
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)
{
_config = config;
_treeFactory = treeFactory;
_name = name;
_actionCapacity = actionCapacity;
_onRefresh = onRefresh;
_drawActions = drawActions;
_unfolded = new HashSet<ResourceNode>();
_trees = null;
}
public void Draw()
{
if (ImGui.Button("Refresh Character List"))
{
try
{
_trees = _treeFactory.FromObjectTable();
}
catch (Exception e)
{
Penumbra.Log.Error($"Could not get character list for {_name}:\n{e}");
_trees = Array.Empty<ResourceTree>();
}
if (ImGui.Button("Refresh Character List") || _task == null)
_task = RefreshCharacterList();
_unfolded.Clear();
_onRefresh();
}
try
{
_trees ??= _treeFactory.FromObjectTable();
}
catch (Exception e)
{
Penumbra.Log.Error($"Could not get character list for {_name}:\n{e}");
_trees ??= Array.Empty<ResourceTree>();
}
using var child = ImRaii.Child("##Data");
if (!child)
return;
var textColorNonPlayer = ImGui.GetColorU32(ImGuiCol.Text);
var textColorPlayer = (textColorNonPlayer & 0xFF000000u) | ((textColorNonPlayer & 0x00FEFEFE) >> 1) | 0x8000u; // Half green
foreach (var (tree, index) in _trees.WithIndex())
if (!_task.IsCompleted)
{
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())
{
using (var c = ImRaii.PushColor(ImGuiCol.Text, tree.PlayerRelated ? textColorPlayer : textColorNonPlayer))
{
@ -92,6 +85,21 @@ public class ResourceTreeViewer
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)
{
@ -105,7 +113,7 @@ public class ResourceTreeViewer
using var id = ImRaii.PushId(index);
ImGui.TableNextColumn();
var unfolded = _unfolded!.Contains(resourceNode);
var unfolded = _unfolded.Contains(resourceNode);
using (var indent = ImRaii.PushIndent(level))
{
ImGui.TableHeader((resourceNode.Children.Count > 0 ? unfolded ? "[-] " : "[+] " : string.Empty) + resourceNode.Name);

View file

@ -8,21 +8,17 @@ namespace Penumbra.UI.Tabs;
public class OnScreenTab : ITab
{
private readonly Configuration _config;
private readonly ResourceTreeFactory _treeFactory;
private ResourceTreeViewer? _viewer;
private ResourceTreeViewer _viewer;
public OnScreenTab(Configuration config, ResourceTreeFactory treeFactory)
{
_config = config;
_treeFactory = treeFactory;
_viewer = new ResourceTreeViewer(_config, treeFactory, 0, delegate { }, delegate { });
}
public ReadOnlySpan<byte> Label
=> "On-Screen"u8;
public void DrawContent()
{
_viewer ??= new ResourceTreeViewer(_config, _treeFactory, "On-Screen tab", 0, delegate { }, delegate { });
_viewer.Draw();
}
=> _viewer.Draw();
}