diff --git a/StabilityMatrix.Avalonia/App.axaml.cs b/StabilityMatrix.Avalonia/App.axaml.cs
index cb4a3e37..2f059078 100644
--- a/StabilityMatrix.Avalonia/App.axaml.cs
+++ b/StabilityMatrix.Avalonia/App.axaml.cs
@@ -3,6 +3,7 @@ using StabilityMatrix.Avalonia.Diagnostics.LogViewer;
using StabilityMatrix.Avalonia.Diagnostics.LogViewer.Extensions;
#endif
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
@@ -16,11 +17,15 @@ using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Controls.Primitives;
using Avalonia.Input.Platform;
+using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
+using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Platform.Storage;
using Avalonia.Styling;
+using Avalonia.Threading;
using FluentAvalonia.UI.Controls;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -37,6 +42,7 @@ using Polly.Timeout;
using Refit;
using Sentry;
using StabilityMatrix.Avalonia.Controls;
+using StabilityMatrix.Avalonia.Controls.CodeCompletion;
using StabilityMatrix.Avalonia.DesignData;
using StabilityMatrix.Avalonia.Helpers;
using StabilityMatrix.Avalonia.Languages;
@@ -59,11 +65,13 @@ using StabilityMatrix.Avalonia.Views.Settings;
using StabilityMatrix.Core.Api;
using StabilityMatrix.Core.Converters.Json;
using StabilityMatrix.Core.Database;
+using StabilityMatrix.Core.Extensions;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Helper.Cache;
using StabilityMatrix.Core.Helper.Factory;
using StabilityMatrix.Core.Models.Api;
using StabilityMatrix.Core.Models.Configs;
+using StabilityMatrix.Core.Models.FileInterfaces;
using StabilityMatrix.Core.Models.PackageModification;
using StabilityMatrix.Core.Models.Packages;
using StabilityMatrix.Core.Models.Settings;
@@ -754,6 +762,69 @@ public sealed class App : Application
return LogManager.Configuration;
}
+ ///
+ /// Opens a dialog to save the current view as a screenshot.
+ ///
+ /// Only available in debug builds.
+ [Conditional("DEBUG")]
+ internal static void DebugSaveScreenshot(int dpi = 96)
+ {
+ const int scale = 2;
+ dpi *= scale;
+
+ var results = new List();
+ var targets = new List { VisualRoot };
+
+ foreach (var visual in targets.Where(x => x != null))
+ {
+ var rect = new Rect(visual!.Bounds.Size);
+
+ var pixelSize = new PixelSize((int)rect.Width * scale, (int)rect.Height * scale);
+ var dpiVector = new Vector(dpi, dpi);
+
+ var ms = new MemoryStream();
+
+ using (var bitmap = new RenderTargetBitmap(pixelSize, dpiVector))
+ {
+ bitmap.Render(visual);
+ bitmap.Save(ms);
+ }
+
+ results.Add(ms);
+ }
+
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ {
+ var dest = await StorageProvider.SaveFilePickerAsync(
+ new FilePickerSaveOptions()
+ {
+ SuggestedFileName = "screenshot.png",
+ ShowOverwritePrompt = true
+ }
+ );
+
+ if (dest?.TryGetLocalPath() is { } localPath)
+ {
+ var localFile = new FilePath(localPath);
+ foreach (var (i, stream) in results.Enumerate())
+ {
+ var name = localFile.NameWithoutExtension;
+ if (results.Count > 1)
+ {
+ name += $"_{i + 1}";
+ }
+
+ localFile = localFile.Directory!.JoinFile(name + ".png");
+ localFile.Create();
+
+ await using var fileStream = localFile.Info.OpenWrite();
+ stream.Seek(0, SeekOrigin.Begin);
+ await stream.CopyToAsync(fileStream);
+ }
+ }
+ });
+ }
+
[Conditional("DEBUG")]
private static void ConditionalAddLogViewer(IServiceCollection services)
{
diff --git a/StabilityMatrix.Avalonia/Extensions/VisualExtensions.cs b/StabilityMatrix.Avalonia/Extensions/VisualExtensions.cs
new file mode 100644
index 00000000..173dba98
--- /dev/null
+++ b/StabilityMatrix.Avalonia/Extensions/VisualExtensions.cs
@@ -0,0 +1,25 @@
+using System.Diagnostics;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using StabilityMatrix.Avalonia.Controls;
+
+namespace StabilityMatrix.Avalonia.Extensions;
+
+public static class VisualExtensions
+{
+ [Conditional("DEBUG")]
+ public static void AttachDebugSaveScreenshot(this AppWindowBase visual)
+ {
+ visual.AddDisposableHandler(
+ InputElement.KeyDownEvent,
+ (s, e) =>
+ {
+ if (new KeyGesture(Key.F10).Matches(e))
+ {
+ App.DebugSaveScreenshot();
+ }
+ },
+ RoutingStrategies.Tunnel
+ );
+ }
+}
diff --git a/StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs b/StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs
index 2c59cd4c..84d9d1c3 100644
--- a/StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs
+++ b/StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs
@@ -25,6 +25,7 @@ using Microsoft.Extensions.DependencyInjection;
using NLog;
using StabilityMatrix.Avalonia.Animations;
using StabilityMatrix.Avalonia.Controls;
+using StabilityMatrix.Avalonia.Extensions;
using StabilityMatrix.Avalonia.Languages;
using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels;
@@ -65,6 +66,7 @@ public partial class MainWindow : AppWindowBase
#if DEBUG
this.AttachDevTools();
+ this.AttachDebugSaveScreenshot();
LogWindow.Attach(this, App.Services);
#endif
TitleBar.ExtendsContentIntoTitleBar = true;