Browse Source

New HeadlessWindow.SetRenderScaling API (#20888)

* Add `void IHeadlessWindow.SetRenderScaling` API

* Add tests

* Enforce Window

* Try to fix failing test
pull/17825/merge
Max Katz 1 week ago
committed by GitHub
parent
commit
74c20f1fdc
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 15
      src/Headless/Avalonia.Headless/HeadlessWindowExtensions.cs
  2. 16
      src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
  3. 1
      src/Headless/Avalonia.Headless/IHeadlessWindow.cs
  4. 62
      tests/Avalonia.Headless.UnitTests/RenderingTests.cs

15
src/Headless/Avalonia.Headless/HeadlessWindowExtensions.cs

@ -20,9 +20,9 @@ public static class HeadlessWindowExtensions
/// <returns>Bitmap with last rendered frame. Null, if nothing was rendered.</returns>
public static WriteableBitmap? CaptureRenderedFrame(this TopLevel topLevel)
{
Dispatcher.UIThread.RunJobs();
AvaloniaHeadlessPlatform.ForceRenderTimerTick();
return topLevel.GetLastRenderedFrame();
WriteableBitmap? bitmap = null;
topLevel.RunJobsOnImpl(w => bitmap = w.GetLastRenderedFrame());
return bitmap;
}
/// <summary>
@ -114,6 +114,15 @@ public static class HeadlessWindowExtensions
DragDropEffects effects, RawInputModifiers modifiers = RawInputModifiers.None) =>
RunJobsOnImpl(topLevel, w => w.DragDrop(point, type, data, effects, modifiers));
/// <summary>
/// Changes the render scaling (DPI) of the headless window/toplevel.
/// This simulates a DPI change, triggering scaling changed notifications and a layout pass.
/// </summary>
/// <param name="topLevel">The target headless top level.</param>
/// <param name="scaling">The new render scaling factor. Must be greater than zero.</param>
public static void SetRenderScaling(this TopLevel topLevel, double scaling) =>
RunJobsOnImpl(topLevel, w => w.SetRenderScaling(scaling));
private static void RunJobsOnImpl(this TopLevel topLevel, Action<IHeadlessWindow> action)
{
RunJobsAndRender();

16
src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs

@ -49,7 +49,7 @@ namespace Avalonia.Headless
public Size ClientSize { get; set; }
public Size? FrameSize => null;
public double RenderScaling { get; } = 1;
public double RenderScaling { get; private set; } = 1;
public double DesktopScaling => RenderScaling;
public IPlatformRenderSurface[] Surfaces { get; }
public Action<RawInputEventArgs>? Input { get; set; }
@ -358,6 +358,20 @@ namespace Avalonia.Headless
Input?.Invoke(new RawDragEvent(device, type, InputRoot!, point, data, effects, modifiers));
}
void IHeadlessWindow.SetRenderScaling(double scaling)
{
if (scaling <= 0)
throw new ArgumentOutOfRangeException(nameof(scaling), "Scaling must be greater than zero.");
if (RenderScaling == scaling)
return;
var oldScaledSize = ClientSize;
RenderScaling = scaling;
ScalingChanged?.Invoke(scaling);
Resize(oldScaledSize, WindowResizeReason.DpiChange);
}
void IWindowImpl.Move(PixelPoint point)
{
Position = point;

1
src/Headless/Avalonia.Headless/IHeadlessWindow.cs

@ -16,5 +16,6 @@ namespace Avalonia.Headless
void MouseUp(Point point, MouseButton button, RawInputModifiers modifiers = RawInputModifiers.None);
void MouseWheel(Point point, Vector delta, RawInputModifiers modifiers = RawInputModifiers.None);
void DragDrop(Point point, RawDragEventType type, IDataTransfer data, DragDropEffects effects, RawInputModifiers modifiers = RawInputModifiers.None);
void SetRenderScaling(double scaling);
}
}

62
tests/Avalonia.Headless.UnitTests/RenderingTests.cs

@ -169,4 +169,66 @@ public class RenderingTests
AssertHelper.Equal(100, snapshot.Size.Width);
AssertHelper.Equal(100, snapshot.Size.Height);
}
#if NUNIT
[AvaloniaTest]
#elif XUNIT
[AvaloniaFact]
#endif
public void Should_Change_Render_Scaling()
{
var window = new Window
{
Content = new Border
{
Background = Brushes.Red
},
Width = 100,
Height = 100,
};
window.Show();
var frameBefore = window.CaptureRenderedFrame();
AssertHelper.NotNull(frameBefore);
var sizeBefore = frameBefore!.PixelSize;
window.SetRenderScaling(2.0);
AssertHelper.Equal(2.0, window.RenderScaling);
var frameAfter = window.CaptureRenderedFrame();
AssertHelper.NotNull(frameAfter);
var sizeAfter = frameAfter!.PixelSize;
AssertHelper.Equal(sizeBefore.Width * 2, sizeAfter.Width);
AssertHelper.Equal(sizeBefore.Height * 2, sizeAfter.Height);
}
#if NUNIT
[AvaloniaTest]
#elif XUNIT
[AvaloniaFact]
#endif
public void Should_Keep_Client_Size_After_Scaling_Change()
{
var window = new Window
{
Width = 200,
Height = 150
};
window.Show();
window.CaptureRenderedFrame();
var clientSizeBefore = window.ClientSize;
window.SetRenderScaling(2.0);
window.CaptureRenderedFrame();
AssertHelper.Equal(clientSizeBefore.Width, window.ClientSize.Width);
AssertHelper.Equal(clientSizeBefore.Height, window.ClientSize.Height);
}
}

Loading…
Cancel
Save