diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs index 5f5a0b71b..06795aa53 100644 --- a/Dalamud/Interface/Internal/DalamudInterface.cs +++ b/Dalamud/Interface/Internal/DalamudInterface.cs @@ -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; /// @@ -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(); } - /// Shows a context menu confirming texture save. - /// Name of the initiator. - /// Suggested name of the file being saved. - /// A task returning the texture to save. - /// A representing the asynchronous operation. - public async Task ShowTextureSaveMenuAsync( - string initiatorName, - string name, - Task texture) - { - try - { - var initiatorScreenOffset = ImGui.GetMousePos(); - using var textureWrap = await texture; - var textureManager = await Service.GetAsync(); - var popupName = $"{nameof(this.ShowTextureSaveMenuAsync)}_{textureWrap.ImGuiHandle:X}"; - - BitmapCodecInfo encoder; - { - var first = true; - var encoders = textureManager.Wic.GetSupportedEncoderInfos().ToList(); - var tcs = new TaskCompletionSource(); - Service.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.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(); - 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(); - 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.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.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) { diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs index e10fca0e7..20e549f27 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs @@ -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.Get().ShowTextureSaveMenuAsync( + _ = Service.Get().ShowTextureSaveMenuAsync( this.DisplayName, iconId.ToString(), Task.FromResult(texture.CreateWrapSharingLowLevelResource())); diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs index 85b0dc00d..f579b4d59 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs @@ -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.Get().ShowTextureSaveMenuAsync( + _ = Service.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.Get().ShowTextureSaveMenuAsync( + _ = Service.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.Get().ShowTextureSaveMenuAsync( + _ = Service.Get().ShowTextureSaveMenuAsync( this.DisplayName, name, texture.RentAsync()); diff --git a/Dalamud/Interface/Utility/Internal/DevTextureSaveMenu.cs b/Dalamud/Interface/Utility/Internal/DevTextureSaveMenu.cs new file mode 100644 index 000000000..c9e20ff1c --- /dev/null +++ b/Dalamud/Interface/Utility/Internal/DevTextureSaveMenu.cs @@ -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; + +/// Utility function for saving textures. +[ServiceManager.EarlyLoadedService] +internal sealed class DevTextureSaveMenu : IInternalDisposableService +{ + [ServiceManager.ServiceDependency] + private readonly InterfaceManager interfaceManager = Service.Get(); + + private readonly FileDialogManager fileDialogManager; + + [ServiceManager.ServiceConstructor] + private DevTextureSaveMenu() + { + this.fileDialogManager = new(); + this.interfaceManager.Draw += this.InterfaceManagerOnDraw; + } + + /// + void IInternalDisposableService.DisposeService() => this.interfaceManager.Draw -= this.InterfaceManagerOnDraw; + + /// Shows a context menu confirming texture save. + /// Name of the initiator. + /// Suggested name of the file being saved. + /// A task returning the texture to save. + /// A representing the asynchronous operation. + public async Task ShowTextureSaveMenuAsync( + string initiatorName, + string name, + Task texture) + { + try + { + var initiatorScreenOffset = ImGui.GetMousePos(); + using var textureWrap = await texture; + var textureManager = await Service.GetAsync(); + var popupName = $"{nameof(this.ShowTextureSaveMenuAsync)}_{textureWrap.ImGuiHandle:X}"; + + BitmapCodecInfo encoder; + { + var first = true; + var encoders = textureManager.Wic.GetSupportedEncoderInfos().ToList(); + var tcs = new TaskCompletionSource(); + Service.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.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(); + 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(); + 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.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.Get().AddNotification( + $"Failed to save file: {e}", + initiatorName, + NotificationType.Error); + } + } + + private void InterfaceManagerOnDraw() => this.fileDialogManager.Draw(); +}