A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

373 lines
10 KiB

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Media;
using Avalonia.Media.Fonts;
using Avalonia.Media.TextFormatting;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Platform;
using Avalonia.Platform.Storage;
using Avalonia.Platform.Storage.FileIO;
using Avalonia.Utilities;
namespace Avalonia.Headless
{
internal class HeadlessClipboardStub : IClipboard
{
private string? _text;
private IDataObject? _data;
public Task<string?> GetTextAsync()
{
return Task.Run(() => _text);
}
public Task SetTextAsync(string? text)
{
return Task.Run(() => _text = text);
}
public Task ClearAsync()
{
return Task.Run(() => _text = null);
}
public Task SetDataObjectAsync(IDataObject data)
{
return Task.Run(() => _data = data);
}
public Task<string[]> GetFormatsAsync()
{
return Task.Run(() =>
{
if (_data is not null)
{
return _data.GetDataFormats().ToArray();
}
if (_text is not null)
{
return new[] { DataFormats.Text };
}
return Array.Empty<string>();
});
}
public async Task<object?> GetDataAsync(string format)
{
return await Task.Run(() => _data);
}
}
internal class HeadlessCursorFactoryStub : ICursorFactory
{
public ICursorImpl GetCursor(StandardCursorType cursorType) => new CursorStub();
public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) => new CursorStub();
private class CursorStub : ICursorImpl
{
public void Dispose() { }
}
}
internal class HeadlessGlyphTypefaceImpl : IGlyphTypeface
{
public HeadlessGlyphTypefaceImpl(string familyName, FontStyle style, FontWeight weight, FontStretch stretch)
{
FamilyName = familyName;
Style = style;
Weight = weight;
Stretch = stretch;
}
public FontMetrics Metrics => new FontMetrics
{
DesignEmHeight = 10,
Ascent = 2,
Descent = 10,
IsFixedPitch = true,
LineGap = 0,
UnderlinePosition = 2,
UnderlineThickness = 1,
StrikethroughPosition = 2,
StrikethroughThickness = 1
};
public int GlyphCount => 1337;
public FontSimulations FontSimulations => FontSimulations.None;
public string FamilyName { get; }
public FontWeight Weight { get; }
public FontStyle Style { get; }
public FontStretch Stretch { get; }
public void Dispose()
{
}
public ushort GetGlyph(uint codepoint)
{
return (ushort)codepoint;
}
public bool TryGetGlyph(uint codepoint, out ushort glyph)
{
glyph = 8;
return true;
}
public int GetGlyphAdvance(ushort glyph)
{
return 8;
}
public int[] GetGlyphAdvances(ReadOnlySpan<ushort> glyphs)
{
var advances = new int[glyphs.Length];
for (var i = 0; i < advances.Length; i++)
{
advances[i] = 8;
}
return advances;
}
public ushort[] GetGlyphs(ReadOnlySpan<uint> codepoints)
{
return codepoints.ToArray().Select(x => (ushort)x).ToArray();
}
public bool TryGetTable(uint tag, out byte[] table)
{
table = null!;
return false;
}
public bool TryGetGlyphMetrics(ushort glyph, out GlyphMetrics metrics)
{
metrics = new GlyphMetrics
{
Width = 10,
Height = 10
};
return true;
}
}
internal class HeadlessTextShaperStub : ITextShaperImpl
{
public ShapedBuffer ShapeText(ReadOnlyMemory<char> text, TextShaperOptions options)
{
var typeface = options.Typeface;
var fontRenderingEmSize = options.FontRenderingEmSize;
var bidiLevel = options.BidiLevel;
var shapedBuffer = new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel);
var textSpan = text.Span;
var textStartIndex = TextTestHelper.GetStartCharIndex(text);
for (var i = 0; i < shapedBuffer.Length;)
{
var glyphCluster = i + textStartIndex;
var codepoint = Codepoint.ReadAt(textSpan, i, out var count);
var glyphIndex = typeface.GetGlyph(codepoint);
for (var j = 0; j < count; ++j)
{
shapedBuffer[i + j] = new GlyphInfo(glyphIndex, glyphCluster, 10);
}
i += count;
}
return shapedBuffer;
}
}
internal class HeadlessFontManagerStub : IFontManagerImpl
{
private readonly string _defaultFamilyName;
public HeadlessFontManagerStub(string defaultFamilyName = "Default")
{
_defaultFamilyName = defaultFamilyName;
}
public int TryCreateGlyphTypefaceCount { get; private set; }
public string GetDefaultFontFamilyName()
{
return _defaultFamilyName;
}
string[] IFontManagerImpl.GetInstalledFontFamilyNames(bool checkForUpdates)
{
return new[] { _defaultFamilyName };
}
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight,
FontStretch fontStretch,
CultureInfo? culture, out Typeface fontKey)
{
fontKey = new Typeface(_defaultFamilyName);
return false;
}
public virtual bool TryCreateGlyphTypeface(string familyName, FontStyle style, FontWeight weight,
FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface)
{
glyphTypeface = null;
TryCreateGlyphTypefaceCount++;
if (familyName == "Unknown")
{
return false;
}
glyphTypeface = new HeadlessGlyphTypefaceImpl(familyName, style, weight, stretch);
return true;
}
public virtual bool TryCreateGlyphTypeface(Stream stream, FontSimulations fontSimulations, out IGlyphTypeface glyphTypeface)
{
glyphTypeface = new HeadlessGlyphTypefaceImpl(FontFamily.DefaultFontFamilyName, FontStyle.Normal, FontWeight.Normal, FontStretch.Normal);
TryCreateGlyphTypefaceCount++;
return true;
}
}
internal class HeadlessFontManagerWithMultipleSystemFontsStub : IFontManagerImpl
{
private readonly string[] _installedFontFamilyNames;
private readonly string _defaultFamilyName;
public HeadlessFontManagerWithMultipleSystemFontsStub(
string[] installedFontFamilyNames,
string defaultFamilyName = "Default")
{
_installedFontFamilyNames = installedFontFamilyNames;
_defaultFamilyName = defaultFamilyName;
}
public int TryCreateGlyphTypefaceCount { get; private set; }
public string GetDefaultFontFamilyName()
{
return _defaultFamilyName;
}
string[] IFontManagerImpl.GetInstalledFontFamilyNames(bool checkForUpdates)
{
return _installedFontFamilyNames;
}
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight,
FontStretch fontStretch,
CultureInfo? culture, out Typeface fontKey)
{
fontKey = new Typeface(_defaultFamilyName);
return false;
}
public virtual bool TryCreateGlyphTypeface(string familyName, FontStyle style, FontWeight weight,
FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface)
{
glyphTypeface = null;
TryCreateGlyphTypefaceCount++;
if (familyName == "Unknown")
{
return false;
}
glyphTypeface = new HeadlessGlyphTypefaceImpl(familyName, style, weight, stretch);
return true;
}
public virtual bool TryCreateGlyphTypeface(Stream stream, FontSimulations fontSimulations, out IGlyphTypeface glyphTypeface)
{
glyphTypeface = new HeadlessGlyphTypefaceImpl(FontFamily.DefaultFontFamilyName, FontStyle.Normal, FontWeight.Normal, FontStretch.Normal);
return true;
}
}
internal class HeadlessIconLoaderStub : IPlatformIconLoader
{
private class IconStub : IWindowIconImpl
{
public void Save(Stream outputStream)
{
}
}
public IWindowIconImpl LoadIcon(string fileName)
{
return new IconStub();
}
public IWindowIconImpl LoadIcon(Stream stream)
{
return new IconStub();
}
public IWindowIconImpl LoadIcon(IBitmapImpl bitmap)
{
return new IconStub();
}
}
internal class HeadlessScreensStub : ScreensBase<int, PlatformScreen>
{
protected override IReadOnlyList<int> GetAllScreenKeys() => new[] { 1 };
protected override PlatformScreen CreateScreenFromKey(int key) => new PlatformScreenStub(key);
private class PlatformScreenStub : PlatformScreen
{
public PlatformScreenStub(int key) : base(new PlatformHandle((nint)key, nameof(HeadlessScreensStub)))
{
Scaling = 1;
Bounds = WorkingArea = new PixelRect(0, 0, 1920, 1280);
IsPrimary = true;
}
}
}
internal static class TextTestHelper
{
public static int GetStartCharIndex(ReadOnlyMemory<char> text)
{
if (!MemoryMarshal.TryGetString(text, out _, out var start, out _))
throw new InvalidOperationException("text memory should have been a string");
return start;
}
}
}