Move ShowTextureSaveMenuAsync to its own class

This commit is contained in:
Soreepeong 2024-06-05 00:08:05 +09:00
parent 0d7c0a0375
commit 6d8102dc79
4 changed files with 170 additions and 133 deletions

View file

@ -1,12 +1,9 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using CheapLoc;
using Dalamud.Configuration.Internal;
@ -18,9 +15,6 @@ using Dalamud.Game.Internal;
using Dalamud.Hooking;
using Dalamud.Interface.Animation.EasingFunctions;
using Dalamud.Interface.Colors;
using Dalamud.Interface.ImGuiFileDialog;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.ImGuiNotification.Internal;
using Dalamud.Interface.Internal.ManagedAsserts;
using Dalamud.Interface.Internal.Windows;
using Dalamud.Interface.Internal.Windows.Data;
@ -30,7 +24,6 @@ using Dalamud.Interface.Internal.Windows.Settings;
using Dalamud.Interface.Internal.Windows.StyleEditor;
using Dalamud.Interface.ManagedFontAtlas.Internals;
using Dalamud.Interface.Style;
using Dalamud.Interface.Textures.Internal;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
@ -47,10 +40,6 @@ using ImPlotNET;
using PInvoke;
using Serilog.Events;
using TerraFX.Interop.Windows;
using Task = System.Threading.Tasks.Task;
namespace Dalamud.Interface.Internal;
/// <summary>
@ -67,8 +56,6 @@ internal class DalamudInterface : IInternalDisposableService
private readonly DalamudConfiguration configuration;
private readonly InterfaceManager interfaceManager;
private readonly FileDialogManager fileDialogManager;
private readonly ChangelogWindow changelogWindow;
private readonly ColorDemoWindow colorDemoWindow;
private readonly ComponentDemoWindow componentDemoWindow;
@ -121,7 +108,6 @@ internal class DalamudInterface : IInternalDisposableService
this.interfaceManager = interfaceManager;
this.WindowSystem = new WindowSystem("DalamudCore");
this.fileDialogManager = new();
this.colorDemoWindow = new ColorDemoWindow() { IsOpen = false };
this.componentDemoWindow = new ComponentDemoWindow() { IsOpen = false };
@ -501,119 +487,6 @@ internal class DalamudInterface : IInternalDisposableService
this.creditsDarkeningAnimation.Restart();
}
/// <summary>Shows a context menu confirming texture save.</summary>
/// <param name="initiatorName">Name of the initiator.</param>
/// <param name="name">Suggested name of the file being saved.</param>
/// <param name="texture">A task returning the texture to save.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task ShowTextureSaveMenuAsync(
string initiatorName,
string name,
Task<IDalamudTextureWrap> texture)
{
try
{
var initiatorScreenOffset = ImGui.GetMousePos();
using var textureWrap = await texture;
var textureManager = await Service<TextureManager>.GetAsync();
var popupName = $"{nameof(this.ShowTextureSaveMenuAsync)}_{textureWrap.ImGuiHandle:X}";
BitmapCodecInfo encoder;
{
var first = true;
var encoders = textureManager.Wic.GetSupportedEncoderInfos().ToList();
var tcs = new TaskCompletionSource<BitmapCodecInfo>();
Service<InterfaceManager>.Get().Draw += DrawChoices;
encoder = await tcs.Task;
[SuppressMessage("ReSharper", "AccessToDisposedClosure", Justification = "This shall not escape")]
void DrawChoices()
{
if (first)
{
ImGui.OpenPopup(popupName);
first = false;
}
ImGui.SetNextWindowPos(initiatorScreenOffset, ImGuiCond.Appearing);
if (!ImGui.BeginPopup(
popupName,
ImGuiWindowFlags.AlwaysAutoResize |
ImGuiWindowFlags.NoTitleBar |
ImGuiWindowFlags.NoSavedSettings))
{
Service<InterfaceManager>.Get().Draw -= DrawChoices;
tcs.TrySetCanceled();
return;
}
foreach (var encoder2 in encoders)
{
if (ImGui.Selectable(encoder2.Name))
tcs.TrySetResult(encoder2);
}
const float previewImageWidth = 320;
var size = textureWrap.Size;
if (size.X > previewImageWidth)
size *= previewImageWidth / size.X;
if (size.Y > previewImageWidth)
size *= previewImageWidth / size.Y;
ImGui.Image(textureWrap.ImGuiHandle, size);
if (tcs.Task.IsCompleted)
ImGui.CloseCurrentPopup();
ImGui.EndPopup();
}
}
string path;
{
var tcs = new TaskCompletionSource<string>();
this.fileDialogManager.SaveFileDialog(
"Save texture...",
$"{encoder.Name.Replace(',', '.')}{{{string.Join(',', encoder.Extensions)}}}",
name + encoder.Extensions.First(),
encoder.Extensions.First(),
(ok, path2) =>
{
if (!ok)
tcs.SetCanceled();
else
tcs.SetResult(path2);
});
path = await tcs.Task.ConfigureAwait(false);
}
var props = new Dictionary<string, object>();
if (encoder.ContainerGuid == GUID.GUID_ContainerFormatTiff)
props["CompressionQuality"] = 1.0f;
else if (encoder.ContainerGuid == GUID.GUID_ContainerFormatJpeg ||
encoder.ContainerGuid == GUID.GUID_ContainerFormatHeif ||
encoder.ContainerGuid == GUID.GUID_ContainerFormatWmp)
props["ImageQuality"] = 1.0f;
await textureManager.SaveToFileAsync(textureWrap, encoder.ContainerGuid, path, props: props);
Service<NotificationManager>.Get().AddNotification(
$"File saved to: {path}",
initiatorName,
NotificationType.Success);
}
catch (Exception e)
{
if (e is OperationCanceledException)
return;
Log.Error(e, $"{nameof(DalamudInterface)}.{nameof(this.ShowTextureSaveMenuAsync)}({initiatorName}, {name})");
Service<NotificationManager>.Get().AddNotification(
$"Failed to save file: {e}",
initiatorName,
NotificationType.Error);
}
}
private void OnDraw()
{
this.FrameCount++;
@ -665,8 +538,6 @@ internal class DalamudInterface : IInternalDisposableService
{
ImGui.SetWindowFocus(null);
}
this.fileDialogManager.Draw();
}
catch (Exception ex)
{

View file

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Textures.Internal;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Internal;
using ImGuiNET;
@ -170,7 +171,7 @@ public class IconBrowserWidget : IDataWindowWidget
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
_ = Service<DalamudInterface>.Get().ShowTextureSaveMenuAsync(
_ = Service<DevTextureSaveMenu>.Get().ShowTextureSaveMenuAsync(
this.DisplayName,
iconId.ToString(),
Task.FromResult(texture.CreateWrapSharingLowLevelResource()));

View file

@ -13,6 +13,7 @@ using Dalamud.Interface.ImGuiNotification.Internal;
using Dalamud.Interface.Textures;
using Dalamud.Interface.Textures.Internal.SharedImmediateTextures;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Internal;
using Dalamud.Plugin.Services;
using Dalamud.Storage.Assets;
using Dalamud.Utility;
@ -249,7 +250,7 @@ internal class TexWidget : IDataWindowWidget
ImGui.SameLine();
if (ImGui.Button("Save"))
{
_ = Service<DalamudInterface>.Get().ShowTextureSaveMenuAsync(
_ = Service<DevTextureSaveMenu>.Get().ShowTextureSaveMenuAsync(
this.DisplayName,
$"Texture {t.Id}",
t.CreateNewTextureWrapReference(this.textureManager));
@ -458,7 +459,7 @@ internal class TexWidget : IDataWindowWidget
ImGui.TableNextColumn();
if (ImGuiComponents.IconButton(FontAwesomeIcon.Save))
{
_ = Service<DalamudInterface>.Get().ShowTextureSaveMenuAsync(
_ = Service<DevTextureSaveMenu>.Get().ShowTextureSaveMenuAsync(
this.DisplayName,
$"{wrap.ImGuiHandle:X16}",
Task.FromResult(wrap.CreateWrapSharingLowLevelResource()));
@ -585,7 +586,7 @@ internal class TexWidget : IDataWindowWidget
if (ImGuiComponents.IconButton(FontAwesomeIcon.Save))
{
var name = Path.ChangeExtension(Path.GetFileName(texture.SourcePathForDebug), null);
_ = Service<DalamudInterface>.Get().ShowTextureSaveMenuAsync(
_ = Service<DevTextureSaveMenu>.Get().ShowTextureSaveMenuAsync(
this.DisplayName,
name,
texture.RentAsync());

View file

@ -0,0 +1,164 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using Dalamud.Interface.ImGuiFileDialog;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.ImGuiNotification.Internal;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Textures.Internal;
using ImGuiNET;
using Serilog;
using TerraFX.Interop.Windows;
namespace Dalamud.Interface.Utility.Internal;
/// <summary>Utility function for saving textures.</summary>
[ServiceManager.EarlyLoadedService]
internal sealed class DevTextureSaveMenu : IInternalDisposableService
{
[ServiceManager.ServiceDependency]
private readonly InterfaceManager interfaceManager = Service<InterfaceManager>.Get();
private readonly FileDialogManager fileDialogManager;
[ServiceManager.ServiceConstructor]
private DevTextureSaveMenu()
{
this.fileDialogManager = new();
this.interfaceManager.Draw += this.InterfaceManagerOnDraw;
}
/// <inheritdoc/>
void IInternalDisposableService.DisposeService() => this.interfaceManager.Draw -= this.InterfaceManagerOnDraw;
/// <summary>Shows a context menu confirming texture save.</summary>
/// <param name="initiatorName">Name of the initiator.</param>
/// <param name="name">Suggested name of the file being saved.</param>
/// <param name="texture">A task returning the texture to save.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task ShowTextureSaveMenuAsync(
string initiatorName,
string name,
Task<IDalamudTextureWrap> texture)
{
try
{
var initiatorScreenOffset = ImGui.GetMousePos();
using var textureWrap = await texture;
var textureManager = await Service<TextureManager>.GetAsync();
var popupName = $"{nameof(this.ShowTextureSaveMenuAsync)}_{textureWrap.ImGuiHandle:X}";
BitmapCodecInfo encoder;
{
var first = true;
var encoders = textureManager.Wic.GetSupportedEncoderInfos().ToList();
var tcs = new TaskCompletionSource<BitmapCodecInfo>();
Service<InterfaceManager>.Get().Draw += DrawChoices;
encoder = await tcs.Task;
[SuppressMessage("ReSharper", "AccessToDisposedClosure", Justification = "This shall not escape")]
void DrawChoices()
{
if (first)
{
ImGui.OpenPopup(popupName);
first = false;
}
ImGui.SetNextWindowPos(initiatorScreenOffset, ImGuiCond.Appearing);
if (!ImGui.BeginPopup(
popupName,
ImGuiWindowFlags.AlwaysAutoResize |
ImGuiWindowFlags.NoTitleBar |
ImGuiWindowFlags.NoSavedSettings))
{
Service<InterfaceManager>.Get().Draw -= DrawChoices;
tcs.TrySetCanceled();
return;
}
foreach (var encoder2 in encoders)
{
if (ImGui.Selectable(encoder2.Name))
tcs.TrySetResult(encoder2);
}
const float previewImageWidth = 320;
var size = textureWrap.Size;
if (size.X > previewImageWidth)
size *= previewImageWidth / size.X;
if (size.Y > previewImageWidth)
size *= previewImageWidth / size.Y;
ImGui.Image(textureWrap.ImGuiHandle, size);
if (tcs.Task.IsCompleted)
ImGui.CloseCurrentPopup();
ImGui.EndPopup();
}
}
string path;
{
var tcs = new TaskCompletionSource<string>();
this.fileDialogManager.SaveFileDialog(
"Save texture...",
$"{encoder.Name.Replace(',', '.')}{{{string.Join(',', encoder.Extensions)}}}",
name + encoder.Extensions.First(),
encoder.Extensions.First(),
(ok, path2) =>
{
if (!ok)
tcs.SetCanceled();
else
tcs.SetResult(path2);
});
path = await tcs.Task.ConfigureAwait(false);
}
var props = new Dictionary<string, object>();
if (encoder.ContainerGuid == GUID.GUID_ContainerFormatTiff)
props["CompressionQuality"] = 1.0f;
else if (encoder.ContainerGuid == GUID.GUID_ContainerFormatJpeg ||
encoder.ContainerGuid == GUID.GUID_ContainerFormatHeif ||
encoder.ContainerGuid == GUID.GUID_ContainerFormatWmp)
props["ImageQuality"] = 1.0f;
await textureManager.SaveToFileAsync(textureWrap, encoder.ContainerGuid, path, props: props);
var notif = Service<NotificationManager>.Get().AddNotification(
new()
{
Content = $"File saved to: {path}",
Title = initiatorName,
Type = NotificationType.Success,
});
notif.Click += n =>
{
Process.Start(new ProcessStartInfo(path) { UseShellExecute = true });
n.Notification.DismissNow();
};
}
catch (Exception e)
{
if (e is OperationCanceledException)
return;
Log.Error(
e,
$"{nameof(DalamudInterface)}.{nameof(this.ShowTextureSaveMenuAsync)}({initiatorName}, {name})");
Service<NotificationManager>.Get().AddNotification(
$"Failed to save file: {e}",
initiatorName,
NotificationType.Error);
}
}
private void InterfaceManagerOnDraw() => this.fileDialogManager.Draw();
}