mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-27 02:49:18 +01:00
TexWidget: add test/examples for new APIs
This commit is contained in:
parent
3fe2920e92
commit
eb4d9aba7e
1 changed files with 361 additions and 80 deletions
|
|
@ -1,12 +1,16 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.Internal.SharableTextures;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Storage.Assets;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using ImGuiNET;
|
||||
using ImGuiScene;
|
||||
using Serilog;
|
||||
|
||||
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
|
||||
|
||||
|
|
@ -15,23 +19,24 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
|
|||
/// </summary>
|
||||
internal class TexWidget : IDataWindowWidget
|
||||
{
|
||||
private readonly List<IDalamudTextureWrap> addedTextures = new();
|
||||
|
||||
private readonly List<TextureEntry> addedTextures = new();
|
||||
|
||||
private string iconId = "18";
|
||||
private bool hiRes = true;
|
||||
private bool hq = false;
|
||||
private bool keepAlive = false;
|
||||
private string inputTexPath = string.Empty;
|
||||
private string inputFilePath = string.Empty;
|
||||
private Vector2 inputTexUv0 = Vector2.Zero;
|
||||
private Vector2 inputTexUv1 = Vector2.One;
|
||||
private Vector4 inputTintCol = Vector4.One;
|
||||
private Vector2 inputTexScale = Vector2.Zero;
|
||||
private TextureManager textureManager = null!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string[]? CommandShortcuts { get; init; } = { "tex", "texture" };
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string DisplayName { get; init; } = "Tex";
|
||||
public string DisplayName { get; init; } = "Tex";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Ready { get; set; }
|
||||
|
|
@ -39,91 +44,81 @@ internal class TexWidget : IDataWindowWidget
|
|||
/// <inheritdoc/>
|
||||
public void Load()
|
||||
{
|
||||
this.addedTextures.AggregateToDisposable().Dispose();
|
||||
this.addedTextures.Clear();
|
||||
this.inputTexPath = "ui/loadingimage/-nowloading_base25_hr1.tex";
|
||||
this.inputFilePath = Path.Join(
|
||||
Service<Dalamud>.Get().StartInfo.AssetDirectory!,
|
||||
DalamudAsset.Logo.GetAttribute<DalamudAssetPathAttribute>()!.FileName);
|
||||
this.Ready = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Draw()
|
||||
{
|
||||
var texManager = Service<TextureManager>.Get();
|
||||
this.textureManager = Service<TextureManager>.Get();
|
||||
|
||||
ImGui.InputText("Icon ID", ref this.iconId, 32);
|
||||
ImGui.Checkbox("HQ Item", ref this.hq);
|
||||
ImGui.Checkbox("Hi-Res", ref this.hiRes);
|
||||
ImGui.Checkbox("Keep alive", ref this.keepAlive);
|
||||
if (ImGui.Button("Load Icon"))
|
||||
if (ImGui.Button("GC"))
|
||||
GC.Collect();
|
||||
|
||||
ImGui.PushID("loadedGameTextures");
|
||||
if (ImGui.CollapsingHeader($"Loaded Game Textures: {this.textureManager.GamePathTextures.Count}###header"))
|
||||
this.DrawLoadedTextures(this.textureManager.GamePathTextures);
|
||||
ImGui.PopID();
|
||||
|
||||
ImGui.PushID("loadedFileTextures");
|
||||
if (ImGui.CollapsingHeader($"Loaded File Textures: {this.textureManager.FileSystemTextures.Count}###header"))
|
||||
this.DrawLoadedTextures(this.textureManager.FileSystemTextures);
|
||||
ImGui.PopID();
|
||||
|
||||
if (ImGui.CollapsingHeader("Load Game File by Icon ID", ImGuiTreeNodeFlags.DefaultOpen))
|
||||
this.DrawIconInput();
|
||||
|
||||
if (ImGui.CollapsingHeader("Load Game File by Path", ImGuiTreeNodeFlags.DefaultOpen))
|
||||
this.DrawGamePathInput();
|
||||
|
||||
if (ImGui.CollapsingHeader("Load File", ImGuiTreeNodeFlags.DefaultOpen))
|
||||
this.DrawFileInput();
|
||||
|
||||
if (ImGui.CollapsingHeader("UV"))
|
||||
this.DrawUvInput();
|
||||
|
||||
TextureEntry? toRemove = null;
|
||||
TextureEntry? toCopy = null;
|
||||
foreach (var t in this.addedTextures)
|
||||
{
|
||||
try
|
||||
ImGui.PushID(t.Id);
|
||||
if (ImGui.CollapsingHeader($"Tex #{t.Id} {t}###header", ImGuiTreeNodeFlags.DefaultOpen))
|
||||
{
|
||||
var flags = ITextureProvider.IconFlags.None;
|
||||
if (this.hq)
|
||||
flags |= ITextureProvider.IconFlags.ItemHighQuality;
|
||||
|
||||
if (this.hiRes)
|
||||
flags |= ITextureProvider.IconFlags.HiRes;
|
||||
|
||||
this.addedTextures.Add(texManager.GetIcon(uint.Parse(this.iconId), flags, keepAlive: this.keepAlive));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Could not load tex");
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
ImGui.InputText("Tex Path", ref this.inputTexPath, 255);
|
||||
if (ImGui.Button("Load Tex"))
|
||||
{
|
||||
try
|
||||
{
|
||||
this.addedTextures.Add(texManager.GetTextureFromGame(this.inputTexPath, this.keepAlive));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Could not load tex");
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.Button("Load File"))
|
||||
{
|
||||
try
|
||||
{
|
||||
this.addedTextures.Add(texManager.GetTextureFromFile(new FileInfo(this.inputTexPath), this.keepAlive));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Could not load tex");
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
ImGui.InputFloat2("UV0", ref this.inputTexUv0);
|
||||
ImGui.InputFloat2("UV1", ref this.inputTexUv1);
|
||||
ImGui.InputFloat4("Tint", ref this.inputTintCol);
|
||||
ImGui.InputFloat2("Scale", ref this.inputTexScale);
|
||||
|
||||
ImGuiHelpers.ScaledDummy(10);
|
||||
|
||||
IDalamudTextureWrap? toRemove = null;
|
||||
for (var i = 0; i < this.addedTextures.Count; i++)
|
||||
{
|
||||
if (ImGui.CollapsingHeader($"Tex #{i}"))
|
||||
{
|
||||
var tex = this.addedTextures[i];
|
||||
|
||||
var scale = new Vector2(tex.Width, tex.Height);
|
||||
if (this.inputTexScale != Vector2.Zero)
|
||||
scale = this.inputTexScale;
|
||||
|
||||
ImGui.Image(tex.ImGuiHandle, scale, this.inputTexUv0, this.inputTexUv1, this.inputTintCol);
|
||||
|
||||
if (ImGui.Button($"X##{i}"))
|
||||
toRemove = tex;
|
||||
if (ImGui.Button("X"))
|
||||
toRemove = t;
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button($"Clone##{i}"))
|
||||
this.addedTextures.Add(tex.CreateWrapSharingLowLevelResource());
|
||||
if (ImGui.Button("Copy"))
|
||||
toCopy = t;
|
||||
|
||||
try
|
||||
{
|
||||
if (t.GetTexture(this.textureManager) is { } tex)
|
||||
{
|
||||
var scale = new Vector2(tex.Width, tex.Height);
|
||||
if (this.inputTexScale != Vector2.Zero)
|
||||
scale = this.inputTexScale;
|
||||
|
||||
ImGui.Image(tex.ImGuiHandle, scale, this.inputTexUv0, this.inputTexUv1, this.inputTintCol);
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextUnformatted(t.DescribeError());
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ImGui.TextUnformatted(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.PopID();
|
||||
}
|
||||
|
||||
if (toRemove != null)
|
||||
|
|
@ -131,5 +126,291 @@ internal class TexWidget : IDataWindowWidget
|
|||
toRemove.Dispose();
|
||||
this.addedTextures.Remove(toRemove);
|
||||
}
|
||||
|
||||
if (toCopy != null)
|
||||
{
|
||||
this.addedTextures.Add(toCopy.CreateFromSharedLowLevelResource(this.textureManager));
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void DrawLoadedTextures(IReadOnlyDictionary<string, SharableTexture> textures)
|
||||
{
|
||||
if (!ImGui.BeginTable("##table", 6))
|
||||
return;
|
||||
|
||||
ImGui.TableSetupScrollFreeze(0, 1);
|
||||
ImGui.TableSetupColumn("ID", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("000000").X);
|
||||
ImGui.TableSetupColumn("Preview", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Preview_").X);
|
||||
ImGui.TableSetupColumn("Source", ImGuiTableColumnFlags.WidthStretch);
|
||||
ImGui.TableSetupColumn("RefCount", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("RefCount__").X);
|
||||
ImGui.TableSetupColumn("SelfRef", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("00.000___").X);
|
||||
ImGui.TableSetupColumn("CanRevive", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("CanRevive__").X);
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
var clipper = new ImGuiListClipperPtr(ImGuiNative.ImGuiListClipper_ImGuiListClipper());
|
||||
clipper.Begin(textures.Count);
|
||||
|
||||
using (var enu = textures.GetEnumerator())
|
||||
{
|
||||
var row = 0;
|
||||
while (clipper.Step())
|
||||
{
|
||||
var valid = true;
|
||||
for (; row < clipper.DisplayStart && valid; row++)
|
||||
valid = enu.MoveNext();
|
||||
|
||||
if (!valid)
|
||||
break;
|
||||
|
||||
for (; row < clipper.DisplayEnd; row++)
|
||||
{
|
||||
valid = enu.MoveNext();
|
||||
if (!valid)
|
||||
break;
|
||||
|
||||
var (key, texture) = enu.Current;
|
||||
ImGui.TableNextRow();
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
this.TextRightAlign($"{texture.InstanceIdForDebug:n0}");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, Vector2.Zero);
|
||||
ImGui.PushStyleColor(ImGuiCol.Button, Vector4.Zero);
|
||||
ImGui.PushStyleColor(ImGuiCol.ButtonActive, Vector4.Zero);
|
||||
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, Vector4.Zero);
|
||||
ImGui.Button("Hover");
|
||||
ImGui.PopStyleColor(3);
|
||||
ImGui.PopStyleVar();
|
||||
if (ImGui.IsItemHovered() && texture.GetImmediate() is { } immediate)
|
||||
{
|
||||
ImGui.BeginTooltip();
|
||||
ImGui.Image(immediate.ImGuiHandle, immediate.Size);
|
||||
ImGui.EndTooltip();
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
this.TextCopiable(key);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
this.TextRightAlign($"{texture.RefCountForDebug:n0}");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
var remain = texture.SelfReferenceExpiresInForDebug;
|
||||
this.TextRightAlign(remain <= 0 ? "-" : $"{remain:00.000}");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(texture.RevivalPossibility?.TryGetTarget(out _) is true ? "Yes" : "No");
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
clipper.Destroy();
|
||||
ImGui.EndTable();
|
||||
|
||||
ImGuiHelpers.ScaledDummy(10);
|
||||
}
|
||||
|
||||
private void DrawIconInput()
|
||||
{
|
||||
ImGui.InputText("Icon ID", ref this.iconId, 32);
|
||||
ImGui.Checkbox("HQ Item", ref this.hq);
|
||||
ImGui.Checkbox("Hi-Res", ref this.hiRes);
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (ImGui.Button("Load Icon (API9)"))
|
||||
{
|
||||
var flags = ITextureProvider.IconFlags.None;
|
||||
if (this.hq)
|
||||
flags |= ITextureProvider.IconFlags.ItemHighQuality;
|
||||
if (this.hiRes)
|
||||
flags |= ITextureProvider.IconFlags.HiRes;
|
||||
this.addedTextures.Add(new(Api9: this.textureManager.GetIcon(uint.Parse(this.iconId), flags)));
|
||||
}
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Load Icon (Async)"))
|
||||
{
|
||||
this.addedTextures.Add(
|
||||
new(
|
||||
Api10: this.textureManager.GetFromGameIconAsync(
|
||||
new(uint.Parse(this.iconId), this.hq, this.hiRes))));
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Load Icon (Immediate)"))
|
||||
this.addedTextures.Add(new(Api10ImmGameIcon: new(uint.Parse(this.iconId), this.hq, this.hiRes)));
|
||||
|
||||
ImGuiHelpers.ScaledDummy(10);
|
||||
}
|
||||
|
||||
private void DrawGamePathInput()
|
||||
{
|
||||
ImGui.InputText("Tex Path", ref this.inputTexPath, 255);
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (ImGui.Button("Load Tex (API9)"))
|
||||
this.addedTextures.Add(new(Api9: this.textureManager.GetTextureFromGame(this.inputTexPath)));
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Load Tex (Async)"))
|
||||
this.addedTextures.Add(new(Api10: this.textureManager.GetFromGameAsync(this.inputTexPath)));
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Load Tex (Immediate)"))
|
||||
this.addedTextures.Add(new(Api10ImmGamePath: this.inputTexPath));
|
||||
|
||||
ImGuiHelpers.ScaledDummy(10);
|
||||
}
|
||||
|
||||
private void DrawFileInput()
|
||||
{
|
||||
ImGui.InputText("File Path", ref this.inputFilePath, 255);
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (ImGui.Button("Load File (API9)"))
|
||||
this.addedTextures.Add(new(Api9: this.textureManager.GetTextureFromFile(new(this.inputFilePath))));
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Load File (Async)"))
|
||||
this.addedTextures.Add(new(Api10: this.textureManager.GetFromFileAsync(this.inputFilePath)));
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Load File (Immediate)"))
|
||||
this.addedTextures.Add(new(Api10ImmFile: this.inputFilePath));
|
||||
|
||||
ImGuiHelpers.ScaledDummy(10);
|
||||
}
|
||||
|
||||
private void DrawUvInput()
|
||||
{
|
||||
ImGui.InputFloat2("UV0", ref this.inputTexUv0);
|
||||
ImGui.InputFloat2("UV1", ref this.inputTexUv1);
|
||||
ImGui.InputFloat4("Tint", ref this.inputTintCol);
|
||||
ImGui.InputFloat2("Scale", ref this.inputTexScale);
|
||||
|
||||
ImGuiHelpers.ScaledDummy(10);
|
||||
}
|
||||
|
||||
private void TextRightAlign(string s)
|
||||
{
|
||||
var width = ImGui.CalcTextSize(s).X;
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetColumnWidth() - width);
|
||||
ImGui.TextUnformatted(s);
|
||||
}
|
||||
|
||||
private void TextCopiable(string s, bool framepad = false)
|
||||
{
|
||||
var offset = ImGui.GetCursorScreenPos() + new Vector2(0, framepad ? ImGui.GetStyle().FramePadding.Y : 0);
|
||||
if (framepad)
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted(s);
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
ImGui.SetNextWindowPos(offset - ImGui.GetStyle().WindowPadding);
|
||||
var vp = ImGui.GetWindowViewport();
|
||||
var wrx = (vp.WorkPos.X + vp.WorkSize.X) - offset.X;
|
||||
ImGui.SetNextWindowSizeConstraints(Vector2.One, new(wrx, float.MaxValue));
|
||||
ImGui.BeginTooltip();
|
||||
ImGui.PushTextWrapPos(wrx);
|
||||
ImGui.TextWrapped(s.Replace("%", "%%"));
|
||||
ImGui.PopTextWrapPos();
|
||||
ImGui.EndTooltip();
|
||||
}
|
||||
|
||||
if (ImGui.IsItemClicked())
|
||||
{
|
||||
ImGui.SetClipboardText(s);
|
||||
Service<NotificationManager>.Get().AddNotification(
|
||||
$"Copied {ImGui.TableGetColumnName()} to clipboard.",
|
||||
this.DisplayName,
|
||||
NotificationType.Success);
|
||||
}
|
||||
}
|
||||
|
||||
private record TextureEntry(
|
||||
IDalamudTextureWrap? SharedResource = null,
|
||||
IDalamudTextureWrap? Api9 = null,
|
||||
Task<IDalamudTextureWrap>? Api10 = null,
|
||||
GameIconLookup? Api10ImmGameIcon = null,
|
||||
string? Api10ImmGamePath = null,
|
||||
string? Api10ImmFile = null) : IDisposable
|
||||
{
|
||||
private static int idCounter;
|
||||
|
||||
public int Id { get; } = idCounter++;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.SharedResource?.Dispose();
|
||||
this.Api9?.Dispose();
|
||||
_ = this.Api10?.ToContentDisposedTask();
|
||||
}
|
||||
|
||||
public string DescribeError()
|
||||
{
|
||||
if (this.SharedResource is not null)
|
||||
return "Unknown error";
|
||||
if (this.Api9 is not null)
|
||||
return "Unknown error";
|
||||
if (this.Api10 is not null)
|
||||
{
|
||||
return !this.Api10.IsCompleted
|
||||
? "Loading"
|
||||
: this.Api10.Exception?.ToString() ?? (this.Api10.IsCanceled ? "Canceled" : "Unknown error");
|
||||
}
|
||||
|
||||
if (this.Api10ImmGameIcon is not null)
|
||||
return "Must not happen";
|
||||
if (this.Api10ImmGamePath is not null)
|
||||
return "Must not happen";
|
||||
if (this.Api10ImmFile is not null)
|
||||
return "Must not happen";
|
||||
return "Not implemented";
|
||||
}
|
||||
|
||||
public IDalamudTextureWrap? GetTexture(ITextureProvider tp)
|
||||
{
|
||||
if (this.SharedResource is not null)
|
||||
return this.SharedResource;
|
||||
if (this.Api9 is not null)
|
||||
return this.Api9;
|
||||
if (this.Api10 is not null)
|
||||
return this.Api10.IsCompletedSuccessfully ? this.Api10.Result : null;
|
||||
if (this.Api10ImmGameIcon is not null)
|
||||
return tp.ImmediateGetFromGameIcon(this.Api10ImmGameIcon.Value);
|
||||
if (this.Api10ImmGamePath is not null)
|
||||
return tp.ImmediateGetFromGame(this.Api10ImmGamePath);
|
||||
if (this.Api10ImmFile is not null)
|
||||
return tp.ImmediateGetFromFile(this.Api10ImmFile);
|
||||
return null;
|
||||
}
|
||||
|
||||
public TextureEntry CreateFromSharedLowLevelResource(ITextureProvider tp) =>
|
||||
new() { SharedResource = this.GetTexture(tp)?.CreateWrapSharingLowLevelResource() };
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (this.SharedResource is not null)
|
||||
return $"{nameof(this.SharedResource)}: {this.SharedResource}";
|
||||
if (this.Api9 is not null)
|
||||
return $"{nameof(this.Api9)}: {this.Api9}";
|
||||
if (this.Api10 is { IsCompletedSuccessfully: true })
|
||||
return $"{nameof(this.Api10)}: {this.Api10.Result}";
|
||||
if (this.Api10 is not null)
|
||||
return $"{nameof(this.Api10)}: {this.Api10}";
|
||||
if (this.Api10ImmGameIcon is not null)
|
||||
return $"{nameof(this.Api10ImmGameIcon)}: {this.Api10ImmGameIcon}";
|
||||
if (this.Api10ImmGamePath is not null)
|
||||
return $"{nameof(this.Api10ImmGamePath)}: {this.Api10ImmGamePath}";
|
||||
if (this.Api10ImmFile is not null)
|
||||
return $"{nameof(this.Api10ImmFile)}: {this.Api10ImmFile}";
|
||||
return "Not implemented";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue