Browse Source

Merge branch 'master' into fixes/zero-dur-and-unittestfixes

pull/11800/head
Jumar Macato 3 years ago
committed by GitHub
parent
commit
4f960a2801
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      src/Avalonia.Base/Data/Core/ClrPropertyInfo.cs
  2. 4
      src/Avalonia.Base/Input/AccessKeyHandler.cs
  3. 10
      src/Avalonia.Base/Input/TextInput/InputMethodManager.cs
  4. 7
      src/Avalonia.Base/Media/FontManager.cs
  5. 2
      src/Avalonia.Controls/Control.cs
  6. 13
      src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
  7. 35
      src/Skia/Avalonia.Skia/GlyphRunImpl.cs
  8. 104
      src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs
  9. 56
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  10. 4
      src/Windows/Avalonia.Win32/WindowImpl.cs
  11. 24
      tests/Avalonia.Base.UnitTests/Data/ReflectionClrPropertyInfoTests.cs
  12. 205
      tests/Avalonia.Base.UnitTests/Input/AccessKeyHandlerTests.cs
  13. 37
      tests/Avalonia.Headless.UnitTests/RenderingTests.cs
  14. 16
      tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs

3
src/Avalonia.Base/Data/Core/ClrPropertyInfo.cs

@ -60,7 +60,8 @@ namespace Avalonia.Data.Core
var target = Expression.Parameter(typeof(object), "target"); var target = Expression.Parameter(typeof(object), "target");
return Expression.Lambda<Func<object, object>>( return Expression.Lambda<Func<object, object>>(
Expression.Convert(Expression.Call(Expression.Convert(target, info.DeclaringType!), info.GetMethod), Expression.Convert(Expression.Call(Expression.Convert(target, info.DeclaringType!), info.GetMethod),
typeof(object))) typeof(object)),
target)
.Compile(); .Compile();
} }

4
src/Avalonia.Base/Input/AccessKeyHandler.cs

@ -159,8 +159,6 @@ namespace Avalonia.Input
_restoreFocusElement?.Focus(); _restoreFocusElement?.Focus();
_restoreFocusElement = null; _restoreFocusElement = null;
e.Handled = true;
} }
} }
else if (_altIsDown) else if (_altIsDown)
@ -200,7 +198,6 @@ namespace Avalonia.Input
if (match is not null) if (match is not null)
{ {
match.RaiseEvent(new RoutedEventArgs(AccessKeyPressedEvent)); match.RaiseEvent(new RoutedEventArgs(AccessKeyPressedEvent));
e.Handled = true;
} }
} }
} }
@ -225,7 +222,6 @@ namespace Avalonia.Input
else if (_showingAccessKeys && MainMenu != null) else if (_showingAccessKeys && MainMenu != null)
{ {
MainMenu.Open(); MainMenu.Open();
e.Handled = true;
} }
break; break;

10
src/Avalonia.Base/Input/TextInput/InputMethodManager.cs

@ -22,11 +22,18 @@ namespace Avalonia.Input.TextInput
set set
{ {
if(_client == value) if(_client == value)
{
return; return;
}
if (_client != null) if (_client != null)
{ {
_client.CursorRectangleChanged -= OnCursorRectangleChanged; _client.CursorRectangleChanged -= OnCursorRectangleChanged;
_client.TextViewVisualChanged -= OnTextViewVisualChanged; _client.TextViewVisualChanged -= OnTextViewVisualChanged;
_client = null;
_im?.Reset();
} }
_client = value; _client = value;
@ -35,8 +42,6 @@ namespace Avalonia.Input.TextInput
{ {
_client.CursorRectangleChanged += OnCursorRectangleChanged; _client.CursorRectangleChanged += OnCursorRectangleChanged;
_client.TextViewVisualChanged += OnTextViewVisualChanged; _client.TextViewVisualChanged += OnTextViewVisualChanged;
_im?.Reset();
if (_focusedElement is StyledElement target) if (_focusedElement is StyledElement target)
{ {
@ -50,6 +55,7 @@ namespace Avalonia.Input.TextInput
_transformTracker.SetVisual(_client?.TextViewVisual); _transformTracker.SetVisual(_client?.TextViewVisual);
_im?.SetClient(_client); _im?.SetClient(_client);
UpdateCursorRect(); UpdateCursorRect();
} }
else else

7
src/Avalonia.Base/Media/FontManager.cs

@ -151,8 +151,13 @@ namespace Avalonia.Media
} }
} }
if(typeface.FontFamily == DefaultFontFamily)
{
return false;
}
//Nothing was found so use the default //Nothing was found so use the default
return TryGetGlyphTypeface(new Typeface(DefaultFontFamily, typeface.Style, typeface.Weight, typeface.Stretch), out glyphTypeface); return TryGetGlyphTypeface(new Typeface(FontFamily.DefaultFontFamilyName, typeface.Style, typeface.Weight, typeface.Stretch), out glyphTypeface);
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/Control.cs

@ -480,7 +480,7 @@ namespace Avalonia.Controls
if (e.Source == this if (e.Source == this
&& !e.Handled) && !e.Handled)
{ {
var keymap = Application.Current!.PlatformSettings?.HotkeyConfiguration.OpenContextMenu; var keymap = TopLevel.GetTopLevel(this)?.PlatformSettings?.HotkeyConfiguration.OpenContextMenu;
if (keymap is null) if (keymap is null)
{ {

13
src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs

@ -114,23 +114,26 @@ namespace Avalonia.Headless
public Size MaxClientSize { get; } = new Size(1920, 1280); public Size MaxClientSize { get; } = new Size(1920, 1280);
public void Resize(Size clientSize, WindowResizeReason reason) public void Resize(Size clientSize, WindowResizeReason reason)
{ {
if (ClientSize == clientSize)
return;
// Emulate X11 behavior here // Emulate X11 behavior here
if (IsPopup) if (IsPopup)
DoResize(clientSize); DoResize(clientSize, reason);
else else
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
DoResize(clientSize); DoResize(clientSize, reason);
}); }, DispatcherPriority.Send);
} }
private void DoResize(Size clientSize) private void DoResize(Size clientSize, WindowResizeReason reason)
{ {
// Uncomment this check and experience a weird bug in layout engine // Uncomment this check and experience a weird bug in layout engine
if (ClientSize != clientSize) if (ClientSize != clientSize)
{ {
ClientSize = clientSize; ClientSize = clientSize;
Resized?.Invoke(clientSize, WindowResizeReason.Unspecified); Resized?.Invoke(clientSize, reason);
} }
} }

35
src/Skia/Avalonia.Skia/GlyphRunImpl.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.TextFormatting; using Avalonia.Media.TextFormatting;
@ -14,7 +15,7 @@ namespace Avalonia.Skia
private readonly ushort[] _glyphIndices; private readonly ushort[] _glyphIndices;
private readonly SKPoint[] _glyphPositions; private readonly SKPoint[] _glyphPositions;
private readonly Dictionary<SKFontEdging, SKTextBlob> _textBlobCache = new(1); private readonly ConcurrentDictionary<SKFontEdging, SKTextBlob> _textBlobCache = new();
public GlyphRunImpl(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, public GlyphRunImpl(IGlyphTypeface glyphTypeface, double fontRenderingEmSize,
IReadOnlyList<GlyphInfo> glyphInfos, Point baselineOrigin) IReadOnlyList<GlyphInfo> glyphInfos, Point baselineOrigin)
@ -100,32 +101,28 @@ namespace Avalonia.Skia
break; break;
} }
if (_textBlobCache.TryGetValue(edging, out var textBlob)) return _textBlobCache.GetOrAdd(edging, (_) =>
{ {
return textBlob; var font = _glyphTypefaceImpl.SKFont;
}
var font = _glyphTypefaceImpl.SKFont;
font.Hinting = SKFontHinting.Full; font.Hinting = SKFontHinting.Full;
font.Subpixel = edging == SKFontEdging.SubpixelAntialias; font.Subpixel = edging == SKFontEdging.SubpixelAntialias;
font.Edging = edging; font.Edging = edging;
font.Size = (float)FontRenderingEmSize; font.Size = (float)FontRenderingEmSize;
var builder = SKTextBlobBuilderCache.Shared.Get(); var builder = SKTextBlobBuilderCache.Shared.Get();
var runBuffer = builder.AllocatePositionedRun(font, _glyphIndices.Length); var runBuffer = builder.AllocatePositionedRun(font, _glyphIndices.Length);
runBuffer.SetPositions(_glyphPositions); runBuffer.SetPositions(_glyphPositions);
runBuffer.SetGlyphs(_glyphIndices); runBuffer.SetGlyphs(_glyphIndices);
textBlob = builder.Build(); var textBlob = builder.Build();
SKTextBlobBuilderCache.Shared.Return(builder); SKTextBlobBuilderCache.Shared.Return(builder);
_textBlobCache.Add(edging, textBlob); return textBlob;
});
return textBlob;
} }
public void Dispose() public void Dispose()

104
src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Text; using System.Text;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Input.TextInput; using Avalonia.Input.TextInput;
using Avalonia.Threading; using Avalonia.Threading;
@ -22,6 +24,8 @@ namespace Avalonia.Win32.Input
private ushort _langId; private ushort _langId;
private const int CaretMargin = 1; private const int CaretMargin = 1;
private bool _ignoreComposition;
public ITextInputMethodClient? Client { get; private set; } public ITextInputMethodClient? Client { get; private set; }
[MemberNotNullWhen(true, nameof(Client))] [MemberNotNullWhen(true, nameof(Client))]
@ -121,19 +125,35 @@ namespace Avalonia.Win32.Input
{ {
var himc = ImmGetContext(Hwnd); var himc = ImmGetContext(Hwnd);
if (IsComposing) if (himc != IntPtr.Zero)
{ {
_ignoreComposition = true;
if (_parent != null)
{
_parent._ignoreWmChar = true;
}
ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
ImmReleaseContext(Hwnd, himc);
IsComposing = false; IsComposing = false;
}
ImmReleaseContext(Hwnd, himc); Composition = null;
}
}); });
} }
public void SetClient(ITextInputMethodClient? client) public void SetClient(ITextInputMethodClient? client)
{ {
if(Client != null)
{
Composition = null;
Client.SetPreeditText(null);
}
Client = client; Client = client;
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
@ -297,6 +317,82 @@ namespace Avalonia.Win32.Input
return ImmGetCompositionString(himc, flag); return ImmGetCompositionString(himc, flag);
} }
public void HandleCompositionStart()
{
Composition = null;
if (IsActive)
{
Client.SetPreeditText(null);
}
IsComposing = true;
}
public void HandleCompositionEnd()
{
//Cleanup composition state.
IsComposing = false;
Composition = null;
if (IsActive)
{
Client.SetPreeditText(null);
}
}
public void HandleComposition(IntPtr wParam, IntPtr lParam, uint timestamp)
{
if (_ignoreComposition)
{
_ignoreComposition = false;
return;
}
var flags = (GCS)ToInt32(lParam);
if ((flags & GCS.GCS_RESULTSTR) != 0)
{
var resultString = GetCompositionString(GCS.GCS_RESULTSTR);
if (_parent != null && !string.IsNullOrEmpty(resultString))
{
Composition = null;
if (IsActive)
{
Client.SetPreeditText(null);
}
var e = new RawTextInputEventArgs(WindowsKeyboardDevice.Instance, timestamp, _parent.Owner, resultString);
if (_parent.Input != null)
{
_parent.Input(e);
_parent._ignoreWmChar = true;
}
}
}
if ((flags & GCS.GCS_COMPSTR) != 0)
{
var compositionString = GetCompositionString(GCS.GCS_COMPSTR);
CompositionChanged(compositionString);
}
}
private static int ToInt32(IntPtr ptr)
{
if (IntPtr.Size == 4)
return ptr.ToInt32();
return (int)(ptr.ToInt64() & 0xffffffff);
}
~Imm32InputMethod() ~Imm32InputMethod()
{ {
_caretManager.TryDestroy(); _caretManager.TryDestroy();

56
src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

@ -87,7 +87,7 @@ namespace Avalonia.Win32
{ {
// The first and foremost thing to do - notify the TopLevel // The first and foremost thing to do - notify the TopLevel
Closed?.Invoke(); Closed?.Invoke();
if (UiaCoreTypesApi.IsNetComInteropAvailable) if (UiaCoreTypesApi.IsNetComInteropAvailable)
{ {
UiaCoreProviderApi.UiaReturnRawElementProvider(_hwnd, IntPtr.Zero, IntPtr.Zero, null); UiaCoreProviderApi.UiaReturnRawElementProvider(_hwnd, IntPtr.Zero, IntPtr.Zero, null);
@ -98,7 +98,7 @@ namespace Avalonia.Win32
{ {
Imm32InputMethod.Current.ClearLanguageAndWindow(); Imm32InputMethod.Current.ClearLanguageAndWindow();
} }
// Cleanup render targets // Cleanup render targets
(_gl as IDisposable)?.Dispose(); (_gl as IDisposable)?.Dispose();
@ -724,26 +724,7 @@ namespace Avalonia.Win32
} }
case WindowsMessage.WM_IME_COMPOSITION: case WindowsMessage.WM_IME_COMPOSITION:
{ {
var flags = (GCS)ToInt32(lParam); Imm32InputMethod.Current.HandleComposition(wParam, lParam, timestamp);
if ((flags & GCS.GCS_COMPSTR) != 0)
{
var currentComposition = Imm32InputMethod.Current.GetCompositionString(GCS.GCS_COMPSTR);
Imm32InputMethod.Current.CompositionChanged(currentComposition);
}
if ((flags & GCS.GCS_RESULTSTR) != 0)
{
var result = Imm32InputMethod.Current.GetCompositionString(GCS.GCS_RESULTSTR);
if (!string.IsNullOrEmpty(result))
{
Imm32InputMethod.Current.Composition = result;
_ignoreWmChar = true;
}
}
break; break;
} }
@ -757,35 +738,16 @@ namespace Avalonia.Win32
case WindowsMessage.WM_IME_NOTIFY: case WindowsMessage.WM_IME_NOTIFY:
break; break;
case WindowsMessage.WM_IME_STARTCOMPOSITION: case WindowsMessage.WM_IME_STARTCOMPOSITION:
Imm32InputMethod.Current.Composition = null;
if (Imm32InputMethod.Current.IsActive)
{ {
Imm32InputMethod.Current.Client.SetPreeditText(null); Imm32InputMethod.Current.HandleCompositionStart();
}
Imm32InputMethod.Current.IsComposing = true; return IntPtr.Zero;
return IntPtr.Zero; }
case WindowsMessage.WM_IME_ENDCOMPOSITION: case WindowsMessage.WM_IME_ENDCOMPOSITION:
{ {
var currentComposition = Imm32InputMethod.Current.Composition; Imm32InputMethod.Current.HandleCompositionEnd();
//In case composition has not been comitted yet we need to do that here.
if (!string.IsNullOrEmpty(currentComposition))
{
e = new RawTextInputEventArgs(WindowsKeyboardDevice.Instance, timestamp, Owner, currentComposition);
}
//Cleanup composition state.
Imm32InputMethod.Current.IsComposing = false;
Imm32InputMethod.Current.Composition = null;
if (Imm32InputMethod.Current.IsActive) return IntPtr.Zero;
{
Imm32InputMethod.Current.Client.SetPreeditText(null);
}
break;
} }
case WindowsMessage.WM_GETOBJECT: case WindowsMessage.WM_GETOBJECT:
if ((long)lParam == uiaRootObjectId && UiaCoreTypesApi.IsNetComInteropAvailable && _owner is Control control) if ((long)lParam == uiaRootObjectId && UiaCoreTypesApi.IsNetComInteropAvailable && _owner is Control control)
@ -830,7 +792,7 @@ namespace Avalonia.Win32
return IntPtr.Zero; return IntPtr.Zero;
} }
} }
return DefWindowProc(hWnd, msg, wParam, lParam); return DefWindowProc(hWnd, msg, wParam, lParam);
} }

4
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -97,7 +97,7 @@ namespace Avalonia.Win32
private bool _shown; private bool _shown;
private bool _hiddenWindowIsParent; private bool _hiddenWindowIsParent;
private uint _langid; private uint _langid;
private bool _ignoreWmChar; internal bool _ignoreWmChar;
private WindowTransparencyLevel _transparencyLevel; private WindowTransparencyLevel _transparencyLevel;
private const int MaxPointerHistorySize = 512; private const int MaxPointerHistorySize = 512;
@ -186,7 +186,7 @@ namespace Avalonia.Win32
s_instances.Add(this); s_instances.Add(this);
} }
private IInputRoot Owner internal IInputRoot Owner
=> _owner ?? throw new InvalidOperationException($"{nameof(SetInputRoot)} must have been called"); => _owner ?? throw new InvalidOperationException($"{nameof(SetInputRoot)} must have been called");
public Action? Activated { get; set; } public Action? Activated { get; set; }

24
tests/Avalonia.Base.UnitTests/Data/ReflectionClrPropertyInfoTests.cs

@ -0,0 +1,24 @@
using Avalonia.Data.Core;
using Xunit;
namespace Avalonia.Base.UnitTests.Data;
public class ReflectionClrPropertyInfoTests
{
public class TestClass
{
public string Test { get; set; }
}
[Fact]
public void Can_Compile()
{
var propertyInfo = new ReflectionClrPropertyInfo(
typeof(TestClass).GetProperty(nameof(TestClass.Test))!);
var target = new TestClass();
const string result = "qwerty";
propertyInfo.Set(target, result);
Assert.Equal(result, target.Test);
Assert.Equal(result, (string)propertyInfo.Get(target));
}
}

205
tests/Avalonia.Base.UnitTests/Input/AccessKeyHandlerTests.cs

@ -0,0 +1,205 @@
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.UnitTests;
using Moq;
using Xunit;
namespace Avalonia.Base.UnitTests.Input
{
public class AccessKeyHandlerTests
{
[Fact]
public void Should_Raise_Key_Events_For_Unregistered_Access_Key()
{
var root = new TestRoot();
var target = new AccessKeyHandler();
var events = new List<string>();
target.SetOwner(root);
root.KeyDown += (s, e) => events.Add($"KeyDown {e.Key}");
root.KeyUp += (s, e) => events.Add($"KeyUp {e.Key}");
KeyDown(root, Key.LeftAlt);
KeyDown(root, Key.A, KeyModifiers.Alt);
KeyUp(root, Key.A, KeyModifiers.Alt);
KeyUp(root, Key.LeftAlt);
Assert.Equal(new[]
{
"KeyDown LeftAlt",
"KeyDown A",
"KeyUp A",
"KeyUp LeftAlt",
}, events);
}
[Fact]
public void Should_Raise_Key_Events_For_Unregistered_Access_Key_With_MainMenu()
{
var root = new TestRoot();
var target = new AccessKeyHandler();
var menu = Mock.Of<IMainMenu>();
var events = new List<string>();
target.SetOwner(root);
target.MainMenu = menu;
root.KeyDown += (s, e) => events.Add($"KeyDown {e.Key}");
root.KeyUp += (s, e) => events.Add($"KeyUp {e.Key}");
KeyDown(root, Key.LeftAlt);
KeyDown(root, Key.A, KeyModifiers.Alt);
KeyUp(root, Key.A, KeyModifiers.Alt);
KeyUp(root, Key.LeftAlt);
Assert.Equal(new[]
{
"KeyDown LeftAlt",
"KeyDown A",
"KeyUp A",
"KeyUp LeftAlt",
}, events);
}
[Fact]
public void Should_Raise_Key_Events_For_Alt_Key()
{
var root = new TestRoot();
var target = new AccessKeyHandler();
var events = new List<string>();
target.SetOwner(root);
root.KeyDown += (s, e) => events.Add($"KeyDown {e.Key}");
root.KeyUp += (s, e) => events.Add($"KeyUp {e.Key}");
KeyDown(root, Key.LeftAlt);
KeyUp(root, Key.LeftAlt);
Assert.Equal(new[]
{
"KeyDown LeftAlt",
"KeyUp LeftAlt",
}, events);
}
[Fact]
public void Should_Raise_Key_Events_For_Alt_Key_With_MainMenu()
{
var root = new TestRoot();
var target = new AccessKeyHandler();
var menu = new Mock<IMainMenu>();
var events = new List<string>();
menu.SetupAllProperties();
menu.Setup(x => x.Open()).Callback(() => menu.Setup(x => x.IsOpen).Returns(true));
target.SetOwner(root);
target.MainMenu = menu.Object;
root.KeyDown += (s, e) => events.Add($"KeyDown {e.Key}");
root.KeyUp += (s, e) => events.Add($"KeyUp {e.Key}");
KeyDown(root, Key.LeftAlt);
KeyUp(root, Key.LeftAlt);
KeyDown(root, Key.LeftAlt);
KeyUp(root, Key.LeftAlt);
Assert.Equal(new[]
{
"KeyDown LeftAlt",
"KeyUp LeftAlt",
"KeyDown LeftAlt",
"KeyUp LeftAlt",
}, events);
}
[Fact]
public void Should_Raise_Key_Events_For_Registered_Access_Key()
{
var button = new Button();
var root = new TestRoot(button);
var target = new AccessKeyHandler();
var events = new List<string>();
target.SetOwner(root);
target.Register('A', button);
root.KeyDown += (s, e) => events.Add($"KeyDown {e.Key}");
root.KeyUp += (s, e) => events.Add($"KeyUp {e.Key}");
KeyDown(root, Key.LeftAlt);
KeyDown(root, Key.A, KeyModifiers.Alt);
KeyUp(root, Key.A, KeyModifiers.Alt);
KeyUp(root, Key.LeftAlt);
// This differs from WPF which doesn't raise the `A` key event, but matches UWP.
Assert.Equal(new[]
{
"KeyDown LeftAlt",
"KeyDown A",
"KeyUp A",
"KeyUp LeftAlt",
}, events);
}
[Fact]
public void Should_Raise_AccessKeyPressed_For_Registered_Access_Key()
{
var button = new Button();
var root = new TestRoot(button);
var target = new AccessKeyHandler();
var raised = 0;
target.SetOwner(root);
target.Register('A', button);
button.AddHandler(AccessKeyHandler.AccessKeyPressedEvent, (s, e) => ++raised);
KeyDown(root, Key.LeftAlt);
Assert.Equal(0, raised);
KeyDown(root, Key.A, KeyModifiers.Alt);
Assert.Equal(1, raised);
KeyUp(root, Key.A, KeyModifiers.Alt);
KeyUp(root, Key.LeftAlt);
Assert.Equal(1, raised);
}
[Fact]
public void Should_Open_MainMenu_On_Alt_KeyUp()
{
var root = new TestRoot();
var target = new AccessKeyHandler();
var menu = new Mock<IMainMenu>();
target.SetOwner(root);
target.MainMenu = menu.Object;
KeyDown(root, Key.LeftAlt);
menu.Verify(x => x.Open(), Times.Never);
KeyUp(root, Key.LeftAlt);
menu.Verify(x => x.Open(), Times.Once);
}
private static void KeyDown(IInputElement target, Key key, KeyModifiers modifiers = KeyModifiers.None)
{
target.RaiseEvent(new KeyEventArgs
{
RoutedEvent = InputElement.KeyDownEvent,
Key = key,
KeyModifiers = modifiers,
});
}
private static void KeyUp(IInputElement target, Key key, KeyModifiers modifiers = KeyModifiers.None)
{
target.RaiseEvent(new KeyEventArgs
{
RoutedEvent = InputElement.KeyUpEvent,
Key = key,
KeyModifiers = modifiers,
});
}
}
}

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

@ -1,4 +1,5 @@
using Avalonia.Controls; using System.Collections.ObjectModel;
using Avalonia.Controls;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
@ -28,6 +29,40 @@ public class RenderingTests
}, },
SizeToContent = SizeToContent.WidthAndHeight SizeToContent = SizeToContent.WidthAndHeight
}; };
window.Show();
var frame = window.CaptureRenderedFrame();
Assert.NotNull(frame);
}
#if NUNIT
[AvaloniaTest, Timeout(10000)]
#elif XUNIT
[AvaloniaFact(Timeout = 10000)]
#endif
public void Should_Not_Hang_With_Non_Trivial_Layout()
{
var window = new Window
{
Content = new ContentControl
{
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch,
Padding = new Thickness(1),
Content = new ListBox
{
ItemsSource = new ObservableCollection<string>()
{
"Test 1",
"Test 2"
}
}
},
SizeToContent = SizeToContent.WidthAndHeight
};
window.Show(); window.Show();
var frame = window.CaptureRenderedFrame(); var frame = window.CaptureRenderedFrame();

16
tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs

@ -123,5 +123,21 @@ namespace Avalonia.Skia.UnitTests.Media
} }
} }
} }
[Fact]
public void Should_Return_False_For_Invalid_DefaultFontFamily()
{
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface.With(fontManagerImpl: new FontManagerImpl())))
{
using (AvaloniaLocator.EnterScope())
{
AvaloniaLocator.CurrentMutable.BindToSelf(new FontManagerOptions { DefaultFamilyName = "avares://resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#Unknown" });
var result = FontManager.Current.TryGetGlyphTypeface(Typeface.Default, out _);
Assert.False(result);
}
}
}
} }
} }

Loading…
Cancel
Save