Browse Source

Merge branch 'master' into fix-headless-layout

pull/11763/head
Jan Kristian Bjerke 3 years ago
committed by GitHub
parent
commit
579d5c6707
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      src/Avalonia.Base/Input/TextInput/InputMethodManager.cs
  2. 7
      src/Avalonia.Base/Media/FontManager.cs
  3. 35
      src/Skia/Avalonia.Skia/GlyphRunImpl.cs
  4. 104
      src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs
  5. 56
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  6. 4
      src/Windows/Avalonia.Win32/WindowImpl.cs
  7. 16
      tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs

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

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

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

@ -1,5 +1,6 @@
using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
@ -14,7 +15,7 @@ namespace Avalonia.Skia
private readonly ushort[] _glyphIndices;
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,
IReadOnlyList<GlyphInfo> glyphInfos, Point baselineOrigin)
@ -100,32 +101,28 @@ namespace Avalonia.Skia
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.Subpixel = edging == SKFontEdging.SubpixelAntialias;
font.Edging = edging;
font.Size = (float)FontRenderingEmSize;
font.Hinting = SKFontHinting.Full;
font.Subpixel = edging == SKFontEdging.SubpixelAntialias;
font.Edging = edging;
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.SetGlyphs(_glyphIndices);
runBuffer.SetPositions(_glyphPositions);
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()

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

@ -1,6 +1,8 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Input.TextInput;
using Avalonia.Threading;
@ -22,6 +24,8 @@ namespace Avalonia.Win32.Input
private ushort _langId;
private const int CaretMargin = 1;
private bool _ignoreComposition;
public ITextInputMethodClient? Client { get; private set; }
[MemberNotNullWhen(true, nameof(Client))]
@ -121,19 +125,35 @@ namespace Avalonia.Win32.Input
{
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);
ImmReleaseContext(Hwnd, himc);
IsComposing = false;
}
ImmReleaseContext(Hwnd, himc);
Composition = null;
}
});
}
public void SetClient(ITextInputMethodClient? client)
{
if(Client != null)
{
Composition = null;
Client.SetPreeditText(null);
}
Client = client;
Dispatcher.UIThread.Post(() =>
@ -297,6 +317,82 @@ namespace Avalonia.Win32.Input
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()
{
_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
Closed?.Invoke();
if (UiaCoreTypesApi.IsNetComInteropAvailable)
{
UiaCoreProviderApi.UiaReturnRawElementProvider(_hwnd, IntPtr.Zero, IntPtr.Zero, null);
@ -98,7 +98,7 @@ namespace Avalonia.Win32
{
Imm32InputMethod.Current.ClearLanguageAndWindow();
}
// Cleanup render targets
(_gl as IDisposable)?.Dispose();
@ -724,26 +724,7 @@ namespace Avalonia.Win32
}
case WindowsMessage.WM_IME_COMPOSITION:
{
var flags = (GCS)ToInt32(lParam);
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;
}
}
Imm32InputMethod.Current.HandleComposition(wParam, lParam, timestamp);
break;
}
@ -757,35 +738,16 @@ namespace Avalonia.Win32
case WindowsMessage.WM_IME_NOTIFY:
break;
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:
{
var currentComposition = Imm32InputMethod.Current.Composition;
//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;
Imm32InputMethod.Current.HandleCompositionEnd();
if (Imm32InputMethod.Current.IsActive)
{
Imm32InputMethod.Current.Client.SetPreeditText(null);
}
break;
return IntPtr.Zero;
}
case WindowsMessage.WM_GETOBJECT:
if ((long)lParam == uiaRootObjectId && UiaCoreTypesApi.IsNetComInteropAvailable && _owner is Control control)
@ -830,7 +792,7 @@ namespace Avalonia.Win32
return IntPtr.Zero;
}
}
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 _hiddenWindowIsParent;
private uint _langid;
private bool _ignoreWmChar;
internal bool _ignoreWmChar;
private WindowTransparencyLevel _transparencyLevel;
private const int MaxPointerHistorySize = 512;
@ -186,7 +186,7 @@ namespace Avalonia.Win32
s_instances.Add(this);
}
private IInputRoot Owner
internal IInputRoot Owner
=> _owner ?? throw new InvalidOperationException($"{nameof(SetInputRoot)} must have been called");
public Action? Activated { get; set; }

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